package readline

import (
	

	
	
	
)

// commands maps widget names to their implementation.
type commands map[string]func()

// viCommands returns all Vim commands.
// Under each comment are gathered all commands related to the comment's
// subject. When there are two subgroups separated by an empty line, the
// second one comprises commands that are not legacy readline commands.
//
// Modes
// Moving
// Changing text
// Killing and Yanking
// Selecting text
// Miscellaneous.
func ( *Shell) () commands {
	return map[string]func(){
		// Modes
		"vi-append-mode":    .viAddNext,
		"vi-append-eol":     .viAddEol,
		"vi-insertion-mode": .viInsertMode,
		"vi-insert-beg":     .viInsertBol,
		"vi-movement-mode":  .viCommandMode,
		"vi-visual-mode":    .viVisualMode,
		"vi-editing-mode":   .viInsertMode,

		"vi-visual-line-mode": .viVisualLineMode,

		// Movement
		"vi-backward-char":    .viBackwardChar,
		"vi-forward-char":     .viForwardChar,
		"vi-prev-word":        .viBackwardWord,
		"vi-next-word":        .viForwardWord,
		"vi-backward-word":    .viBackwardWord,
		"vi-forward-word":     .viForwardWord,
		"vi-backward-bigword": .viBackwardBlankWord,
		"vi-forward-bigword":  .viForwardBlankWord,
		"vi-end-word":         .viForwardWordEnd,
		"vi-end-bigword":      .viForwardBlankWordEnd,
		"vi-match":            .viMatchBracket,
		"vi-column":           .viGotoColumn,
		"vi-end-of-line":      .viEndOfLine,
		"vi-back-to-indent":   .viBackToIndent,
		"vi-first-print":      .viFirstPrint,
		"vi-goto-mark":        .viGotoMark,

		"vi-backward-end-word":    .viBackwardWordEnd,
		"vi-backward-end-bigword": .viBackwardBlankWordEnd,

		// Changing text
		"vi-change-to":            .viChangeTo,
		"vi-delete-to":            .viDeleteTo,
		"vi-delete":               .viDeleteChar,
		"vi-change-char":          .viChangeChar,
		"vi-backward-delete-char": .viBackwardDeleteChar,
		"vi-replace":              .viReplace, // missing vi-overstrike-delete
		"vi-overstrike":           .viReplace,
		"vi-change-case":          .viChangeCase,
		"vi-subst":                .viSubstitute,

		"vi-change-eol":      .viChangeEol,
		"vi-add-surround":    .viAddSurround,
		"vi-open-line-above": .viOpenLineAbove,
		"vi-open-line-below": .viOpenLineBelow,
		"vi-down-case":       .viDownCase,
		"vi-up-case":         .viUpCase,

		// Kill and Yanking
		"vi-kill-eol":         .viKillEol,
		"vi-unix-word-rubout": .backwardKillWord,
		"vi-rubout":           .viRubout,
		"vi-yank-to":          .viYankTo,
		"vi-yank-pop":         .yankPop,
		"vi-yank-arg":         .yankLastArg,

		"vi-kill-line":       .viKillLine,
		"vi-put":             .viPut,
		"vi-put-after":       .viPutAfter,
		"vi-put-before":      .viPutBefore,
		"vi-set-buffer":      .viSetBuffer,
		"vi-yank-whole-line": .viYankWholeLine,

		// Selecting text
		"select-a-blank-word":  .viSelectABlankWord,
		"select-a-shell-word":  .viSelectAShellWord,
		"select-a-word":        .viSelectAWord,
		"select-in-blank-word": .viSelectInBlankWord,
		"select-in-shell-word": .viSelectInShellWord,
		"select-in-word":       .viSelectInWord,
		"vi-select-inside":     .viSelectInside,
		"vi-select-surround":   .viSelectSurround,

		// Miscellaneous
		"vi-eof-maybe":                .viEOFMaybe,
		"vi-search":                   .viSearch,
		"vi-search-again":             .viSearchAgain,
		"vi-arg-digit":                .viArgDigit,
		"vi-char-search":              .viCharSearch,
		"vi-set-mark":                 .viSetMark,
		"vi-edit-and-execute-command": .viEditAndExecuteCommand,
		"vi-undo":                     .undoLast,
		"vi-redo":                     .viRedo,

		"vi-edit-command-line":     .viEditCommandLine,
		"vi-find-next-char":        .viFindNextChar,
		"vi-find-next-char-skip":   .viFindNextCharSkip,
		"vi-find-prev-char":        .viFindPrevChar,
		"vi-find-prev-char-skip":   .viFindPrevCharSkip,
		"vi-search-forward":        .viSearchForward,
		"vi-search-backward":       .viSearchBackward,
		"vi-search-again-forward":  .viSearchAgainForward,
		"vi-search-again-backward": .viSearchAgainBackward,
	}
}

//
// Modes ----------------------------------------------------------------
//

// Enter Vim insertion mode.
func ( *Shell) () {
	.History.Save()

	// Reset any visual selection and iterations.
	.selection.Reset()
	.Iterations.Reset()
	.Buffers.Reset()

	// Change the keymap and mark the insertion point.
	.Keymap.SetLocal("")
	.Keymap.SetMain(keymap.ViInsert)
	.cursor.SetMark()
}

// Enter Vim command mode.
func ( *Shell) () {
	// Reset any visual selection and iterations.
	.selection.Reset()
	.Iterations.Reset()
	.Buffers.Reset()

	// Cancel completions and hints if any, and reassign the
	// current line/cursor/selection for the cursor check below
	// to be effective. This is needed when in isearch mode.
	.Hint.Reset()
	.completer.Reset()
	.line, .cursor, .selection = .completer.GetBuffer()

	// Only go back if not in insert mode
	if .Keymap.Main() == keymap.ViInsert && !.cursor.AtBeginningOfLine() {
		.cursor.Dec()
	}

	// Update the cursor position, keymap and insertion point.
	.cursor.CheckCommand()
	.Keymap.SetLocal("")
	.Keymap.SetMain(keymap.ViCommand)
}

// Enter Vim visual mode.
func ( *Shell) () {
	.History.SkipSave()
	.Iterations.Reset()
	.Buffers.Reset()

	// Cancel completions and hints if any.
	.Hint.Reset()
	.completer.Reset()

	// Mark the selection as visual at the current cursor position.
	.selection.Mark(.cursor.Pos())
	.selection.Visual(false)
	.Keymap.SetLocal(keymap.Visual)
}

// Enter Vim visual mode, selecting the entire
// line on which the cursor is currently.
func ( *Shell) () {
	.History.SkipSave()
	.Iterations.Reset()
	.Buffers.Reset()

	.Hint.Reset()
	.completer.Reset()

	// Mark the selection as visual at the current
	// cursor position, in visual line mode.
	.selection.Mark(.cursor.Pos())
	.selection.Visual(true)
	.Keymap.SetLocal(keymap.Visual)

	.Keymap.PrintCursor(keymap.Visual)
}

// Go to the beginning of the current line, and enter Vim insert mode.
func ( *Shell) () {
	.Iterations.Reset()
	.beginningOfLine()
	.viInsertMode()
}

// Enter insert mode on the next character.
func ( *Shell) () {
	if .line.Len() > 0 {
		.cursor.Inc()
	}

	.viInsertMode()
}

// Go to the end of the current line, and enter insert mode.
func ( *Shell) () {
	.Iterations.Reset()

	if .Keymap.Local() == keymap.Visual {
		.cursor.Inc()
		.viInsertMode()

		return
	}

	.endOfLine()
	.viInsertMode()
}

//
// Movement -------------------------------------------------------------
//

// Move forward one character, without changing lines.
func ( *Shell) () {
	// Only exception where we actually don't forward a character.
	if .Config.GetBool("history-autosuggest") && .cursor.Pos() == .line.Len()-1 {
		.autosuggestAccept()
		return
	}

	.History.SkipSave()

	// In vi-cmd-mode, we don't go further than the
	// last character in the line, hence rl.line-1
	if .Keymap.Main() != keymap.ViInsert && .cursor.Pos() < .line.Len()-1 {
		 := .Iterations.Get()

		for  := 1;  <= ; ++ {
			if (*.line)[.cursor.Pos()+1] == '\n' {
				break
			}

			.cursor.Inc()
		}
	}
}

// Move backward one character, without changing lines.
func ( *Shell) () {
	.History.SkipSave()

	 := .Iterations.Get()

	if .cursor.Pos() == 0 {
		return
	}

	for  := 1;  <= ; ++ {
		if (*.line)[.cursor.Pos()-1] == '\n' {
			break
		}

		.cursor.Dec()
	}
}

// Move to the beginning of the previous word, vi-style.
func ( *Shell) () {
	.History.SkipSave()

	 := .Iterations.Get()
	for  := 1;  <= ; ++ {
		 := .line.Backward(.line.Tokenize, .cursor.Pos())
		.cursor.Move()
	}
}

// Move to the beginning of the next word.
func ( *Shell) () {
	.History.Save()

	 := .Iterations.Get()
	for  := 1;  <= ; ++ {
		// When we have an autosuggested history and if we are at the end
		// of the line, insert the next word from this suggested line.
		.insertAutosuggestPartial(false)

		 := .line.Forward(.line.Tokenize, .cursor.Pos())
		.cursor.Move()
	}
}

// Move backward one word, where a word is defined as a series of non-blank characters.
func ( *Shell) () {
	.History.SkipSave()

	 := .Iterations.Get()
	for  := 1;  <= ; ++ {
		 := .line.Backward(.line.TokenizeSpace, .cursor.Pos())
		.cursor.Move()
	}
}

// Move forward one word, where a word is defined as a series of non-blank characters.
func ( *Shell) () {
	.History.SkipSave()

	 := .Iterations.Get()
	for  := 1;  <= ; ++ {
		 := .line.Forward(.line.TokenizeSpace, .cursor.Pos())
		.cursor.Move()
	}
}

// Move to the end of the previous word, vi-style.
func ( *Shell) () {
	.History.SkipSave()

	 := .Iterations.Get()

	if .line.Len() == 0 {
		return
	}

	for  := 1;  <= ; ++ {
		.cursor.Inc()

		.cursor.Move(.line.Backward(.line.Tokenize, .cursor.Pos()))
		.cursor.Move(.line.Backward(.line.Tokenize, .cursor.Pos()))

		// Then move forward, adjusting if we are on a punctuation.
		if unicode.IsPunct(.cursor.Char()) {
			.cursor.Dec()
		}

		.cursor.Move(.line.ForwardEnd(.line.Tokenize, .cursor.Pos()))
	}
}

func ( *Shell) () {
	.History.SkipSave()
	 := .Iterations.Get()

	for  := 1;  <= ; ++ {
		 := .line.ForwardEnd(.line.Tokenize, .cursor.Pos())
		.cursor.Move()
	}
}

// Move to the end of the previous word, where a word is defined as a series of non-blank characters.
func ( *Shell) () {
	.History.SkipSave()

	 := .Iterations.Get()

	for  := 1;  <= ; ++ {
		.cursor.Inc()

		.cursor.Move(.line.Backward(.line.TokenizeSpace, .cursor.Pos()))
		.cursor.Move(.line.Backward(.line.TokenizeSpace, .cursor.Pos()))

		.cursor.Move(.line.ForwardEnd(.line.TokenizeSpace, .cursor.Pos()))
	}
}

// Move to the end of the current word, or, if at the end
// of the current word, to the end of the next word, where
// a word is defined as a series of non-blank characters.
func ( *Shell) () {
	.History.SkipSave()
	 := .Iterations.Get()

	for  := 1;  <= ; ++ {
		.cursor.Move(.line.ForwardEnd(.line.TokenizeSpace, .cursor.Pos()))
	}
}

// Move to the bracket character (one of {}, () or []) that matches the one under
// the cursor. If the cursor is not on a bracket character, move forward without
// going past the end of the line to find one, and then go to the matching bracket.
func ( *Shell) () {
	.History.SkipSave()

	 := .cursor.Pos()
	 := false

	// If we are on a bracket/brace/parenthesis, we just find the matcher
	if !strutil.IsBracket(.cursor.Char()) {
		for  := .cursor.Pos() + 1;  < .line.Len(); ++ {
			 := (*.line)[]
			if  == '}' ||  == ')' ||  == ']' {
				 =  - .cursor.Pos()
				 = true

				break
			}
		}

		if ! {
			return
		}

		.cursor.Move()
	}

	var  int

	, ,  := .line.TokenizeBlock(.cursor.Pos())

	switch {
	case len() == 0:
		return
	case  == 0:
		 = len([])
	default:
		 =  * -1
	}

	.cursor.Move()
}

// Move to the column specified by the numeric argument.
func ( *Shell) () {
	.History.SkipSave()

	 := .Iterations.Get()

	if  < 0 {
		return
	}

	 := .cursor.Pos()

	.cursor.BeginningOfLine()
	 := .cursor.Pos()
	.cursor.EndOfLine()
	 := .cursor.Pos()

	.cursor.Set()

	switch {
	case  > -:
		.cursor.Set()
	default:
		.cursor.Set( +  - 1)
	}
}

// Move to the end of the line, vi-style.
func ( *Shell) () {
	.History.SkipSave()
	// We use append so that any y$ / d$
	// will include the last character.
	.cursor.EndOfLineAppend()
}

// Move to the first non-blank character after cursor.
func ( *Shell) () {
	.cursor.BeginningOfLine()
	.cursor.ToFirstNonSpace(true)
}

// Move to the first non-blank character in the line.
func ( *Shell) () {
	.cursor.BeginningOfLine()
	.cursor.ToFirstNonSpace(true)
}

// Move to the specified mark.
func ( *Shell) () {
	switch {
	case .selection.Active():
		// We either an active selection, in which case
		// we go to the position (begin or end) that is
		// set and not equal to the cursor.
		,  := .selection.Pos()
		if  != .cursor.Pos() {
			.cursor.Set()
		} else {
			.cursor.Set()
		}

	case .cursor.Mark() != -1:
		// Or we go to the cursor mark, which was set when
		// entering insert mode. This might have no effect.
		.cursor.Set(.cursor.Mark())
	}
}

//
// Changing Text --------------------------------------------------------
//

// Read a movement command from the keyboard, and kill from the cursor
// position to the endpoint of the movement. Then enter insert mode.
// If the command is vi-change, change the current line.
func ( *Shell) () {
	switch {
	case .Keymap.IsPending():
		// In vi operator pending mode, it's that we've been called
		// twice in a row (eg. `cc`), so copy the entire current line.
		.Keymap.CancelPending()
		.History.Save()

		.selection.Mark(.cursor.Pos())
		.selection.Visual(true)
		.selection.Cut()
		.viInsertMode()

	case len(.selection.Surrounds()) == 2:
		// In surround selection mode, change the surrounding chars.
		.Display.Refresh()
		defer .selection.Reset()

		// Now read another key
		 := .Keymap.PendingCursor()
		defer ()

		,  := .Keys.ReadKey()
		if  {
			return
		}

		.History.Save()

		// There might be a matching equivalent.
		,  := strutil.MatchSurround()

		 := .selection.Surrounds()

		,  := [0].Pos()
		,  := [1].Pos()

		(*.line)[] = 
		(*.line)[] = 

	case .selection.Active():
		// In visual mode, we have just have a selection to delete.
		.History.Save()

		.adjustSelectionPending()
		 := .selection.Cursor()
		 := .selection.Cut()
		.Buffers.Write([]rune()...)
		.cursor.Set()

		.viInsertMode()

	default:
		// Since we must emulate the default readline behavior,
		// we vary our behavior depending on the caller key.
		 := .Keys.Caller()

		switch [0] {
		case 'c':
			.Keymap.Pending()
			.selection.Mark(.cursor.Pos())
		case 'C':
			.viChangeEol()
		}
	}
}

// Read a movement command from the keyboard, and kill from the cursor
// position to the endpoint of the movement. If the command is vi-delete,
// kill the current line.
func ( *Shell) () {
	switch {
	case .Keymap.IsPending():
		// In vi operator pending mode, it's that we've been called
		// twice in a row (eg. `dd`), so delete the entire current line.
		.Keymap.CancelPending()
		.History.Save()

		.selection.Mark(.cursor.Pos())
		.selection.Visual(true)
		 := .selection.Cursor()

		 := .selection.Cut()

		// Get buffer and add newline if there isn't one at the end
		if len() > 0 && rune([len()-1]) != inputrc.Newline {
			 += string(inputrc.Newline)
		}

		.Buffers.Write([]rune()...)
		.cursor.Set()

	case .selection.Active():
		// In visual mode, or with a non-empty selection, just cut it.
		.History.Save()

		.adjustSelectionPending()
		 := .selection.Cursor()
		 := .selection.Cut()
		.Buffers.Write([]rune()...)
		.cursor.Set()

		.viCommandMode()

	default:
		// Since we must emulate the default readline behavior,
		// we vary our behavior depending on the caller key.
		 := .Keys.Caller()

		switch [0] {
		case 'd':
			.Keymap.Pending()
			.selection.Mark(.cursor.Pos())
		case 'D':
			.viKillEol()
		}
	}
}

// Delete the character under the cursor, without going past the end of the line.
func ( *Shell) () {
	if .line.Len() == 0 || .cursor.Pos() == .line.Len() {
		return
	}

	.History.Save()

	 := make([]rune, 0)

	 := .Iterations.Get()

	for  := 1;  <= ; ++ {
		 = append(, .cursor.Char())
		.line.CutRune(.cursor.Pos())
	}

	.Buffers.Write(...)
}

// Replace the character under the cursor with a character read from the keyboard.
func ( *Shell) () {
	.History.Save()

	// We read a character to use first.
	 := .Keymap.PendingCursor()
	defer ()

	,  := .Keys.ReadKey()
	if  {
		.History.SkipSave()
		return
	}

	switch {
	case .selection.Active() && .selection.IsVisual():
		// In visual mode, we replace all chars of the selection
		.selection.ReplaceWith(func( rune) rune {
			return 
		})
	default:
		// Or simply the character under the cursor.
		.cursor.ReplaceWith()
	}
}

// Delete the character behind the cursor, without changing lines.
func ( *Shell) () {
	if !.cursor.AtBeginningOfLine() {
		.backwardDeleteChar()
	}
}

// Enter overwrite mode.
func ( *Shell) () {
	// The the standard emacs replace loop,
	// which blocks until the ESC is pressed
	.overwriteMode()

	// And after exiting, move the cursor back
	.cursor.Dec()
}

// Swap the case of the character under the cursor and move past it.
// If in visual mode, change the case of each character in the selection.
func ( *Shell) () {
	switch {
	case .selection.Active() && .selection.IsVisual():
		.selection.ReplaceWith(func( rune) rune {
			if unicode.IsLower() {
				return unicode.ToUpper()
			}

			return unicode.ToLower()
		})

	default:
		if .line.Len() == 0 || .cursor.Pos() == .line.Len() {
			return
		}

		 := .cursor.Char()
		if unicode.IsLower() {
			 = unicode.ToUpper()
		} else {
			 = unicode.ToLower()
		}

		.cursor.ReplaceWith()
	}
}

