Added some more commands and mades some quality of life improvements
All checks were successful
Build / build (push) Successful in 4m27s
All checks were successful
Build / build (push) Successful in 4m27s
This commit is contained in:
parent
3a23118036
commit
3cb5795ca5
11
.idea/go.imports.xml
Normal file
11
.idea/go.imports.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GoImports">
|
||||
<option name="excludedPackages">
|
||||
<array>
|
||||
<option value="github.com/pkg/errors" />
|
||||
<option value="golang.org/x/net/context" />
|
||||
</array>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
97
app.go
97
app.go
|
|
@ -2,29 +2,56 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"lmika.dev/pkg/modash/moslice"
|
||||
"ucl.lmika.dev/ucl"
|
||||
"ucl.lmika.dev/ucl/builtins"
|
||||
)
|
||||
|
||||
// App struct
|
||||
type App struct {
|
||||
uclInst *ucl.Inst
|
||||
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewApp creates a new App application struct
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
uclInst := ucl.New(
|
||||
ucl.WithModule(builtins.CSV(nil)),
|
||||
ucl.WithModule(builtins.Fns()),
|
||||
ucl.WithModule(builtins.FS(nil)),
|
||||
ucl.WithModule(builtins.Itrs()),
|
||||
ucl.WithModule(builtins.Lists()),
|
||||
ucl.WithModule(builtins.Log(nil)),
|
||||
ucl.WithModule(builtins.OS()),
|
||||
ucl.WithModule(builtins.Strs()),
|
||||
ucl.WithModule(builtins.Time()),
|
||||
)
|
||||
return &App{
|
||||
uclInst: uclInst,
|
||||
}
|
||||
}
|
||||
|
||||
// startup is called when the app starts. The context is saved
|
||||
// so we can call the runtime methods
|
||||
func (a *App) startup(ctx context.Context) {
|
||||
a.ctx = ctx
|
||||
a.ctx = context.WithValue(ctx, uclInstKey, a.uclInst)
|
||||
}
|
||||
|
||||
func (a *App) ListProcessors() (resp []ListProcessorsResponse) {
|
||||
for k, v := range TextFilters {
|
||||
resp = append(resp, ListProcessorsResponse{Name: k, Label: v.Label})
|
||||
}
|
||||
sort.Slice(resp, func(i, j int) bool { return resp[i].Label < resp[j].Label })
|
||||
return resp
|
||||
}
|
||||
|
||||
// Greet returns a greeting for the given name
|
||||
func (a *App) ProcessText(req ProcessTextRequest) {
|
||||
filter, ok := TextFilters[req.Action]
|
||||
if !ok {
|
||||
|
|
@ -32,23 +59,53 @@ func (a *App) ProcessText(req ProcessTextRequest) {
|
|||
return
|
||||
}
|
||||
|
||||
resp, err := moslice.MapWithError(req.Input, func(span TextSpan) (TextSpan, error) {
|
||||
outStr, err := filter(span.Text)
|
||||
if err != nil {
|
||||
return TextSpan{}, err
|
||||
switch {
|
||||
case filter.Analyze != nil:
|
||||
inBfr := strings.Builder{}
|
||||
for _, span := range req.Input {
|
||||
if inBfr.Len() > 0 {
|
||||
inBfr.WriteString("\n")
|
||||
}
|
||||
inBfr.WriteString(span.Text)
|
||||
}
|
||||
return TextSpan{
|
||||
Text: outStr,
|
||||
Pos: span.Pos,
|
||||
Len: span.Len,
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Error running filter: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
runtime.EventsEmit(a.ctx, "process-text-response", ProcessTextResponse{
|
||||
Output: resp,
|
||||
})
|
||||
msg, err := filter.Analyze(a.ctx, inBfr.String())
|
||||
if err != nil {
|
||||
runtime.EventsEmit(a.ctx, "set-statusbar-message", SetStatusbarMessage{
|
||||
Message: fmt.Sprintf("Error running analysis: %v", err.Error()),
|
||||
Error: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
runtime.EventsEmit(a.ctx, "set-statusbar-message", SetStatusbarMessage{
|
||||
Message: msg,
|
||||
})
|
||||
case filter.Filter != nil:
|
||||
resp, err := moslice.MapWithError(req.Input, func(span TextSpan) (TextSpan, error) {
|
||||
outRes, err := filter.Filter(a.ctx, span.Text)
|
||||
if err != nil {
|
||||
return TextSpan{}, err
|
||||
}
|
||||
|
||||
return TextSpan{
|
||||
Text: outRes.Output,
|
||||
Pos: span.Pos,
|
||||
Len: span.Len,
|
||||
Append: outRes.Append,
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
runtime.EventsEmit(a.ctx, "set-statusbar-message", SetStatusbarMessage{
|
||||
Message: fmt.Sprintf("Error running filter: %v", err.Error()),
|
||||
Error: true,
|
||||
})
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
runtime.EventsEmit(a.ctx, "process-text-response", ProcessTextResponse{
|
||||
Output: resp,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
15
appctx.go
Normal file
15
appctx.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"ucl.lmika.dev/ucl"
|
||||
)
|
||||
|
||||
type uclInstKeyType struct{}
|
||||
|
||||
var uclInstKey = uclInstKeyType{}
|
||||
|
||||
func uclInstFromContext(ctx context.Context) *ucl.Inst {
|
||||
return ctx.Value(uclInstKey).(*ucl.Inst)
|
||||
}
|
||||
|
|
@ -6,21 +6,18 @@
|
|||
<title>dequoter</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div id="app">
|
||||
<div class="editor-mountpoint"></div>
|
||||
<div class="status-bar deemphasized" data-controller="status">Cmd+P: open command palette. Shift+Cmd+P: rerun last command.</div>
|
||||
</div>
|
||||
<dialog id="command-dialog" data-controller="commands"
|
||||
data-action="dq-showcommands@window->commands#showCommands">
|
||||
data-action="dq-showcommands@window->commands#showCommands dq-rerunlastcommand@window->commands#rerunLastCommand">
|
||||
<div class="dialog-body">
|
||||
<div class="command-input">
|
||||
<input data-commands-target="commandInput" type="text" placeholder="Enter command"
|
||||
data-action="keyup.enter->commands#runCommand keydown.esc->commands#dismissDialog keyup->commands#handleKeyup">
|
||||
data-action="keyup.enter->commands#runCommand keyup.down->commands#focusSelect keydown.esc->commands#dismissDialog keyup->commands#handleKeyup">
|
||||
</div>
|
||||
<select multiple class="command-options" data-commands-target="commandSelect">
|
||||
<option value="format-json"><span class="option-label">JSON: Format</span></option>
|
||||
<option value="lorem-ipsum"><span class="option-label">Lorem Ipsum: Generate</span></option>
|
||||
<option value="lower-case"><span class="option-label">Lower Case</span></option>
|
||||
<option value="unquote"><span class="option-label">Unquote</span></option>
|
||||
<option value="upper-case"><span class="option-label">Upper Case</span></option>
|
||||
</select>
|
||||
<select multiple class="command-options" data-commands-target="commandSelect" data-action="keyup.enter->commands#runCommand"></select>
|
||||
</div>
|
||||
</dialog>
|
||||
<script src="./src/main.js" type="module"></script>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,26 @@
|
|||
#app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.editor-mountpoint {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.status-bar {
|
||||
height: 2em;
|
||||
background-color: rgb(245, 245, 245);
|
||||
border-top: solid thin rgb(200, 200, 200);
|
||||
|
||||
align-content: center;
|
||||
padding-inline: 8px;
|
||||
}
|
||||
|
||||
.status-bar.deemphasized {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.cm-editor {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
|
|
|||
|
|
@ -37,4 +37,12 @@ export const commandPalette = keymap.of([{
|
|||
|
||||
return true;
|
||||
}
|
||||
}]);
|
||||
}, {
|
||||
key: "Cmd-P",
|
||||
run: () => {
|
||||
let event = new CustomEvent('dq-rerunlastcommand');
|
||||
window.dispatchEvent(event);
|
||||
|
||||
return true;
|
||||
}
|
||||
}]);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import {ListProcessors} from "../../wailsjs/go/main/App";
|
||||
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
|
||||
import { textProcessor } from "../services.js";
|
||||
|
|
@ -8,7 +10,16 @@ export class CommandsController extends Controller {
|
|||
"commandSelect",
|
||||
];
|
||||
|
||||
connect() {
|
||||
async connect() {
|
||||
this._lastCommand = null;
|
||||
|
||||
let processors = await ListProcessors();
|
||||
processors.forEach((processor) => {
|
||||
let option = document.createElement("option");
|
||||
option.value = processor.name;
|
||||
option.text = processor.label;
|
||||
this.commandSelectTarget.appendChild(option);
|
||||
});
|
||||
this._options = Array.from(this.commandSelectTarget.options);
|
||||
}
|
||||
|
||||
|
|
@ -29,13 +40,29 @@ export class CommandsController extends Controller {
|
|||
this.element.close();
|
||||
}
|
||||
|
||||
focusSelect(ev) {
|
||||
this.commandSelectTarget.focus();
|
||||
}
|
||||
|
||||
runCommand(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
textProcessor.runTextCommand(this.commandSelectTarget.value);
|
||||
this._lastCommand = this.commandSelectTarget.value;
|
||||
|
||||
this.element.close();
|
||||
}
|
||||
|
||||
rerunLastCommand(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
if (this._lastCommand === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
textProcessor.runTextCommand(this._lastCommand);
|
||||
}
|
||||
|
||||
_filterOptions(filterText) {
|
||||
let inputText = filterText.toLowerCase();
|
||||
|
||||
|
|
|
|||
14
frontend/src/controllers/status_controller.js
Normal file
14
frontend/src/controllers/status_controller.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { Controller } from "@hotwired/stimulus"
|
||||
|
||||
export class StatusController extends Controller {
|
||||
connect() {
|
||||
window.runtime.EventsOn("set-statusbar-message", (data) => {
|
||||
this._setStatusbarMessage(data.message);
|
||||
});
|
||||
}
|
||||
|
||||
_setStatusbarMessage(message) {
|
||||
this.element.textContent = message;
|
||||
this.element.classList.remove("deemphasized");
|
||||
}
|
||||
}
|
||||
|
|
@ -7,11 +7,13 @@ import {Application} from "@hotwired/stimulus";
|
|||
import {textProcessor} from "./services.js";
|
||||
import {multiCursorKeymap, commandPalette} from "./cmplugins.js";
|
||||
|
||||
import {StatusController} from "./controllers/status_controller.js";
|
||||
import {CommandsController} from "./controllers/commands_controller.js";
|
||||
|
||||
|
||||
|
||||
const view = new EditorView({
|
||||
parent: document.querySelector("#app"),
|
||||
parent: document.querySelector("#app .editor-mountpoint"),
|
||||
doc: "",
|
||||
extensions: [
|
||||
basicSetup,
|
||||
|
|
@ -23,6 +25,7 @@ const view = new EditorView({
|
|||
|
||||
window.Stimulus = Application.start()
|
||||
Stimulus.register("commands", CommandsController);
|
||||
Stimulus.register("status", StatusController);
|
||||
|
||||
textProcessor.setCodeMirrorEditor(view);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,21 @@ class TextProcessor {
|
|||
setCodeMirrorEditor(editor) {
|
||||
this._editor = editor;
|
||||
window.runtime.EventsOn("process-text-response", (data) => {
|
||||
const changes = data.output.map(span => ({
|
||||
from: span.pos,
|
||||
to: span.pos + span.len,
|
||||
insert: span.text,
|
||||
}));
|
||||
const changes = data.output.map(span => {
|
||||
if (span.append) {
|
||||
return {
|
||||
from: span.pos + span.len,
|
||||
to: span.pos + span.len,
|
||||
insert: span.text,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
from: span.pos,
|
||||
to: span.pos + span.len,
|
||||
insert: span.text,
|
||||
};
|
||||
}
|
||||
});
|
||||
this._editor.dispatch({ changes: changes });
|
||||
});
|
||||
}
|
||||
|
|
|
|||
2
frontend/wailsjs/go/main/App.d.ts
vendored
2
frontend/wailsjs/go/main/App.d.ts
vendored
|
|
@ -2,4 +2,6 @@
|
|||
// This file is automatically generated. DO NOT EDIT
|
||||
import {main} from '../models';
|
||||
|
||||
export function ListProcessors():Promise<Array<main.ListProcessorsResponse>>;
|
||||
|
||||
export function ProcessText(arg1:main.ProcessTextRequest):Promise<void>;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export function ListProcessors() {
|
||||
return window['go']['main']['App']['ListProcessors']();
|
||||
}
|
||||
|
||||
export function ProcessText(arg1) {
|
||||
return window['go']['main']['App']['ProcessText'](arg1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,24 @@
|
|||
export namespace main {
|
||||
|
||||
export class ListProcessorsResponse {
|
||||
name: string;
|
||||
label: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new ListProcessorsResponse(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.name = source["name"];
|
||||
this.label = source["label"];
|
||||
}
|
||||
}
|
||||
export class TextSpan {
|
||||
text: string;
|
||||
pos: number;
|
||||
len: number;
|
||||
append: boolean;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new TextSpan(source);
|
||||
|
|
@ -14,6 +29,7 @@ export namespace main {
|
|||
this.text = source["text"];
|
||||
this.pos = source["pos"];
|
||||
this.len = source["len"];
|
||||
this.append = source["append"];
|
||||
}
|
||||
}
|
||||
export class ProcessTextRequest {
|
||||
|
|
|
|||
9
go.mod
9
go.mod
|
|
@ -1,15 +1,16 @@
|
|||
module dequoter
|
||||
|
||||
go 1.23.3
|
||||
|
||||
toolchain go1.24.3
|
||||
go 1.25
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.10.2
|
||||
lmika.dev/pkg/modash v0.0.0-20250729120720-cdaa1c8abbe3
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
lmika.dev/pkg/modash v0.1.0
|
||||
ucl.lmika.dev v0.1.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/alecthomas/participle/v2 v2.1.1 // indirect
|
||||
github.com/bep/debounce v1.2.1 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
|
|
|
|||
21
go.sum
21
go.sum
|
|
@ -1,3 +1,9 @@
|
|||
github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0=
|
||||
github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
|
||||
github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8=
|
||||
github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c=
|
||||
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
|
||||
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
|
|
@ -10,8 +16,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
||||
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
|
|
@ -34,6 +44,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
|
@ -77,7 +89,12 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lmika.dev/pkg/modash v0.0.0-20250729120720-cdaa1c8abbe3 h1:EqYZdM6ui++F2NPdFCjKAboOr14eVTIIiRfngBgg/WA=
|
||||
lmika.dev/pkg/modash v0.0.0-20250729120720-cdaa1c8abbe3/go.mod h1:8NDl/yR1eCCEhip9FJlVuMNXIeaztQ0Ks/tizExFcTI=
|
||||
lmika.dev/pkg/modash v0.1.0 h1:fltroSvP0nKj9K0E6G+S9LULvB9Qhj47+SZ2b9v/v/c=
|
||||
lmika.dev/pkg/modash v0.1.0/go.mod h1:8NDl/yR1eCCEhip9FJlVuMNXIeaztQ0Ks/tizExFcTI=
|
||||
ucl.lmika.dev v0.1.2 h1:dTqLKGw/pPqE7UrkrJd5qPu2i6BTDzJLaM0cRkJGn6A=
|
||||
ucl.lmika.dev v0.1.2/go.mod h1:f5RzeCTyBO+4k6LYFuDkwGRujnj4/4ONM60AEtQj02k=
|
||||
|
|
|
|||
17
models.go
17
models.go
|
|
@ -1,9 +1,10 @@
|
|||
package main
|
||||
|
||||
type TextSpan struct {
|
||||
Text string `json:"text"`
|
||||
Pos int `json:"pos"`
|
||||
Len int `json:"len"`
|
||||
Text string `json:"text"`
|
||||
Pos int `json:"pos"`
|
||||
Len int `json:"len"`
|
||||
Append bool `json:"append"`
|
||||
}
|
||||
|
||||
type ProcessTextRequest struct {
|
||||
|
|
@ -11,6 +12,16 @@ type ProcessTextRequest struct {
|
|||
Input []TextSpan `json:"input"`
|
||||
}
|
||||
|
||||
type ListProcessorsResponse struct {
|
||||
Name string `json:"name"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
type SetStatusbarMessage struct {
|
||||
Message string `json:"message"`
|
||||
Error bool `json:"error"`
|
||||
}
|
||||
|
||||
type ProcessTextResponse struct {
|
||||
Output []TextSpan `json:"output"`
|
||||
}
|
||||
|
|
|
|||
197
textfilters.go
197
textfilters.go
|
|
@ -3,44 +3,185 @@ package main
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"ucl.lmika.dev/ucl"
|
||||
)
|
||||
|
||||
type TextFilter func(input string) (output string, err error)
|
||||
type TextProcessor struct {
|
||||
Label string
|
||||
Description string
|
||||
|
||||
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
|
||||
// Filters the supplied text. This is used for manipulating the text input.
|
||||
Filter TextFilter
|
||||
|
||||
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 {
|
||||
// Analyses the supplied text. This is used for extracting information from the text input. The result
|
||||
// will be displayed in the status bar. Multiple selected regions will be returned as a single string separated by line numbers.
|
||||
Analyze TextAnalyzer
|
||||
}
|
||||
|
||||
type TextFilter func(ctx context.Context, input string) (resp TextFilterResponse, err error)
|
||||
type TextAnalyzer func(ctx context.Context, input string) (resp string, err error)
|
||||
|
||||
type TextFilterResponse struct {
|
||||
Output string
|
||||
Append bool
|
||||
}
|
||||
|
||||
var TextFilters = map[string]TextProcessor{
|
||||
"upper-case": {
|
||||
Label: "String: To Upper Case",
|
||||
Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) {
|
||||
return TextFilterResponse{Output: strings.ToUpper(input)}, nil
|
||||
},
|
||||
},
|
||||
"lower-case": {
|
||||
Label: "String: To Lower Case",
|
||||
Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) {
|
||||
return TextFilterResponse{Output: strings.ToLower(input)}, nil
|
||||
},
|
||||
},
|
||||
"unquote": {
|
||||
Label: "String: Unquote",
|
||||
Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) {
|
||||
out, err := strconv.Unquote(input)
|
||||
if err != nil {
|
||||
return TextFilterResponse{}, err
|
||||
}
|
||||
return TextFilterResponse{Output: out}, nil
|
||||
},
|
||||
},
|
||||
"join-lines-with-commas": {
|
||||
Label: "Lines: Join with Commas",
|
||||
Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) {
|
||||
return TextFilterResponse{Output: strings.Replace(input, "\n", ",", -1)}, nil
|
||||
},
|
||||
},
|
||||
"split-lines-on-commas": {
|
||||
Label: "Lines: Split on Commas",
|
||||
Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) {
|
||||
return TextFilterResponse{Output: strings.Replace(input, ",", "\n", -1)}, nil
|
||||
},
|
||||
},
|
||||
"trim-space": {
|
||||
Label: "Lines: Trim Spaces",
|
||||
Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) {
|
||||
lines := strings.Split(input, "\n")
|
||||
for i, line := range lines {
|
||||
lines[i] = strings.TrimSpace(line)
|
||||
}
|
||||
result := strings.Join(lines, "\n")
|
||||
return TextFilterResponse{Output: result}, nil
|
||||
},
|
||||
},
|
||||
"format-json": {
|
||||
Label: "JSON: Format",
|
||||
Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) {
|
||||
var dst bytes.Buffer
|
||||
|
||||
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 TextFilterResponse{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return TextFilterResponse{Output: dst.String()}, nil
|
||||
},
|
||||
},
|
||||
"convert-json-to-yaml": {
|
||||
Label: "Convert: JSON to YAML",
|
||||
Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) {
|
||||
var data interface{}
|
||||
if err := json.Unmarshal([]byte(input), &data); err != nil {
|
||||
return TextFilterResponse{}, err
|
||||
}
|
||||
|
||||
var dst bytes.Buffer
|
||||
if err := yaml.NewEncoder(&dst).Encode(data); err != nil {
|
||||
return TextFilterResponse{}, err
|
||||
}
|
||||
|
||||
return TextFilterResponse{Output: dst.String()}, nil
|
||||
},
|
||||
},
|
||||
"convert-yaml-to-json": {
|
||||
Label: "Convert: YAML to JSON",
|
||||
Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) {
|
||||
var data interface{}
|
||||
if err := yaml.Unmarshal([]byte(input), &data); err != nil {
|
||||
return TextFilterResponse{}, err
|
||||
}
|
||||
|
||||
jsonBytes, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return TextFilterResponse{}, err
|
||||
}
|
||||
|
||||
return TextFilterResponse{Output: string(jsonBytes)}, nil
|
||||
},
|
||||
},
|
||||
"lorem-ipsum": {
|
||||
Label: "Generate: Lorem Ipsum",
|
||||
Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) {
|
||||
return TextFilterResponse{
|
||||
Output: "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.",
|
||||
Append: true,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
|
||||
"lines": {
|
||||
Label: "Lines: Count",
|
||||
Analyze: func(ctx context.Context, input string) (result string, err error) {
|
||||
lineCount := len(strings.Split(input, "\n"))
|
||||
return fmt.Sprintf("Lines = %v", lineCount), nil
|
||||
},
|
||||
},
|
||||
|
||||
"ucl-evaluate": {
|
||||
Label: "UCL: Evaluate",
|
||||
Description: "Evaluates the input as a UCL expression and displays the result in the status bar.",
|
||||
Analyze: func(ctx context.Context, input string) (result string, err error) {
|
||||
res, err := uclInstFromContext(ctx).EvalString(ctx, input)
|
||||
if err != nil {
|
||||
if errors.Is(err, ucl.ErrNotConvertable) {
|
||||
return "Evaluated successfully. No result.", err
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return dst.String(), nil
|
||||
return fmt.Sprintf("Result: %v", res), 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
|
||||
|
||||
"ucl-replace": {
|
||||
Label: "UCL: Replace",
|
||||
Description: "Evaluates the input as a UCL expression and replaces it with the result.",
|
||||
Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) {
|
||||
res, err := uclInstFromContext(ctx).EvalString(ctx, input)
|
||||
if err != nil {
|
||||
if errors.Is(err, ucl.ErrNotConvertable) {
|
||||
return TextFilterResponse{}, err
|
||||
}
|
||||
return TextFilterResponse{}, err
|
||||
}
|
||||
|
||||
return TextFilterResponse{Output: fmt.Sprint(res)}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue