From 3a231180369740165f6b257fc181667727031b4c Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Sun, 14 Sep 2025 21:58:30 +1000 Subject: [PATCH] Have made some large improvements - Added support for multiple cursors - Enabled wrapping - Added uppercase, lowercase, and lorem ipsum support - Added support for processing only selected ranges --- .gitignore | 1 + frontend/index.html | 5 ++++- frontend/src/app.css | 1 + frontend/src/cmplugins.js | 40 +++++++++++++++++++++++++++++++++++++++ frontend/src/main.js | 20 ++++++++------------ frontend/src/services.js | 22 ++++++++++++++++++--- textfilters.go | 28 +++++++++++++++++++++++++-- 7 files changed, 99 insertions(+), 18 deletions(-) create mode 100644 frontend/src/cmplugins.js diff --git a/.gitignore b/.gitignore index 96f2731..ca8759d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build node_modules frontend/dist +.DS_Store diff --git a/frontend/index.html b/frontend/index.html index 12d146c..1ee5df5 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -15,8 +15,11 @@ data-action="keyup.enter->commands#runCommand keydown.esc->commands#dismissDialog keyup->commands#handleKeyup"> diff --git a/frontend/src/app.css b/frontend/src/app.css index 0e38594..2cfbffe 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -1,5 +1,6 @@ .cm-editor { height: 100%; + width: 100%; font-size: 1.1em; } diff --git a/frontend/src/cmplugins.js b/frontend/src/cmplugins.js new file mode 100644 index 0000000..e3c4440 --- /dev/null +++ b/frontend/src/cmplugins.js @@ -0,0 +1,40 @@ +import {EditorSelection} from "@codemirror/state"; +import {keymap} from "@codemirror/view"; + +function spawnNewCursor(view, forward) { + const { state } = view + const selection = state.selection + const newRanges = [] + + for (let range of selection.ranges) { + // Keep existing cursor + newRanges.push(range) + newRanges.push(view.moveVertically(range, forward, 1)); + } + + view.dispatch({ + selection: EditorSelection.create(newRanges) + }) + return true +} + +export const multiCursorKeymap = keymap.of([ + { + key: "Ctrl-Alt-ArrowDown", // Control+Option+Down on Mac + run: (view) => spawnNewCursor(view, true), + }, + { + key: "Ctrl-Alt-ArrowUp", // Control+Option+Down on Mac + run: (view) => spawnNewCursor(view, false), + } +]) + +export const commandPalette = keymap.of([{ + key: "Cmd-p", + run: () => { + let event = new CustomEvent('dq-showcommands'); + window.dispatchEvent(event); + + return true; + } +}]); \ No newline at end of file diff --git a/frontend/src/main.js b/frontend/src/main.js index 6ca66d9..d313ede 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -2,26 +2,22 @@ import './style.css'; import './app.css'; import {EditorView, basicSetup} from "codemirror"; -import {keymap} from "@codemirror/view"; -import { Application } from "@hotwired/stimulus"; +import {Application} from "@hotwired/stimulus"; + +import {textProcessor} from "./services.js"; +import {multiCursorKeymap, commandPalette} from "./cmplugins.js"; -import { textProcessor } from "./services.js"; import {CommandsController} from "./controllers/commands_controller.js"; + const view = new EditorView({ parent: document.querySelector("#app"), doc: "", extensions: [ basicSetup, - keymap.of([{ - key: "Cmd-p", - run: () => { - let event = new CustomEvent('dq-showcommands'); - window.dispatchEvent(event); - - return true; - } - }]), + EditorView.lineWrapping, + multiCursorKeymap, + commandPalette, ] }) diff --git a/frontend/src/services.js b/frontend/src/services.js index 60981c7..c968a4e 100644 --- a/frontend/src/services.js +++ b/frontend/src/services.js @@ -18,11 +18,27 @@ class TextProcessor { return; } + let ranges = this._editor.state.selection.ranges; + let shouldBeAll = ranges.reduce((a, r) => a && r.from === r.to, true); + + let inputs = []; + if (shouldBeAll) { + inputs.push({ + text: this._editor.state.doc.toString(), + pos: 0, + len: this._editor.state.doc.length, + }); + } else { + inputs = ranges.map(r => ({ + text: this._editor.state.doc.slice(r.from, r.to).toString(), + pos: r.from, + len: r.to - r.from, + })) + } + ProcessText({ action: command, - input: [ - {text: this._editor.state.doc.toString(), pos: 0, len: this._editor.state.doc.length} - ], + input: inputs, }); } } diff --git a/textfilters.go b/textfilters.go index ce70201..10fcf53 100644 --- a/textfilters.go +++ b/textfilters.go @@ -1,22 +1,46 @@ package main import ( + "bufio" "bytes" "encoding/json" "strconv" + "strings" ) type TextFilter func(input string) (output string, err error) var TextFilters = map[string]TextFilter{ + "upper-case": func(input string) (output string, err error) { + return strings.ToUpper(input), nil + }, + "lower-case": func(input string) (output string, err error) { + return strings.ToLower(input), nil + }, "unquote": func(input string) (output string, err error) { return strconv.Unquote(input) }, "format-json": func(input string) (output string, err error) { var dst bytes.Buffer - if err := json.Indent(&dst, []byte(input), "", " "); err != nil { - return "", err + + scnr := bufio.NewScanner(strings.NewReader(input)) + for scnr.Scan() { + line := scnr.Text() + if err := json.Indent(&dst, []byte(line), "", " "); err == nil { + dst.WriteString("\n") + } else { + return "", err + } } + return dst.String(), nil }, + "lorem-ipsum": func(input string) (output string, err error) { + return "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor " + + "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud " + + "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " + + "dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " + + "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " + + "mollit anim id est laborum.", nil + }, }