// Substitute the next character(s).
func ( *Shell) () {
	.History.Save()

	defer .viInsertMode()

	switch {
	case .selection.Active():
		// Delete the selection and enter insert mode.
		 := .selection.Cursor()
		.selection.Cut()
		.cursor.Set()

	default:
		// Since we must emulate the default readline behavior,
		// we vary our behavior depending on the caller key.
		 := .Keys.Caller()

		switch [0] {
		case 's':
			// Delete next characters and enter insert mode.
			 := .Iterations.Get()
			for  := 1;  <= ; ++ {
				.line.CutRune(.cursor.Pos())
			}
		case 'S':
			if .cursor.OnEmptyLine() {
				return
			}

			// Pass the buffer to register.
			.selection.Mark(.cursor.Pos())
			.selection.Visual(true)

			,  := .selection.Pos()
			.Buffers.Write((*.line)[:]...)

			// If selection has a new line, remove it.
			if (*.line)[-1] == '\n' {
				--
			}

			// Kill the line
			.line.Cut(, )
			.cursor.Set()
		}
	}
}

// Kill to the end of the line and enter insert mode.
func ( *Shell) () {
	.History.Save()
	.History.SkipSave()

	 := .cursor.Pos()
	.selection.Mark()
	.cursor.EndOfLineAppend()
	.selection.Cut()
	.cursor.Set()

	.Iterations.Reset()
	.Display.ResetHelpers()
	.viInsertMode()
}

// Read a key from the keyboard, and add it to the lead and trail of the current
// selection. If the key is matcher type (quote/bracket/brace, etc), each of the
// matchers is used accordingly (an opening and closing one).
func ( *Shell) () {
	// Get the surround character to change.
	 := .Keymap.PendingCursor()
	defer ()

	,  := .Keys.ReadKey()
	if  {
		.History.SkipSave()
		return
	}

	,  := strutil.MatchSurround()

	.History.Save()

	// Surround the selection
	.selection.Surround(, )
}

// Create a new line above the current one, and enter insert mode.
func ( *Shell) () {
	.History.Save()

	if !.cursor.OnEmptyLine() {
		.beginningOfLine()
	}

	.cursor.InsertAt('\n')
	.cursor.Dec()
	.viInsertMode()
}

// Create a new line below the current one, and enter insert mode.
func ( *Shell) () {
	.History.Save()

	if !.cursor.OnEmptyLine() {
		.endOfLine()
	}

	.cursor.InsertAt('\n')
	.viInsertMode()
}

// Convert the current word to all lowercase and move past it.
// If in visual mode, operate on the whole selection.
func ( *Shell) () {
	switch {
	case .Keymap.IsPending():
		// In vi operator pending mode, it's that we've been called
		// twice in a row (eg. `uu`), so modify the entire current line.
		.History.Save()

		.selection.Mark(.cursor.Pos())
		.selection.Visual(true)
		.selection.ReplaceWith(unicode.ToLower)
		.viCommandMode()

	case .selection.Active():
		.History.Save()
		.selection.ReplaceWith(unicode.ToLower)
		.viCommandMode()

	default:
		// Else if we are actually starting a yank action.
		.History.SkipSave()
		.Keymap.Pending()
		.selection.Mark(.cursor.Pos())
	}
}

// Convert the current word to all uppercase and move past it.
// If in visual mode, operate on the whole selection.
func ( *Shell) () {
	switch {
	case .Keymap.IsPending():
		// In vi operator pending mode, it's that we've been called
		// twice in a row (eg. `uu`), so modify the entire current line.
		.History.Save()

		.selection.Mark(.cursor.Pos())
		.selection.Visual(true)
		.selection.ReplaceWith(unicode.ToUpper)
		.viCommandMode()

	case .selection.Active():
		.History.Save()
		.selection.ReplaceWith(unicode.ToUpper)
		.viCommandMode()

	default:
		// Else if we are actually starting a yank action.
		.History.SkipSave()
		.Keymap.Pending()
		.selection.Mark(.cursor.Pos())
	}
}

//
// Killing & Yanking ----------------------------------------------------
//

// Kill from the cursor to the end of the line.
func ( *Shell) () {
	.History.Save()

	 := .cursor.Pos()
	.selection.Mark(.cursor.Pos())
	.cursor.EndOfLineAppend()

	 := .selection.Cut()
	.Buffers.Write([]rune()...)
	.cursor.Set()

	if !.cursor.AtBeginningOfLine() {
		.cursor.Dec()
	}

	.Iterations.Reset()
	.Display.ResetHelpers()
}

