From 0b4fe2a37cad43116a55d9a80462e936d6131070 Mon Sep 17 00:00:00 2001 From: "Randall C. O'Reilly" Date: Mon, 22 Jul 2024 12:54:14 -0700 Subject: [PATCH] code: finally getting command output syntax highlighting! --- code/commands.go | 63 +++++++++++++++++++++++++----------------------- code/console.go | 4 +-- go.mod | 2 +- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/code/commands.go b/code/commands.go index b7612329..a6949cc7 100644 --- a/code/commands.go +++ b/code/commands.go @@ -8,6 +8,7 @@ import ( "bytes" "encoding/json" "fmt" + "html" "log" "os" "os/exec" @@ -26,6 +27,8 @@ import ( "cogentcore.org/core/parse/lexer" "cogentcore.org/core/styles" "cogentcore.org/core/texteditor" + "cogentcore.org/core/texteditor/highlighting" + "github.com/alecthomas/chroma/v2/lexers" "github.com/mattn/go-shellwords" ) @@ -136,16 +139,6 @@ func (cm *CmdAndArgs) PrepCmd(avp *ArgVarVals) (*exec.Cmd, string) { } cmd := exec.Command(cstr, args...) return cmd, cmdstr - // case "open": // TODO: this doesn't really make sense or seem necessary - // cstr = filetree.OSOpenCommand() - // cmdstr := cstr - // args := cm.BindArgs(avp) - // if args != nil { - // astr := strings.Join(args, " ") - // cmdstr += " " + astr - // } - // cmd := exec.Command(cstr, args...) - // return cmd, cmdstr default: cmdstr := cstr args := cm.BindArgs(avp) @@ -426,13 +419,6 @@ func (cm *Command) PromptUser(cv *Code, buf *texteditor.Buffer, pvals map[string // occurs. Status is updated with status of command exec. User is prompted // for any values that might be needed for command. func (cm *Command) Run(cv *Code, buf *texteditor.Buffer) { - // if cm.Hilight != fileinfo.Unknown { - // buf.Info.Known = cm.Hilight - // buf.Info.Mime = fileinfo.MimeString(fileinfo.Bash) - // buf.Hi.Lang = cm.Hilight.String() - // } - // todo: trying to use native highlighting - // buf.Hi.Init(&buf.Info, nil) if cm.Confirm { d := core.NewBody().AddTitle("Confirm command"). AddText(fmt.Sprintf("Command: %v: %v", cm.Label(), cm.Desc)) @@ -586,9 +572,6 @@ func (cm *Command) RunStatus(cv *Code, buf *texteditor.Buffer, cmdstr string, er fsb := []byte(finstat) buf.AppendTextLineMarkup([]byte(""), []byte(""), texteditor.EditSignal) buf.AppendTextLineMarkup(fsb, cm.MarkupCmdOutput(fsb), texteditor.EditSignal) - // todo: attempt to support syntax highlighting using builtin texteditor formatting - // buf.AppendTextLine([]byte(""), texteditor.EditSignal) - // buf.AppendTextLine(cm.MarkupCmdOutput(fsb), texteditor.EditSignal) buf.AutoScrollEditors() if cm.Focus { cv.FocusOnTabs() @@ -603,24 +586,44 @@ func (cm *Command) LangMatch(lang fileinfo.Known) bool { return fileinfo.IsMatch(cm.Lang, lang) } -// MarkupCmdOutput applies links to the first element in command output line -// if it looks like a file name / position func (cm *Command) MarkupCmdOutput(out []byte) []byte { - flds := strings.Fields(string(out)) - if len(flds) == 0 { + lexName := "" + cmdnm := strings.ToLower(cm.Name) + switch { + case strings.Contains(cmdnm, "diff"): + lexName = cmdnm + } + return MarkupCmdOutput(out, lexName) +} + +// MarkupCmdOutput applies links to the first element in command output line +// if it looks like a file name / position, and runs markup using given lexer +// name if provided (default is "bash") +func MarkupCmdOutput(out []byte, lexName string) []byte { + if len(out) == 0 { return out } + clex := lexers.Get("bash") + if lexName != "" { + nl := lexers.Get(lexName) + if nl != nil { + clex = nl + } + } + uout := html.UnescapeString(string(out)) + flds := strings.Fields(uout) orig, link := lexer.MarkupPathsAsLinks(flds, 2) // only first 2 fields - nt := out + ctags, _ := highlighting.ChromaTagsLine(clex, uout) + mu := highlighting.MarkupLine([]rune(uout), ctags, nil, highlighting.NoEscapeHTML) if len(link) > 0 { - nt = bytes.Replace(out, orig, link, -1) + mu = bytes.Replace(mu, orig, link, -1) } - return nt + return mu } -// MarkupCmdOutput applies links to the first element in command output line +// MarkupStdOutput applies links to the first element in command output line // if it looks like a file name / position -func MarkupCmdOutput(out []byte) []byte { +func MarkupStdOutput(out []byte) []byte { flds := strings.Fields(string(out)) if len(flds) == 0 { return out @@ -628,7 +631,7 @@ func MarkupCmdOutput(out []byte) []byte { orig, link := lexer.MarkupPathsAsLinks(flds, 2) // only first 2 fields nt := out if len(link) > 0 { - nt = bytes.Replace(out, orig, link, -1) + nt = bytes.Replace(nt, orig, link, -1) } return nt } diff --git a/code/console.go b/code/console.go index 5a921d87..791044e9 100644 --- a/code/console.go +++ b/code/console.go @@ -104,7 +104,7 @@ func MarkupStdout(out []byte) []byte { if TheConsole.LogWrite != nil { fmt.Fprintln(TheConsole.LogWrite, string(out)) } - return MarkupCmdOutput(out) + return MarkupCmdOutput(out, "") } func MarkupStderr(out []byte) []byte { @@ -117,7 +117,7 @@ func MarkupStderr(out []byte) []byte { if TheConsole.LogWrite != nil { fmt.Fprintln(TheConsole.LogWrite, string(out)) } - mb := MarkupCmdOutput(out) + mb := MarkupCmdOutput(out, "") mbb := make([]byte, 0, len(mb)+esz) mbb = append(mbb, sst...) mbb = append(mbb, mb...) diff --git a/go.mod b/go.mod index 2d1ac430..c3a2c681 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.22 require ( cogentcore.org/core v0.2.4-0.20240722113400-da3ded05fc74 github.com/aandrew-me/tgpt/v2 v2.7.2 + github.com/alecthomas/chroma/v2 v2.13.0 github.com/bogdanfinn/fhttp v0.5.27 github.com/bogdanfinn/tls-client v1.7.2 github.com/cogentcore/yaegi v0.0.0-20240714043945-dadb98936955 @@ -28,7 +29,6 @@ require ( require ( github.com/Bios-Marcel/wastebasket v0.0.4-0.20240213135800-f26f1ae0a7c4 // indirect github.com/Masterminds/vcs v1.13.3 // indirect - github.com/alecthomas/chroma/v2 v2.13.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/anthonynsimon/bild v0.13.0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect