mirror of
https://github.com/heyman/heynote.git
synced 2024-11-07 16:44:14 +01:00
Initial import
This commit is contained in:
commit
254ca836ef
3478
heynote-codemirror/package-lock.json
generated
Normal file
3478
heynote-codemirror/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
heynote-codemirror/package.json
Normal file
38
heynote-codemirror/package.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "heynote-codemirror",
|
||||
"type": "module",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "rollup -c",
|
||||
"watch": "rollup -c -w",
|
||||
"build_grammar": "lezer-generator src/lang-heynote/heynote.grammar -o src/lang-heynote/heynote.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@codemirror/commands": "^6.1.2",
|
||||
"@codemirror/lang-html": "^6.4.0",
|
||||
"@codemirror/lang-java": "^6.0.1",
|
||||
"@codemirror/lang-javascript": "^6.1.2",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-lezer": "^6.0.1",
|
||||
"@codemirror/lang-markdown": "^6.0.5",
|
||||
"@codemirror/lang-php": "^6.0.1",
|
||||
"@codemirror/lang-python": "^6.1.1",
|
||||
"@codemirror/lang-sql": "^6.3.3",
|
||||
"@codemirror/rangeset": "^0.19.9",
|
||||
"@codemirror/search": "^6.2.3",
|
||||
"@lezer/generator": "^1.1.3",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"i": "^0.3.7",
|
||||
"npm": "^9.2.0",
|
||||
"rollup": "^3.8.1",
|
||||
"rollup-plugin-typescript2": "^0.34.1",
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
}
|
30
heynote-codemirror/rollup.config.mjs
Normal file
30
heynote-codemirror/rollup.config.mjs
Normal file
@ -0,0 +1,30 @@
|
||||
//import typescript from 'rollup-plugin-typescript2'
|
||||
import { nodeResolve } from "@rollup/plugin-node-resolve"
|
||||
//import { lezer } from "@lezer/generator/rollup"
|
||||
|
||||
|
||||
export default {
|
||||
input: "./src/editor.js",
|
||||
output: {
|
||||
file: "./src/editor.bundle.js",
|
||||
format: "iife",
|
||||
//globals: {
|
||||
// //
|
||||
//},
|
||||
},
|
||||
plugins: [
|
||||
// typescript({
|
||||
// check: false,
|
||||
// tsconfigOverride: {
|
||||
// compilerOptions: {
|
||||
// lib: ['es5', 'es6'],
|
||||
// sourceMap: true,
|
||||
// target: 'es6',
|
||||
// strict: false
|
||||
// }
|
||||
// }
|
||||
// }),
|
||||
nodeResolve(),
|
||||
//lezer(),
|
||||
]
|
||||
}
|
5
heynote-codemirror/src/annotation.js
Normal file
5
heynote-codemirror/src/annotation.js
Normal file
@ -0,0 +1,5 @@
|
||||
import { Annotation } from "@codemirror/state"
|
||||
|
||||
export const heynoteEvent = Annotation.define()
|
||||
export const INITIAL_DATA = "initial-data"
|
||||
|
85
heynote-codemirror/src/editor.js
Normal file
85
heynote-codemirror/src/editor.js
Normal file
@ -0,0 +1,85 @@
|
||||
import {Annotation, EditorState, Compartment} from "@codemirror/state"
|
||||
import {EditorView, keymap, drawSelection} from "@codemirror/view"
|
||||
|
||||
import {indentWithTab, insertTab, indentLess, indentMore} from "@codemirror/commands"
|
||||
import {nord} from "./theme/nord.mjs"
|
||||
import initialData from "./fixture.js"
|
||||
import { customSetup } from "./setup.js"
|
||||
import { heynoteLang } from "./lang-heynote/parser.js"
|
||||
import { noteBlockExtension } from "./note-block.js"
|
||||
import { heynoteEvent, INITIAL_DATA } from "./annotation.js"
|
||||
|
||||
|
||||
let state = EditorState.create({
|
||||
extensions: [
|
||||
/*keymap.of([
|
||||
{
|
||||
key: "Shift-Tab",
|
||||
preventDefault: true,
|
||||
run: () => {
|
||||
console.log("debug:", syntaxTree(editor.state).toString())
|
||||
},
|
||||
},
|
||||
]),*/
|
||||
customSetup,
|
||||
//minimalSetup,
|
||||
|
||||
keymap.of([
|
||||
{
|
||||
key: 'Tab',
|
||||
preventDefault: true,
|
||||
//run: insertTab,
|
||||
run: indentMore,
|
||||
},
|
||||
{
|
||||
key: 'Shift-Tab',
|
||||
preventDefault: true,
|
||||
run: indentLess,
|
||||
},
|
||||
]),
|
||||
nord,
|
||||
|
||||
heynoteLang(),
|
||||
noteBlockExtension(),
|
||||
|
||||
// set cursor blink rate to 1 second
|
||||
drawSelection({cursorBlinkRate:1000}),
|
||||
],
|
||||
})
|
||||
|
||||
let editor = new EditorView({
|
||||
state,
|
||||
parent: document.getElementById("editor"),
|
||||
})
|
||||
|
||||
// set initial data
|
||||
editor.update([
|
||||
editor.state.update({
|
||||
changes:{
|
||||
from: 0,
|
||||
to: editor.state.length,
|
||||
insert: initialData,
|
||||
},
|
||||
annotations: heynoteEvent.of(INITIAL_DATA),
|
||||
})
|
||||
])
|
||||
|
||||
/*
|
||||
// render syntax tree
|
||||
setTimeout(() => {
|
||||
function render(tree) {
|
||||
let lists = ''
|
||||
tree.iterate({
|
||||
enter(type) {
|
||||
lists += `<ul><li>${type.name} (${type.from},${type.to})`
|
||||
},
|
||||
leave() {
|
||||
lists += '</ul>'
|
||||
}
|
||||
})
|
||||
return lists
|
||||
}
|
||||
let html = render(syntaxTree(editor.state))
|
||||
document.getElementById("syntaxTree").innerHTML = html;
|
||||
}, 1000)
|
||||
*/
|
84
heynote-codemirror/src/fixture.js
Normal file
84
heynote-codemirror/src/fixture.js
Normal file
@ -0,0 +1,84 @@
|
||||
/*export default `∞∞∞text
|
||||
kuk∞∞∞javascript
|
||||
oj∞∞∞prutt
|
||||
hej∞∞∞python
|
||||
f = lambda: 2 +1`;*/
|
||||
|
||||
export default `∞∞∞python
|
||||
# hmm
|
||||
def my_func():
|
||||
print("hejsan")
|
||||
|
||||
|
||||
import {basicSetup} from "codemirror"
|
||||
import {EditorView, keymap} from "@codemirror/view"
|
||||
import {javascript} from "@codemirror/lang-javascript"
|
||||
import {indentWithTab, insertTab, indentLess, indentMore} from "@codemirror/commands"
|
||||
import {nord} from "./nord.mjs"∞∞∞javascript
|
||||
let editor = new EditorView({
|
||||
//extensions: [basicSetup, javascript()],
|
||||
extensions: [
|
||||
basicSetup,
|
||||
javascript(),
|
||||
//keymap.of([indentWithTab]),
|
||||
keymap.of([
|
||||
{
|
||||
key: 'Tab',
|
||||
preventDefault: true,
|
||||
//run: insertTab,
|
||||
run: indentMore,
|
||||
},
|
||||
{
|
||||
key: 'Shift-Tab',
|
||||
preventDefault: true,
|
||||
run: indentLess,
|
||||
},
|
||||
]),
|
||||
nord,
|
||||
],
|
||||
parent: document.getElementById("editor"),
|
||||
})
|
||||
∞∞∞json
|
||||
{
|
||||
"name": "heynote-codemirror",
|
||||
"type": "module",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "rollup -c"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@codemirror/commands": "^6.1.2",
|
||||
"@codemirror/lang-javascript": "^6.1.2",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-python": "^6.1.1",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"i": "^0.3.7",
|
||||
"npm": "^9.2.0",
|
||||
"rollup": "^3.8.1",
|
||||
"rollup-plugin-typescript2": "^0.34.1",
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
}
|
||||
∞∞∞html
|
||||
<html>
|
||||
<head>
|
||||
<title>Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test</h1>
|
||||
<script>
|
||||
console.log("hej")
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
∞∞∞sql
|
||||
SELECT * FROM table WHERE id = 1;
|
||||
|
||||
`;
|
15
heynote-codemirror/src/index.html
Normal file
15
heynote-codemirror/src/index.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Heynote</title>
|
||||
<link rel=stylesheet href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="editor"></div>
|
||||
<!--<div id="syntaxTree"></div>-->
|
||||
<script src="editor.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
31
heynote-codemirror/src/lang-heynote/external-tokens.js
Normal file
31
heynote-codemirror/src/lang-heynote/external-tokens.js
Normal file
@ -0,0 +1,31 @@
|
||||
import { ExternalTokenizer } from '@lezer/lr'
|
||||
import { NoteContent } from "./heynote.terms.js"
|
||||
|
||||
const EOF = -1;
|
||||
|
||||
export const noteContent = new ExternalTokenizer((input) => {
|
||||
let current = input.peek(0);
|
||||
let next = input.peek(1);
|
||||
|
||||
if (current === EOF) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
let potentialLang = "";
|
||||
for (let i=0; i<15; i++) {
|
||||
potentialLang += String.fromCharCode(input.peek(i));
|
||||
}
|
||||
if (potentialLang.match(/^∞∞∞(text|javascript|json|python|html|sql|markdown|java|lezer|php)\n/g)) {
|
||||
input.acceptToken(NoteContent);
|
||||
return;
|
||||
}
|
||||
if (next === EOF) {
|
||||
input.acceptToken(NoteContent, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
current = input.advance();
|
||||
next = input.peek(1);
|
||||
}
|
||||
});
|
21
heynote-codemirror/src/lang-heynote/heynote.grammar
Normal file
21
heynote-codemirror/src/lang-heynote/heynote.grammar
Normal file
@ -0,0 +1,21 @@
|
||||
@top Document { Note* }
|
||||
|
||||
Note {
|
||||
NoteDelimiter NoteContent
|
||||
}
|
||||
|
||||
NoteDelimiter {
|
||||
noteDelimiterMark NoteLanguage noteDelimiterEnter
|
||||
}
|
||||
|
||||
|
||||
@tokens {
|
||||
noteDelimiterMark { "∞∞∞" }
|
||||
NoteLanguage { "text" | "javascript" | "json" | "python" | "html" | "sql" | "markdown" | "java" | "lezer" | "php" }
|
||||
noteDelimiterEnter { "\n" }
|
||||
//NoteContent { String }
|
||||
//String { (![∞])+ }
|
||||
}
|
||||
|
||||
@external tokens noteContent from "./external-tokens.js" { NoteContent }
|
||||
|
17
heynote-codemirror/src/lang-heynote/heynote.js
Normal file
17
heynote-codemirror/src/lang-heynote/heynote.js
Normal file
@ -0,0 +1,17 @@
|
||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
import {LRParser} from "@lezer/lr"
|
||||
import {noteContent} from "./external-tokens.js"
|
||||
export const parser = LRParser.deserialize({
|
||||
version: 14,
|
||||
states: "!WQQOPOOOVOPO'#C`O[OQO'#C_OOOO'#Cb'#CbQQOPOOOaOPO,58zOOOO,58y,58yOOOO-E6`-E6`OOOQ1G.f1G.f",
|
||||
stateData: "f~OWPO~OTTO~OPUO~OXWO~O",
|
||||
goto: "fVPPPW[P`TROSTQOSQSORVS",
|
||||
nodeNames: "⚠ NoteContent Document Note NoteDelimiter NoteLanguage",
|
||||
maxTerm: 9,
|
||||
skippedNodes: [0],
|
||||
repeatNodeCount: 1,
|
||||
tokenData: "&e~RXYZn#[#]s#^#_![#`#a#u#a#b$_#d#e%T#g#h%p#h#i%v%&x%&y&S~sOX~~vP#h#iy~|P#a#b!P~!SP#`#a!V~![OT~~!_Q#T#U!e#g#h#i~!hP#j#k!k~!nP#T#U!q~!vPT~#g#h!y~!|P#V#W#P~#SP#f#g#V~#YP#]#^#]~#`P#d#e#c~#fP#h#i!V~#lP#c#d#o~#rP#b#c!V~#xP#X#Y#{~$OP#n#o$R~$UP#X#Y$X~$[P#f#g!V~$bP#T#U$e~$hP#f#g$k~$nP#_#`$q~$tP#W#X$w~$zP#c#d$}~%QP#k#l#o~%WQ#[#]%^#m#n%d~%aP#d#e!V~%gP#h#i%j~%mP#[#]#i~%sP#e#f!P~%yP#X#Y%|~&PP#l#m#c~&VP%&x%&y&Y~&]P%&x%&y&`~&eOW~",
|
||||
tokenizers: [0, noteContent],
|
||||
topRules: {"Document":[0,2]},
|
||||
tokenPrec: 0
|
||||
})
|
7
heynote-codemirror/src/lang-heynote/heynote.terms.js
Normal file
7
heynote-codemirror/src/lang-heynote/heynote.terms.js
Normal file
@ -0,0 +1,7 @@
|
||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
export const
|
||||
NoteContent = 1,
|
||||
Document = 2,
|
||||
Note = 3,
|
||||
NoteDelimiter = 4,
|
||||
NoteLanguage = 5
|
45
heynote-codemirror/src/lang-heynote/nested-parser.js
Normal file
45
heynote-codemirror/src/lang-heynote/nested-parser.js
Normal file
@ -0,0 +1,45 @@
|
||||
import { parseMixed } from "@lezer/common"
|
||||
|
||||
import { jsonLanguage } from "@codemirror/lang-json"
|
||||
import { pythonLanguage } from "@codemirror/lang-python"
|
||||
import { javascriptLanguage } from "@codemirror/lang-javascript"
|
||||
import { htmlLanguage } from "@codemirror/lang-html"
|
||||
import { StandardSQL } from "@codemirror/lang-sql"
|
||||
import { markdownLanguage } from "@codemirror/lang-markdown"
|
||||
import { javaLanguage } from "@codemirror/lang-java"
|
||||
import { lezerLanguage } from "@codemirror/lang-lezer"
|
||||
import { phpLanguage } from "@codemirror/lang-php"
|
||||
|
||||
import { NoteContent, NoteLanguage } from "./heynote.terms.js"
|
||||
|
||||
|
||||
const languageMapping = {
|
||||
"json": jsonLanguage.parser,
|
||||
"javascript": javascriptLanguage.parser,
|
||||
"python": pythonLanguage.parser,
|
||||
"html": htmlLanguage.parser,
|
||||
"sql": StandardSQL.language.parser,
|
||||
"markdown": markdownLanguage.parser,
|
||||
"java": javaLanguage.parser,
|
||||
"lezer": lezerLanguage.parser,
|
||||
"php": phpLanguage.parser,
|
||||
}
|
||||
|
||||
|
||||
export function configureNesting() {
|
||||
return parseMixed((node, input) => {
|
||||
let id = node.type.id
|
||||
if (id == NoteContent) {
|
||||
let noteLang = node.node.parent.firstChild.getChildren(NoteLanguage)[0]
|
||||
let langName = input.read(noteLang?.from, noteLang?.to)
|
||||
//console.log("langName:", langName)
|
||||
if (langName in languageMapping) {
|
||||
//console.log("found parser for language:", langName)
|
||||
return {
|
||||
parser:languageMapping[langName],
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
})
|
||||
}
|
44
heynote-codemirror/src/lang-heynote/parser.js
Normal file
44
heynote-codemirror/src/lang-heynote/parser.js
Normal file
@ -0,0 +1,44 @@
|
||||
import { parser } from "./heynote.js"
|
||||
import { configureNesting } from "./nested-parser.js";
|
||||
|
||||
import {LRLanguage, LanguageSupport, indentNodeProp, foldNodeProp, foldInside, delimitedIndent} from "@codemirror/language"
|
||||
import {styleTags, tags as t} from "@lezer/highlight"
|
||||
|
||||
import { json } from "@codemirror/lang-json"
|
||||
import { javascript } from "@codemirror/lang-javascript"
|
||||
|
||||
|
||||
function foldNode(node) {
|
||||
//console.log("foldNode", node);
|
||||
return {from:node.from, to:node.to-1};
|
||||
//let first = node.firstChild, last = node.lastChild;
|
||||
//return first && first.to < last.from ? { from: first.to, to: last.type.isError ? node.to : last.from } : null;
|
||||
}
|
||||
|
||||
export const HeynoteLanguage = LRLanguage.define({
|
||||
parser: parser.configure({
|
||||
props: [
|
||||
styleTags({
|
||||
NoteDelimiter: t.tagName,
|
||||
}),
|
||||
|
||||
foldNodeProp.add({
|
||||
NoteContent: foldNode,
|
||||
}),
|
||||
],
|
||||
wrap: configureNesting(),
|
||||
}),
|
||||
languageData: {
|
||||
commentTokens: {line: ";"}
|
||||
}
|
||||
})
|
||||
|
||||
export function heynoteLang() {
|
||||
let wrap = configureNesting();
|
||||
let lang = HeynoteLanguage.configure({dialect:"", wrap:wrap});
|
||||
return new LanguageSupport(lang, [json().support])
|
||||
}
|
||||
|
||||
/*export function heynote() {
|
||||
return new LanguageSupport(HeynoteLanguage)
|
||||
}*/
|
178
heynote-codemirror/src/note-block.js
Normal file
178
heynote-codemirror/src/note-block.js
Normal file
@ -0,0 +1,178 @@
|
||||
import { ViewPlugin, EditorView, Decoration, WidgetType } from "@codemirror/view"
|
||||
import { layer, RectangleMarker } from "@codemirror/view"
|
||||
import { EditorState, RangeSetBuilder, StateField } from "@codemirror/state";
|
||||
import { RangeSet } from "@codemirror/rangeset";
|
||||
import { syntaxTree } from "@codemirror/language"
|
||||
import { Note, Document, NoteDelimiter } from "./lang-heynote/heynote.terms.js"
|
||||
import { IterMode } from "@lezer/common";
|
||||
import { INITIAL_DATA } from "./annotation.js";
|
||||
|
||||
class NoteBlockStart extends WidgetType {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
eq(other) {
|
||||
//return other.checked == this.checked
|
||||
return true
|
||||
}
|
||||
toDOM() {
|
||||
let wrap = document.createElement("div")
|
||||
wrap.className = "block-start"
|
||||
//wrap.innerHTML = "<br>"
|
||||
return wrap
|
||||
}
|
||||
ignoreEvent() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
class FirstNoteBlockStart extends WidgetType {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
eq(other) {
|
||||
return true
|
||||
}
|
||||
toDOM() {
|
||||
let wrap = document.createElement("span")
|
||||
wrap.className = "block-start-first"
|
||||
return wrap
|
||||
}
|
||||
ignoreEvent() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const noteBlockWidget = () => {
|
||||
const decorate = (state) => {
|
||||
const widgets = [];
|
||||
|
||||
syntaxTree(state).iterate({
|
||||
enter: (type) => {
|
||||
if (type.name === "NoteDelimiter") {
|
||||
//console.log("found!", type.name, type.from, type.to)
|
||||
let deco = Decoration.replace({
|
||||
widget: type.from === 0 ? new FirstNoteBlockStart() : new NoteBlockStart(),
|
||||
inclusive: false,
|
||||
block: type.from === 0 ? false : true,
|
||||
side: 0,
|
||||
});
|
||||
widgets.push(deco.range(type.from, type.from === 0 ? type.to :type.to-1));
|
||||
}
|
||||
},
|
||||
mode: IterMode.IgnoreMounts,
|
||||
});
|
||||
|
||||
return widgets.length > 0 ? RangeSet.of(widgets) : Decoration.none;
|
||||
};
|
||||
|
||||
const noteBlockStartField = StateField.define({
|
||||
create(state) {
|
||||
return decorate(state);
|
||||
},
|
||||
update(widgets, transaction) {
|
||||
if (transaction.docChanged) {
|
||||
return decorate(transaction.state);
|
||||
}
|
||||
|
||||
//return widgets.map(transaction.changes);
|
||||
return widgets
|
||||
},
|
||||
provide(field) {
|
||||
return EditorView.decorations.from(field);
|
||||
}
|
||||
});
|
||||
|
||||
return [noteBlockStartField];
|
||||
};
|
||||
|
||||
|
||||
|
||||
function atomicRanges(view) {
|
||||
let builder = new RangeSetBuilder()
|
||||
syntaxTree(view.state).iterate({
|
||||
enter: (type) => {
|
||||
if (type.type.id === NoteDelimiter) {
|
||||
builder.add(type.from, type.to, {})
|
||||
}
|
||||
},
|
||||
mode: IterMode.IgnoreMounts,
|
||||
});
|
||||
return builder.finish()
|
||||
}
|
||||
|
||||
const atomicNoteBlock = ViewPlugin.fromClass(
|
||||
class {
|
||||
constructor(view) {
|
||||
this.atomicRanges = atomicRanges(view)
|
||||
}
|
||||
|
||||
update(update) {
|
||||
if (update.docChanged) {
|
||||
this.atomicRanges = atomicRanges(update.view)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
provide: plugin => EditorView.atomicRanges.of(view => {
|
||||
return view.plugin(plugin)?.atomicRanges || Decoration.none
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
const blockLayer = () => {
|
||||
return layer({
|
||||
above: false,
|
||||
|
||||
markers(view) {
|
||||
const markers = []
|
||||
let idx = 0
|
||||
syntaxTree(view.state).iterate({
|
||||
enter: (type) => {
|
||||
//console.log("type", type.name, type.type.id, Document)
|
||||
if (type.type.id == Document || type.type.id == Note) {
|
||||
return true
|
||||
} else if (type.type.id === NoteDelimiter) {
|
||||
const contentNode = type.node.nextSibling
|
||||
//console.log("adding marker", type.node.nextSibling.name)
|
||||
//let line = view.state.doc.lineAt(type.from)
|
||||
const fromCoords = view.coordsAtPos(contentNode.from)
|
||||
const toCoords = view.coordsAtPos(contentNode.to)
|
||||
//console.log("line", fromCoords.top, toCoords.bottom)
|
||||
//console.log("documentTop", view.documentTop)
|
||||
markers.push(new RectangleMarker(
|
||||
idx++ % 2 == 0 ? "block-even" : "block-odd",
|
||||
0,
|
||||
fromCoords.top - (view.documentTop - view.documentPadding.top),
|
||||
2000,
|
||||
(toCoords.bottom - fromCoords.top),
|
||||
))
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
mode: IterMode.IgnoreMounts,
|
||||
});
|
||||
return markers
|
||||
|
||||
},
|
||||
|
||||
update(update, dom) {
|
||||
return update.docChanged || update.viewportChanged
|
||||
},
|
||||
|
||||
class: "blocks-layer"
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const preventFirstBlockFromBeingDeleted = EditorState.changeFilter.of((tr) => {
|
||||
if (!tr.annotations.some(a => a.value === INITIAL_DATA)) {
|
||||
return [-1,10]
|
||||
}
|
||||
})
|
||||
|
||||
export const noteBlockExtension = () => {
|
||||
return [noteBlockWidget(), atomicNoteBlock, blockLayer(), preventFirstBlockFromBeingDeleted]
|
||||
}
|
96
heynote-codemirror/src/setup.js
Normal file
96
heynote-codemirror/src/setup.js
Normal file
@ -0,0 +1,96 @@
|
||||
import { lineNumbers, highlightActiveLineGutter, highlightSpecialChars, drawSelection, dropCursor, rectangularSelection, crosshairCursor, highlightActiveLine, keymap } from '@codemirror/view';
|
||||
export { EditorView } from '@codemirror/view';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
import { foldGutter, indentOnInput, syntaxHighlighting, defaultHighlightStyle, bracketMatching, foldKeymap } from '@codemirror/language';
|
||||
import { history, defaultKeymap, historyKeymap } from '@codemirror/commands';
|
||||
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search';
|
||||
import { closeBrackets, autocompletion, closeBracketsKeymap, completionKeymap } from '@codemirror/autocomplete';
|
||||
import { lintKeymap } from '@codemirror/lint';
|
||||
|
||||
// (The superfluous function calls around the list of extensions work
|
||||
// around current limitations in tree-shaking software.)
|
||||
/**
|
||||
This is an extension value that just pulls together a number of
|
||||
extensions that you might want in a basic editor. It is meant as a
|
||||
convenient helper to quickly set up CodeMirror without installing
|
||||
and importing a lot of separate packages.
|
||||
|
||||
Specifically, it includes...
|
||||
|
||||
- [the default command bindings](https://codemirror.net/6/docs/ref/#commands.defaultKeymap)
|
||||
- [line numbers](https://codemirror.net/6/docs/ref/#view.lineNumbers)
|
||||
- [special character highlighting](https://codemirror.net/6/docs/ref/#view.highlightSpecialChars)
|
||||
- [the undo history](https://codemirror.net/6/docs/ref/#commands.history)
|
||||
- [a fold gutter](https://codemirror.net/6/docs/ref/#language.foldGutter)
|
||||
- [custom selection drawing](https://codemirror.net/6/docs/ref/#view.drawSelection)
|
||||
- [drop cursor](https://codemirror.net/6/docs/ref/#view.dropCursor)
|
||||
- [multiple selections](https://codemirror.net/6/docs/ref/#state.EditorState^allowMultipleSelections)
|
||||
- [reindentation on input](https://codemirror.net/6/docs/ref/#language.indentOnInput)
|
||||
- [the default highlight style](https://codemirror.net/6/docs/ref/#language.defaultHighlightStyle) (as fallback)
|
||||
- [bracket matching](https://codemirror.net/6/docs/ref/#language.bracketMatching)
|
||||
- [bracket closing](https://codemirror.net/6/docs/ref/#autocomplete.closeBrackets)
|
||||
- [autocompletion](https://codemirror.net/6/docs/ref/#autocomplete.autocompletion)
|
||||
- [rectangular selection](https://codemirror.net/6/docs/ref/#view.rectangularSelection) and [crosshair cursor](https://codemirror.net/6/docs/ref/#view.crosshairCursor)
|
||||
- [active line highlighting](https://codemirror.net/6/docs/ref/#view.highlightActiveLine)
|
||||
- [active line gutter highlighting](https://codemirror.net/6/docs/ref/#view.highlightActiveLineGutter)
|
||||
- [selection match highlighting](https://codemirror.net/6/docs/ref/#search.highlightSelectionMatches)
|
||||
- [search](https://codemirror.net/6/docs/ref/#search.searchKeymap)
|
||||
- [linting](https://codemirror.net/6/docs/ref/#lint.lintKeymap)
|
||||
|
||||
(You'll probably want to add some language package to your setup
|
||||
too.)
|
||||
|
||||
This extension does not allow customization. The idea is that,
|
||||
once you decide you want to configure your editor more precisely,
|
||||
you take this package's source (which is just a bunch of imports
|
||||
and an array literal), copy it into your own code, and adjust it
|
||||
as desired.
|
||||
*/
|
||||
const customSetup = /*@__PURE__*/(() => [
|
||||
lineNumbers(),
|
||||
highlightActiveLineGutter(),
|
||||
highlightSpecialChars(),
|
||||
history(),
|
||||
foldGutter(),
|
||||
drawSelection(),
|
||||
dropCursor(),
|
||||
EditorState.allowMultipleSelections.of(true),
|
||||
indentOnInput(),
|
||||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||
bracketMatching(),
|
||||
//closeBrackets(),
|
||||
autocompletion(),
|
||||
rectangularSelection(),
|
||||
crosshairCursor(),
|
||||
highlightActiveLine(),
|
||||
highlightSelectionMatches(),
|
||||
keymap.of([
|
||||
...closeBracketsKeymap,
|
||||
...defaultKeymap,
|
||||
...searchKeymap,
|
||||
...historyKeymap,
|
||||
...foldKeymap,
|
||||
...completionKeymap,
|
||||
...lintKeymap
|
||||
])
|
||||
])();
|
||||
/**
|
||||
A minimal set of extensions to create a functional editor. Only
|
||||
includes [the default keymap](https://codemirror.net/6/docs/ref/#commands.defaultKeymap), [undo
|
||||
history](https://codemirror.net/6/docs/ref/#commands.history), [special character
|
||||
highlighting](https://codemirror.net/6/docs/ref/#view.highlightSpecialChars), [custom selection
|
||||
drawing](https://codemirror.net/6/docs/ref/#view.drawSelection), and [default highlight
|
||||
style](https://codemirror.net/6/docs/ref/#language.defaultHighlightStyle).
|
||||
*/
|
||||
const minimalSetup = /*@__PURE__*/(() => [
|
||||
highlightSpecialChars(),
|
||||
history(),
|
||||
drawSelection(),
|
||||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||
keymap.of([
|
||||
...defaultKeymap,
|
||||
...historyKeymap,
|
||||
])
|
||||
])();
|
||||
|
||||
export { customSetup, minimalSetup };
|
41
heynote-codemirror/src/styles.css
Normal file
41
heynote-codemirror/src/styles.css
Normal file
@ -0,0 +1,41 @@
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #fff;
|
||||
color: #444;
|
||||
font-family: 'Gill Sans','Gill Sans MT',Calibri,'Trebuchet MS',sans-serif;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#editor {
|
||||
height: 100%;
|
||||
}
|
||||
#editor .cm-editor {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#syntaxTree {
|
||||
height: 20%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.kuken {
|
||||
background: #f00;
|
||||
}
|
||||
.balle .test-marker {
|
||||
background: rgb(94, 84, 106);
|
||||
}
|
||||
|
||||
|
||||
.blocks-layer .block-even {
|
||||
background: #3f3b4b;
|
||||
}
|
||||
.blocks-layer .block-odd {
|
||||
background: rgb(57, 71, 77);
|
||||
}
|
||||
|
||||
.block-start {
|
||||
/*background: rgb(0, 0, 0);
|
||||
height: 2px*/
|
||||
}
|
214
heynote-codemirror/src/theme/nord.mjs
Normal file
214
heynote-codemirror/src/theme/nord.mjs
Normal file
@ -0,0 +1,214 @@
|
||||
import { EditorView } from '@codemirror/view';
|
||||
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
||||
import { tags } from '@lezer/highlight';
|
||||
|
||||
|
||||
// Colors from https://www.nordtheme.com/docs/colors-and-palettes
|
||||
// Polar Night
|
||||
const base00 = '#2e3440', // black
|
||||
base01 = '#3b4252', // dark grey
|
||||
base02 = '#434c5e', base03 = '#4c566a'; // grey
|
||||
// Snow Storm
|
||||
const base04 = '#d8dee9', // grey
|
||||
base05 = '#e5e9f0', // off white
|
||||
base06 = '#eceff4'; // white
|
||||
// Frost
|
||||
const base07 = '#8fbcbb', // moss green
|
||||
base08 = '#88c0d0', // ice blue
|
||||
base09 = '#81a1c1', // water blue
|
||||
base0A = '#5e81ac'; // deep blue
|
||||
// Aurora
|
||||
const base0b = '#bf616a', // red
|
||||
base0C = '#d08770', // orange
|
||||
base0D = '#ebcb8b', // yellow
|
||||
base0E = '#a3be8c', // green
|
||||
base0F = '#b48ead'; // purple
|
||||
const invalid = '#d30102', darkBackground = '#252a33', background = base00, tooltipBackground = base01, selection = base03, cursor = '#fff';
|
||||
const highlightBackground = 'rgba(255,255,255,0.04)';
|
||||
|
||||
const lineNumberColor = '#535963';
|
||||
const commentColor = '#888d97';
|
||||
const matchingBracket = 'rgba(255,255,255,0.1)';
|
||||
|
||||
|
||||
/**
|
||||
The editor theme styles for Nord.
|
||||
*/
|
||||
|
||||
const nordTheme = /*@__PURE__*/EditorView.theme({
|
||||
'&': {
|
||||
color: base04,
|
||||
backgroundColor: background
|
||||
},
|
||||
'.cm-content': {
|
||||
caretColor: cursor
|
||||
},
|
||||
'.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor, borderLeftWidth:'2px', height:'19px !important', marginTop:'-2px !important' },
|
||||
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': { backgroundColor: selection },
|
||||
'.cm-panels': { backgroundColor: darkBackground, color: base03 },
|
||||
'.cm-panels.cm-panels-top': { borderBottom: '2px solid black' },
|
||||
'.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' },
|
||||
'.cm-searchMatch': {
|
||||
backgroundColor: 'transparent',
|
||||
outline: `1px solid ${base07}`
|
||||
},
|
||||
'.cm-searchMatch.cm-searchMatch-selected': {
|
||||
backgroundColor: base04,
|
||||
color: base00
|
||||
},
|
||||
'.cm-activeLine': { backgroundColor: highlightBackground },
|
||||
'.cm-selectionMatch': {
|
||||
backgroundColor: base05,
|
||||
color: base01
|
||||
},
|
||||
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
|
||||
outline: `0.5px solid ${base07}`
|
||||
},
|
||||
'&.cm-focused .cm-matchingBracket': {
|
||||
backgroundColor: matchingBracket,
|
||||
color: base02
|
||||
},
|
||||
'&.cm-focused .cm-nonmatchingBracket': {
|
||||
outline: `0.5px solid #bc8f8f`
|
||||
},
|
||||
'.cm-gutters': {
|
||||
backgroundColor: base00,
|
||||
color: lineNumberColor,
|
||||
border: 'none'
|
||||
},
|
||||
'.cm-activeLineGutter': {
|
||||
backgroundColor: highlightBackground,
|
||||
color: base04
|
||||
},
|
||||
'.cm-foldPlaceholder': {
|
||||
backgroundColor: 'transparent',
|
||||
border: 'none',
|
||||
color: '#ddd'
|
||||
},
|
||||
'.cm-tooltip': {
|
||||
border: 'none',
|
||||
backgroundColor: tooltipBackground
|
||||
},
|
||||
'.cm-tooltip .cm-tooltip-arrow:before': {
|
||||
borderTopColor: 'transparent',
|
||||
borderBottomColor: 'transparent'
|
||||
},
|
||||
'.cm-tooltip .cm-tooltip-arrow:after': {
|
||||
borderTopColor: tooltipBackground,
|
||||
borderBottomColor: tooltipBackground
|
||||
},
|
||||
'.cm-tooltip-autocomplete': {
|
||||
'& > ul > li[aria-selected]': {
|
||||
backgroundColor: highlightBackground,
|
||||
color: base03
|
||||
}
|
||||
}
|
||||
}, { dark: true });
|
||||
/**
|
||||
The highlighting style for code in the Nord theme.
|
||||
*/
|
||||
const nordHighlightStyle = /*@__PURE__*/HighlightStyle.define([
|
||||
{ tag: tags.keyword, color: base0A },
|
||||
{
|
||||
tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
|
||||
color: base08
|
||||
},
|
||||
{ tag: [tags.variableName], color: base07 },
|
||||
{ tag: [/*@__PURE__*/tags.function(tags.variableName)], color: base07 },
|
||||
{ tag: [tags.labelName], color: base09 },
|
||||
{
|
||||
tag: [tags.color, /*@__PURE__*/tags.constant(tags.name), /*@__PURE__*/tags.standard(tags.name)],
|
||||
color: base0A
|
||||
},
|
||||
{ tag: [/*@__PURE__*/tags.definition(tags.name), tags.separator], color: base0E },
|
||||
{ tag: [tags.brace], color: base07 },
|
||||
{
|
||||
tag: [tags.annotation],
|
||||
color: invalid
|
||||
},
|
||||
{
|
||||
tag: [tags.number, tags.changed, tags.annotation, tags.modifier, tags.self, tags.namespace],
|
||||
color: base0F
|
||||
},
|
||||
{
|
||||
tag: [tags.typeName, tags.className],
|
||||
color: base0D
|
||||
},
|
||||
{
|
||||
tag: [tags.operator, tags.operatorKeyword],
|
||||
color: base0E
|
||||
},
|
||||
{
|
||||
tag: [tags.tagName],
|
||||
color: base0F
|
||||
},
|
||||
{
|
||||
tag: [tags.squareBracket],
|
||||
color: base0b
|
||||
},
|
||||
{
|
||||
tag: [tags.angleBracket],
|
||||
color: base0C
|
||||
},
|
||||
{
|
||||
tag: [tags.attributeName],
|
||||
color: base0D
|
||||
},
|
||||
{
|
||||
tag: [tags.regexp],
|
||||
color: base0A
|
||||
},
|
||||
{
|
||||
tag: [tags.quote],
|
||||
color: base0F
|
||||
},
|
||||
{ tag: [tags.string], color: base0E },
|
||||
{
|
||||
tag: tags.link,
|
||||
color: base0E,
|
||||
textDecoration: 'underline',
|
||||
textUnderlinePosition: 'under'
|
||||
},
|
||||
{
|
||||
tag: [tags.url, tags.escape, /*@__PURE__*/tags.special(tags.string)],
|
||||
color: base07
|
||||
},
|
||||
{ tag: [tags.meta], color: base08 },
|
||||
{ tag: [tags.monospace], color: base04, fontStyle: 'italic' },
|
||||
{ tag: [tags.comment], color: commentColor, fontStyle: 'italic' },
|
||||
{ tag: tags.strong, fontWeight: 'bold', color: base0A },
|
||||
{ tag: tags.emphasis, fontStyle: 'italic', color: base0A },
|
||||
{ tag: tags.strikethrough, textDecoration: 'line-through' },
|
||||
{ tag: tags.heading, fontWeight: 'bold', color: base0A },
|
||||
{ tag: /*@__PURE__*/tags.special(tags.heading1), fontWeight: 'bold', color: base0A },
|
||||
{ tag: tags.heading1, fontWeight: 'bold', color: base0A },
|
||||
{
|
||||
tag: [tags.heading2, tags.heading3, tags.heading4],
|
||||
fontWeight: 'bold',
|
||||
color: base0A
|
||||
},
|
||||
{
|
||||
tag: [tags.heading5, tags.heading6],
|
||||
color: base0A
|
||||
},
|
||||
{ tag: [tags.atom, tags.bool, /*@__PURE__*/tags.special(tags.variableName)], color: base0C },
|
||||
{
|
||||
tag: [tags.processingInstruction, tags.inserted],
|
||||
color: base07
|
||||
},
|
||||
{
|
||||
tag: [tags.contentSeparator],
|
||||
color: base0D
|
||||
},
|
||||
{ tag: tags.invalid, color: base02, borderBottom: `1px dotted ${invalid}` }
|
||||
]);
|
||||
/**
|
||||
Extension to enable the Nord theme (both the editor theme and
|
||||
the highlight style).
|
||||
*/
|
||||
const nord = [
|
||||
nordTheme,
|
||||
/*@__PURE__*/syntaxHighlighting(nordHighlightStyle)
|
||||
];
|
||||
|
||||
export { nord, nordHighlightStyle, nordTheme };
|
Loading…
Reference in New Issue
Block a user