// Kill the word from its beginning up to the cursor point.
func ( *Shell) () {
	if .Keymap.Main() != keymap.ViInsert {
		.History.Save()
	}

	 := .Iterations.Get()

	 := make([]rune, 0)

	// Delete the chars in the line anyway
	for  := 1;  <= ; ++ {
		if .cursor.Pos() == 0 {
			break
		}

		.cursor.Dec()
		 = append(, .cursor.Char())
		.line.CutRune(.cursor.Pos())
	}

	.Buffers.Write(...)
}

// Read a movement command from the keyboard, and copy the region
// from the cursor position to the endpoint of the movement into
// the kill buffer. If the command is vi-yank, copy the current line.
func ( *Shell) () {
	switch {
	case .Keymap.IsPending():
		// In vi operator pending mode, it's that we've been called
		// twice in a row (eg. `yy`), so copy the entire current line.
		.Keymap.CancelPending()
		.History.Save()

		.selection.Mark(.cursor.Pos())
		.selection.Visual(true)

		// Get buffer and add newline if there isn't one at the end
		, , ,  := .selection.Pop()
		if len() > 0 && rune([len()-1]) != inputrc.Newline {
			 += string(inputrc.Newline)
		}

		.Buffers.Write([]rune()...)

	case .selection.Active():
		// In visual mode, or with a non-empty selection, just yank.
		.History.Save()
		.adjustSelectionPending()
		, , ,  := .selection.Pop()

		.Buffers.Write([]rune()...)
		.cursor.Set()

		.viCommandMode()

	default:
		// Since we must emulate the default readline behavior,
		// we vary our behavior depending on the caller key.
		 := .Keys.Caller()

		switch [0] {
		case 'y':
			.Keymap.Pending()
			.selection.Mark(.cursor.Pos())
		case 'Y':
			.viYankWholeLine()
		}
	}
}

// Copy the current line into the kill buffer.
func ( *Shell) () {
	.History.SkipSave()

	// calculate line selection.
	.selection.Mark(.cursor.Pos())
	.selection.Visual(true)

	,  := .selection.Pos()

	// If selection has a new line, remove it.
	if (*.line)[-1] == '\n' {
		--
	}

	// Pass the buffer to register.
	 := (*.line)[:]
	.Buffers.Write(...)

	// Done with any selection.
	.selection.Reset()
}

// Kill from the cursor back to wherever insert mode was last entered.
func ( *Shell) () {
	if .cursor.Pos() <= .cursor.Mark() || .cursor.Pos() == 0 {
		return
	}

	.History.Save()

	.selection.MarkRange(.cursor.Mark(), .line.Len())
	.cursor.Dec()
	 := .selection.Cut()
	.Buffers.Write([]rune()...)
}

// Readline-compatible version dispatching to vi-put-after or vi-put-before.
func ( *Shell) () {
	 := .Keys.Caller()

	switch [0] {
	case 'P':
		.viPutBefore()
	case 'p':
		fallthrough
	default:
		.viPutAfter()
	}
}

// Insert the contents of the kill buffer after the cursor.
func ( *Shell) () {
	.History.Save()

	 := .Buffers.Active()

	if len() == 0 {
		return
	}

	// Add newlines when pasting an entire line.
	if [len()-1] == '\n' {
		if !.cursor.OnEmptyLine() {
			.cursor.EndOfLineAppend()
		}

		if .cursor.Pos() == .line.Len() {
			 = append([]rune{'\n'}, [:len()-1]...)
		}
	}

	.cursor.Inc()
	 := .cursor.Pos()

	 := .Iterations.Get()
	for  := 1;  <= ; ++ {
		.line.Insert(, ...)
	}
}

// Insert the contents of the kill buffer before the cursor.
func ( *Shell) () {
	.History.Save()

	 := .Buffers.Active()

	if len() == 0 {
		return
	}

	if [len()-1] == '\n' {
		.cursor.BeginningOfLine()

		if .cursor.OnEmptyLine() {
			 = append(, '\n')

			.cursor.Dec()
		}
	}

	 := .cursor.Pos()

	 := .Iterations.Get()
	for  := 1;  <= ; ++ {
		.line.Insert(, ...)
	}

	.cursor.Set()
}

// Specify a buffer to be used in the following command. See the registers section in the Vim page.
func ( *Shell) () {
	.History.SkipSave()

	// Always reset the active register.
	.Buffers.Reset()

	// Then read a key to select the register
	 := .Keymap.PendingCursor()
	defer ()

	,  := .Keys.ReadKey()
	if  {
		return
	}

	.Buffers.SetActive()
}

//
// Selecting Text -------------------------------------------------------
//

// Select a word including adjacent blanks, where a word is defined as a series of non-blank characters.
func ( *Shell) () {
	.History.SkipSave()
	.cursor.CheckCommand()

	.selection.SelectABlankWord()
}

// Select the current command argument applying the normal rules for quoting.
func ( *Shell) () {
	.History.SkipSave()
	.cursor.CheckCommand()

	// First find the blank word under cursor,
	// and put or cursor at the beginning of it.
	,  := .line.SelectBlankWord(.cursor.Pos())
	.cursor.Set()

	// Then find any enclosing quotes, if valid.
	.selection.SelectAShellWord()
}

// Select a word including adjacent blanks, using the normal vi-style word definition.
func ( *Shell) () {
	.History.SkipSave()
	.selection.SelectAWord()
}

// Select a word, where a word is defined as a series of non-blank characters.
func ( *Shell) () {
	.History.SkipSave()

	,  := .line.SelectBlankWord(.cursor.Pos())
	.cursor.Set()
	.selection.Mark()
}

// Select the current command argument applying the normal rules for quoting.
// If the argument begins and ends with matching quote characters, these are
// not included in the selection.
func ( *Shell) () {
	.History.SkipSave()

	// First find the blank word under cursor,
	// and put or cursor at the beginning of it.
	,  := .line.SelectBlankWord(.cursor.Pos())
	.cursor.Set()

	// Then find any enclosing quotes, if valid.
	,  := .line.SurroundQuotes(true, .cursor.Pos())
	,  := .line.SurroundQuotes(false, .cursor.Pos())
	,  := strutil.AdjustSurroundQuotes(, , , )

	// If none matched, use blankword
	if  == -1 &&  == -1 {
		.viSelectInBlankWord()

		return
	}

	.cursor.Set( - 1)

	// Select the range and return: the caller will decide what
	// to do with the cursor position and the selection itself.
	.selection.Mark( + 1)
}

// Select a word, using the normal vi-style word definition.
func ( *Shell) () {
	.History.SkipSave()

	,  := .line.SelectWord(.cursor.Pos())
	.cursor.Set()
	.selection.Mark()
}

// Read a key from the keyboard, and attempt to select a region surrounded by those keys.
// If the key triggering this command is 'i', the selection excludes the surrounding chars.
func ( *Shell) () {
	.History.SkipSave()

	var  bool

	// The surround can be either inside or around a surrounding
	// character, so we look at the input keys: the first one is
	// the only that triggered this command, so check the second.
	// Use the first key to know if inside/around is used.
	 := .Keys.Caller()
	if [0] == 'i' {
		 = true
	}

	// Then use the next key as the surrounding character.
	,  := .Keys.Pop()
	if  {
		return
	}

	, , ,  := .line.FindSurround(rune(), .cursor.Pos())
	if  == -1 &&  == -1 {
		return
	}

	if  {
		++
		--
	}

	// Select the range and return: the caller will decide what
	// to do with the cursor position and the selection itself.
	.selection.Mark()
	.cursor.Set()
}

// Read a key from the keyboard, and attempt to create a selection
// consisting of a pair of this character, if any such pair can be found.
func ( *Shell) () {
	.History.SkipSave()

	// Read a key as a rune to search for
	 := .Keymap.PendingCursor()
	defer ()

	,  := .Keys.ReadKey()
	if  {
		return
	}

	// Find the corresponding enclosing chars
	, , ,  := .line.FindSurround(, .cursor.Pos())
	if  == -1 ||  == -1 {
		return
	}

	// Add those two positions to highlighting and update.
	.selection.MarkSurround(, )
}

//
// Miscellaneous --------------------------------------------------------
//

// The character indicating end-of-file as set, for example,
// by “stty”.  If this character is read when there are no
// characters on the line, and point is at the beginning of
// the line, readline interprets it as the end of input and
// returns EOF.
func ( *Shell) () {
	.endOfFile()
}

// Search forward or backward through the history for the string
// of characters between the start of the current line and the point.
// This is a non-incremental search.
func ( *Shell) () {
	var  bool

	 := .Keys.Caller()

	switch [0] {
	case '/':
		 = true
	case '?':
		 = false
	}

	.completer.NonIsearchStart(.History.Name()+" "+string([0]), false, , true)
}

// Search again, through the history for the string of characters
// between the start of the current line and the point, using the
// same search string used by the previous search.
// This is a non-incremental search.
func ( *Shell) () {
	var  bool
	var  string

	 := .Keys.Caller()

	switch [0] {
	case 'n':
		 = true
		 = " /"
	case 'N':
		 = false
		 = " ?"
	}

	.completer.NonIsearchStart(.History.Name()+, true, , true)

	, ,  := .completer.GetBuffer()

	.History.InsertMatch(, , true, , true)
	.completer.NonIsearchStop()
}

// Start a new numeric argument, or add to the current one.
// This only works if bound to a key sequence ending in a decimal digit.
func ( *Shell) () {
	.History.SkipSave()

	 := .Keys.Caller()
	.Iterations.Add(string())
}

