Compare commits

...

2 Commits

Author SHA1 Message Date
TorchedSammy 2fb481c4cb docs: [ci] generate new docs 2022-03-26 21:34:42 +00:00
TorchedSammy 6ea25a22b3
feat: add inline hint text and change what were hints previously to info (closes #126) 2022-03-26 17:34:09 -04:00
18 changed files with 219 additions and 82 deletions

14
api.go
View File

@ -28,6 +28,7 @@ var exports = map[string]lua.LGFunction {
"exec": hlexec, "exec": hlexec,
"runnerMode": hlrunnerMode, "runnerMode": hlrunnerMode,
"goro": hlgoro, "goro": hlgoro,
"hinter": hlhinter,
"multiprompt": hlmlprompt, "multiprompt": hlmlprompt,
"prependPath": hlprependPath, "prependPath": hlprependPath,
"prompt": hlprompt, "prompt": hlprompt,
@ -503,3 +504,16 @@ func hlrunnerMode(L *lua.LState) int {
return 0 return 0
} }
// hinter(cb)
// Sets the hinter function. This will be called on every key insert to determine
// what text to use as an inline hint. The callback is passed 2 arguments:
// the current line and the position. It is expected to return a string
// which will be used for the hint.
// --- @param cb function
func hlhinter(L *lua.LState) int {
hinterCb := L.CheckFunction(1)
hinter = hinterCb
return 0
}

View File

@ -16,6 +16,11 @@ exec(cmd) > Replaces running hilbish with `cmd`
goro(fn) > Puts `fn` in a goroutine goro(fn) > Puts `fn` in a goroutine
hinter(cb) > Sets the hinter function. This will be called on every key insert to determine
what text to use as an inline hint. The callback is passed 2 arguments:
the current line and the position. It is expected to return a string
which will be used for the hint.
inputMode(mode) > Sets the input mode for Hilbish's line reader. Accepts either emacs for vim inputMode(mode) > Sets the input mode for Hilbish's line reader. Accepts either emacs for vim
interval(cb, time) > Runs the `cb` function every `time` milliseconds interval(cb, time) > Runs the `cb` function every `time` milliseconds

View File

@ -33,6 +33,13 @@ function hilbish.exec(cmd) end
--- @param fn function --- @param fn function
function hilbish.goro(fn) end function hilbish.goro(fn) end
--- Sets the hinter function. This will be called on every key insert to determine
--- what text to use as an inline hint. The callback is passed 2 arguments:
--- the current line and the position. It is expected to return a string
--- which will be used for the hint.
--- @param cb function
function hilbish.hinter(cb) end
--- Sets the input mode for Hilbish's line reader. Accepts either emacs for vim --- Sets the input mode for Hilbish's line reader. Accepts either emacs for vim
--- @param mode string --- @param mode string
function hilbish.inputMode(mode) end function hilbish.inputMode(mode) end

View File

@ -7,7 +7,7 @@ type EventReturn struct {
ForwardKey bool ForwardKey bool
ClearHelpers bool ClearHelpers bool
CloseReadline bool CloseReadline bool
HintText []rune InfoText []rune
NewLine []rune NewLine []rune
NewPos int NewPos int
} }

View File

