Added some more commands and mades some quality of life improvements
All checks were successful
Build / build (push) Successful in 4m27s

This commit is contained in:
Leon Mika 2026-01-25 11:00:40 +11:00
parent 3a23118036
commit 3cb5795ca5
17 changed files with 432 additions and 75 deletions

11
.idea/go.imports.xml Normal file
View 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>

69
app.go
View file

@ -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
}
switch {
case filter.Analyze != nil:
inBfr := strings.Builder{}
for _, span := range req.Input {
if inBfr.Len() > 0 {
inBfr.WriteString("\n")
}
inBfr.WriteString(span.Text)
}
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) {
outStr, err := filter(span.Text)
outRes, err := filter.Filter(a.ctx, span.Text)
if err != nil {
return TextSpan{}, err
}
return TextSpan{
Text: outStr,
Text: outRes.Output,
Pos: span.Pos,
Len: span.Len,
Append: outRes.Append,
}, nil
})
if err != nil {
log.Printf("Error running filter: %s", err)
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
View 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)
}

View file

@ -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>

View file

@ -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%;

View file

@ -35,6 +35,14 @@ export const commandPalette = keymap.of([{
let event = new CustomEvent('dq-showcommands');
window.dispatchEvent(event);
return true;
}
}, {
key: "Cmd-P",
run: () => {
let event = new CustomEvent('dq-rerunlastcommand');
window.dispatchEvent(event);
return true;
}
}]);

View file

@ -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();

View 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");
}
}

View file

@ -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);

View file

@ -4,11 +4,21 @@ class TextProcessor {
setCodeMirrorEditor(editor) {
this._editor = editor;
window.runtime.EventsOn("process-text-response", (data) => {
const changes = data.output.map(span => ({
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 });
});
}

View file

@ -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>;

View file

@ -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);
}

View file

@ -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
View file

@ -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
View file

@ -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=

View file

@ -4,6 +4,7 @@ type TextSpan struct {
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"`
}

View file

@ -3,24 +3,86 @@ 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
// Filters the supplied text. This is used for manipulating the text input.
Filter TextFilter
// 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": func(input string) (output string, err error) {
return strings.ToLower(input), nil
},
"unquote": func(input string) (output string, err error) {
return strconv.Unquote(input)
"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
},
"format-json": func(input string) (output string, err error) {
},
"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))
@ -29,18 +91,97 @@ var TextFilters = map[string]TextFilter{
if err := json.Indent(&dst, []byte(line), "", " "); err == nil {
dst.WriteString("\n")
} else {
return "", err
return TextFilterResponse{}, err
}
}
return dst.String(), nil
return TextFilterResponse{Output: 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 " +
},
"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.", nil
"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 fmt.Sprintf("Result: %v", res), 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
},
},
}