Added go template processor
All checks were successful
Build / build (push) Successful in 34s

This commit is contained in:
Leon Mika 2026-03-14 22:47:56 +11:00
parent d64779f660
commit 855a0aa114
2 changed files with 87 additions and 22 deletions

59
app.go
View file

@ -91,6 +91,33 @@ func (a *App) ProcessText(req ProcessTextRequest) {
return return
} }
applyFilter := func(filter TextFilter) {
resp, err := moslice.MapWithError(req.Input, func(span TextSpan) (TextSpan, error) {
outRes, err := 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,
})
}
switch { switch {
case filter.Analyze != nil: case filter.Analyze != nil:
inBfr := strings.Builder{} inBfr := strings.Builder{}
@ -114,30 +141,18 @@ func (a *App) ProcessText(req ProcessTextRequest) {
Message: msg, Message: msg,
}) })
case filter.Filter != nil: case filter.Filter != nil:
resp, err := moslice.MapWithError(req.Input, func(span TextSpan) (TextSpan, error) { applyFilter(filter.Filter)
outRes, err := filter.Filter(a.ctx, span.Text) case filter.FilterWithArg != nil:
a.PromptUser(filter.FilterWithArg.Label, func(ans string) {
filter, err := filter.FilterWithArg.OnConfirm(a.ctx, ans)
if err != nil { if err != nil {
return TextSpan{}, err runtime.EventsEmit(a.ctx, "set-statusbar-message", SetStatusbarMessage{
Message: fmt.Sprintf("Error running filter: %v", err.Error()),
Error: true,
})
return
} }
applyFilter(filter)
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,
}) })
} }
} }

View file

@ -10,6 +10,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"text/template"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"ucl.lmika.dev/ucl" "ucl.lmika.dev/ucl"
) )
@ -24,11 +26,20 @@ type TextProcessor struct {
// Analyses the supplied text. This is used for extracting information from the text input. The result // 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. // will be displayed in the status bar. Multiple selected regions will be returned as a single string separated by line numbers.
Analyze TextAnalyzer Analyze TextAnalyzer
// FilterWithArg requests some input from the user. If the user supplies it, it will return a text filter
// to process the input.
FilterWithArg *FilterWithArg
} }
type TextFilter func(ctx context.Context, input string) (resp TextFilterResponse, err error) 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 TextAnalyzer func(ctx context.Context, input string) (resp string, err error)
type FilterWithArg struct {
Label string
OnConfirm func(ctx context.Context, input string) (filter TextFilter, err error)
}
type TextFilterResponse struct { type TextFilterResponse struct {
Output string Output string
Append bool Append bool
@ -165,6 +176,45 @@ var TextFilters = map[string]TextProcessor{
}, },
}, },
"template-each-line": {
Label: "Lines: Go Template…",
Description: "Evaluates the input as a Go template and replaces each line with the result.",
FilterWithArg: &FilterWithArg{
Label: "Template",
OnConfirm: func(ctx context.Context, prompt string) (filter TextFilter, err error) {
tmpl, err := template.New("").Parse(prompt)
if err != nil {
return nil, err
}
return func(ctx context.Context, input string) (resp TextFilterResponse, err error) {
var (
dst bytes.Buffer
dstLine bytes.Buffer
)
scnr := bufio.NewScanner(strings.NewReader(input))
isFirst := true
for scnr.Scan() {
if isFirst {
isFirst = false
} else {
dst.WriteString("\n")
}
dstLine.Reset()
line := scnr.Text()
if err := tmpl.Execute(&dstLine, line); err != nil {
return TextFilterResponse{}, err
}
dst.WriteString(dstLine.String())
}
return TextFilterResponse{Output: dst.String()}, nil
}, nil
},
},
},
"ucl-evaluate": { "ucl-evaluate": {
Label: "UCL: Evaluate", Label: "UCL: Evaluate",
Description: "Evaluates the input as a UCL expression and displays the result in the status bar.", Description: "Evaluates the input as a UCL expression and displays the result in the status bar.",