@ -4,10 +4,12 @@ import "regexp"
// SetHintText - a nasty function to force writing a new hint text. It does not update helpers, it just renders // SetHintText - a nasty function to force writing a new hint text. It does not update helpers, it just renders
// them, so the hint will survive until the helpers (thus including the hint) will be updated/recomputed. // them, so the hint will survive until the helpers (thus including the hint) will be updated/recomputed.
/*
func (rl *Instance) SetHintText(s string) { func (rl *Instance) SetHintText(s string) {
rl.hintText = []rune(s) rl.hintText = []rune(s)
rl.renderHelpers() rl.renderHelpers()
} }
*/
func (rl *Instance) getHintText() { func (rl *Instance) getHintText() {
@ -27,7 +29,7 @@ func (rl *Instance) getHintText() {
// writeHintText - only writes the hint text and computes its offsets. // writeHintText - only writes the hint text and computes its offsets.
func (rl *Instance) writeHintText() { func (rl *Instance) writeHintText() {
if len(rl.hintText) == 0 { if len(rl.hintText) == 0 {
rl.hintY = 0 //rl.hintY = 0
return return
} }
@ -41,16 +43,16 @@ func (rl *Instance) writeHintText() {
wrapped, hintLen := WrapText(string(rl.hintText), width) wrapped, hintLen := WrapText(string(rl.hintText), width)
offset += hintLen offset += hintLen
rl.hintY = offset // rl.hintY = offset
hintText := string(wrapped) hintText := string(wrapped)
if len(hintText) > 0 { if len(hintText) > 0 {
print("\r" + rl.HintFormatting + string(hintText) + seqReset) print(rl.HintFormatting + string(hintText) + seqReset)
} }
} }
func (rl *Instance) resetHintText() { func (rl *Instance) resetHintText() {
rl.hintY = 0 //rl.hintY = 0
rl.hintText = []rune{} rl.hintText = []rune{}
} }

View File

@ -183,13 +183,13 @@ func (rl *Instance) completeHistory() (hist []*CompletionGroup) {
return return
} }
history = rl.altHistory history = rl.altHistory
rl.histHint = []rune(rl.altHistName + ": ") rl.histInfo = []rune(rl.altHistName + ": ")
} else { } else {
if rl.mainHistory == nil { if rl.mainHistory == nil {
return return
} }
history = rl.mainHistory history = rl.mainHistory
rl.histHint = []rune(rl.mainHistName + ": ") rl.histInfo = []rune(rl.mainHistName + ": ")
} }
hist[0].init(rl) hist[0].init(rl)

56
readline/info.go 100644
View File

@ -0,0 +1,56 @@
package readline
import "regexp"
// SetInfoText - a nasty function to force writing a new info text. It does not update helpers, it just renders
// them, so the info will survive until the helpers (thus including the info) will be updated/recomputed.
func (rl *Instance) SetInfoText(s string) {
rl.infoText = []rune(s)
rl.renderHelpers()
}
func (rl *Instance) getInfoText() {
if !rl.modeAutoFind && !rl.modeTabFind {
// Return if no infos provided by the user/engine
if rl.InfoText == nil {
rl.resetInfoText()
return
}
// The info text also works with the virtual completion line system.
// This way, the info is also refreshed depending on what we are pointing
// at with our cursor.
rl.infoText = rl.InfoText(rl.getCompletionLine())
}
}
// writeInfoText - only writes the info text and computes its offsets.
func (rl *Instance) writeInfoText() {
if len(rl.infoText) == 0 {
rl.infoY = 0
return
}
width := GetTermWidth()
// Wraps the line, and counts the number of newlines in the string,
// adjusting the offset as well.
re := regexp.MustCompile(`\r?\n`)
newlines := re.Split(string(rl.infoText), -1)
offset := len(newlines)
wrapped, infoLen := WrapText(string(rl.infoText), width)
offset += infoLen
rl.infoY = offset
infoText := string(wrapped)
if len(infoText) > 0 {
print("\r" + rl.InfoFormatting + string(infoText) + seqReset)
}
}
func (rl *Instance) resetInfoText() {
rl.infoY = 0
rl.infoText = []rune{}
}

View File

@ -110,7 +110,7 @@ type Instance struct {
searchMode FindMode // Used for varying hints, and underlying functions called searchMode FindMode // Used for varying hints, and underlying functions called
regexSearch *regexp.Regexp // Holds the current search regex match regexSearch *regexp.Regexp // Holds the current search regex match
mainHist bool // Which history stdin do we want mainHist bool // Which history stdin do we want
histHint []rune // We store a hist hint, for dual history sources histInfo []rune // We store a piece of hist info, for dual history sources
// //
// History ----------------------------------------------------------------------------------- // History -----------------------------------------------------------------------------------
@ -134,19 +134,33 @@ type Instance struct {
histNavIdx int // Used for quick history navigation. histNavIdx int // Used for quick history navigation.
// //
// Hints ------------------------------------------------------------------------------------- // Info -------------------------------------------------------------------------------------
// HintText is a helper function which displays hint text the prompt. // InfoText is a helper function which displays infio text below the prompt.
// HintText takes the line input from the promt and the cursor position. // InfoText takes the line input from the prompt and the cursor position.
// It returns the info text to display.
InfoText func([]rune, int) []rune
// InfoColor is any ANSI escape codes you wish to use for info formatting. By
// default this will just be blue.
InfoFormatting string
infoText []rune // The actual info text
infoY int // Offset to info, if it spans multiple lines
//
// Hints -----------------------------------------------------------------------------------
// HintText is a helper function which displays hint text right after the user's input.
// It takes the line input and cursor position.
// It returns the hint text to display. // It returns the hint text to display.
HintText func([]rune, int) []rune HintText func([]rune, int) []rune
// HintColor any ANSI escape codes you wish to use for hint formatting. By // HintFormatting is just a string to use as the formatting for the hint. By default
// default this will just be blue. // this will be a grey color.
HintFormatting string HintFormatting string
hintText []rune // The actual hint text hintText []rune
hintY int // Offset to hints, if it spans multiple lines
// //
// Vim Operatng Parameters ------------------------------------------------------------------- // Vim Operatng Parameters -------------------------------------------------------------------
@ -205,7 +219,8 @@ func NewInstance() *Instance {
rl.HistoryAutoWrite = true rl.HistoryAutoWrite = true
// Others // Others
rl.HintFormatting = seqFgBlue rl.InfoFormatting = seqFgBlue
rl.HintFormatting = "\x1b[2m"
rl.evtKeyPress = make(map[string]func(string, []rune, int) *EventReturn) rl.evtKeyPress = make(map[string]func(string, []rune, int) *EventReturn)
rl.TempDirectory = os.TempDir() rl.TempDirectory = os.TempDir()

View File

@ -57,9 +57,9 @@ func (rl *Instance) echo() {
// Print the input line with optional syntax highlighting // Print the input line with optional syntax highlighting
if rl.SyntaxHighlighter != nil { if rl.SyntaxHighlighter != nil {
print(rl.SyntaxHighlighter(line) + " ") print(rl.SyntaxHighlighter(line))
} else { } else {
print(string(line) + " ") print(string(line))
} }
} }

View File

@ -20,7 +20,7 @@ func (rl *Instance) RefreshPromptLog(log string) (err error) {
// We adjust cursor movement, depending on which mode we're currently in. // We adjust cursor movement, depending on which mode we're currently in.
if !rl.modeTabCompletion { if !rl.modeTabCompletion {
rl.tcUsedY = 1 rl.tcUsedY = 1
// Account for the hint line // Account for the info line
} else if rl.modeTabCompletion && rl.modeAutoFind { } else if rl.modeTabCompletion && rl.modeAutoFind {
rl.tcUsedY = 0 rl.tcUsedY = 0
} else { } else {
@ -40,7 +40,7 @@ func (rl *Instance) RefreshPromptLog(log string) (err error) {
moveCursorUp(1) moveCursorUp(1)
} }
rl.stillOnRefresh = true rl.stillOnRefresh = true
moveCursorUp(rl.hintY + rl.tcUsedY) moveCursorUp(rl.infoY + rl.tcUsedY)
moveCursorBackwards(GetTermWidth()) moveCursorBackwards(GetTermWidth())
print("\r\n" + seqClearScreenBelow) print("\r\n" + seqClearScreenBelow)
@ -73,7 +73,7 @@ func (rl *Instance) RefreshPromptInPlace(prompt string) (err error) {
// Prompt data intependent // Prompt data intependent
if !rl.modeTabCompletion { if !rl.modeTabCompletion {
rl.tcUsedY = 1 rl.tcUsedY = 1
// Account for the hint line // Account for the info line
} else if rl.modeTabCompletion && rl.modeAutoFind { } else if rl.modeTabCompletion && rl.modeAutoFind {
rl.tcUsedY = 0 rl.tcUsedY = 0
} else { } else {
@ -91,7 +91,7 @@ func (rl *Instance) RefreshPromptInPlace(prompt string) (err error) {
// Clear the input line and everything below // Clear the input line and everything below
print(seqClearLine) print(seqClearLine)
moveCursorUp(rl.hintY + rl.tcUsedY) moveCursorUp(rl.infoY + rl.tcUsedY)
moveCursorBackwards(GetTermWidth()) moveCursorBackwards(GetTermWidth())
print("\r\n" + seqClearScreenBelow) print("\r\n" + seqClearScreenBelow)
@ -118,7 +118,7 @@ func (rl *Instance) RefreshPromptCustom(prompt string, offset int, clearLine boo
// We adjust cursor movement, depending on which mode we're currently in. // We adjust cursor movement, depending on which mode we're currently in.
if !rl.modeTabCompletion { if !rl.modeTabCompletion {
rl.tcUsedY = 1 rl.tcUsedY = 1
} else if rl.modeTabCompletion && rl.modeAutoFind { // Account for the hint line } else if rl.modeTabCompletion && rl.modeAutoFind { // Account for the info line
rl.tcUsedY = 0 rl.tcUsedY = 0
} else { } else {
rl.tcUsedY = 1 rl.tcUsedY = 1

View File

@ -40,10 +40,10 @@ func (rl *Instance) Readline() (string, error) {
rl.posY = 0 rl.posY = 0
rl.tcPrefix = "" rl.tcPrefix = ""
// Completion && hints init // Completion && infos init
rl.resetHintText() rl.resetInfoText()
rl.resetTabCompletion() rl.resetTabCompletion()
rl.getHintText() rl.getInfoText()
// History Init // History Init
// We need this set to the last command, so that we can access it quickly // We need this set to the last command, so that we can access it quickly
@ -63,7 +63,7 @@ func (rl *Instance) Readline() (string, error) {
return string(rl.line), nil return string(rl.line), nil
} }
// Finally, print any hints or completions // Finally, print any info or completions
// if the TabCompletion engines so desires // if the TabCompletion engines so desires
rl.renderHelpers() rl.renderHelpers()
@ -128,8 +128,8 @@ func (rl *Instance) Readline() (string, error) {
rl.updateHelpers() rl.updateHelpers()
} }
if len(ret.HintText) > 0 { if len(ret.InfoText) > 0 {
rl.hintText = ret.HintText rl.infoText = ret.InfoText
rl.clearHelpers() rl.clearHelpers()
rl.renderHelpers() rl.renderHelpers()
} }
@ -174,8 +174,8 @@ func (rl *Instance) Readline() (string, error) {
} }
print(seqClearScreenBelow) print(seqClearScreenBelow)
rl.resetHintText() rl.resetInfoText()
rl.getHintText() rl.getInfoText()
rl.renderHelpers() rl.renderHelpers()
// Line Editing ------------------------------------------------------------------------------------ // Line Editing ------------------------------------------------------------------------------------
@ -533,6 +533,7 @@ func (rl *Instance) editorInput(r []rune) {
// We don't need it when inserting text. // We don't need it when inserting text.
rl.histNavIdx = 0 rl.histNavIdx = 0
rl.insert(r) rl.insert(r)
rl.writeHintText()
} }
if len(rl.multisplit) == 0 { if len(rl.multisplit) == 0 {
@ -696,6 +697,7 @@ func (rl *Instance) escapeSeq(r []rune) {
rl.updateHelpers() rl.updateHelpers()
return return
case seqCtrlRightArrow: case seqCtrlRightArrow:
rl.insert(rl.hintText)
rl.moveCursorByAdjust(rl.viJumpW(tokeniseLine)) rl.moveCursorByAdjust(rl.viJumpW(tokeniseLine))
rl.updateHelpers() rl.updateHelpers()
return return

View File

@ -259,9 +259,9 @@ func (r *registers) resetRegister() {
// The user can show registers completions and insert, no matter the cursor position. // The user can show registers completions and insert, no matter the cursor position.
func (rl *Instance) completeRegisters() (groups []*CompletionGroup) { func (rl *Instance) completeRegisters() (groups []*CompletionGroup) {
// We set the hint exceptionally // We set the info exceptionally
hint := BLUE + "-- registers --" + RESET info := BLUE + "-- registers --" + RESET
rl.hintText = []rune(hint) rl.infoText = []rune(info)
// Make the groups // Make the groups
anonRegs := &CompletionGroup{ anonRegs := &CompletionGroup{

View File

@ -93,16 +93,16 @@ func (rl *Instance) getTabSearchCompletion() {
} }
rl.getCurrentGroup() rl.getCurrentGroup()
// Set the hint for this completion mode // Set the info for this completion mode
rl.hintText = append([]rune("Completion search: "), rl.tfLine...) rl.infoText = append([]rune("Completion search: "), rl.tfLine...)
for _, g := range rl.tcGroups { for _, g := range rl.tcGroups {
g.updateTabFind(rl) g.updateTabFind(rl)
} }
// If total number of matches is zero, we directly change the hint, and return // If total number of matches is zero, we directly change the info, and return
if comps, _, _ := rl.getCompletionCount(); comps == 0 { if comps, _, _ := rl.getCompletionCount(); comps == 0 {
rl.hintText = append(rl.hintText, []rune(DIM+RED+" ! no matches (Ctrl-G/Esc to cancel)"+RESET)...) rl.infoText = append(rl.infoText, []rune(DIM+RED+" ! no matches (Ctrl-G/Esc to cancel)"+RESET)...)
} }
} }
@ -117,25 +117,25 @@ func (rl *Instance) getHistorySearchCompletion() {
rl.tcGroups = checkNilItems(rl.tcGroups) // Avoid nil maps in groups rl.tcGroups = checkNilItems(rl.tcGroups) // Avoid nil maps in groups
rl.getCurrentGroup() // Make sure there is a current group rl.getCurrentGroup() // Make sure there is a current group
// The history hint is already set, but overwrite it if we don't have completions // The history info is already set, but overwrite it if we don't have completions
if len(rl.tcGroups[0].Suggestions) == 0 { if len(rl.tcGroups[0].Suggestions) == 0 {
rl.histHint = []rune(fmt.Sprintf("%s%s%s %s", DIM, RED, rl.histInfo = []rune(fmt.Sprintf("%s%s%s %s", DIM, RED,
"No command history source, or empty (Ctrl-G/Esc to cancel)", RESET)) "No command history source, or empty (Ctrl-G/Esc to cancel)", RESET))
rl.hintText = rl.histHint rl.infoText = rl.histInfo
return return
} }
// Set the hint line with everything // Set the info line with everything
rl.histHint = append([]rune("\033[38;5;183m"+string(rl.histHint)+RESET), rl.tfLine...) rl.histInfo = append([]rune("\033[38;5;183m"+string(rl.histInfo)+RESET), rl.tfLine...)
rl.histHint = append(rl.histHint, []rune(RESET)...) rl.histInfo = append(rl.histInfo, []rune(RESET)...)
rl.hintText = rl.histHint rl.infoText = rl.histInfo
// Refresh filtered candidates // Refresh filtered candidates
rl.tcGroups[0].updateTabFind(rl) rl.tcGroups[0].updateTabFind(rl)
// If no items matched history, add hint text that we failed to search // If no items matched history, add info text that we failed to search
if len(rl.tcGroups[0].Suggestions) == 0 { if len(rl.tcGroups[0].Suggestions) == 0 {
rl.hintText = append(rl.histHint, []rune(DIM+RED+" ! no matches (Ctrl-G/Esc to cancel)"+RESET)...) rl.infoText = append(rl.histInfo, []rune(DIM+RED+" ! no matches (Ctrl-G/Esc to cancel)"+RESET)...)
return return
} }
} }
@ -298,15 +298,15 @@ func (rl *Instance) cropCompletions(comps string) (cropped string, usedY int) {
// Else we go on, but we have more comps than what allowed: // Else we go on, but we have more comps than what allowed:
// we will add a line to the end of the comps, giving the actualized // we will add a line to the end of the comps, giving the actualized
// number of completions remaining and not printed // number of completions remaining and not printed
var moreComps = func(cropped string, offset int) (hinted string, noHint bool) { var moreComps = func(cropped string, offset int) (infoed string, noInfo bool) {
_, _, adjusted := rl.getCompletionCount() _, _, adjusted := rl.getCompletionCount()
remain := adjusted - offset remain := adjusted - offset
if remain == 0 { if remain == 0 {
return cropped, true return cropped, true
} }
hint := fmt.Sprintf(DIM+YELLOW+" %d more completions... (scroll down to show)"+RESET+"\n", remain) info := fmt.Sprintf(DIM+YELLOW+" %d more completions... (scroll down to show)"+RESET+"\n", remain)
hinted = cropped + hint infoed = cropped + info
return hinted, false return infoed, false
} }
// Get the current absolute candidate position (prev groups x suggestions + curGroup.tcPosY) // Get the current absolute candidate position (prev groups x suggestions + curGroup.tcPosY)
@ -509,7 +509,7 @@ func (rl *Instance) hasOneCandidate() bool {
// - The terminal lengh // - The terminal lengh
// we use this function to prompt for confirmation before printing comps. // we use this function to prompt for confirmation before printing comps.
func (rl *Instance) promptCompletionConfirm(sentence string) { func (rl *Instance) promptCompletionConfirm(sentence string) {
rl.hintText = []rune(sentence) rl.infoText = []rune(sentence)
rl.compConfirmWait = true rl.compConfirmWait = true
rl.viUndoSkipAppend = true rl.viUndoSkipAppend = true

View File

@ -33,7 +33,7 @@ func (rl *Instance) updateTabFind(r []rune) {
var err error var err error
rl.regexSearch, err = regexp.Compile("(?i)" + string(rl.tfLine)) rl.regexSearch, err = regexp.Compile("(?i)" + string(rl.tfLine))
if err != nil { if err != nil {
rl.hintText = []rune(Red("Failed to match search regexp")) rl.infoText = []rune(Red("Failed to match search regexp"))
} }
// We update and print // We update and print

View File

@ -1,12 +1,13 @@
package readline package readline
// updateHelpers is a key part of the whole refresh process: // updateHelpers is a key part of the whole refresh process:
// it should coordinate reprinting the input line, any hints and completions // it should coordinate reprinting the input line, any Infos and completions
// and manage to get back to the current (computed) cursor coordinates // and manage to get back to the current (computed) cursor coordinates
func (rl *Instance) updateHelpers() { func (rl *Instance) updateHelpers() {
// Load all hints & completions before anything. // Load all Infos & completions before anything.
// Thus overwrites anything having been dirtily added/forced/modified, like rl.SetHintText() // Thus overwrites anything having been dirtily added/forced/modified, like rl.SetInfoText()
rl.getInfoText()
rl.getHintText() rl.getHintText()
if rl.modeTabCompletion { if rl.modeTabCompletion {
rl.getTabCompletion() rl.getTabCompletion()
@ -75,11 +76,11 @@ func (rl *Instance) resetHelpers() {
rl.modeAutoFind = false rl.modeAutoFind = false
// Now reset all below-input helpers // Now reset all below-input helpers
rl.resetHintText() rl.resetInfoText()
rl.resetTabCompletion() rl.resetTabCompletion()
} }
// clearHelpers - Clears everything: prompt, input, hints & comps, // clearHelpers - Clears everything: prompt, input, Infos & comps,
// and comes back at the prompt. // and comes back at the prompt.
func (rl *Instance) clearHelpers() { func (rl *Instance) clearHelpers() {
@ -97,25 +98,40 @@ func (rl *Instance) clearHelpers() {
moveCursorForwards(rl.posX) moveCursorForwards(rl.posX)
} }
// renderHelpers - pritns all components (prompt, line, hints & comps) // renderHelpers - pritns all components (prompt, line, Infos & comps)
// and replaces the cursor to its current position. This function never // and replaces the cursor to its current position. This function never
// computes or refreshes any value, except from inside the echo function. // computes or refreshes any value, except from inside the echo function.
func (rl *Instance) renderHelpers() { func (rl *Instance) renderHelpers() {
// Optional, because neutral on placement // when the instance is in this state we want it to be "below" the user's
// input for it to be aligned properly
if !rl.compConfirmWait {
rl.writeHintText()
}
rl.echo() rl.echo()
if rl.modeTabCompletion {
// in tab complete mode we want it to update
// when something has been selected
// (dynamic!!)
rl.getHintText()
rl.writeHintText()
} else if !rl.compConfirmWait {
// for the same reason above, do nothing here
} else {
rl.writeHintText()
}
// Go at beginning of first line after input remainder // Go at beginning of first line after input remainder
moveCursorDown(rl.fullY - rl.posY) moveCursorDown(rl.fullY - rl.posY)
moveCursorBackwards(GetTermWidth()) moveCursorBackwards(GetTermWidth())
// Print hints, check for any confirmation hint current. // Print Infos, check for any confirmation Info current.
// (do not overwrite the confirmation question hint) // (do not overwrite the confirmation question Info)
if !rl.compConfirmWait { if !rl.compConfirmWait {
if len(rl.hintText) > 0 { if len(rl.infoText) > 0 {
print("\n") print("\n")
} }
rl.writeHintText() rl.writeInfoText()
moveCursorBackwards(GetTermWidth()) moveCursorBackwards(GetTermWidth())
// Print completions and go back to beginning of this line // Print completions and go back to beginning of this line
@ -126,17 +142,17 @@ func (rl *Instance) renderHelpers() {
} }
// If we are still waiting for the user to confirm too long completions // If we are still waiting for the user to confirm too long completions
// Immediately refresh the hints // Immediately refresh the Infos
if rl.compConfirmWait { if rl.compConfirmWait {
print("\n") print("\n")
rl.writeHintText() rl.writeInfoText()
rl.getHintText() rl.getInfoText()
moveCursorBackwards(GetTermWidth()) moveCursorBackwards(GetTermWidth())
} }
// Anyway, compensate for hint printout // Anyway, compensate for Info printout
if len(rl.hintText) > 0 { if len(rl.infoText) > 0 {
moveCursorUp(rl.hintY) moveCursorUp(rl.infoY)
} else if !rl.compConfirmWait { } else if !rl.compConfirmWait {
moveCursorUp(1) moveCursorUp(1)
} else if rl.compConfirmWait { } else if rl.compConfirmWait {

View File

@ -399,22 +399,22 @@ func (rl *Instance) refreshVimStatus() {
rl.updateHelpers() rl.updateHelpers()
} }
// viHintMessage - lmorg's way of showing Vim status is to overwrite the hint. // viInfoMessage - lmorg's way of showing Vim status is to overwrite the info.
// Currently not used, as there is a possibility to show the current Vim mode in the prompt. // Currently not used, as there is a possibility to show the current Vim mode in the prompt.
func (rl *Instance) viHintMessage() { func (rl *Instance) viInfoMessage() {
switch rl.modeViMode { switch rl.modeViMode {
case VimKeys: case VimKeys:
rl.hintText = []rune("-- VIM KEYS -- (press `i` to return to normal editing mode)") rl.infoText = []rune("-- VIM KEYS -- (press `i` to return to normal editing mode)")
case VimInsert: case VimInsert:
rl.hintText = []rune("-- INSERT --") rl.infoText = []rune("-- INSERT --")
case VimReplaceOnce: case VimReplaceOnce:
rl.hintText = []rune("-- REPLACE CHARACTER --") rl.infoText = []rune("-- REPLACE CHARACTER --")
case VimReplaceMany: case VimReplaceMany:
rl.hintText = []rune("-- REPLACE --") rl.infoText = []rune("-- REPLACE --")
case VimDelete: case VimDelete:
rl.hintText = []rune("-- DELETE --") rl.infoText = []rune("-- DELETE --")
default: default:
rl.getHintText() rl.getInfoText()
} }
rl.clearHelpers() rl.clearHelpers()

View File

@ -33,7 +33,7 @@ func (rl *Instance) viDelete(r rune) {
rl.saveBufToRegister(rl.line) rl.saveBufToRegister(rl.line)
rl.clearLine() rl.clearLine()
rl.resetHelpers() rl.resetHelpers()
rl.getHintText() rl.getInfoText()
case 'e': case 'e':
vii := rl.getViIterations() vii := rl.getViIterations()

24
rl.go
View File

@ -13,6 +13,7 @@ type lineReader struct {
rl *readline.Instance rl *readline.Instance
} }
var fileHist *fileHistory var fileHist *fileHistory
var hinter lua.LValue
// other gophers might hate this naming but this is local, shut up // other gophers might hate this naming but this is local, shut up
func newLineReader(prompt string, noHist bool) *lineReader { func newLineReader(prompt string, noHist bool) *lineReader {
@ -44,6 +45,25 @@ func newLineReader(prompt string, noHist bool) *lineReader {
} }
hooks.Em.Emit("hilbish.vimAction", actionStr, args) hooks.Em.Emit("hilbish.vimAction", actionStr, args)
} }
rl.HintText = func(line []rune, pos int) []rune {
err := l.CallByParam(lua.P{
Fn: hinter,
NRet: 1,
Protect: true,
}, lua.LString(string(line)), lua.LNumber(pos))
if err != nil {
fmt.Println(err)
return []rune{}
}
retVal := l.Get(-1)
hintText := ""
if luaStr, ok := retVal.(lua.LString); retVal != lua.LNil && ok {
hintText = luaStr.String()
}
return []rune(hintText)
}
rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) { rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) {
ctx := string(line) ctx := string(line)
var completions []string var completions []string
@ -253,7 +273,7 @@ func (lr *lineReader) luaGetHistory(L *lua.LState) int {
cmd, _ := fileHist.GetLine(idx) cmd, _ := fileHist.GetLine(idx)
L.Push(lua.LString(cmd)) L.Push(lua.LString(cmd))
return 0 return 1
} }
func (lr *lineReader) luaAllHistory(L *lua.LState) int { func (lr *lineReader) luaAllHistory(L *lua.LState) int {
@ -267,7 +287,7 @@ func (lr *lineReader) luaAllHistory(L *lua.LState) int {
L.Push(tbl) L.Push(tbl)
return 0 return 1
} }
func (lr *lineReader) luaClearHistory(l *lua.LState) int { func (lr *lineReader) luaClearHistory(l *lua.LState) int {