navigation.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package edit
  2. import (
  3. "strings"
  4. "src.elv.sh/pkg/cli"
  5. "src.elv.sh/pkg/cli/modes"
  6. "src.elv.sh/pkg/cli/tk"
  7. "src.elv.sh/pkg/eval"
  8. "src.elv.sh/pkg/eval/vals"
  9. "src.elv.sh/pkg/eval/vars"
  10. "src.elv.sh/pkg/parse"
  11. )
  12. func navInsertSelected(app cli.App) {
  13. w, ok := activeNavigation(app)
  14. if !ok {
  15. return
  16. }
  17. codeArea, ok := focusedCodeArea(app)
  18. if !ok {
  19. return
  20. }
  21. fname := w.SelectedName()
  22. if fname == "" {
  23. // User pressed Alt-Enter or Enter in an empty directory with nothing
  24. // selected; don't do anything.
  25. return
  26. }
  27. codeArea.MutateState(func(s *tk.CodeAreaState) {
  28. dot := s.Buffer.Dot
  29. if dot != 0 && !strings.ContainsRune(" \n", rune(s.Buffer.Content[dot-1])) {
  30. // The dot is not at the beginning of a buffer, and the previous
  31. // character is not a space or newline. Insert a space.
  32. s.Buffer.InsertAtDot(" ")
  33. }
  34. // Insert the selected filename.
  35. s.Buffer.InsertAtDot(parse.Quote(fname))
  36. })
  37. }
  38. func navInsertSelectedAndQuit(app cli.App) {
  39. navInsertSelected(app)
  40. closeMode(app)
  41. }
  42. func convertNavWidthRatio(v any) [3]int {
  43. var (
  44. numbers []int
  45. hasErr bool
  46. )
  47. vals.Iterate(v, func(elem any) bool {
  48. var i int
  49. err := vals.ScanToGo(elem, &i)
  50. if err != nil {
  51. hasErr = true
  52. return false
  53. }
  54. numbers = append(numbers, i)
  55. return true
  56. })
  57. if hasErr || len(numbers) != 3 {
  58. // TODO: Handle the error.
  59. return [3]int{1, 3, 4}
  60. }
  61. var ret [3]int
  62. copy(ret[:], numbers)
  63. return ret
  64. }
  65. func initNavigation(ed *Editor, ev *eval.Evaler, nb eval.NsBuilder) {
  66. bindingVar := newBindingVar(emptyBindingsMap)
  67. bindings := newMapBindings(ed, ev, bindingVar)
  68. widthRatioVar := newListVar(vals.MakeList(1.0, 3.0, 4.0))
  69. selectedFileVar := vars.FromGet(func() any {
  70. if w, ok := activeNavigation(ed.app); ok {
  71. return w.SelectedName()
  72. }
  73. return nil
  74. })
  75. app := ed.app
  76. nb.AddVar("selected-file", selectedFileVar)
  77. nb.AddNs("navigation",
  78. eval.BuildNsNamed("edit:navigation").
  79. AddVars(map[string]vars.Var{
  80. "binding": bindingVar,
  81. "width-ratio": widthRatioVar,
  82. }).
  83. AddGoFns(map[string]any{
  84. "start": func() {
  85. w, err := modes.NewNavigation(app, modes.NavigationSpec{
  86. Bindings: bindings,
  87. Cursor: modes.NewOSNavigationCursor(ev.Chdir),
  88. WidthRatio: func() [3]int {
  89. return convertNavWidthRatio(widthRatioVar.Get())
  90. },
  91. Filter: filterSpec,
  92. })
  93. if err != nil {
  94. app.Notify(modes.ErrorText(err))
  95. } else {
  96. startMode(app, w, nil)
  97. }
  98. },
  99. "left": actOnNavigation(app, modes.Navigation.Ascend),
  100. "right": actOnNavigation(app, modes.Navigation.Descend),
  101. "up": actOnNavigation(app,
  102. func(w modes.Navigation) { w.Select(tk.Prev) }),
  103. "down": actOnNavigation(app,
  104. func(w modes.Navigation) { w.Select(tk.Next) }),
  105. "page-up": actOnNavigation(app,
  106. func(w modes.Navigation) { w.Select(tk.PrevPage) }),
  107. "page-down": actOnNavigation(app,
  108. func(w modes.Navigation) { w.Select(tk.NextPage) }),
  109. "file-preview-up": actOnNavigation(app,
  110. func(w modes.Navigation) { w.ScrollPreview(-1) }),
  111. "file-preview-down": actOnNavigation(app,
  112. func(w modes.Navigation) { w.ScrollPreview(1) }),
  113. "insert-selected": func() { navInsertSelected(app) },
  114. "insert-selected-and-quit": func() { navInsertSelectedAndQuit(app) },
  115. "trigger-filter": actOnNavigation(app,
  116. func(w modes.Navigation) { w.MutateFiltering(neg) }),
  117. "trigger-shown-hidden": actOnNavigation(app,
  118. func(w modes.Navigation) { w.MutateShowHidden(neg) }),
  119. }))
  120. }
  121. func neg(b bool) bool { return !b }
  122. func activeNavigation(app cli.App) (modes.Navigation, bool) {
  123. w, ok := app.ActiveWidget().(modes.Navigation)
  124. return w, ok
  125. }
  126. func actOnNavigation(app cli.App, f func(modes.Navigation)) func() {
  127. return func() {
  128. if w, ok := activeNavigation(app); ok {
  129. f(w)
  130. }
  131. }
  132. }