Add support for Math blocks

This commit is contained in:
Jonatan Heyman 2023-03-02 18:40:44 +01:00
parent cc6cda0217
commit 4d12404d77
10 changed files with 123 additions and 2 deletions

View File

@ -13,6 +13,19 @@ Welcome to Heynote!
[${modChar} + Up] Goto previous block
[${modChar} + A] Select all text in a note block. Press again to select the whole scratchpad
[${modChar} + + Up/Down]  Add additional cursor above/below
math
This is a Math block. Here, rows are evaluated as math expressions.
length = 10
radius = 5
volume = length * radius^2 * PI
sqrt(9)
It also supports some basic unit conversions:
13 inches in cm
time = 3900 seconds to minutes
time * 2
text-a
`
@ -27,6 +40,20 @@ Welcome to Heynote!
[${modChar} + Up] Goto previous block
[${modChar} + A] Select all text in a note block. Press again to select the whole scratchpad
[${modChar} + + Up/Down]  Add additional cursor above/below
math
This is a Math block. Here, rows are evaluated as math expressions.
length = 10
radius = 5
volume = length * radius^2 * PI
sqrt(9)
It also supports some basic unit conversions:
13 inches in cm
time = 3900 seconds to minutes
time * 2
text-a
python-a
# hmm

View File

@ -16,6 +16,7 @@
<div id="app"></div>
<script type="module" src="src/main.js"></script>
<script src="math.js" type="text/javascript"></script>
<!--<div id="editor" class="editor"></div>
<script type="module" src="src/editor/index.js"></script>-->

3
public/math.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,7 @@ import { Note, Document, NoteDelimiter } from "../lang-heynote/parser.terms.js"
import { IterMode } from "@lezer/common";
import { heynoteEvent, LANGUAGE_CHANGE } from "../annotation.js";
import { SelectionChangeEvent } from "../event.js"
import { mathBlock } from "./math.js"
// tracks the size of the first delimiter
@ -310,5 +311,6 @@ export const noteBlockExtension = (editor) => {
preventFirstBlockFromBeingDeleted,
preventSelectionBeforeFirstBlock,
emitCursorChange(editor),
mathBlock,
]
}

76
src/editor/block/math.js Normal file
View File

@ -0,0 +1,76 @@
import { ViewPlugin } from "@codemirror/view"
import { Decoration } from "@codemirror/view"
import { RangeSetBuilder } from "@codemirror/state"
import { WidgetType } from "@codemirror/view"
import { getNoteBlockFromPos } from "./block"
class MathResult extends WidgetType {
constructor(result) {
super()
this.result = result
}
eq(other) { return other.result == this.result }
toDOM() {
let wrap = document.createElement("span")
wrap.className = "heynote-math-result"
wrap.innerHTML = this.result
return wrap
}
ignoreEvent() { return false }
}
function mathDeco(view) {
let mathParsers = new WeakMap()
let builder = new RangeSetBuilder()
for (let { from, to } of view.visibleRanges) {
for (let pos = from; pos <= to;) {
let line = view.state.doc.lineAt(pos)
var block = getNoteBlockFromPos(view.state, pos)
if (block && block.language.name == "math") {
// get math.js parser and cache it for this block
let parser = mathParsers.get(block)
if (!parser) {
parser = math.parser()
mathParsers.set(block, parser)
}
// evaluate math line
let result
try {
result = parser.evaluate(line.text)
} catch (e) {
// suppress any errors
}
// if we got a result from math.js, add the result decoration
if (result !== undefined) {
builder.add(line.to, line.to, Decoration.widget({widget: new MathResult(math.format(result, 8)), side: 1}))
}
}
pos = line.to + 1
}
}
return builder.finish()
}
export const mathBlock = ViewPlugin.fromClass(class {
decorations
constructor(view) {
this.decorations = mathDeco(view)
}
update(update) {
if (update.docChanged || update.viewportChanged) {
this.decorations = mathDeco(update.view)
}
}
}, {
decorations: v => v.decorations
})

View File

@ -11,7 +11,7 @@ NoteDelimiter {
@tokens {
noteDelimiterMark { "∞∞∞" }
NoteLanguage { "text" | "javascript" | "json" | "python" | "html" | "sql" | "markdown" | "java" | "php" | "css" | "xml" | "cpp" | "rust" }
NoteLanguage { "text" | "math" | "javascript" | "json" | "python" | "html" | "sql" | "markdown" | "java" | "php" | "css" | "xml" | "cpp" | "rust" }
Auto { "-a" }
noteDelimiterEnter { "\n" }
//NoteContent { String }

View File

@ -10,7 +10,7 @@ export const parser = LRParser.deserialize({
maxTerm: 10,
skippedNodes: [0],
repeatNodeCount: 1,
tokenData: "&|~R[YZw}!O|#V#W!X#[#]!s#^#_#V#a#b$p#d#e%f#f#g%{#g#h&X#h#i&_#l#m!y%&x%&y&k~|OX~~!PP#T#U!S~!XOU~~![Q#d#e!b#g#h!m~!eP#d#e!h~!mOT~~!pP#g#h!h~!vP#h#i!y~!|P#a#b#P~#SP#`#a!h~#YQ#T#U#`#g#h$d~#cP#j#k#f~#iP#T#U#l~#qPT~#g#h#t~#wP#V#W#z~#}P#f#g$Q~$TP#]#^$W~$ZP#d#e$^~$aP#h#i!h~$gP#c#d$j~$mP#b#c!h~$sP#T#U$v~$yP#f#g$|~%PP#_#`%S~%VP#W#X%Y~%]P#c#d%`~%cP#k#l$j~%iQ#[#]!b#m#n%o~%rP#h#i%u~%xP#[#]$d~&OP#i#j&R~&UP#g#h$^~&[P#e#f#P~&bP#X#Y&e~&hP#l#m$^~&nP%&x%&y&q~&tP%&x%&y&w~&|OY~",
tokenData: "'V~R[YZw}!O|#V#W!X#[#]!s#^#_#V#a#b$p#d#e%o#f#g&U#g#h&b#h#i&h#l#m!y%&x%&y&t~|OX~~!PP#T#U!S~!XOU~~![Q#d#e!b#g#h!m~!eP#d#e!h~!mOT~~!pP#g#h!h~!vP#h#i!y~!|P#a#b#P~#SP#`#a!h~#YQ#T#U#`#g#h$d~#cP#j#k#f~#iP#T#U#l~#qPT~#g#h#t~#wP#V#W#z~#}P#f#g$Q~$TP#]#^$W~$ZP#d#e$^~$aP#h#i!h~$gP#c#d$j~$mP#b#c!h~$sP#T#U$v~$yQ#f#g%P#h#i%i~%SP#_#`%V~%YP#W#X%]~%`P#c#d%c~%fP#k#l$j~%lP#[#]!h~%rQ#[#]!b#m#n%x~%{P#h#i&O~&RP#[#]$d~&XP#i#j&[~&_P#g#h$^~&eP#e#f#P~&kP#X#Y&n~&qP#l#m$^~&wP%&x%&y&z~&}P%&x%&y'Q~'VOY~",
tokenizers: [0, noteContent],
topRules: {"Document":[0,2]},
tokenPrec: 0

View File

@ -24,6 +24,7 @@ class Language {
export const LANGUAGES = [
new Language("text", "Plain Text", null, "plaintext"),
new Language("math", "Math", null, null),
new Language("javascript", "JavaScript", javascriptLanguage.parser, "javascript"),
new Language("json", "JSON", jsonLanguage.parser, "json"),
new Language("python", "Python", pythonLanguage.parser, "python"),

View File

@ -43,4 +43,11 @@ export const heynoteBase = EditorView.theme({
'.heynote-block-start.first': {
height: '0px',
},
'.heynote-math-result': {
background: '#48b57e',
color: '#fff',
padding: '0px 4px',
borderRadius: '2px',
marginLeft: '12px',
},
})

View File

@ -111,6 +111,10 @@ const darkTheme = EditorView.theme({
background: "#213644",
borderTop: "1px solid #1e222a",
},
".heynote-math-result": {
background: "#0e1217",
color: "#7fcba9",
},
}, { dark: true });
/**