diff --git a/web/cm_plugins/clean.ts b/web/cm_plugins/clean.ts index 611232d9..e8eb5e16 100644 --- a/web/cm_plugins/clean.ts +++ b/web/cm_plugins/clean.ts @@ -13,6 +13,7 @@ import { cleanWikiLinkPlugin } from "./wiki_link.ts"; import { cleanCommandLinkPlugin } from "./command_link.ts"; import { fencedCodePlugin } from "./fenced_code.ts"; import { frontmatterPlugin } from "./frontmatter.ts"; +import { cleanEscapePlugin } from "./escapes.ts"; export function cleanModePlugins(client: Client) { return [ @@ -42,5 +43,6 @@ export function cleanModePlugins(client: Client) { tablePlugin(client), cleanWikiLinkPlugin(client), cleanCommandLinkPlugin(client), + cleanEscapePlugin(), ] as Extension[]; } diff --git a/web/cm_plugins/escapes.ts b/web/cm_plugins/escapes.ts new file mode 100644 index 00000000..30b8edcd --- /dev/null +++ b/web/cm_plugins/escapes.ts @@ -0,0 +1,28 @@ +import { EditorState } from "@codemirror/state"; +import { syntaxTree } from "@codemirror/language"; +import { Decoration } from "@codemirror/view"; +import { + decoratorStateField, + invisibleDecoration, + isCursorInRange, +} from "./util.ts"; + +export function cleanEscapePlugin() { + return decoratorStateField( + (state: EditorState) => { + const widgets: any[] = []; + + syntaxTree(state).iterate({ + enter({ type, from, to }) { + if ( + type.name === "Escape" && + !isCursorInRange(state, [from, to]) + ) { + widgets.push(invisibleDecoration.range(from, from + 1)); + } + }, + }); + return Decoration.set(widgets, true); + }, + ); +}