mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-12 02:42:56 +02:00
Port custom processor for hints
This commit is contained in:
@@ -76,15 +76,15 @@ func parse_input(text string) string {
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Match []string `json:"match"`
|
||||
Programs []string `json:"programs"`
|
||||
Multiple_joiner string `json:"multiple_joiner"`
|
||||
Customize_processing string `json:"customize_processing"`
|
||||
Type string `json:"type"`
|
||||
Groupdicts []map[string]string `json:"groupdicts"`
|
||||
Extra_cli_args []string `json:"extra_cli_args"`
|
||||
Linenum_action string `json:"linenum_action"`
|
||||
Cwd string `json:"cwd"`
|
||||
Match []string `json:"match"`
|
||||
Programs []string `json:"programs"`
|
||||
Multiple_joiner string `json:"multiple_joiner"`
|
||||
Customize_processing string `json:"customize_processing"`
|
||||
Type string `json:"type"`
|
||||
Groupdicts []map[string]any `json:"groupdicts"`
|
||||
Extra_cli_args []string `json:"extra_cli_args"`
|
||||
Linenum_action string `json:"linenum_action"`
|
||||
Cwd string `json:"cwd"`
|
||||
}
|
||||
|
||||
func encode_hint(num int, alphabet string) (res string) {
|
||||
@@ -125,7 +125,7 @@ func main(_ *cli.Command, o *Options, args []string) (rc int, err error) {
|
||||
return 1, nil
|
||||
}
|
||||
input_text := parse_input(utils.UnsafeBytesToString(stdin))
|
||||
text, all_marks, index_map, err := find_marks(input_text, o)
|
||||
text, all_marks, index_map, err := find_marks(input_text, o, os.Args[2:]...)
|
||||
if err != nil {
|
||||
tui.ReportError(err)
|
||||
return 1, nil
|
||||
@@ -313,7 +313,7 @@ func main(_ *cli.Command, o *Options, args []string) (rc int, err error) {
|
||||
return lp.ExitCode(), nil
|
||||
}
|
||||
result.Match = make([]string, len(chosen))
|
||||
result.Groupdicts = make([]map[string]string, len(chosen))
|
||||
result.Groupdicts = make([]map[string]any, len(chosen))
|
||||
for i, m := range chosen {
|
||||
result.Match[i] = m.Text + match_suffix
|
||||
result.Groupdicts[i] = m.Groupdict
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
package hints
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"kitty"
|
||||
"kitty/tools/config"
|
||||
"kitty/tools/utils"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -32,10 +36,13 @@ func default_linenum_regex() string {
|
||||
}
|
||||
|
||||
type Mark struct {
|
||||
Index, Start, End int
|
||||
Text, Group_id string
|
||||
Is_hyperlink bool
|
||||
Groupdict map[string]string
|
||||
Index int `json:"index"`
|
||||
Start int `json:"start"`
|
||||
End int `json:"end"`
|
||||
Text string `json:"text"`
|
||||
Group_id string `json:"group_id"`
|
||||
Is_hyperlink bool `json:"is_hyperlink"`
|
||||
Groupdict map[string]any `json:"groupdict"`
|
||||
}
|
||||
|
||||
func process_escape_codes(text string) (ans string, hyperlinks []Mark) {
|
||||
@@ -302,8 +309,12 @@ func mark(r *regexp.Regexp, post_processors []PostProcessorFunc, group_processor
|
||||
for _, f := range group_processors {
|
||||
f(gd)
|
||||
}
|
||||
gd2 := make(map[string]any, len(gd))
|
||||
for k, v := range gd {
|
||||
gd2[k] = v
|
||||
}
|
||||
ans = append(ans, Mark{
|
||||
Index: i, Start: match_start, End: match_end, Text: full_match, Groupdict: gd,
|
||||
Index: i, Start: match_start, End: match_end, Text: full_match, Groupdict: gd2,
|
||||
})
|
||||
}
|
||||
return
|
||||
@@ -322,18 +333,51 @@ func (self *ErrNoMatches) Error() string {
|
||||
return fmt.Sprintf("No %s found", none_of)
|
||||
}
|
||||
|
||||
func find_marks(text string, opts *Options) (sanitized_text string, ans []Mark, index_map map[int]*Mark, err error) {
|
||||
func find_marks(text string, opts *Options, cli_args ...string) (sanitized_text string, ans []Mark, index_map map[int]*Mark, err error) {
|
||||
sanitized_text, hyperlinks := process_escape_codes(text)
|
||||
pattern, post_processors, group_processors := functions_for(opts)
|
||||
if opts.Type == "hyperlink" {
|
||||
ans = hyperlinks
|
||||
} else {
|
||||
|
||||
run_basic_matching := func() error {
|
||||
pattern, post_processors, group_processors := functions_for(opts)
|
||||
r, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
return "", nil, nil, fmt.Errorf("Failed to compile the regex pattern: %#v with error: %w", pattern, err)
|
||||
return fmt.Errorf("Failed to compile the regex pattern: %#v with error: %w", pattern, err)
|
||||
}
|
||||
ans = mark(r, post_processors, group_processors, sanitized_text, opts)
|
||||
return nil
|
||||
}
|
||||
|
||||
if opts.CustomizeProcessing != "" {
|
||||
cmd := exec.Command(utils.KittyExe(), append([]string{"+runpy", "from kittens.hints.main import custom_marking; custom_marking()"}, cli_args...)...)
|
||||
cmd.Stdin = strings.NewReader(sanitized_text)
|
||||
stdout, stderr := bytes.Buffer{}, bytes.Buffer{}
|
||||
cmd.Stdout, cmd.Stderr = &stdout, &stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
var e *exec.ExitError
|
||||
if errors.As(err, &e) && e.ExitCode() == 2 {
|
||||
err = run_basic_matching()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
goto process_answer
|
||||
} else {
|
||||
return "", nil, nil, fmt.Errorf("Failed to run custom processor %#v with error: %w\n%s", opts.CustomizeProcessing, err, stderr.String())
|
||||
}
|
||||
}
|
||||
ans = make([]Mark, 0, 32)
|
||||
err = json.Unmarshal(stdout.Bytes(), &ans)
|
||||
if err != nil {
|
||||
return "", nil, nil, fmt.Errorf("Failed to load output from custom processor %#v with error: %w", opts.CustomizeProcessing, err)
|
||||
}
|
||||
} else if opts.Type == "hyperlink" {
|
||||
ans = hyperlinks
|
||||
} else {
|
||||
err = run_basic_matching()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
process_answer:
|
||||
if len(ans) == 0 {
|
||||
return "", nil, nil, &ErrNoMatches{Type: opts.Type}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"fmt"
|
||||
"kitty"
|
||||
"kitty/tools/utils"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
@@ -17,11 +19,19 @@ var _ = fmt.Print
|
||||
|
||||
func TestHintMarking(t *testing.T) {
|
||||
|
||||
opts := &Options{Type: "url", UrlPrefixes: "default", Regex: kitty.HintsDefaultRegex}
|
||||
var opts *Options
|
||||
cols := 20
|
||||
r := func(text string, url ...string) {
|
||||
cli_args := []string{}
|
||||
|
||||
reset := func() {
|
||||
opts = &Options{Type: "url", UrlPrefixes: "default", Regex: kitty.HintsDefaultRegex}
|
||||
cols = 20
|
||||
cli_args = []string{}
|
||||
}
|
||||
|
||||
r := func(text string, url ...string) (marks []Mark) {
|
||||
ptext := convert_text(text, cols)
|
||||
_, marks, _, err := find_marks(ptext, opts)
|
||||
_, marks, _, err := find_marks(ptext, opts, cli_args...)
|
||||
if err != nil {
|
||||
var e *ErrNoMatches
|
||||
if len(url) != 0 || !errors.As(err, &e) {
|
||||
@@ -33,8 +43,10 @@ func TestHintMarking(t *testing.T) {
|
||||
if diff := cmp.Diff(url, actual); diff != "" {
|
||||
t.Fatalf("%#v failed:\n%s", text, diff)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
reset()
|
||||
u := `http://test.me/`
|
||||
r(u, u)
|
||||
r(`"`+u+`"`, u)
|
||||
@@ -51,11 +63,11 @@ func TestHintMarking(t *testing.T) {
|
||||
opts.Type = "linenum"
|
||||
m := func(text, path string, line int) {
|
||||
ptext := convert_text(text, cols)
|
||||
_, marks, _, err := find_marks(ptext, opts)
|
||||
_, marks, _, err := find_marks(ptext, opts, cli_args...)
|
||||
if err != nil {
|
||||
t.Fatalf("%#v failed with error: %s", text, err)
|
||||
}
|
||||
gd := map[string]string{"path": path, "line": strconv.Itoa(line)}
|
||||
gd := map[string]any{"path": path, "line": strconv.Itoa(line)}
|
||||
if diff := cmp.Diff(marks[0].Groupdict, gd); diff != "" {
|
||||
t.Fatalf("%#v failed:\n%s", text, diff)
|
||||
}
|
||||
@@ -67,6 +79,7 @@ func TestHintMarking(t *testing.T) {
|
||||
m("a/file.c:23:32", "a/file.c", 23)
|
||||
m("~/file.c:23:32", utils.Expanduser("~/file.c"), 23)
|
||||
|
||||
reset()
|
||||
opts.Type = "path"
|
||||
r("file.c", "file.c")
|
||||
r("file.c.", "file.c")
|
||||
@@ -74,6 +87,7 @@ func TestHintMarking(t *testing.T) {
|
||||
r("(file.epub)", "file.epub")
|
||||
r("some/path", "some/path")
|
||||
|
||||
reset()
|
||||
cols = 60
|
||||
opts.Type = "ip"
|
||||
r(`100.64.0.0`, `100.64.0.0`)
|
||||
@@ -86,4 +100,25 @@ func TestHintMarking(t *testing.T) {
|
||||
r(`255.255.255.256`)
|
||||
r(`:1`)
|
||||
|
||||
reset()
|
||||
tdir := t.TempDir()
|
||||
simple := filepath.Join(tdir, "simple.py")
|
||||
cli_args = []string{"--customize-processing", simple, "extra1"}
|
||||
os.WriteFile(simple, []byte(`
|
||||
def mark(text, args, Mark, extra_cli_args, *a):
|
||||
import re
|
||||
for idx, m in enumerate(re.finditer(r'\w+', text)):
|
||||
start, end = m.span()
|
||||
mark_text = text[start:end].replace('\n', '').replace('\0', '')
|
||||
yield Mark(idx, start, end, mark_text, {"idx": idx, "args": extra_cli_args})
|
||||
`), 0o600)
|
||||
opts.Type = "regex"
|
||||
opts.CustomizeProcessing = simple
|
||||
marks := r("a b", `a`, `b`)
|
||||
if diff := cmp.Diff(marks[0].Groupdict, map[string]any{"idx": float64(0), "args": []any{"extra1"}}); diff != "" {
|
||||
t.Fatalf("Did not get expected groupdict from custom processor:\n%s", diff)
|
||||
}
|
||||
opts.Regex = "b"
|
||||
os.WriteFile(simple, []byte(""), 0o600)
|
||||
r("a b", `b`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user