From e1220f4b1fdc9b65d1365e223afd570e33418b35 Mon Sep 17 00:00:00 2001 From: forgodtosave Date: Tue, 25 Apr 2023 00:26:34 +0200 Subject: [PATCH 1/2] created a codemirror extension to generate widgets if extension added to the editor instanze it creates for every "class" a block widget above with a link to the klipper ref TODO: - change to block widget - automatic generation: requires syntax tree - styling --- src/components/TheEditor.vue | 3 +- src/components/inputs/Codemirror.vue | 2 + src/components/inputs/CodemirrorLinkWidget.ts | 53 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 src/components/inputs/CodemirrorLinkWidget.ts diff --git a/src/components/TheEditor.vue b/src/components/TheEditor.vue index 75420cad1..1d98c9859 100644 --- a/src/components/TheEditor.vue +++ b/src/components/TheEditor.vue @@ -52,7 +52,8 @@ ref="editor" v-model="sourcecode" :name="filename" - :file-extension="fileExtension"> + :file-extension="fileExtension"> + diff --git a/src/components/inputs/Codemirror.vue b/src/components/inputs/Codemirror.vue index 242072988..582a223b6 100644 --- a/src/components/inputs/Codemirror.vue +++ b/src/components/inputs/Codemirror.vue @@ -19,6 +19,7 @@ import { gcode } from '@/plugins/StreamParserGcode' import { indentWithTab } from '@codemirror/commands' import { json } from '@codemirror/lang-json' import { css } from '@codemirror/lang-css' +import { LingWidgetPlugin } from './CodemirrorLinkWidget' @Component export default class Codemirror extends Mixins(BaseMixin) { @@ -82,6 +83,7 @@ export default class Codemirror extends Mixins(BaseMixin) { get cmExtensions() { const extensions = [ basicSetup, + LingWidgetPlugin, mainsailTheme, keymap.of([indentWithTab]), EditorView.updateListener.of((update) => { diff --git a/src/components/inputs/CodemirrorLinkWidget.ts b/src/components/inputs/CodemirrorLinkWidget.ts new file mode 100644 index 000000000..824ae4a7a --- /dev/null +++ b/src/components/inputs/CodemirrorLinkWidget.ts @@ -0,0 +1,53 @@ +import {ViewUpdate, ViewPlugin, DecorationSet} from "@codemirror/view" +import { Decoration, EditorView, WidgetType } from '@codemirror/view' +import { syntaxTree } from '@codemirror/language' + + +class LinkWidget extends WidgetType { + constructor(readonly linkName: string, readonly url: string) { super() } + eq(other: LinkWidget) { return false } + toDOM() { + let wrap = document.createElement("a") + wrap.innerHTML = this.linkName + wrap.setAttribute("href", this.url) + return wrap + } + ignoreEvent() { return false } +} + +function CreateLinkWidgetDecoration(view: EditorView) { + const LinkBlockWidget = (linkName: string, url: string) => Decoration.widget({ + widget: new LinkWidget(linkName, url), + side: -1, + block: false, + }) + + let widgets = [] + let deco = LinkBlockWidget("test", "https://google.com") + let pos = view.state.doc.lineAt(23).from + + /* console.log(pos) + const co = syntaxTree(view.state).resolve(pos); + console.log(co) + console.log(view.state.sliceDoc(co.from, co.to)) + console.log(view.state.getTokenAt(pos)) */ + + + widgets.push(deco.range(pos)) + return Decoration.set(widgets) +} + +export const LingWidgetPlugin = ViewPlugin.fromClass(class { + decorations: DecorationSet + constructor(view: EditorView) { + this.decorations = CreateLinkWidgetDecoration(view) + } + update(update: ViewUpdate) { + if (update.docChanged || update.viewportChanged) + this.decorations = CreateLinkWidgetDecoration(update.view) + } + }, { + decorations: v => v.decorations, + } +) + From 5f12d5c15aed9f797119425902eed9cbdbdc8769 Mon Sep 17 00:00:00 2001 From: forgodtosave Date: Mon, 1 May 2023 19:20:54 +0200 Subject: [PATCH 2/2] working (static) example for link widgets --- src/components/inputs/Codemirror.vue | 4 +- src/components/inputs/CodemirrorLinkWidget.ts | 53 -------------- .../inputs/CodemirrorLinkWidgets.ts | 70 +++++++++++++++++++ 3 files changed, 72 insertions(+), 55 deletions(-) delete mode 100644 src/components/inputs/CodemirrorLinkWidget.ts create mode 100644 src/components/inputs/CodemirrorLinkWidgets.ts diff --git a/src/components/inputs/Codemirror.vue b/src/components/inputs/Codemirror.vue index 582a223b6..73d75f14c 100644 --- a/src/components/inputs/Codemirror.vue +++ b/src/components/inputs/Codemirror.vue @@ -19,7 +19,7 @@ import { gcode } from '@/plugins/StreamParserGcode' import { indentWithTab } from '@codemirror/commands' import { json } from '@codemirror/lang-json' import { css } from '@codemirror/lang-css' -import { LingWidgetPlugin } from './CodemirrorLinkWidget' +import { linkWidgets } from './CodemirrorLinkWidgets' @Component export default class Codemirror extends Mixins(BaseMixin) { @@ -83,8 +83,8 @@ export default class Codemirror extends Mixins(BaseMixin) { get cmExtensions() { const extensions = [ basicSetup, - LingWidgetPlugin, mainsailTheme, + linkWidgets(), keymap.of([indentWithTab]), EditorView.updateListener.of((update) => { this.content = update.state?.doc.toString() diff --git a/src/components/inputs/CodemirrorLinkWidget.ts b/src/components/inputs/CodemirrorLinkWidget.ts deleted file mode 100644 index 824ae4a7a..000000000 --- a/src/components/inputs/CodemirrorLinkWidget.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {ViewUpdate, ViewPlugin, DecorationSet} from "@codemirror/view" -import { Decoration, EditorView, WidgetType } from '@codemirror/view' -import { syntaxTree } from '@codemirror/language' - - -class LinkWidget extends WidgetType { - constructor(readonly linkName: string, readonly url: string) { super() } - eq(other: LinkWidget) { return false } - toDOM() { - let wrap = document.createElement("a") - wrap.innerHTML = this.linkName - wrap.setAttribute("href", this.url) - return wrap - } - ignoreEvent() { return false } -} - -function CreateLinkWidgetDecoration(view: EditorView) { - const LinkBlockWidget = (linkName: string, url: string) => Decoration.widget({ - widget: new LinkWidget(linkName, url), - side: -1, - block: false, - }) - - let widgets = [] - let deco = LinkBlockWidget("test", "https://google.com") - let pos = view.state.doc.lineAt(23).from - - /* console.log(pos) - const co = syntaxTree(view.state).resolve(pos); - console.log(co) - console.log(view.state.sliceDoc(co.from, co.to)) - console.log(view.state.getTokenAt(pos)) */ - - - widgets.push(deco.range(pos)) - return Decoration.set(widgets) -} - -export const LingWidgetPlugin = ViewPlugin.fromClass(class { - decorations: DecorationSet - constructor(view: EditorView) { - this.decorations = CreateLinkWidgetDecoration(view) - } - update(update: ViewUpdate) { - if (update.docChanged || update.viewportChanged) - this.decorations = CreateLinkWidgetDecoration(update.view) - } - }, { - decorations: v => v.decorations, - } -) - diff --git a/src/components/inputs/CodemirrorLinkWidgets.ts b/src/components/inputs/CodemirrorLinkWidgets.ts new file mode 100644 index 000000000..0e7418ddb --- /dev/null +++ b/src/components/inputs/CodemirrorLinkWidgets.ts @@ -0,0 +1,70 @@ +import { syntaxTree } from '@codemirror/language' +import type { EditorState, Extension, Range } from '@codemirror/state' +import { RangeSet, StateField } from '@codemirror/state' +import type { DecorationSet } from '@codemirror/view' +import { Decoration, EditorView, WidgetType } from '@codemirror/view' + +class LinkWidget extends WidgetType { + readonly linkName + readonly url + + constructor(linkName: string, url: string) { + super() + this.linkName = linkName + this.url = url + } + + eq(LinkWidget: LinkWidget) { + return LinkWidget.url === this.url + } + toDOM() { + let container = document.createElement('a') + container.innerHTML = this.linkName + container.setAttribute('href', this.url) + container.setAttribute('target', '_blank') + container.setAttribute('rel', 'noopener noreferrer') + container.setAttribute('style', 'color:Tomato;text-decoration: none;') + return container + } +} + +export const linkWidgets = (): Extension => { + const linkWidgetDecoration = (linkName: string, url: string) => + Decoration.widget({ + widget: new LinkWidget(linkName, url), + side: -1, + block: true, + }) + + const decorate = (state: EditorState) => { + const widgets: Range[] = [] + widgets.push( + linkWidgetDecoration( + "View 'mcu' documentation", + 'https://www.klipper3d.org/Config_Reference.html#mcu' + ).range(state.doc.lineAt(245).from) + ) + widgets.push( + linkWidgetDecoration( + "View 'printer' documentation", + 'https://www.klipper3d.org/Config_Reference.html#printer' + ).range(state.doc.lineAt(300).from) + ) + return widgets.length > 0 ? RangeSet.of(widgets) : Decoration.none + } + + const linkWidgetsField = StateField.define({ + create(state) { + return decorate(state) + }, + update(images, transaction) { + if (transaction.docChanged) return decorate(transaction.state) + return images.map(transaction.changes) + }, + provide(field) { + return EditorView.decorations.from(field) + }, + }) + + return [linkWidgetsField] +} \ No newline at end of file