From 855a0aa1142461c9bf094a9a9127407604231db7 Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Sat, 14 Mar 2026 22:47:56 +1100 Subject: [PATCH] Added go template processor --- app.go | 59 +++++++++++++++++++++++++++++++------------------- textfilters.go | 50 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 22 deletions(-) diff --git a/app.go b/app.go index a0d3a74..7148f8b 100644 --- a/app.go +++ b/app.go @@ -91,6 +91,33 @@ func (a *App) ProcessText(req ProcessTextRequest) { 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 { case filter.Analyze != nil: inBfr := strings.Builder{} @@ -114,30 +141,18 @@ func (a *App) ProcessText(req ProcessTextRequest) { 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) + applyFilter(filter.Filter) + case filter.FilterWithArg != nil: + a.PromptUser(filter.FilterWithArg.Label, func(ans string) { + filter, err := filter.FilterWithArg.OnConfirm(a.ctx, ans) 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 } - - 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, + applyFilter(filter) }) } } diff --git a/textfilters.go b/textfilters.go index 57ebdf1..7945a04 100644 --- a/textfilters.go +++ b/textfilters.go @@ -10,6 +10,8 @@ import ( "strconv" "strings" + "text/template" + "gopkg.in/yaml.v3" "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 // will be displayed in the status bar. Multiple selected regions will be returned as a single string separated by line numbers. 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 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 { Output string 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": { Label: "UCL: Evaluate", Description: "Evaluates the input as a UCL expression and displays the result in the status bar.",