github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/edit/navigation.go (about) 1 package edit 2 3 import ( 4 "strings" 5 6 "src.elv.sh/pkg/cli" 7 "src.elv.sh/pkg/cli/mode" 8 "src.elv.sh/pkg/cli/tk" 9 "src.elv.sh/pkg/eval" 10 "src.elv.sh/pkg/eval/vals" 11 "src.elv.sh/pkg/eval/vars" 12 "src.elv.sh/pkg/parse" 13 ) 14 15 //elvdoc:var selected-file 16 // 17 // Name of the currently selected file in navigation mode. $nil if not in 18 // navigation mode. 19 20 //elvdoc:var navigation:binding 21 // 22 // Keybinding for the navigation mode. 23 24 //elvdoc:fn navigation:start 25 // 26 // Start the navigation mode. 27 28 //elvdoc:fn navigation:insert-selected 29 // 30 // Inserts the selected filename. 31 32 func navInsertSelected(app cli.App) { 33 w, ok := getNavigation(app) 34 if !ok { 35 return 36 } 37 fname := w.SelectedName() 38 if fname == "" { 39 // User pressed Alt-Enter or Enter in an empty directory with nothing 40 // selected; don't do anything. 41 return 42 } 43 44 app.CodeArea().MutateState(func(s *tk.CodeAreaState) { 45 dot := s.Buffer.Dot 46 if dot != 0 && !strings.ContainsRune(" \n", rune(s.Buffer.Content[dot-1])) { 47 // The dot is not at the beginning of a buffer, and the previous 48 // character is not a space or newline. Insert a space. 49 s.Buffer.InsertAtDot(" ") 50 } 51 // Insert the selected filename. 52 s.Buffer.InsertAtDot(parse.Quote(fname)) 53 }) 54 } 55 56 //elvdoc:fn navigation:insert-selected-and-quit 57 // 58 // Inserts the selected filename and closes the navigation addon. 59 60 func navInsertSelectedAndQuit(app cli.App) { 61 navInsertSelected(app) 62 closeMode(app) 63 } 64 65 //elvdoc:fn navigation:trigger-filter 66 // 67 // Toggles the filtering status of the navigation addon. 68 69 //elvdoc:fn navigation:trigger-shown-hidden 70 // 71 // Toggles whether the navigation addon should be showing hidden files. 72 73 //elvdoc:var navigation:width-ratio 74 // 75 // A list of 3 integers, used for specifying the width ratio of the 3 columns in 76 // navigation mode. 77 78 func convertNavWidthRatio(v interface{}) [3]int { 79 var ( 80 numbers []int 81 hasErr bool 82 ) 83 vals.Iterate(v, func(elem interface{}) bool { 84 var i int 85 err := vals.ScanToGo(elem, &i) 86 if err != nil { 87 hasErr = true 88 return false 89 } 90 numbers = append(numbers, i) 91 return true 92 }) 93 if hasErr || len(numbers) != 3 { 94 // TODO: Handle the error. 95 return [3]int{1, 3, 4} 96 } 97 var ret [3]int 98 copy(ret[:], numbers) 99 return ret 100 } 101 102 func initNavigation(ed *Editor, ev *eval.Evaler, nb eval.NsBuilder) { 103 bindingVar := newBindingVar(emptyBindingsMap) 104 bindings := newMapBindings(ed, ev, bindingVar) 105 widthRatioVar := newListVar(vals.MakeList(1.0, 3.0, 4.0)) 106 107 selectedFileVar := vars.FromGet(func() interface{} { 108 if w, ok := getNavigation(ed.app); ok { 109 return w.SelectedName() 110 } 111 return nil 112 }) 113 114 app := ed.app 115 nb.Add("selected-file", selectedFileVar) 116 nb.AddNs("navigation", 117 eval.NsBuilder{ 118 "binding": bindingVar, 119 "width-ratio": widthRatioVar, 120 }.AddGoFns("<edit:navigation>", map[string]interface{}{ 121 "start": func() { 122 w := mode.NewNavigation(app, mode.NavigationSpec{ 123 Bindings: bindings, 124 WidthRatio: func() [3]int { 125 return convertNavWidthRatio(widthRatioVar.Get()) 126 }, 127 Filter: filterSpec, 128 }) 129 startMode(app, w, nil) 130 }, 131 "left": actOnNavigation(app, mode.Navigation.Ascend), 132 "right": actOnNavigation(app, mode.Navigation.Descend), 133 "up": actOnNavigation(app, 134 func(w mode.Navigation) { w.Select(tk.Prev) }), 135 "down": actOnNavigation(app, 136 func(w mode.Navigation) { w.Select(tk.Next) }), 137 "page-up": actOnNavigation(app, 138 func(w mode.Navigation) { w.Select(tk.PrevPage) }), 139 "page-down": actOnNavigation(app, 140 func(w mode.Navigation) { w.Select(tk.NextPage) }), 141 142 "file-preview-up": actOnNavigation(app, 143 func(w mode.Navigation) { w.ScrollPreview(-1) }), 144 "file-preview-down": actOnNavigation(app, 145 func(w mode.Navigation) { w.ScrollPreview(1) }), 146 147 "insert-selected": func() { navInsertSelected(app) }, 148 "insert-selected-and-quit": func() { navInsertSelectedAndQuit(app) }, 149 150 "trigger-filter": actOnNavigation(app, 151 func(w mode.Navigation) { w.MutateFiltering(neg) }), 152 "trigger-shown-hidden": actOnNavigation(app, 153 func(w mode.Navigation) { w.MutateShowHidden(neg) }), 154 }).Ns()) 155 } 156 157 func neg(b bool) bool { return !b } 158 159 func getNavigation(app cli.App) (mode.Navigation, bool) { 160 w, ok := app.CopyState().Addon.(mode.Navigation) 161 return w, ok 162 } 163 164 func actOnNavigation(app cli.App, f func(mode.Navigation)) func() { 165 return func() { 166 if w, ok := getNavigation(app); ok { 167 f(w) 168 } 169 } 170 }