// Readline-compatible command for F/f/T/t character search commands.
func ( *Shell) () {
	var ,  bool

	// In order to keep readline compatibility,
	// we check the key triggering the command
	// so set the specific behavior.
	 := .Keys.Caller()

	switch [0] {
	case 'F':
		 = false
		 = false
	case 't':
		 = true
		 = true
	case 'T':
		 = false
		 = true
	case 'f':
		fallthrough
	default:
		 = true
		 = false
	}

	 := .Iterations.Get()

	for  := 1;  <= ; ++ {
		.viFindChar(, )
	}
}

// Set the specified mark at the cursor position.
func ( *Shell) () {
	.History.SkipSave()
	.selection.Mark(.cursor.Pos())
}

// Invoke an editor on the current command line, and execute the result as shell commands.
// Readline attempts to invoke $VISUAL, $EDITOR, and Vi as the editor, in that order.
func ( *Shell) () {
	.editAndExecuteCommand()
}

// Incrementally redo undone text modifications.
// If at the beginning of the line changes, enter insert mode.
func ( *Shell) () {
	if .History.Pos() > 0 {
		.History.Redo()
		return
	}

	// Enter insert mode when no redo possible.
	.viInsertMode()
}

// Invoke an editor on the current command line.
// Readline attempts to invoke $VISUAL, $EDITOR, and Vi as the editor, in that order.
func ( *Shell) () {
	 := .Keymap.Main()

	.editCommandLine()

	// We're done with visual mode when we were in.
	switch  {
	case keymap.ViCommand, keymap.Vi:
		.viCommandMode()
	default:
		.viInsertMode()
	}
}

// Read a character from the keyboard, and move to the next occurrence of it in the line.
func ( *Shell) () {
	 := .Iterations.Get()

	for  := 1;  <= ; ++ {
		.viFindChar(true, false)
	}
}

// Read a character from the keyboard, and move to the position just before the next occurrence of it in the line.
func ( *Shell) () {
	 := .Iterations.Get()

	for  := 1;  <= ; ++ {
		.viFindChar(true, true)
	}
}

// Read a character from the keyboard, and move to the previous occurrence of it in the line.
func ( *Shell) () {
	 := .Iterations.Get()

	for  := 1;  <= ; ++ {
		.viFindChar(false, false)
	}
}

// Read a character from the keyboard, and move to the position just after the previous occurrence of it in the line.
func ( *Shell) () {
	 := .Iterations.Get()

	for  := 1;  <= ; ++ {
		.viFindChar(false, true)
	}
}

func ( *Shell) (,  bool) {
	.History.SkipSave()

	// Read the argument key to use as a pattern to search
	 := .Keymap.PendingCursor()
	defer ()

	,  := .Keys.ReadKey()
	if  {
		return
	}

	 := .Iterations.Get()

	for  := 1;  <= ; ++ {
		 := .line.Find(, .cursor.Pos(), )

		if  == .cursor.Pos() ||  == -1 {
			break
		}

		if  &&  {
			--
		} else if ! &&  {
			++
		}

		.cursor.Set()
	}
}

// Start a non-incremental search buffer, finds the first forward
// matching line (as a regexp), and makes it the current buffer.
func ( *Shell) () {
	.completer.NonIsearchStart(.History.Name()+" /", false, true, true)
}

// Start a non-incremental search buffer, finds the first backward
// matching line (as a regexp), and makes it the current buffer.
func ( *Shell) () {
	.completer.NonIsearchStart(.History.Name()+" ?", false, false, true)
}

// Reuses the last vi-search buffer and finds the previous search match occurrence in the history.
func ( *Shell) () {
	.completer.NonIsearchStart(.History.Name()+" /", true, true, true)

	, ,  := .completer.GetBuffer()

	.History.InsertMatch(, , true, true, true)
	.completer.NonIsearchStop()
}

// Reuses the last vi-search buffer and finds the next search match occurrence in the history.
func ( *Shell) () {
	.completer.NonIsearchStart(.History.Name()+" ?", true, false, true)

	, ,  := .completer.GetBuffer()

	.History.InsertMatch(, , true, false, true)
	.completer.NonIsearchStop()
}

//
// Utils ---------------------------------------------------------------
//

// Some commands accepting a pending operator command (yw/de... etc), must
// either encompass the character under cursor into the selection, or not.
// Note that when this command while a yank/delete command has been called
// in visual mode, no adjustments will take place, since the active command
// is not one of those in the below switch statement.
func ( *Shell) () {
	if !.selection.Active() {
		return
	}

	switch .Keymap.ActiveCommand().Action {
	// Movements
	case "vi-end-word", "vi-end-bigword",
		"vi-find-next-char", "vi-find-next-char-skip",
		"vi-find-prev-char", "vi-find-prev-char-skip",
		"vi-match":
		.selection.Visual(false)

		// Selectors
	case "select-in-word", "select-a-word",
		"select-in-blank-word", "select-a-blank-word",
		"select-in-shell-word", "select-a-shell-word",
		"vi-select-inside":
		.selection.Visual(false)

		// Modifiers
	case "vi-change-to":
		.selection.Visual(false)
	}
}