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  }