github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/utils/readline/vim.go (about) 1 package readline 2 3 import ( 4 "strconv" 5 "strings" 6 7 "github.com/mattn/go-runewidth" 8 ) 9 10 type viMode int 11 12 const ( 13 vimInsert viMode = iota 14 vimReplaceOnce 15 vimReplaceMany 16 vimDelete 17 vimKeys 18 ) 19 20 func (rl *Instance) vi(r rune) string { 21 var output string 22 switch r { 23 case 'a': 24 if rl.line.CellLen() > 0 { 25 output = moveCursorForwardsStr(1) 26 rl.line.SetRunePos(rl.line.RunePos()) 27 } 28 rl.modeViMode = vimInsert 29 rl.viIteration = "" 30 rl.viUndoSkipAppend = true 31 32 case 'A': 33 if rl.line.RuneLen() > 0 { 34 output = moveCursorForwardsStr(rl.line.CellLen() - rl.line.CellPos()) 35 rl.line.SetRunePos(rl.line.RuneLen()) 36 } 37 rl.modeViMode = vimInsert 38 rl.viIteration = "" 39 rl.viUndoSkipAppend = true 40 41 case 'b': 42 rl.viUndoSkipAppend = true 43 vii := rl.getViIterations() 44 for i := 1; i <= vii; i++ { 45 output += rl.moveCursorByRuneAdjustStr(rl.viJumpB(tokeniseLine)) 46 } 47 48 case 'B': 49 rl.viUndoSkipAppend = true 50 vii := rl.getViIterations() 51 for i := 1; i <= vii; i++ { 52 output += rl.moveCursorByRuneAdjustStr(rl.viJumpB(tokeniseSplitSpaces)) 53 } 54 55 case 'd': 56 rl.modeViMode = vimDelete 57 rl.viUndoSkipAppend = true 58 59 case 'D': 60 output = moveCursorBackwardsStr(rl.line.CellPos()) 61 output += strings.Repeat(" ", rl.line.CellLen()) 62 63 output += moveCursorBackwardsStr(rl.line.CellLen() - rl.line.CellPos()) 64 rl.line.Set(rl, rl.line.Runes()[:rl.line.RunePos()]) 65 output += rl.echoStr() 66 67 r := rl.line.Runes()[rl.line.RuneLen()-1] 68 output += moveCursorBackwardsStr(1 + runewidth.RuneWidth(r)) 69 rl.line.SetRunePos(rl.line.RunePos() - 1) 70 rl.viIteration = "" 71 72 case 'e': 73 rl.viUndoSkipAppend = true 74 vii := rl.getViIterations() 75 for i := 1; i <= vii; i++ { 76 output += rl.moveCursorByRuneAdjustStr(rl.viJumpE(tokeniseLine)) 77 } 78 79 case 'E': 80 rl.viUndoSkipAppend = true 81 vii := rl.getViIterations() 82 for i := 1; i <= vii; i++ { 83 output += rl.moveCursorByRuneAdjustStr(rl.viJumpE(tokeniseSplitSpaces)) 84 } 85 86 case 'h': 87 if rl.line.RunePos() > 0 { 88 r := rl.line.Runes()[rl.line.RunePos()-1] 89 output += moveCursorBackwardsStr(runewidth.RuneWidth(r)) 90 rl.line.SetRunePos(rl.line.RunePos() - 1) 91 } 92 rl.viUndoSkipAppend = true 93 94 case 'i': 95 rl.modeViMode = vimInsert 96 rl.viIteration = "" 97 rl.viUndoSkipAppend = true 98 99 case 'I': 100 rl.modeViMode = vimInsert 101 rl.viIteration = "" 102 rl.viUndoSkipAppend = true 103 output += moveCursorBackwardsStr(rl.line.CellPos()) 104 rl.line.SetRunePos(0) 105 106 case 'l': 107 // TODO: test me 108 if (rl.modeViMode == vimInsert && rl.line.RunePos() < rl.line.RuneLen()) || 109 (rl.modeViMode != vimInsert && rl.line.RunePos() < rl.line.RuneLen()-1) { 110 r := rl.line.Runes()[rl.line.RunePos()+1] 111 output += moveCursorForwardsStr(runewidth.RuneWidth(r)) 112 rl.line.SetRunePos(rl.line.RunePos() + 1) 113 } 114 rl.viUndoSkipAppend = true 115 116 case 'p': 117 // paste after 118 if len(rl.line.Runes()) == 0 { 119 return "" 120 } 121 122 rl.viUndoSkipAppend = true 123 w := runewidth.RuneWidth(rl.line.Runes()[rl.line.RunePos()]) 124 125 rl.line.SetRunePos(rl.line.RunePos() + 1) 126 output += moveCursorForwardsStr(w) 127 128 vii := rl.getViIterations() 129 for i := 1; i <= vii; i++ { 130 output += rl.insertStr([]rune(rl.viYankBuffer)) 131 } 132 133 rl.line.SetRunePos(rl.line.RunePos() - 1) 134 output += moveCursorBackwardsStr(w) 135 136 case 'P': 137 // paste before 138 rl.viUndoSkipAppend = true 139 vii := rl.getViIterations() 140 for i := 1; i <= vii; i++ { 141 output += rl.insertStr([]rune(rl.viYankBuffer)) 142 } 143 144 case 'r': 145 rl.modeViMode = vimReplaceOnce 146 rl.viIteration = "" 147 rl.viUndoSkipAppend = true 148 149 case 'R': 150 rl.modeViMode = vimReplaceMany 151 rl.viIteration = "" 152 rl.viUndoSkipAppend = true 153 154 case 'u': 155 output = rl.undoLastStr() 156 rl.viUndoSkipAppend = true 157 158 case 'v': 159 output = rl.clearHelpersStr() 160 var multiline []rune 161 if rl.GetMultiLine == nil { 162 multiline = rl.line.Runes() 163 } else { 164 multiline = rl.GetMultiLine(rl.line.Runes()) 165 } 166 167 new, err := rl.launchEditor(multiline) 168 if err != nil || len(new) == 0 || string(new) == string(multiline) { 169 rl.viUndoSkipAppend = true 170 return "" 171 } 172 rl.clearPrompt() 173 rl.multiline = []byte(string(new)) 174 175 case 'w': 176 rl.viUndoSkipAppend = true 177 vii := rl.getViIterations() 178 for i := 1; i <= vii; i++ { 179 output += rl.moveCursorByRuneAdjustStr(rl.viJumpW(tokeniseLine)) 180 } 181 182 case 'W': 183 rl.viUndoSkipAppend = true 184 vii := rl.getViIterations() 185 for i := 1; i <= vii; i++ { 186 output += rl.moveCursorByRuneAdjustStr(rl.viJumpW(tokeniseSplitSpaces)) 187 } 188 189 case 'x': 190 vii := rl.getViIterations() 191 for i := 1; i <= vii; i++ { 192 output += rl.deleteStr() 193 } 194 if rl.line.RunePos() == rl.line.RuneLen() && rl.line.RuneLen() > 0 { 195 ///// TODO !!!!!!!!!! 196 r := rl.line.Runes()[rl.line.RunePos()-1] 197 output += moveCursorBackwardsStr(runewidth.RuneWidth(r)) 198 rl.line.SetRunePos(rl.line.RunePos() - 1) 199 } 200 201 case 'y', 'Y': 202 rl.viYankBuffer = rl.line.String() 203 rl.viUndoSkipAppend = true 204 //rl.hintText = []rune("-- LINE YANKED --") 205 //rl.renderHelpers() 206 207 case '[': 208 rl.viUndoSkipAppend = true 209 output = rl.moveCursorByRuneAdjustStr(rl.viJumpPreviousBrace()) 210 211 case ']': 212 rl.viUndoSkipAppend = true 213 output = rl.moveCursorByRuneAdjustStr(rl.viJumpNextBrace()) 214 215 case '$': 216 output = moveCursorForwardsStr(rl.line.CellLen() - rl.line.CellPos()) 217 rl.line.SetRunePos(rl.line.RuneLen()) 218 rl.viUndoSkipAppend = true 219 220 case '%': 221 rl.viUndoSkipAppend = true 222 output = rl.moveCursorByRuneAdjustStr(rl.viJumpBracket()) 223 224 default: 225 if r <= '9' && '0' <= r { 226 rl.viIteration += string(r) 227 } 228 rl.viUndoSkipAppend = true 229 230 } 231 232 return output 233 } 234 235 func (rl *Instance) getViIterations() int { 236 i, _ := strconv.Atoi(rl.viIteration) 237 if i < 1 { 238 i = 1 239 } 240 rl.viIteration = "" 241 return i 242 } 243 244 func (rl *Instance) viHintMessageStr() string { 245 switch rl.modeViMode { 246 case vimKeys: 247 rl.hintText = []rune("-- VIM KEYS -- (press `i` to return to normal editing mode)") 248 case vimInsert: 249 rl.hintText = []rune("-- INSERT --") 250 case vimReplaceOnce: 251 rl.hintText = []rune("-- REPLACE CHARACTER --") 252 case vimReplaceMany: 253 rl.hintText = []rune("-- REPLACE --") 254 case vimDelete: 255 rl.hintText = []rune("-- DELETE --") 256 default: 257 rl.getHintText() 258 } 259 260 output := rl.clearHelpersStr() 261 output += rl.renderHelpersStr() 262 return output 263 } 264 265 func (rl *Instance) viJumpB(tokeniser func([]rune, int) ([]string, int, int)) (adjust int) { 266 split, index, pos := tokeniser(rl.line.Runes(), rl.line.RunePos()) 267 switch { 268 case len(split) == 0: 269 return 270 case index == 0 && pos == 0: 271 return 272 case pos == 0: 273 adjust = len(split[index-1]) 274 default: 275 adjust = pos 276 } 277 return adjust * -1 278 } 279 280 func (rl *Instance) viJumpE(tokeniser func([]rune, int) ([]string, int, int)) (adjust int) { 281 split, index, pos := tokeniser(rl.line.Runes(), rl.line.RunePos()) 282 if len(split) == 0 { 283 return 284 } 285 286 word := rTrimWhiteSpace(split[index]) 287 288 switch { 289 case len(split) == 0: 290 return 291 case index == len(split)-1 && pos >= len(word)-1: 292 return 293 case pos >= len(word)-1: 294 word = rTrimWhiteSpace(split[index+1]) 295 adjust = len(split[index]) - pos 296 adjust += len(word) - 1 297 default: 298 adjust = len(word) - pos - 1 299 } 300 return 301 } 302 303 func (rl *Instance) viJumpW(tokeniser func([]rune, int) ([]string, int, int)) (adjust int) { 304 split, index, pos := tokeniser(rl.line.Runes(), rl.line.RunePos()) 305 switch { 306 case len(split) == 0: 307 return 308 case index+1 == len(split): 309 adjust = rl.line.RuneLen() - 1 - rl.line.RunePos() 310 default: 311 adjust = len(split[index]) - pos 312 } 313 return 314 } 315 316 func (rl *Instance) viJumpPreviousBrace() (adjust int) { 317 if rl.line.RunePos() == 0 { 318 return 0 319 } 320 321 for i := rl.line.RunePos() - 1; i != 0; i-- { 322 if rl.line.Runes()[i] == '{' { 323 return i - rl.line.RunePos() 324 } 325 } 326 327 return 0 328 } 329 330 func (rl *Instance) viJumpNextBrace() (adjust int) { 331 if rl.line.RunePos() >= rl.line.RuneLen()-1 { 332 return 0 333 } 334 335 for i := rl.line.RunePos() + 1; i < rl.line.RuneLen(); i++ { 336 if rl.line.Runes()[i] == '{' { 337 return i - rl.line.RunePos() 338 } 339 } 340 341 return 0 342 } 343 344 func (rl *Instance) viJumpBracket() (adjust int) { 345 split, index, pos := tokeniseBrackets(rl.line.Runes(), rl.line.RunePos()) 346 switch { 347 case len(split) == 0: 348 return 349 case pos == 0: 350 adjust = len(split[index]) 351 default: 352 adjust = pos * -1 353 } 354 return 355 }