From 4c239c12f15af2b10fdf75b41433fcf4d6d469f1 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Thu, 3 Aug 2023 18:38:48 +0200 Subject: [PATCH] Adding a new MarkDown note buffer for each universe --- index.html | 7 +++ package.json | 1 + src/API.ts | 5 -- src/AppSettings.ts | 11 ++-- src/highlightSelection.ts | 114 -------------------------------------- src/main.ts | 80 +++++++++++++++----------- src/universes/tutorial.ts | 5 +- yarn.lock | 74 +++++++++++++++++++++++-- 8 files changed, 135 insertions(+), 162 deletions(-) delete mode 100644 src/highlightSelection.ts diff --git a/index.html b/index.html index 16dd1ec..0f43126 100644 --- a/index.html +++ b/index.html @@ -202,6 +202,13 @@ + + + + + + + diff --git a/package.json b/package.json index 6be486a..1fe4e67 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@codemirror/lang-javascript": "^6.1.9", + "@codemirror/lang-markdown": "^6.2.0", "@codemirror/theme-one-dark": "^6.1.2", "@replit/codemirror-vim": "^6.0.14", "@strudel.cycles/webaudio": "^0.8.2", diff --git a/src/API.ts b/src/API.ts index 927fd8c..9376152 100644 --- a/src/API.ts +++ b/src/API.ts @@ -4,8 +4,6 @@ import { tryEvaluate } from "./Evaluator"; import { MidiConnection } from "./IO/MidiConnection"; // @ts-ignore import { webaudioOutput, samples } from '@strudel.cycles/webaudio'; -// @ts-ignore -import { ZZFX, zzfx } from "zzfx"; interface TimePoint { bar: number, @@ -426,9 +424,6 @@ export class UserAPI { // Trivial functions // ============================================================= - // Small ZZFX interface for playing with this synth - zzfx = (...thing: number[]) => zzfx(...thing); - sound = async (values: object) => { webaudioOutput(sound(values), 0.00) } diff --git a/src/AppSettings.ts b/src/AppSettings.ts index d4c7bfe..951c59e 100644 --- a/src/AppSettings.ts +++ b/src/AppSettings.ts @@ -6,12 +6,13 @@ export interface Universe { global: File locals: { [key: number]: File } init: File + notes: File } export interface File { candidate: string - committed: string - evaluations: number + committed?: string + evaluations?: number } export interface Settings { @@ -36,7 +37,8 @@ export const template_universe = { 8: { candidate: "", committed: "", evaluations: 0}, 9: { candidate: "", committed: "", evaluations: 0}, }, - init: { candidate: "", committed: "", evaluations: 0 } + init: { candidate: "", committed: "", evaluations: 0 }, + notes: { candidate: "" }, } export const template_universes = { @@ -53,7 +55,8 @@ export const template_universes = { 8: { candidate: "", committed: "", evaluations: 0}, 9: { candidate: "", committed: "", evaluations: 0}, }, - init: { candidate: "", committed: "", evaluations: 0 } + init: { candidate: "", committed: "", evaluations: 0 }, + notes: { candidate: "// NOTES" }, }, "Help": tutorial_universe, } diff --git a/src/highlightSelection.ts b/src/highlightSelection.ts deleted file mode 100644 index eae44d3..0000000 --- a/src/highlightSelection.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { Decoration, DecorationSet } from "@codemirror/view" -import { StateField, StateEffect, ChangeDesc } from "@codemirror/state" -import { EditorView } from "@codemirror/view" -import { invertedEffects } from "@codemirror/commands" -import { Extension } from "@codemirror/state" - - -function mapRange(range: {from: number, to: number}, change: ChangeDesc) { - let from = change.mapPos(range.from), to = change.mapPos(range.to) - return from < to ? {from, to} : undefined -} - -const addHighlight = StateEffect.define<{from: number, to: number}>({ - map: mapRange -}) - -const removeHighlight = StateEffect.define<{from: number, to: number}>({ - map: mapRange -}) - -const highlight = Decoration.mark({ - attributes: {style: `background-color: #ffad42`} -}) - -const highlightedRanges = StateField.define({ - create() { - return Decoration.none - }, - update(ranges, tr) { - ranges = ranges.map(tr.changes) - for (let e of tr.effects) { - if (e.is(addHighlight)) - ranges = addRange(ranges, e.value) - else if (e.is(removeHighlight)) - ranges = cutRange(ranges, e.value) - } - return ranges - }, - provide: field => EditorView.decorations.from(field) -}) - -function cutRange(ranges: DecorationSet, r: {from: number, to: number}) { - let leftover: any[] = [] - ranges.between(r.from, r.to, (from, to, deco) => { - if (from < r.from) leftover.push(deco.range(from, r.from)) - if (to > r.to) leftover.push(deco.range(r.to, to)) - }) - return ranges.update({ - filterFrom: r.from, - filterTo: r.to, - filter: () => false, - add: leftover - }) -} - -function addRange(ranges: DecorationSet, r: {from: number, to: number}) { - ranges.between(r.from, r.to, (from, to) => { - if (from < r.from) r = {from, to: r.to} - if (to > r.to) r = {from: r.from, to} - }) - return ranges.update({ - filterFrom: r.from, - filterTo: r.to, - filter: () => false, - add: [highlight.range(r.from, r.to)] - }) -} - - -const invertHighlight = invertedEffects.of(tr => { - let found = [] - for (let e of tr.effects) { - if (e.is(addHighlight)) found.push(removeHighlight.of(e.value)) - else if (e.is(removeHighlight)) found.push(addHighlight.of(e.value)) - } - let ranges = tr.startState.field(highlightedRanges) - tr.changes.iterChangedRanges((chFrom, chTo) => { - ranges.between(chFrom, chTo, (rFrom, rTo) => { - if (rFrom >= chFrom || rTo <= chTo) { - let from = Math.max(chFrom, rFrom), to = Math.min(chTo, rTo) - if (from < to) found.push(addHighlight.of({from, to})) - } - }) - }) - return found -}) - -export function highlightSelection(view: EditorView) { - view.dispatch({ - effects: view.state.selection.ranges.filter(r => !r.empty) - .map(r => addHighlight.of(r)) - }) - return true -} - -export function unhighlightSelection(view: EditorView) { - let highlighted = view.state.field(highlightedRanges) - let effects: any[] = [] - for (let sel of view.state.selection.ranges) { - highlighted.between(sel.from, sel.to, (rFrom, rTo) => { - let from = Math.max(sel.from, rFrom), to = Math.min(sel.to, rTo) - if (from < to) effects.push(removeHighlight.of({from, to})) - }) - } - view.dispatch({effects}) - return true -} - -export function rangeHighlighting(): Extension { - return [ - highlightedRanges, - invertHighlight, - ] -} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 878ccc1..e4f0102 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,15 +3,11 @@ import { EditorView } from "codemirror"; import { editorSetup } from "./EditorSetup"; import { EditorState, Compartment } from "@codemirror/state"; import { javascript } from "@codemirror/lang-javascript"; +import { markdown } from "@codemirror/lang-markdown"; import { Clock } from "./Clock"; import { vim } from "@replit/codemirror-vim"; import { AppSettings } from "./AppSettings"; import { ViewUpdate } from "@codemirror/view"; -import { - highlightSelection, - unhighlightSelection, - rangeHighlighting, -} from "./highlightSelection"; import { UserAPI } from "./API"; import { Extension } from "@codemirror/state"; import { @@ -29,9 +25,10 @@ export class Editor { universes: Universes = template_universes; selected_universe: string; local_index: number = 1; - editor_mode: "global" | "local" | "init" = "local"; + editor_mode: "global" | "local" | "init" | "notes" = "local"; fontSize: Compartment; vimModeCompartment : Compartment; + chosenLanguage: Compartment settings = new AppSettings(); editorExtensions: Extension[] = []; @@ -77,6 +74,9 @@ export class Editor { init_button: HTMLButtonElement = document.getElementById( "init-button" ) as HTMLButtonElement; + note_button: HTMLButtonElement = document.getElementById( + "note-button" + ) as HTMLButtonElement; settings_button: HTMLButtonElement = document.getElementById( "settings-button" ) as HTMLButtonElement; @@ -136,6 +136,7 @@ export class Editor { this.fontSize = new Compartment(); this.vimModeCompartment = new Compartment(); + this.chosenLanguage = new Compartment(); const vimPlugin = this.settings.vimMode ? vim() : []; const fontSizeModif = EditorView.theme( { "&": { @@ -151,8 +152,7 @@ export class Editor { this.vimModeCompartment.of(vimPlugin), editorSetup, oneDark, - rangeHighlighting(), - javascript(), + this.chosenLanguage.of(javascript()), EditorView.updateListener.of((v: ViewUpdate) => { v; // This is the event listener for the editor @@ -221,7 +221,6 @@ export class Editor { // Ctrl + Enter or Return: Evaluate the hovered code block if ((event.key === "Enter" || event.key === "Return") && event.ctrlKey) { event.preventDefault(); - // const code = this.getCodeBlock(); this.currentFile().candidate = this.view.state.doc.toString(); this.flashBackground('#2d313d', 200) } @@ -234,7 +233,6 @@ export class Editor { event.preventDefault(); // Prevents the addition of a new line this.currentFile().candidate = this.view.state.doc.toString(); this.flashBackground('#2d313d', 200) - // const code = this.getSelectedLines(); } // This is the modal to switch between universes @@ -253,6 +251,12 @@ export class Editor { this.view.focus(); } + if (event.ctrlKey && event.key === "n") { + event.preventDefault(); + this.changeModeFromInterface("notes"); + this.view.focus(); + } + if (event.ctrlKey && event.key === "g") { event.preventDefault(); this.changeModeFromInterface("global"); @@ -346,6 +350,10 @@ export class Editor { this.init_button.addEventListener("click", () => this.changeModeFromInterface("init") ); + this.note_button.addEventListener("click", () => + this.changeModeFromInterface("notes") + ); + this.settings_button.addEventListener("click", () => { this.font_size_slider.value = this.settings.font_size.toString(); @@ -391,7 +399,6 @@ export class Editor { }) this.buffer_search.addEventListener("keydown", (event) => { - // this.changeModeFromInterface("local"); if (event.key === "Enter") { let query = this.buffer_search.value; if (query.length > 2 && query.length < 20) { @@ -406,6 +413,10 @@ export class Editor { tryEvaluate(this, this.universes[this.selected_universe.toString()].init) } + get note_buffer() { + return this.universes[this.selected_universe.toString()].notes; + } + get global_buffer() { return this.universes[this.selected_universe.toString()].global; } @@ -431,12 +442,11 @@ export class Editor { this.updateEditorView(); } - changeModeFromInterface(mode: "global" | "local" | "init") { + changeModeFromInterface(mode: "global" | "local" | "init" | "notes") { const interface_buttons: HTMLElement[] = [ - this.local_button, - this.global_button, - this.init_button + this.local_button, this.global_button, + this.init_button, this.note_button, ]; let changeColor = (button: HTMLElement) => { @@ -467,15 +477,32 @@ export class Editor { if (!this.local_script_tabs.classList.contains("hidden")) { this.local_script_tabs.classList.add("hidden"); } - this.editor_mode = "global"; changeColor(this.global_button); + this.editor_mode = "global" + changeColor(this.global_button); break; case "init": if (!this.local_script_tabs.classList.contains("hidden")) { this.local_script_tabs.classList.add("hidden"); } - this.editor_mode = "init"; changeColor(this.init_button); + this.editor_mode = "init" + changeColor(this.init_button); + break; + case "notes": + if (!this.local_script_tabs.classList.contains("hidden")) { + this.local_script_tabs.classList.add("hidden"); + } + this.editor_mode = "notes" + changeColor(this.note_button); break; } + + // If the editor is in notes mode, we need to update the selectedLanguage + + this.view.dispatch({ + effects: this.chosenLanguage.reconfigure(this.editor_mode == "notes" ? markdown() : javascript()) + }) + console.log(this.chosenLanguage.get(this.view.state)) + this.updateEditorView(); } @@ -555,6 +582,8 @@ export class Editor { return this.local_buffer; case "init": return this.init_buffer; + case "notes": + return this.note_buffer; } } @@ -601,15 +630,7 @@ export class Editor { ) { endLine = state.doc.line(endLine.number + 1); } - - // this.view.dispatch({selection: {anchor: 0 + startLine.from, head: endLine.to}}); - highlightSelection(this.view); - - setTimeout(() => { - unhighlightSelection(this.view); - this.view.dispatch({ selection: { anchor: cursor, head: cursor } }); - }, 200); - + let result_string = state.doc.sliceString(startLine.from, endLine.to); result_string = result_string .split("\n") @@ -639,13 +660,6 @@ export class Editor { }); // Release the selection and get the cursor back to its original position - // Blink the text! - highlightSelection(this.view); - - setTimeout(() => { - unhighlightSelection(this.view); - this.view.dispatch({ selection: { anchor: from, head: from } }); - }, 200); return state.doc.sliceString(fromLine.from, toLine.to); }; diff --git a/src/universes/tutorial.ts b/src/universes/tutorial.ts index b87396e..e2a1c94 100644 --- a/src/universes/tutorial.ts +++ b/src/universes/tutorial.ts @@ -19,6 +19,8 @@ const init_buffer=` // loaded! ` +const note_buffer='// Notes buffer: a buffer to write your notes.' + export const tutorial_universe = { global: { candidate: global_text, committed: global_text, evaluations: 0 }, locals: { @@ -32,5 +34,6 @@ export const tutorial_universe = { 8: { candidate: local_buffer, committed: local_buffer, evaluations: 0 }, 9: { candidate: local_buffer, committed: local_buffer, evaluations: 0 }, }, - init: { candidate: init_buffer, committed: init_buffer, evaluations: 0 } + init: { candidate: init_buffer, committed: init_buffer, evaluations: 0 }, + notes: { candidate: note_buffer }, } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 0222e1c..2d6fa41 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,7 +14,7 @@ dependencies: regenerator-runtime "^0.13.11" -"@codemirror/autocomplete@^6.0.0": +"@codemirror/autocomplete@^6.0.0", "@codemirror/autocomplete@^6.7.1": version "6.9.0" resolved "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.9.0.tgz" integrity sha512-Fbwm0V/Wn3BkEJZRhr0hi5BhCo5a7eBL6LYaliPjOSwCyfOpnjXY59HruSxOUNV+1OYer0Tgx1zRNQttjXyDog== @@ -34,7 +34,33 @@ "@codemirror/view" "^6.0.0" "@lezer/common" "^1.0.0" -"@codemirror/lang-javascript@^6.1.9": +"@codemirror/lang-css@^6.0.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@codemirror/lang-css/-/lang-css-6.2.0.tgz#f84f9da392099432445c75e32fdac63ae572315f" + integrity sha512-oyIdJM29AyRPM3+PPq1I2oIk8NpUfEN3kAM05XWDDs6o3gSneIKaVJifT2P+fqONLou2uIgXynFyMUDQvo/szA== + dependencies: + "@codemirror/autocomplete" "^6.0.0" + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@lezer/common" "^1.0.2" + "@lezer/css" "^1.0.0" + +"@codemirror/lang-html@^6.0.0": + version "6.4.5" + resolved "https://registry.yarnpkg.com/@codemirror/lang-html/-/lang-html-6.4.5.tgz#4cf014da02624a8a4365ef6c8e343f35afa0c784" + integrity sha512-dUCSxkIw2G+chaUfw3Gfu5kkN83vJQN8gfQDp9iEHsIZluMJA0YJveT12zg/28BJx+uPsbQ6VimKCgx3oJrZxA== + dependencies: + "@codemirror/autocomplete" "^6.0.0" + "@codemirror/lang-css" "^6.0.0" + "@codemirror/lang-javascript" "^6.0.0" + "@codemirror/language" "^6.4.0" + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.2.2" + "@lezer/common" "^1.0.0" + "@lezer/css" "^1.1.0" + "@lezer/html" "^1.3.0" + +"@codemirror/lang-javascript@^6.0.0", "@codemirror/lang-javascript@^6.1.9": version "6.1.9" resolved "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.1.9.tgz" integrity sha512-z3jdkcqOEBT2txn2a87A0jSy6Te3679wg/U8QzMeftFt+4KA6QooMwfdFzJiuC3L6fXKfTXZcDocoaxMYfGz0w== @@ -47,7 +73,20 @@ "@lezer/common" "^1.0.0" "@lezer/javascript" "^1.0.0" -"@codemirror/language@^6.0.0", "@codemirror/language@^6.6.0": +"@codemirror/lang-markdown@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@codemirror/lang-markdown/-/lang-markdown-6.2.0.tgz#d391d1314911da522bf4cc4edb15ff6b3eb66979" + integrity sha512-deKegEQVzfBAcLPqsJEa+IxotqPVwWZi90UOEvQbfa01NTAw8jNinrykuYPTULGUj+gha0ZG2HBsn4s5d64Qrg== + dependencies: + "@codemirror/autocomplete" "^6.7.1" + "@codemirror/lang-html" "^6.0.0" + "@codemirror/language" "^6.3.0" + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.0.0" + "@lezer/common" "^1.0.0" + "@lezer/markdown" "^1.0.0" + +"@codemirror/language@^6.0.0", "@codemirror/language@^6.3.0", "@codemirror/language@^6.4.0", "@codemirror/language@^6.6.0": version "6.8.0" resolved "https://registry.npmjs.org/@codemirror/language/-/language-6.8.0.tgz" integrity sha512-r1paAyWOZkfY0RaYEZj3Kul+MiQTEbDvYqf8gPGaRvNneHXCmfSaAVFjwRUPlgxS8yflMxw2CTu6uCMp8R8A2g== @@ -92,7 +131,7 @@ "@codemirror/view" "^6.0.0" "@lezer/highlight" "^1.0.0" -"@codemirror/view@^6.0.0", "@codemirror/view@^6.6.0": +"@codemirror/view@^6.0.0", "@codemirror/view@^6.2.2", "@codemirror/view@^6.6.0": version "6.16.0" resolved "https://registry.npmjs.org/@codemirror/view/-/view-6.16.0.tgz" integrity sha512-1Z2HkvkC3KR/oEZVuW9Ivmp8TWLzGEd8T8TA04TTwPvqogfkHBdYSlflytDOqmkUxM2d1ywTg7X2dU5mC+SXvg== @@ -248,11 +287,19 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@lezer/common@^1.0.0": +"@lezer/common@^1.0.0", "@lezer/common@^1.0.2": version "1.0.3" resolved "https://registry.npmjs.org/@lezer/common/-/common-1.0.3.tgz" integrity sha512-JH4wAXCgUOcCGNekQPLhVeUtIqjH0yPBs7vvUdSjyQama9618IOKFJwkv2kcqdhF0my8hQEgCTEJU0GIgnahvA== +"@lezer/css@^1.0.0", "@lezer/css@^1.1.0": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@lezer/css/-/css-1.1.3.tgz#605495b00fd8a122088becf196a93744cbe817fc" + integrity sha512-SjSM4pkQnQdJDVc80LYzEaMiNy9txsFbI7HsMgeVF28NdLaAdHNtQ+kB/QqDUzRBV/75NTXjJ/R5IdC8QQGxMg== + dependencies: + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + "@lezer/highlight@^1.0.0", "@lezer/highlight@^1.1.3": version "1.1.6" resolved "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.6.tgz" @@ -260,6 +307,15 @@ dependencies: "@lezer/common" "^1.0.0" +"@lezer/html@^1.3.0": + version "1.3.6" + resolved "https://registry.yarnpkg.com/@lezer/html/-/html-1.3.6.tgz#26a2a17da4e0f91835e36db9ccd025b2ed8d33f7" + integrity sha512-Kk9HJARZTc0bAnMQUqbtuhFVsB4AnteR2BFUWfZV7L/x1H0aAKz6YabrfJ2gk/BEgjh9L3hg5O4y2IDZRBdzuQ== + dependencies: + "@lezer/common" "^1.0.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + "@lezer/javascript@^1.0.0": version "1.4.4" resolved "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.4.tgz" @@ -275,6 +331,14 @@ dependencies: "@lezer/common" "^1.0.0" +"@lezer/markdown@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@lezer/markdown/-/markdown-1.1.0.tgz#5cee104ef353a3442ecee023ff1912826fac8658" + integrity sha512-JYOI6Lkqbl83semCANkO3CKbKc0pONwinyagBufWBm+k4yhIcqfCF8B8fpEpvJLmIy7CAfwiq7dQ/PzUZA340g== + dependencies: + "@lezer/common" "^1.0.0" + "@lezer/highlight" "^1.0.0" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"