package readline
import (
"fmt"
"io"
"sort"
"strings"
"unicode"
"github.com/rivo/uniseg"
"github.com/reeflective/readline/inputrc"
"github.com/reeflective/readline/internal/color"
"github.com/reeflective/readline/internal/completion"
"github.com/reeflective/readline/internal/keymap"
"github.com/reeflective/readline/internal/strutil"
"github.com/reeflective/readline/internal/term"
)
func (rl *Shell ) standardCommands () commands {
widgets := map [string ]func (){
"emacs-editing-mode" : rl .emacsEditingMode ,
"forward-char" : rl .forwardChar ,
"backward-char" : rl .backwardChar ,
"forward-word" : rl .forwardWord ,
"backward-word" : rl .backwardWord ,
"shell-forward-word" : rl .forwardShellWord ,
"shell-backward-word" : rl .backwardShellWord ,
"beginning-of-line" : rl .beginningOfLine ,
"end-of-line" : rl .endOfLine ,
"previous-screen-line" : rl .upLine ,
"next-screen-line" : rl .downLine ,
"clear-screen" : rl .clearScreen ,
"clear-display" : rl .clearDisplay ,
"redraw-current-line" : rl .Display .Refresh ,
"end-of-file" : rl .endOfFile ,
"delete-char" : rl .deleteChar ,
"backward-delete-char" : rl .backwardDeleteChar ,
"forward-backward-delete-char" : rl .forwardBackwardDeleteChar ,
"quoted-insert" : rl .quotedInsert ,
"tab-insert" : rl .tabInsert ,
"self-insert" : rl .selfInsert ,
"bracketed-paste-begin" : rl .bracketedPasteBegin ,
"transpose-chars" : rl .transposeChars ,
"transpose-words" : rl .transposeWords ,
"shell-transpose-words" : rl .shellTransposeWords ,
"down-case-word" : rl .downCaseWord ,
"up-case-word" : rl .upCaseWord ,
"capitalize-word" : rl .capitalizeWord ,
"overwrite-mode" : rl .overwriteMode ,
"delete-horizontal-whitespace" : rl .deleteHorizontalWhitespace ,
"delete-word" : rl .deleteWord ,
"quote-region" : rl .quoteRegion ,
"quote-line" : rl .quoteLine ,
"keyword-increase" : rl .keywordIncrease ,
"keyword-decrease" : rl .keywordDecrease ,
"kill-line" : rl .killLine ,
"backward-kill-line" : rl .backwardKillLine ,
"unix-line-discard" : rl .backwardKillLine ,
"kill-whole-line" : rl .killWholeLine ,
"kill-word" : rl .killWord ,
"backward-kill-word" : rl .backwardKillWord ,
"unix-word-rubout" : rl .backwardKillWord ,
"kill-region" : rl .killRegion ,
"copy-region-as-kill" : rl .copyRegionAsKill ,
"copy-backward-word" : rl .copyBackwardWord ,
"copy-forward-word" : rl .copyForwardWord ,
"yank" : rl .yank ,
"yank-pop" : rl .yankPop ,
"kill-buffer" : rl .killBuffer ,
"shell-kill-word" : rl .shellKillWord ,
"shell-backward-kill-word" : rl .shellBackwardKillWord ,
"copy-prev-shell-word" : rl .copyPrevShellWord ,
"digit-argument" : rl .digitArgument ,
"start-kbd-macro" : rl .startKeyboardMacro ,
"end-kbd-macro" : rl .endKeyboardMacro ,
"call-last-kbd-macro" : rl .callLastKeyboardMacro ,
"print-last-kbd-macro" : rl .printLastKeyboardMacro ,
"macro-toggle-record" : rl .macroToggleRecord ,
"macro-run" : rl .macroRun ,
"re-read-init-file" : rl .reReadInitFile ,
"abort" : rl .abort ,
"do-lowercase-version" : rl .doLowercaseVersion ,
"prefix-meta" : rl .prefixMeta ,
"undo" : rl .undoLast ,
"revert-line" : rl .revertLine ,
"set-mark" : rl .setMark ,
"exchange-point-and-mark" : rl .exchangePointAndMark ,
"character-search" : rl .characterSearch ,
"character-search-backward" : rl .characterSearchBackward ,
"insert-comment" : rl .insertComment ,
"dump-functions" : rl .dumpFunctions ,
"dump-variables" : rl .dumpVariables ,
"dump-macros" : rl .dumpMacros ,
"magic-space" : rl .magicSpace ,
"edit-and-execute-command" : rl .editAndExecuteCommand ,
"edit-command-line" : rl .editCommandLine ,
"redo" : rl .redo ,
"select-keyword-next" : rl .selectKeywordNext ,
"select-keyword-prev" : rl .selectKeywordPrev ,
}
return widgets
}
func (rl *Shell ) emacsEditingMode () {
rl .selection .Reset ()
rl .Iterations .Reset ()
rl .Buffers .Reset ()
rl .Hint .Reset ()
rl .completer .Reset ()
rl .line , rl .cursor , rl .selection = rl .completer .GetBuffer ()
rl .Keymap .SetMain (keymap .Emacs )
}
func (rl *Shell ) forwardChar () {
startPos := rl .cursor .Pos ()
if rl .Config .GetBool ("history-autosuggest" ) && rl .cursor .Pos () >= rl .line .Len ()-1 {
rl .autosuggestAccept ()
}
if rl .cursor .Pos () > startPos {
return
}
rl .History .SkipSave ()
vii := rl .Iterations .Get ()
for i := 1 ; i <= vii ; i ++ {
rl .cursor .Inc ()
}
}
func (rl *Shell ) backwardChar () {
rl .History .SkipSave ()
vii := rl .Iterations .Get ()
for i := 1 ; i <= vii ; i ++ {
rl .cursor .Dec ()
}
}
func (rl *Shell ) forwardWord () {
rl .History .SkipSave ()
vii := rl .Iterations .Get ()
for i := 1 ; i <= vii ; i ++ {
rl .insertAutosuggestPartial (true )
forward := rl .line .ForwardEnd (rl .line .Tokenize , rl .cursor .Pos ())
rl .cursor .Move (forward + 1 )
}
}
func (rl *Shell ) backwardWord () {
rl .History .SkipSave ()
vii := rl .Iterations .Get ()
for i := 1 ; i <= vii ; i ++ {
backward := rl .line .Backward (rl .line .Tokenize , rl .cursor .Pos ())
rl .cursor .Move (backward )
}
}
func (rl *Shell ) forwardShellWord () {
vii := rl .Iterations .Get ()
for i := 1 ; i <= vii ; i ++ {
rl .selection .SelectAShellWord ()
_ , _ , tepos , _ := rl .selection .Pop ()
rl .cursor .Set (tepos )
}
}
func (rl *Shell ) backwardShellWord () {
vii := rl .Iterations .Get ()
for i := 1 ; i <= vii ; i ++ {
startPos := rl .cursor .Pos ()
backward := rl .line .Backward (rl .line .TokenizeSpace , startPos )
rl .cursor .Move (backward )
bpos , _ := rl .selection .SelectAShellWord ()
rl .cursor .Set (bpos )
}
}
func (rl *Shell ) beginningOfLine () {
rl .History .SkipSave ()
if !rl .Keymap .IsEmacs () && rl .Iterations .IsSet () {
rl .Iterations .Add ("0" )
return
}
rl .cursor .BeginningOfLine ()
}
func (rl *Shell ) endOfLine () {
rl .History .SkipSave ()
rl .cursor .EndOfLineAppend ()
}
func (rl *Shell ) upLine () {
lines := rl .Iterations .Get ()
rl .cursor .LineMove (lines * -1 )
}
func (rl *Shell ) downLine () {
lines := rl .Iterations .Get ()
rl .cursor .LineMove (lines )
}
func (rl *Shell ) clearScreen () {
rl .History .SkipSave ()
fmt .Print (term .CursorTopLeft )
fmt .Print (term .ClearScreen )
rl .Display .PrintPrimaryPrompt ()
}
func (rl *Shell ) clearDisplay () {
rl .History .SkipSave ()
fmt .Print (term .CursorTopLeft )
fmt .Print (term .ClearDisplay )
rl .Display .PrintPrimaryPrompt ()
}
func (rl *Shell ) endOfFile () {
switch rl .line .Len () {
case 0 :
rl .Display .AcceptLine ()
rl .History .Accept (false , false , io .EOF )
default :
rl .deleteChar ()
}
}
func (rl *Shell ) deleteChar () {
rl .History .Save ()
vii := rl .Iterations .Get ()
for i := 1 ; i <= vii ; i ++ {
rl .line .CutRune (rl .cursor .Pos ())
}
}
func (rl *Shell ) backwardDeleteChar () {
if rl .Keymap .Main () == keymap .ViInsert {
rl .History .SkipSave ()
} else {
rl .History .Save ()
}
completion .UpdateInserted (rl .completer )
if rl .cursor .Pos () == 0 {
return
}
vii := rl .Iterations .Get ()
switch vii {
case 1 :
if rl .Config .GetBool ("autopairs" ) {
completion .AutopairDelete (rl .line , rl .cursor )
}
rl .cursor .Dec ()
rl .line .CutRune (rl .cursor .Pos ())
default :
for i := 1 ; i <= vii ; i ++ {
rl .cursor .Dec ()
rl .line .CutRune (rl .cursor .Pos ())
}
}
}
func (rl *Shell ) forwardBackwardDeleteChar () {
switch rl .cursor .Pos () {
case rl .line .Len ():
rl .backwardDeleteChar ()
default :
rl .deleteChar ()
}
}
func (rl *Shell ) quotedInsert () {
rl .History .SkipSave ()
rl .completer .TrimSuffix ()
done := rl .Keymap .PendingCursor ()
defer done ()
key , _ := rl .Keys .ReadKey ()
quoted , _ := strutil .Quote (key )
rl .cursor .InsertAt (quoted ...)
}
func (rl *Shell ) tabInsert () {
rl .History .SkipSave ()
rl .cursor .InsertAt ('\t' )
}
func (rl *Shell ) selfInsert () {
rl .History .SkipSave ()
rl .completer .TrimSuffix ()
key := rl .Keys .Caller ()
searching , _ , _ := rl .completer .NonIncrementallySearching ()
isearch := rl .Keymap .Local () == keymap .Isearch
if !searching && !isearch && rl .Config .GetBool ("autopairs" ) {
if jump := completion .AutopairInsertOrJump (key [0 ], rl .line , rl .cursor ); jump {
return
}
}
var quoted []rune
var length int
if rl .Config .GetBool ("output-meta" ) && key [0 ] != inputrc .Esc {
quoted = append (quoted , key [0 ])
length = uniseg .StringWidth (string (quoted ))
} else {
quoted , length = strutil .Quote (key [0 ])
}
rl .cursor .InsertAt (quoted ...)
rl .cursor .Move (-1 * len (quoted ))
rl .cursor .Move (length )
}
func (rl *Shell ) bracketedPasteBegin () {
}
func (rl *Shell ) transposeChars () {
if rl .cursor .Pos () < 2 || rl .line .Len () < 2 {
rl .History .SkipSave ()
return
}
rl .History .Save ()
switch {
case rl .cursor .Pos () == rl .line .Len ():
last := (*rl .line )[rl .cursor .Pos ()-1 ]
blast := (*rl .line )[rl .cursor .Pos ()-2 ]
(*rl .line )[rl .cursor .Pos ()-2 ] = last
(*rl .line )[rl .cursor .Pos ()-1 ] = blast
default :
last := rl .cursor .Char ()
blast := (*rl .line )[rl .cursor .Pos ()-1 ]
(*rl .line )[rl .cursor .Pos ()-1 ] = last
rl .cursor .ReplaceWith (blast )
}
}
func (rl *Shell ) transposeWords () {
rl .History .Save ()
startPos := rl .cursor .Pos ()
rl .cursor .ToFirstNonSpace (true )
rl .cursor .CheckCommand ()
rl .viSelectInWord ()
rl .selection .Visual (false )
toTranspose , tbpos , tepos , _ := rl .selection .Pop ()
rl .cursor .Set (tbpos )
if tepos >= rl .line .Len ()-1 || rl .Iterations .IsSet () {
rl .backwardWord ()
} else {
rl .viForwardWord ()
}
rl .viSelectInWord ()
rl .selection .Visual (false )
transposeWith , wbpos , wepos , _ := rl .selection .Pop ()
if tbpos == 0 {
rl .cursor .Set (startPos )
return
}
if wbpos > tbpos {
wbpos , tbpos = tbpos , wbpos
wepos , tepos = tepos , wepos
transposeWith , toTranspose = toTranspose , transposeWith
}
begin := string ((*rl .line )[:wbpos ])
newLine := append ([]rune (begin ), []rune (toTranspose )...)
newLine = append (newLine , (*rl .line )[wepos :tbpos ]...)
newLine = append (newLine , []rune (transposeWith )...)
newLine = append (newLine , (*rl .line )[tepos :]...)
rl .line .Set (newLine ...)
rl .cursor .Set (tepos )
}
func (rl *Shell ) shellTransposeWords () {
rl .History .Save ()
startPos := rl .cursor .Pos ()
rl .viSelectAShellWord ()
toTranspose , tbpos , tepos , _ := rl .selection .Pop ()
rl .cursor .Set (tbpos )
rl .backwardShellWord ()
rl .viSelectAShellWord ()
transposeWith , wbpos , wepos , _ := rl .selection .Pop ()
if wepos > tbpos {
rl .cursor .Set (startPos )
return
}
begin := string ((*rl .line )[:wbpos ])
newLine := append ([]rune (begin ), []rune (toTranspose )...)
newLine = append (newLine , (*rl .line )[wepos :tbpos ]...)
newLine = append (newLine , []rune (transposeWith )...)
newLine = append (newLine , (*rl .line )[tepos :]...)
rl .line .Set (newLine ...)
rl .cursor .Set (tepos )
}
func (rl *Shell ) downCaseWord () {
rl .History .Save ()
startPos := rl .cursor .Pos ()
rl .cursor .Inc ()
backward := rl .line .Backward (rl .line .Tokenize , rl .cursor .Pos ())
rl .cursor .Move (backward )
rl .selection .Mark (rl .cursor .Pos ())
forward := rl .line .ForwardEnd (rl .line .Tokenize , rl .cursor .Pos ())
rl .cursor .Move (forward )
rl .selection .ReplaceWith (unicode .ToLower )
rl .cursor .Set (startPos )
}
func (rl *Shell ) upCaseWord () {
rl .History .Save ()
startPos := rl .cursor .Pos ()
rl .cursor .Inc ()
backward := rl .line .Backward (rl .line .Tokenize , rl .cursor .Pos ())
rl .cursor .Move (backward )
rl .selection .Mark (rl .cursor .Pos ())
forward := rl .line .ForwardEnd (rl .line .Tokenize , rl .cursor .Pos ())
rl .cursor .Move (forward )
rl .selection .ReplaceWith (unicode .ToUpper )
rl .cursor .Set (startPos )
}
func (rl *Shell ) capitalizeWord () {
if rl .line .Len () == 0 {
return
}
rl .History .Save ()
startPos := rl .cursor .Pos ()
rl .cursor .Inc ()
backward := rl .line .Backward (rl .line .Tokenize , rl .cursor .Pos ())
rl .cursor .Move (backward )
letter := rl .cursor .Char ()
rl .cursor .ReplaceWith (unicode .ToUpper (letter ))
rl .cursor .Set (startPos )
}
func (rl *Shell ) overwriteMode () {
rl .History .Save ()
done := rl .Keymap .PendingCursor ()
defer done ()
cache := make ([]rune , 0 )
lineStart := rl .line .Len ()
for {
key , isAbort := rl .Keys .ReadKey ()
if isAbort {
break
}
if string (key ) == inputrc .Unescape (string (`\C-?` )) {
if rl .cursor .Pos () > lineStart {
rl .backwardDeleteChar ()
} else if rl .cursor .Pos () > 0 {
rl .cursor .Dec ()
}
if len (cache ) > 0 && rl .cursor .Pos () < lineStart {
key = cache [len (cache )-1 ]
cache = cache [:len (cache )-1 ]
rl .cursor .ReplaceWith (key )
}
} else {
if rl .line .Len () == rl .cursor .Pos () {
rl .cursor .InsertAt (key )
} else {
cache = append (cache , rl .cursor .Char ())
rl .cursor .ReplaceWith (key )
rl .cursor .Inc ()
}
}
rl .Display .Refresh ()
}
}
func (rl *Shell ) deleteHorizontalWhitespace () {
rl .History .Save ()
startPos := rl .cursor .Pos ()
rl .cursor .ToFirstNonSpace (false )
if rl .cursor .Pos () != startPos {
rl .cursor .Inc ()
}
bpos := rl .cursor .Pos ()
rl .cursor .ToFirstNonSpace (true )
if rl .cursor .Pos () != startPos {
rl .cursor .Dec ()
}
epos := rl .cursor .Pos ()
rl .line .Cut (bpos , epos )
rl .cursor .Set (bpos )
}
func (rl *Shell ) deleteWord () {
rl .History .Save ()
rl .selection .Mark (rl .cursor .Pos ())
forward := rl .line .ForwardEnd (rl .line .Tokenize , rl .cursor .Pos ())
rl .cursor .Move (forward )
rl .selection .Cut ()
}
func (rl *Shell ) quoteRegion () {
rl .History .Save ()
rl .selection .Surround ('\'' , '\'' )
rl .cursor .Inc ()
}
func (rl *Shell ) quoteLine () {
if rl .line .Len () == 0 {
return
}
for pos , char := range *rl .line {
if char == '\n' {
break
}
if char == '\'' {
(*rl .line )[pos ] = '"'
}
}
rl .line .Insert (0 , '\'' )
rl .line .Insert (rl .line .Len (), '\'' )
}
func (rl *Shell ) keywordIncrease () {
rl .History .Save ()
rl .keywordSwitch (true )
}
func (rl *Shell ) keywordDecrease () {
rl .History .Save ()
rl .keywordSwitch (false )
}
func (rl *Shell ) keywordSwitch (increase bool ) {
cpos := strutil .AdjustNumberOperatorPos (rl .cursor .Pos (), *rl .line )
bpos , epos := rl .line .SelectWord (cpos )
epos ++
if bpos != 0 && ((*rl .line )[bpos -1 ] == '+' || (*rl .line )[bpos -1 ] == '-' ) {
bpos --
}
selection := string ((*rl .line )[bpos :epos ])
for _ , switcher := range strutil .KeywordSwitchers () {
vii := rl .Iterations .Get ()
changed , word , obpos , oepos := switcher (selection , increase , vii )
if !changed {
continue
}
epos = bpos + oepos
bpos += obpos
if cpos < bpos || cpos >= epos {
continue
}
begin := string ((*rl .line )[:bpos ])
end := string ((*rl .line )[epos :])
newLine := append ([]rune (begin ), []rune (word )...)
newLine = append (newLine , []rune (end )...)
rl .line .Set (newLine ...)
rl .cursor .Set (bpos + len (word ) - 1 )
return
}
}
func (rl *Shell ) killLine () {
rl .Iterations .Reset ()
rl .History .Save ()
if rl .line .Len () == 0 {
return
}
cpos := rl .cursor .Pos ()
rl .cursor .EndOfLineAppend ()
rl .selection .MarkRange (cpos , rl .cursor .Pos ())
text := rl .selection .Cut ()
rl .Buffers .Write ([]rune (text )...)
rl .cursor .Set (cpos )
}
func (rl *Shell ) backwardKillLine () {
rl .Iterations .Reset ()
rl .History .Save ()
if rl .line .Len () == 0 {
return
}
cpos := rl .cursor .Pos ()
rl .cursor .BeginningOfLine ()
rl .selection .MarkRange (rl .cursor .Pos (), cpos )
text := rl .selection .Cut ()
rl .Buffers .Write ([]rune (text )...)
}
func (rl *Shell ) killWholeLine () {
rl .History .Save ()
if rl .line .Len () == 0 {
return
}
rl .Buffers .Write (*rl .line ...)
rl .line .Cut (0 , rl .line .Len ())
}
func (rl *Shell ) killBuffer () {
rl .History .Save ()
if rl .line .Len () == 0 {
return
}
rl .Buffers .Write (*rl .line ...)
rl .line .Cut (0 , rl .line .Len ())
}
func (rl *Shell ) killWord () {
rl .History .Save ()
bpos := rl .cursor .Pos ()
rl .cursor .ToFirstNonSpace (true )
forward := rl .line .Forward (rl .line .TokenizeSpace , rl .cursor .Pos ())
rl .cursor .Move (forward - 1 )
epos := rl .cursor .Pos ()
rl .selection .MarkRange (bpos , epos )
rl .Buffers .Write ([]rune (rl .selection .Cut ())...)
rl .cursor .Set (bpos )
}
func (rl *Shell ) backwardKillWord () {
rl .History .Save ()
rl .History .SkipSave ()
rl .selection .Mark (rl .cursor .Pos ())
adjust := rl .line .Backward (rl .line .Tokenize , rl .cursor .Pos ())
rl .cursor .Move (adjust )
rl .Buffers .Write ([]rune (rl .selection .Cut ())...)
}
func (rl *Shell ) killRegion () {
rl .History .Save ()
if !rl .selection .Active () {
return
}
rl .Buffers .Write ([]rune (rl .selection .Cut ())...)
}
func (rl *Shell ) copyRegionAsKill () {
rl .History .SkipSave ()
if !rl .selection .Active () {
return
}
rl .Buffers .Write ([]rune (rl .selection .Text ())...)
rl .selection .Reset ()
}
func (rl *Shell ) copyBackwardWord () {
rl .History .Save ()
rl .selection .Mark (rl .cursor .Pos ())
adjust := rl .line .Backward (rl .line .Tokenize , rl .cursor .Pos ())
rl .cursor .Move (adjust )
rl .Buffers .Write ([]rune (rl .selection .Text ())...)
rl .selection .Reset ()
}
func (rl *Shell ) copyForwardWord () {
rl .History .Save ()
rl .selection .Mark (rl .cursor .Pos ())
adjust := rl .line .Forward (rl .line .Tokenize , rl .cursor .Pos ())
rl .cursor .Move (adjust + 1 )
rl .Buffers .Write ([]rune (rl .selection .Text ())...)
rl .selection .Reset ()
}
func (rl *Shell ) yank () {
buf := rl .Buffers .Active ()
vii := rl .Iterations .Get ()
for i := 1 ; i <= vii ; i ++ {
rl .cursor .InsertAt (buf ...)
}
}
func (rl *Shell ) yankPop () {
vii := rl .Iterations .Get ()
for i := 1 ; i <= vii ; i ++ {
buf := rl .Buffers .Pop ()
rl .cursor .InsertAt (buf ...)
}
}
func (rl *Shell ) shellKillWord () {
startPos := rl .cursor .Pos ()
rl .viSelectAShellWord ()
_ , epos := rl .selection .Pos ()
rl .Buffers .Write ([]rune ((*rl .line )[startPos :epos ])...)
rl .line .Cut (startPos , epos )
rl .cursor .Set (startPos )
rl .selection .Reset ()
}
func (rl *Shell ) shellBackwardKillWord () {
startPos := rl .cursor .Pos ()
if rl .line .Len () == 0 || startPos == 0 {
return
}
rl .History .Save ()
rl .cursor .Dec ()
rl .cursor .ToFirstNonSpace (false )
unclosed , bpos := strutil .GetQuotedWordStart ((*rl .line )[:startPos ])
if !unclosed {
rl .viSelectAShellWord ()
bpos , _ = rl .selection .Pos ()
}
rl .cursor .Set (bpos )
rl .cursor .ToFirstNonSpace (true )
bpos = rl .cursor .Pos ()
rl .Buffers .Write ([]rune ((*rl .line )[bpos :startPos ])...)
rl .line .Cut (bpos , startPos )
rl .selection .Reset ()
}
func (rl *Shell ) copyPrevShellWord () {
rl .History .Save ()
posInit := rl .cursor .Pos ()
rl .backwardShellWord ()
rl .backwardShellWord ()
rl .viSelectAShellWord ()
word := rl .selection .Text ()
rl .cursor .Set (posInit )
rl .selection .InsertAt (rl .cursor .Pos (), -1 )
rl .cursor .Move (len (word ))
}
func (rl *Shell ) digitArgument () {
rl .History .SkipSave ()
keys := rl .Keys .Caller ()
if len (keys ) > 1 && keys [0 ] == inputrc .Esc {
keys = keys [1 :]
}
rl .Iterations .Add (string (keys ))
}
func (rl *Shell ) startKeyboardMacro () {
rl .Macros .StartRecord (rune (0 ))
}
func (rl *Shell ) endKeyboardMacro () {
rl .Macros .StopRecord ()
}
func (rl *Shell ) callLastKeyboardMacro () {
rl .Macros .RunLastMacro ()
}
func (rl *Shell ) printLastKeyboardMacro () {
rl .Display .ClearHelpers ()
rl .Macros .PrintLastMacro ()
rl .Prompt .PrimaryPrint ()
rl .Display .Refresh ()
}
func (rl *Shell ) macroToggleRecord () {
if rl .Macros .Recording () {
rl .Macros .StopRecord ()
return
}
done := rl .Keymap .PendingCursor ()
defer done ()
rl .Hint .SetTemporary (color .Dim + "REC (macro arg)" )
rl .Display .Refresh ()
key , isAbort := rl .Keys .ReadKey ()
if isAbort {
return
}
rl .Macros .StartRecord (key )
}
func (rl *Shell ) macroRun () {
done := rl .Keymap .PendingCursor ()
defer done ()
rl .Hint .SetTemporary (color .Dim + "Run (macro arg)" )
rl .Display .Refresh ()
key , isAbort := rl .Keys .ReadKey ()
if isAbort {
return
}
rl .Macros .RunMacro (key )
}
func (rl *Shell ) reReadInitFile () {
main := rl .Keymap .Main ()
err := rl .Keymap .ReloadConfig (rl .Opts ...)
if err != nil {
rl .Hint .SetTemporary (color .FgRed + "Inputrc reload error: " + err .Error())
return
}
defer rl .Keymap .UpdateCursor ()
newMain := rl .Keymap .Main ()
if main != newMain {
switch newMain {
case keymap .Emacs , keymap .EmacsStandard , keymap .EmacsMeta , keymap .EmacsCtrlX :
rl .emacsEditingMode ()
case keymap .Vi , keymap .ViCommand , keymap .ViMove :
rl .viCommandMode ()
case keymap .ViInsert :
rl .viInsertMode ()
}
}
rl .Hint .SetTemporary (color .FgGreen + "Inputrc reloaded" )
}
func (rl *Shell ) abort () {
rl .Iterations .Reset ()
rl .selection .Reset ()
if rl .completer .AutoCompleting () || rl .completer .IsInserting () {
rl .Hint .Reset ()
rl .completer .ResetForce ()
return
}
searching , _ , _ := rl .completer .NonIncrementallySearching ()
if searching {
rl .completer .NonIsearchStop ()
return
}
if !rl .Keymap .InputIsTerminator () {
return
}
if rl .Config .GetBool ("echo-control-characters" ) {
key := rl .Keys .Caller ()
if key [0 ] == rune (inputrc .Unescape (`\C-C` )[0 ]) {
quoted , _ := strutil .Quote (key [0 ])
fmt .Print (string (quoted ))
}
}
rl .Display .AcceptLine ()
rl .History .Accept (false , false , ErrInterrupt )
}
func (rl *Shell ) doLowercaseVersion () {
rl .History .SkipSave ()
keys := rl .Keys .Caller ()
escapePrefix := false
if len (keys ) > 1 && keys [0 ] == inputrc .Esc {
escapePrefix = true
keys = keys [1 :]
} else if len (keys ) == 1 && inputrc .IsMeta (keys [0 ]) {
keys = []rune {inputrc .Demeta (keys [0 ])}
}
if unicode .IsLower (keys [0 ]) {
return
}
keys [0 ] = unicode .ToLower (keys [0 ])
if escapePrefix {
input := append ([]rune {inputrc .Esc }, keys ...)
rl .Keys .Feed (false , input ...)
} else {
rl .Keys .Feed (false , inputrc .Enmeta (keys [0 ]))
}
}
func (rl *Shell ) prefixMeta () {
rl .History .SkipSave ()
done := rl .Keymap .PendingCursor ()
defer done ()
key , isAbort := rl .Keys .ReadKey ()
if isAbort {
return
}
keys := append ([]rune {inputrc .Esc }, key )
rl .Keys .Feed (false , keys ...)
}
func (rl *Shell ) undoLast () {
rl .History .Undo ()
}
func (rl *Shell ) revertLine () {
rl .History .Revert ()
}
func (rl *Shell ) setMark () {
switch {
case rl .Iterations .IsSet ():
rl .cursor .SetMark ()
default :
cpos := rl .cursor .Pos ()
mark := rl .Iterations .Get ()
if mark > rl .line .Len ()-1 {
return
}
rl .cursor .Set (mark )
rl .cursor .SetMark ()
rl .cursor .Set (cpos )
}
}
func (rl *Shell ) exchangePointAndMark () {
if rl .cursor .Mark () > rl .line .Len () {
rl .cursor .ResetMark ()
}
if rl .cursor .Mark () < 0 {
cpos := rl .cursor .Pos ()
rl .cursor .Set (0 )
rl .cursor .SetMark ()
rl .cursor .Set (cpos )
} else {
mark := rl .cursor .Mark ()
rl .cursor .SetMark ()
rl .cursor .Set (mark )
rl .selection .MarkRange (rl .cursor .Mark (), rl .cursor .Pos ())
rl .selection .Visual (false )
}
}
func (rl *Shell ) characterSearch () {
if rl .Iterations .Get () < 0 {
rl .viFindChar (false , false )
} else {
rl .viFindChar (true , false )
}
}
func (rl *Shell ) characterSearchBackward () {
if rl .Iterations .Get () < 0 {
rl .viFindChar (true , false )
} else {
rl .viFindChar (false , false )
}
}
func (rl *Shell ) insertComment () {
comment := strings .Trim (rl .Config .GetString ("comment-begin" ), "\"" )
switch {
case !rl .Iterations .IsSet ():
cpos := rl .cursor .Pos ()
rl .cursor .BeginningOfLine ()
rl .cursor .InsertAt ([]rune (comment )...)
rl .cursor .Set (cpos )
default :
cpos := rl .cursor .Pos ()
rl .cursor .BeginningOfLine ()
bpos := rl .cursor .Pos ()
epos := bpos + len (comment )
rl .cursor .Set (cpos )
commentFits := epos < rl .line .Len ()
if commentFits && string ((*rl .line )[bpos :epos ]) == comment {
rl .line .Cut (bpos , epos )
rl .cursor .Move (-1 * len (comment ))
} else {
rl .line .Insert (bpos , []rune (comment )...)
rl .cursor .Move (1 * len (comment ))
}
}
rl .acceptLineWith (false , false )
}
func (rl *Shell ) dumpFunctions () {
rl .Display .ClearHelpers ()
fmt .Println ()
defer func () {
rl .Prompt .PrimaryPrint ()
rl .Display .Refresh ()
}()
inputrcFormat := rl .Iterations .IsSet ()
rl .Keymap .PrintBinds (string (rl .Keymap .Main ()), inputrcFormat )
}
func (rl *Shell ) dumpVariables () {
rl .Display .ClearHelpers ()
fmt .Println ()
defer func () {
rl .Prompt .PrimaryPrint ()
rl .Display .Refresh ()
}()
var variables []string
for variable := range rl .Config .Vars {
variables = append (variables , variable )
}
sort .Strings (variables )
if rl .Iterations .IsSet () {
for _ , variable := range variables {
value := rl .Config .Vars [variable ]
fmt .Printf ("set %s %v\n" , variable , value )
}
} else {
for _ , variable := range variables {
value := rl .Config .Vars [variable ]
fmt .Printf ("%s is set to `%v'\n" , variable , value )
}
}
}
func (rl *Shell ) dumpMacros () {
rl .Display .ClearHelpers ()
fmt .Println ()
defer func () {
rl .Prompt .PrimaryPrint ()
rl .Display .Refresh ()
}()
binds := rl .Config .Binds [string (rl .Keymap .Main ())]
if len (binds ) == 0 {
return
}
var macroBinds []string
for keys , bind := range binds {
if bind .Macro {
macroBinds = append (macroBinds , inputrc .Escape (keys ))
}
}
sort .Strings (macroBinds )
if rl .Iterations .IsSet () {
for _ , key := range macroBinds {
action := inputrc .Escape (binds [inputrc .Unescape (key )].Action )
fmt .Printf ("\"%s\": \"%s\"\n" , key , action )
}
} else {
for _ , key := range macroBinds {
action := inputrc .Escape (binds [inputrc .Unescape (key )].Action )
fmt .Printf ("%s outputs %s\n" , key , action )
}
}
}
func (rl *Shell ) editAndExecuteCommand () {
buffer := *rl .line
edited , err := rl .Buffers .EditBuffer (buffer , "" , "" , rl .Keymap .IsEmacs ())
if err != nil || (len (edited ) == 0 && len (buffer ) != 0 ) {
rl .History .SkipSave ()
errStr := strings .ReplaceAll (err .Error(), "\n" , "" )
changeHint := fmt .Sprintf (color .FgRed +"Editor error: %s" , errStr )
rl .Hint .SetTemporary (changeHint )
return
}
rl .line .Set (edited ...)
rl .Display .AcceptLine ()
rl .History .Accept (false , false , nil )
}
func (rl *Shell ) editCommandLine () {
buffer := *rl .line
keymapCur := rl .Keymap .Main ()
edited , err := rl .Buffers .EditBuffer (buffer , "" , "" , rl .Keymap .IsEmacs ())
if err != nil || (len (edited ) == 0 && len (buffer ) != 0 ) {
rl .History .SkipSave ()
errStr := strings .ReplaceAll (err .Error(), "\n" , "" )
changeHint := fmt .Sprintf (color .FgRed +"Editor error: %s" , errStr )
rl .Hint .SetTemporary (changeHint )
return
}
rl .line .Set (edited ...)
switch keymapCur {
case keymap .Emacs , keymap .EmacsStandard , keymap .EmacsMeta , keymap .EmacsCtrlX :
rl .emacsEditingMode ()
}
}
func (rl *Shell ) redo () {
rl .History .Redo ()
}
func (rl *Shell ) selectKeywordNext () {
rl .History .SkipSave ()
bpos , epos := rl .line .SelectBlankWord (rl .cursor .Pos ())
_ , epos , match := rl .selection .SelectKeyword (bpos , epos , true )
if !match {
return
}
rl .cursor .Set (epos )
rl .selection .Visual (false )
}
func (rl *Shell ) selectKeywordPrev () {
rl .History .SkipSave ()
bpos , epos := rl .line .SelectBlankWord (rl .cursor .Pos ())
_ , epos , match := rl .selection .SelectKeyword (bpos , epos , false )
if !match {
return
}
rl .cursor .Set (epos )
rl .selection .Visual (false )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .