diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index ec7eee85e..5dcd26455 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -24,7 +24,12 @@ import { sliderPlugin, updateSliderWidgets } from './slider.mjs'; import { activateTheme, initTheme, theme } from './themes.mjs'; import { isTooltipEnabled } from './tooltip.mjs'; import { updateWidgets, widgetPlugin } from './widget.mjs'; -import { jumpToCharacter } from './labelJump.mjs'; +import { + deleteAllInlineBeforeCharacter, + jumpToCharacter, + jumpToNextCharacter, + ToggleCharBeforeChar, +} from './labelJump.mjs'; export { toggleBlockComment, toggleBlockCommentByLine, toggleComment, toggleLineComment } from '@codemirror/commands'; @@ -74,6 +79,10 @@ export const codemirrorSettings = persistentAtom('codemirror-settings', defaultS decode: JSON.parse, }); +const ANON_LABEL = '$'; +const SOLO_LABEL = 'S'; +const MUTE_LABEL = '_'; + // https://codemirror.net/docs/guide/ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, root, mondo }) { const settings = codemirrorSettings.get(); @@ -122,12 +131,58 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo }, { key: 'Alt-w', - run: (view) => jumpToCharacter(view, '$', 1), + run: (view) => jumpToNextCharacter(view, ANON_LABEL, 1), }, { key: 'Alt-q', - run: (view) => jumpToCharacter(view, '$', -1), + run: (view) => { + return jumpToNextCharacter(view, ANON_LABEL, -1); + }, }, + // clear all muted + { + key: `Alt-Ctrl-0`, + run: (view) => { + return deleteAllInlineBeforeCharacter(view, MUTE_LABEL + ANON_LABEL); + }, + }, + // clear all solod + { + key: `Alt-Shift-0`, + run: (view) => { + return deleteAllInlineBeforeCharacter(view, SOLO_LABEL + ANON_LABEL); + }, + }, + ...Array.from({ length: 9 }).map((_, i) => { + let num = i + 1; + return { + key: `Alt-${num}`, + run: (view) => { + return jumpToCharacter(view, ANON_LABEL, i); + }, + }; + }), + // handle solo toggles 1-9 + ...Array.from({ length: 9 }).map((_, i) => { + let num = i + 1; + return { + key: `Alt-Shift-${num}`, + run: (view) => { + return ToggleCharBeforeChar(view, ANON_LABEL, SOLO_LABEL, i); + }, + }; + }), + // handle mute toggles 1-9 + ...Array.from({ length: 9 }).map((_, i) => { + let num = i + 1; + return { + key: `Alt-Ctrl-${num}`, + run: (view) => { + return ToggleCharBeforeChar(view, ANON_LABEL, MUTE_LABEL, i); + }, + }; + }), + /* { key: 'Ctrl-Shift-.', run: () => (onPanic ? onPanic() : onStop?.()), diff --git a/packages/codemirror/labelJump.mjs b/packages/codemirror/labelJump.mjs index 0aced4eb5..c0a0c8e59 100644 --- a/packages/codemirror/labelJump.mjs +++ b/packages/codemirror/labelJump.mjs @@ -1,16 +1,42 @@ import { EditorSelection } from '@codemirror/state'; import { SearchCursor } from '@codemirror/search'; +import { EditorView } from '@codemirror/view'; -export function jumpToCharacter(view, character, direction = 1) { - const { state, dispatch } = view; - const pos = state.selection.main.head; +function getCharacterPositions(state, character) { const cursor = new SearchCursor(state.doc, character); - - let characterPositions = []; - let jumpPos; + const characterPositions = []; while (!cursor.next().done) { characterPositions.push(cursor.value.to); } + return characterPositions; +} + +// function jumpToNextCharacter(view,character, direction = 1){ +// const { state, dispatch } = view; +// const pos = state.selection.main.head; +// let jumpPos; +// const characterPositions = getCharacterPositions(state, character) +// if (!characterPositions.length) { +// return false; +// } +// if (direction > 0) { +// jumpPos = characterPositions.find((x) => x > pos + 1) ?? characterPositions.at(0); // Loop back around for convenience +// } else { +// jumpPos = characterPositions.reverse().find((x) => x < pos + 1) ?? characterPositions.at(0); +// } + +// if (jumpPos == null) { +// return false; +// } +// const selection = EditorSelection.cursor(jumpPos - 1); +// } + +const COMMENT_STRING = '//'; +export function jumpToNextCharacter(view, character, direction = 1) { + const { state, dispatch } = view; + const pos = state.selection.main.head; + let jumpPos; + const characterPositions = getCharacterPositions(state, character); if (!characterPositions.length) { return false; } @@ -23,9 +49,75 @@ export function jumpToCharacter(view, character, direction = 1) { if (jumpPos == null) { return false; } + const selection = EditorSelection.cursor(jumpPos - 1); dispatch({ - selection: EditorSelection.cursor(jumpPos - 1), + selection, + scrollIntoView: true, + sequential: true, + }); + return true; +} + +export function jumpToCharacter(view, character, num) { + const { state, dispatch } = view; + const characterPositions = getCharacterPositions(state, character); + const pos = characterPositions.at(num) ?? characterPositions.at(-1); + if (pos == null) { + return false; + } + + const selection = EditorSelection.cursor(pos - 1); + dispatch({ + selection, scrollIntoView: true, }); return true; } +export function deleteAllInlineBeforeCharacter(view, character) { + const { state, dispatch } = view; + const characterPositions = getCharacterPositions(state, character); + + const changes = []; + characterPositions.forEach((pos) => { + const line = state.doc.lineAt(pos); + if (state.doc.sliceString(line.from, line.from + 1) === COMMENT_STRING) { + return; + } + changes.push({ + from: line.from, + to: pos - 1, + insert: '', + }); + }); + dispatch({ changes }); + return true; +} +export function ToggleCharBeforeChar(view, character, character2, num) { + const { state, dispatch } = view; + + const changes = []; + const characterPositions = getCharacterPositions(state, character); + const labelpos = characterPositions.at(num) ?? characterPositions.at(-1); + const line = state.doc.lineAt(labelpos); + + if (state.doc.sliceString(line.from, line.from + 1) === COMMENT_STRING) { + return false; + } + //delete preceeding characters + changes.push({ + from: line.from, + to: labelpos - 1, + insert: '', + }); + + const preceedingLabelPos = labelpos - 2; + if (line.from === labelpos - 1 || state.doc.sliceString(preceedingLabelPos, labelpos - 1) !== character2) { + changes.push({ + insert: character2, + from: line.from, + }); + } + + dispatch({ changes }); + return true; +}