marks_test.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
  2. package hints
  3. import (
  4. "errors"
  5. "fmt"
  6. "kitty"
  7. "kitty/tools/utils"
  8. "os"
  9. "path/filepath"
  10. "strconv"
  11. "strings"
  12. "testing"
  13. "github.com/google/go-cmp/cmp"
  14. )
  15. var _ = fmt.Print
  16. func TestHintMarking(t *testing.T) {
  17. var opts *Options
  18. cols := 20
  19. cli_args := []string{}
  20. reset := func() {
  21. opts = &Options{Type: "url", UrlPrefixes: "default", Regex: kitty.HintsDefaultRegex}
  22. cols = 20
  23. cli_args = []string{}
  24. }
  25. r := func(text string, url ...string) (marks []Mark) {
  26. ptext := convert_text(text, cols)
  27. ptext, marks, _, err := find_marks(ptext, opts, cli_args...)
  28. if err != nil {
  29. var e *ErrNoMatches
  30. if len(url) != 0 || !errors.As(err, &e) {
  31. t.Fatalf("%#v failed with error: %s", text, err)
  32. }
  33. return
  34. }
  35. actual := utils.Map(func(m Mark) string { return m.Text }, marks)
  36. if diff := cmp.Diff(url, actual); diff != "" {
  37. t.Fatalf("%#v failed:\n%s", text, diff)
  38. }
  39. for _, m := range marks {
  40. q := strings.NewReplacer("\n", "", "\r", "", "\x00", "").Replace(ptext[m.Start:m.End])
  41. if diff := cmp.Diff(m.Text, q); diff != "" {
  42. t.Fatalf("Mark start (%d) and end (%d) dont point to correct offset in text for %#v\n%s", m.Start, m.End, text, diff)
  43. }
  44. }
  45. return
  46. }
  47. reset()
  48. u := `http://test.me/`
  49. r(u, u)
  50. r(u+"#fragme", u+"#fragme")
  51. r(`"`+u+`"`, u)
  52. r("("+u+")", u)
  53. cols = len(u)
  54. r(u+"\nxxx", u+"xxx")
  55. cols = 20
  56. r("link:"+u+"[xxx]", u)
  57. r("`xyz <"+u+">`_.", u)
  58. r(`<a href="`+u+`">moo`, u)
  59. r("\x1b[mhttp://test.me/1234\n\x1b[mx", "http://test.me/1234")
  60. r("\x1b[mhttp://test.me/12345\r\x1b[m6\n\x1b[mx", "http://test.me/123456")
  61. opts.Type = "linenum"
  62. m := func(text, path string, line int) {
  63. ptext := convert_text(text, cols)
  64. _, marks, _, err := find_marks(ptext, opts, cli_args...)
  65. if err != nil {
  66. t.Fatalf("%#v failed with error: %s", text, err)
  67. }
  68. gd := map[string]any{"path": path, "line": strconv.Itoa(line)}
  69. if diff := cmp.Diff(marks[0].Groupdict, gd); diff != "" {
  70. t.Fatalf("%#v failed:\n%s", text, diff)
  71. }
  72. }
  73. m("file.c:23", "file.c", 23)
  74. m("file.c:23:32", "file.c", 23)
  75. m("file.cpp:23:1", "file.cpp", 23)
  76. m("a/file.c:23", "a/file.c", 23)
  77. m("a/file.c:23:32", "a/file.c", 23)
  78. m("~/file.c:23:32", utils.Expanduser("~/file.c"), 23)
  79. reset()
  80. opts.Type = "path"
  81. r("file.c", "file.c")
  82. r("file.c.", "file.c")
  83. r("file.epub.", "file.epub")
  84. r("(file.epub)", "file.epub")
  85. r("some/path", "some/path")
  86. reset()
  87. cols = 60
  88. opts.Type = "ip"
  89. r(`100.64.0.0`, `100.64.0.0`)
  90. r(`2001:0db8:0000:0000:0000:ff00:0042:8329`, `2001:0db8:0000:0000:0000:ff00:0042:8329`)
  91. r(`2001:db8:0:0:0:ff00:42:8329`, `2001:db8:0:0:0:ff00:42:8329`)
  92. r(`2001:db8::ff00:42:8329`, `2001:db8::ff00:42:8329`)
  93. r(`2001:DB8::FF00:42:8329`, `2001:DB8::FF00:42:8329`)
  94. r(`0000:0000:0000:0000:0000:0000:0000:0001`, `0000:0000:0000:0000:0000:0000:0000:0001`)
  95. r(`::1`, `::1`)
  96. r(`255.255.255.256`)
  97. r(`:1`)
  98. reset()
  99. opts.Type = "regex"
  100. opts.Regex = `(?ms)^[*]?\s(\S+)`
  101. r(`* 2b687c2 - test1`, `2b687c2`)
  102. opts.Regex = `(?<=got: )sha256.{4}`
  103. r(`got: sha256-L8=`, `sha256-L8=`)
  104. reset()
  105. opts.Type = "word"
  106. r(`#one (two) 😍 a-1b `, `#one`, `two`, `a-1b`)
  107. r("fōtiz час a\u0310b ", `fōtiz`, `час`, "a\u0310b")
  108. reset()
  109. tdir := t.TempDir()
  110. simple := filepath.Join(tdir, "simple.py")
  111. cli_args = []string{"--customize-processing", simple, "extra1"}
  112. os.WriteFile(simple, []byte(`
  113. def mark(text, args, Mark, extra_cli_args, *a):
  114. import re
  115. for idx, m in enumerate(re.finditer(r'\w+', text)):
  116. start, end = m.span()
  117. mark_text = text[start:end].replace('\n', '').replace('\0', '')
  118. yield Mark(idx, start, end, mark_text, {"idx": idx, "args": extra_cli_args})
  119. `), 0o600)
  120. opts.Type = "regex"
  121. opts.CustomizeProcessing = simple
  122. marks := r("漢字 b", `漢字`, `b`)
  123. if diff := cmp.Diff(marks[0].Groupdict, map[string]any{"idx": float64(0), "args": []any{"extra1"}}); diff != "" {
  124. t.Fatalf("Did not get expected groupdict from custom processor:\n%s", diff)
  125. }
  126. opts.Regex = "b"
  127. os.WriteFile(simple, []byte(""), 0o600)
  128. r("a b", `b`)
  129. }