2
2
mirror of https://github.com/Hilbis/Hilbish synced 2025-07-01 16:52:03 +00:00

fix: document functions

This commit is contained in:
sammyette 2025-06-15 15:15:24 -04:00
parent 96250f8227
commit 2fcbe0ad4c
Signed by: sammyette
GPG Key ID: 904FC49417B44DCD
37 changed files with 393 additions and 567 deletions

View File

@ -85,6 +85,7 @@ var prefix = map[string]string{
"bait": "b", "bait": "b",
"terminal": "term", "terminal": "term",
"snail": "snail", "snail": "snail",
"readline": "rl",
} }
func getTagsAndDocs(docs string) (map[string][]tag, []string) { func getTagsAndDocs(docs string) (map[string][]tag, []string) {

View File

@ -1,124 +0,0 @@
---
title: Module hilbish.editor
description: interactions for Hilbish's line reader
layout: doc
menu:
docs:
parent: "API"
---
## Introduction
The hilbish.editor interface provides functions to
directly interact with the line editor in use.
## Functions
|||
|----|----|
|<a href="#editor.deleteByAmount">deleteByAmount(amount)</a>|Deletes characters in the line by the given amount.|
|<a href="#editor.getLine">getLine() -> string</a>|Returns the current input line.|
|<a href="#editor.getVimRegister">getVimRegister(register) -> string</a>|Returns the text that is at the register.|
|<a href="#editor.insert">insert(text)</a>|Inserts text into the Hilbish command line.|
|<a href="#editor.getChar">getChar() -> string</a>|Reads a keystroke from the user. This is in a format of something like Ctrl-L.|
|<a href="#editor.setVimRegister">setVimRegister(register, text)</a>|Sets the vim register at `register` to hold the passed text.|
<hr>
<div id='editor.deleteByAmount'>
<h4 class='heading'>
hilbish.editor.deleteByAmount(amount)
<a href="#editor.deleteByAmount" class='heading-link'>
<i class="fas fa-paperclip"></i>
</a>
</h4>
Deletes characters in the line by the given amount.
#### Parameters
`number` **`amount`**
</div>
<hr>
<div id='editor.getLine'>
<h4 class='heading'>
hilbish.editor.getLine() -> string
<a href="#editor.getLine" class='heading-link'>
<i class="fas fa-paperclip"></i>
</a>
</h4>
Returns the current input line.
#### Parameters
This function has no parameters.
</div>
<hr>
<div id='editor.getVimRegister'>
<h4 class='heading'>
hilbish.editor.getVimRegister(register) -> string
<a href="#editor.getVimRegister" class='heading-link'>
<i class="fas fa-paperclip"></i>
</a>
</h4>
Returns the text that is at the register.
#### Parameters
`string` **`register`**
</div>
<hr>
<div id='editor.insert'>
<h4 class='heading'>
hilbish.editor.insert(text)
<a href="#editor.insert" class='heading-link'>
<i class="fas fa-paperclip"></i>
</a>
</h4>
Inserts text into the Hilbish command line.
#### Parameters
`string` **`text`**
</div>
<hr>
<div id='editor.getChar'>
<h4 class='heading'>
hilbish.editor.getChar() -> string
<a href="#editor.getChar" class='heading-link'>
<i class="fas fa-paperclip"></i>
</a>
</h4>
Reads a keystroke from the user. This is in a format of something like Ctrl-L.
#### Parameters
This function has no parameters.
</div>
<hr>
<div id='editor.setVimRegister'>
<h4 class='heading'>
hilbish.editor.setVimRegister(register, text)
<a href="#editor.setVimRegister" class='heading-link'>
<i class="fas fa-paperclip"></i>
</a>
</h4>
Sets the vim register at `register` to hold the passed text.
#### Parameters
`string` **`register`**
`string` **`text`**
</div>

View File

@ -1,6 +1,6 @@
--- ---
title: Module readline title: Module readline
description: Package readline is a pure-Go re-imagining of the UNIX readline API description: line reader library
layout: doc layout: doc
menu: menu:
docs: docs:
@ -8,38 +8,60 @@ menu:
--- ---
## Introduction ## Introduction
This package is designed to be run independently from murex and at some
point it will be separated into it's own git repository (at a stage when I
am confident that murex will no longer be the primary driver for features,
bugs or other code changes)
line reader library
The readline module is responsible for reading input from the user. The readline module is responsible for reading input from the user.
The readline module is what Hilbish uses to read input from the user, The readline module is what Hilbish uses to read input from the user,
including all the interactive features of Hilbish like history search, including all the interactive features of Hilbish like history search,
syntax highlighting, everything. The global Hilbish readline instance syntax highlighting, everything. The global Hilbish readline instance
is usable at `hilbish.editor`. is usable at `hilbish.editor`.
Package terminal provides support functions for dealing with terminals, as ## Functions
commonly found on UNIX systems. |||
|----|----|
|<a href="#New">new() -> @Readline</a>|Creates a new readline instance.|
Putting a terminal into raw mode is the most common requirement: <hr>
<div id='New'>
<h4 class='heading'>
readline.new() -> <a href="/Hilbish/docs/api/readline/#readline" style="text-decoration: none;" id="lol">Readline</a>
<a href="#New" class='heading-link'>
<i class="fas fa-paperclip"></i>
</a>
</h4>
oldState, err := terminal.MakeRaw(0) Creates a new readline instance.
if err != nil {
panic(err)
}
defer terminal.Restore(0, oldState)
Package terminal provides support functions for dealing with terminals, as #### Parameters
commonly found on UNIX systems. This function has no parameters.
</div>
Putting a terminal into raw mode is the most common requirement: ## Types
<hr>
oldState, err := terminal.MakeRaw(0) ## Readline
if err != nil { blah blah
panic(err)
} ### Methods
defer terminal.Restore(0, oldState) #### deleteByAmount(amount)
Deletes characters in the line by the given amount.
#### getLine() -> string
Returns the current input line.
#### getVimRegister(register) -> string
Returns the text that is at the register.
#### insert(text)
Inserts text into the Hilbish command line.
#### log(text)
Prints a message *before* the prompt without it being interrupted by user input.
#### read() -> string
Reads input from the user.
#### getChar() -> string
Reads a keystroke from the user. This is in a format of something like Ctrl-L.
#### setVimRegister(register, text)
Sets the vim register at `register` to hold the passed text.

128
editor.go
View File

@ -1,128 +0,0 @@
package main
import (
"hilbish/util"
rt "github.com/arnodel/golua/runtime"
)
// #interface editor
// interactions for Hilbish's line reader
// The hilbish.editor interface provides functions to
// directly interact with the line editor in use.
func editorLoader(rtm *rt.Runtime) *rt.Table {
exports := map[string]util.LuaExport{
"insert": {editorInsert, 1, false},
"setVimRegister": {editorSetRegister, 1, false},
"getVimRegister": {editorGetRegister, 2, false},
"getLine": {editorGetLine, 0, false},
"readChar": {editorReadChar, 0, false},
"deleteByAmount": {editorDeleteByAmount, 1, false},
}
mod := rt.NewTable()
util.SetExports(rtm, mod, exports)
return mod
}
// #interface editor
// insert(text)
// Inserts text into the Hilbish command line.
// #param text string
func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
}
text, err := c.StringArg(0)
if err != nil {
return nil, err
}
lr.rl.Insert(text)
return c.Next(), nil
}
// #interface editor
// setVimRegister(register, text)
// Sets the vim register at `register` to hold the passed text.
// #param register string
// #param text string
func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
}
register, err := c.StringArg(0)
if err != nil {
return nil, err
}
text, err := c.StringArg(1)
if err != nil {
return nil, err
}
lr.rl.SetRegisterBuf(register, []rune(text))
return c.Next(), nil
}
// #interface editor
// getVimRegister(register) -> string
// Returns the text that is at the register.
// #param register string
func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
}
register, err := c.StringArg(0)
if err != nil {
return nil, err
}
buf := lr.rl.GetFromRegister(register)
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
}
// #interface editor
// getLine() -> string
// Returns the current input line.
// #returns string
func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
buf := lr.rl.GetLine()
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
}
// #interface editor
// getChar() -> string
// Reads a keystroke from the user. This is in a format of something like Ctrl-L.
func editorReadChar(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
buf := lr.rl.ReadChar()
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
}
// #interface editor
// deleteByAmount(amount)
// Deletes characters in the line by the given amount.
// #param amount number
func editorDeleteByAmount(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
}
amount, err := c.IntArg(0)
if err != nil {
return nil, err
}
lr.rl.DeleteByAmount(int(amount))
return c.Next(), nil
}

View File

@ -7,24 +7,6 @@ local hilbish = {}
--- @param cmd string --- @param cmd string
function hilbish.aliases.add(alias, cmd) end function hilbish.aliases.add(alias, cmd) end
--- Deletes characters in the line by the given amount.
function hilbish.editor.deleteByAmount(amount) end
--- Returns the current input line.
function hilbish.editor.getLine() end
--- Returns the text that is at the register.
function hilbish.editor.getVimRegister(register) end
--- Inserts text into the Hilbish command line.
function hilbish.editor.insert(text) end
--- Reads a keystroke from the user. This is in a format of something like Ctrl-L.
function hilbish.editor.getChar() end
--- Sets the vim register at `register` to hold the passed text.
function hilbish.editor.setVimRegister(register, text) end
--- Return binaries/executables based on the provided parameters. --- Return binaries/executables based on the provided parameters.
--- This function is meant to be used as a helper in a command completion handler. --- This function is meant to be used as a helper in a command completion handler.
--- ---

View File

@ -2,4 +2,31 @@
local readline = {} local readline = {}
--- Deletes characters in the line by the given amount.
function readline:deleteByAmount(amount) end
--- Returns the current input line.
function readline:getLine() end
--- Returns the text that is at the register.
function readline:getVimRegister(register) end
--- Inserts text into the Hilbish command line.
function readline:insert(text) end
--- Prints a message *before* the prompt without it being interrupted by user input.
function readline:log(text) end
--- Creates a new readline instance.
function readline.new() end
--- Reads input from the user.
function readline:read() end
--- Reads a keystroke from the user. This is in a format of something like Ctrl-L.
function readline:getChar() end
--- Sets the vim register at `register` to hold the passed text.
function readline:setVimRegister(register, text) end
return readline return readline

View File

@ -54,14 +54,14 @@ var (
seqCtrlDelete2 = string([]byte{27, 91, 77}) seqCtrlDelete2 = string([]byte{27, 91, 77})
seqAltDelete = string([]byte{27, 91, 51, 59, 51, 126}) seqAltDelete = string([]byte{27, 91, 51, 59, 51, 126})
seqShiftTab = string([]byte{27, 91, 90}) seqShiftTab = string([]byte{27, 91, 90})
seqAltQuote = string([]byte{27, 34}) // Added for showing registers ^[" seqAltQuote = string([]byte{27, 34}) // Added for showing registers ^["
seqAltB = string([]byte{27, 98}) seqAltB = string([]byte{27, 98})
seqAltD = string([]byte{27, 100}) seqAltD = string([]byte{27, 100})
seqAltF = string([]byte{27, 102}) seqAltF = string([]byte{27, 102})
seqAltR = string([]byte{27, 114}) // Used for alternative history seqAltR = string([]byte{27, 114}) // Used for alternative history
seqAltBackspace = string([]byte{27, 127}) seqAltBackspace = string([]byte{27, 127})
seqPageUp = string([]byte{27, 91, 53, 126}) seqPageUp = string([]byte{27, 91, 53, 126})
seqPageDown = string([]byte{27, 91, 54, 126}) seqPageDown = string([]byte{27, 91, 54, 126})
) )
const ( const (
@ -76,7 +76,7 @@ const (
seqCursorTopLeft = "\x1b[H" // Clears screen and places cursor on top-left seqCursorTopLeft = "\x1b[H" // Clears screen and places cursor on top-left
seqGetCursorPos = "\x1b6n" // response: "\x1b{Line};{Column}R" seqGetCursorPos = "\x1b6n" // response: "\x1b{Line};{Column}R"
seqHideCursor = "\x1b[?25l" seqHideCursor = "\x1b[?25l"
seqUnhideCursor = "\x1b[?25h" seqUnhideCursor = "\x1b[?25h"
seqCtrlLeftArrow = "\x1b[1;5D" seqCtrlLeftArrow = "\x1b[1;5D"
@ -143,55 +143,94 @@ const (
// TODO: return whether its actually a sequence or not // TODO: return whether its actually a sequence or not
// remedies the edge case of someone literally typing Ctrl-A for example. // remedies the edge case of someone literally typing Ctrl-A for example.
func (rl *Instance) ReadChar() string { func (rl *Readline) ReadChar() string {
b := make([]byte, 1024) b := make([]byte, 1024)
i, _ := os.Stdin.Read(b) i, _ := os.Stdin.Read(b)
r := []rune(string(b)) r := []rune(string(b))
s := string(r[:i]) s := string(r[:i])
switch b[0] { switch b[0] {
case charCtrlA: return "Ctrl-A" case charCtrlA:
case charCtrlB: return "Ctrl-B" return "Ctrl-A"
case charCtrlC: return "Ctrl-C" case charCtrlB:
case charEOF: return "Ctrl-D" return "Ctrl-B"
case charCtrlE: return "Ctrl-E" case charCtrlC:
case charCtrlF: return "Ctrl-F" return "Ctrl-C"
case charCtrlG: return "Ctrl-G" case charEOF:
case charBackspace, charBackspace2: return "Backspace" return "Ctrl-D"
case charTab: return "Tab" case charCtrlE:
case charCtrlK: return "Ctrl-K" return "Ctrl-E"
case charCtrlL: return "Ctrl-L" case charCtrlF:
case charCtrlN: return "Ctrl-N" return "Ctrl-F"
case charCtrlO: return "Ctrl-O" case charCtrlG:
case charCtrlP: return "Ctrl-P" return "Ctrl-G"
case charCtrlQ: return "Ctrl-Q" case charBackspace, charBackspace2:
case charCtrlR: return "Ctrl-R" return "Backspace"
case charCtrlS: return "Ctrl-S" case charTab:
case charCtrlT: return "Ctrl-T" return "Tab"
case charCtrlU: return "Ctrl-U" case charCtrlK:
case charCtrlV: return "Ctrl-V" return "Ctrl-K"
case charCtrlW: return "Ctrl-W" case charCtrlL:
case charCtrlX: return "Ctrl-X" return "Ctrl-L"
case charCtrlY: return "Ctrl-Y" case charCtrlN:
case charCtrlZ: return "Ctrl-Z" return "Ctrl-N"
case '\r': fallthrough case charCtrlO:
case '\n': return "Enter" return "Ctrl-O"
case charEscape: case charCtrlP:
switch s { return "Ctrl-P"
case string(charEscape): return "Escape" case charCtrlQ:
case seqUp: return "Up" return "Ctrl-Q"
case seqDown: return "Down" case charCtrlR:
case seqBackwards: return "Left" return "Ctrl-R"
case seqForwards: return "Right" case charCtrlS:
case seqCtrlLeftArrow: return "Ctrl-Left" return "Ctrl-S"
case seqCtrlRightArrow: return "Ctrl-Right" case charCtrlT:
case seqCtrlDelete, seqCtrlDelete2: return "Ctrl-Delete" return "Ctrl-T"
case seqHome, seqHomeSc: return "Home" case charCtrlU:
case seqEnd, seqEndSc: return "End" return "Ctrl-U"
case seqDelete, seqDelete2: return "Delete" case charCtrlV:
case seqPageUp: return "Page-Up" return "Ctrl-V"
case seqPageDown: return "Page-Down" case charCtrlW:
} return "Ctrl-W"
case charCtrlX:
return "Ctrl-X"
case charCtrlY:
return "Ctrl-Y"
case charCtrlZ:
return "Ctrl-Z"
case '\r':
fallthrough
case '\n':
return "Enter"
case charEscape:
switch s {
case string(charEscape):
return "Escape"
case seqUp:
return "Up"
case seqDown:
return "Down"
case seqBackwards:
return "Left"
case seqForwards:
return "Right"
case seqCtrlLeftArrow:
return "Ctrl-Left"
case seqCtrlRightArrow:
return "Ctrl-Right"
case seqCtrlDelete, seqCtrlDelete2:
return "Ctrl-Delete"
case seqHome, seqHomeSc:
return "Home"
case seqEnd, seqEndSc:
return "End"
case seqDelete, seqDelete2:
return "Delete"
case seqPageUp:
return "Page-Up"
case seqPageDown:
return "Page-Down"
}
} }
return s return s

View File

@ -4,12 +4,13 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"github.com/rivo/uniseg" "github.com/rivo/uniseg"
) )
// initGrid - Grid display details. Called each time we want to be sure to have // initGrid - Grid display details. Called each time we want to be sure to have
// a working completion group either immediately, or later on. Generally defered. // a working completion group either immediately, or later on. Generally defered.
func (g *CompletionGroup) initGrid(rl *Instance) { func (g *CompletionGroup) initGrid(rl *Readline) {
// Compute size of each completion item box // Compute size of each completion item box
tcMaxLength := 1 tcMaxLength := 1
@ -44,7 +45,7 @@ func (g *CompletionGroup) initGrid(rl *Instance) {
} }
// moveTabGridHighlight - Moves the highlighting for currently selected completion item (grid display) // moveTabGridHighlight - Moves the highlighting for currently selected completion item (grid display)
func (g *CompletionGroup) moveTabGridHighlight(rl *Instance, x, y int) (done bool, next bool) { func (g *CompletionGroup) moveTabGridHighlight(rl *Readline, x, y int) (done bool, next bool) {
g.tcPosX += x g.tcPosX += x
g.tcPosY += y g.tcPosY += y
@ -96,7 +97,7 @@ func (g *CompletionGroup) moveTabGridHighlight(rl *Instance, x, y int) (done boo
} }
// writeGrid - A grid completion string // writeGrid - A grid completion string
func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) { func (g *CompletionGroup) writeGrid(rl *Readline) (comp string) {
// If group title, print it and adjust offset. // If group title, print it and adjust offset.
if g.Name != "" { if g.Name != "" {
@ -127,9 +128,9 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {
sugg := g.Suggestions[i] sugg := g.Suggestions[i]
if len(sugg) > GetTermWidth() { if len(sugg) > GetTermWidth() {
sugg = sugg[:GetTermWidth() - 4] + "..." sugg = sugg[:GetTermWidth()-4] + "..."
} }
formatStr := "%-"+cellWidth+"s%s " formatStr := "%-" + cellWidth + "s%s "
if g.tcMaxX == 1 { if g.tcMaxX == 1 {
formatStr = "%s%s" formatStr = "%s%s"
} }

View File

@ -49,7 +49,7 @@ type CompletionGroup struct {
} }
// init - The completion group computes and sets all its values, and is then ready to work. // init - The completion group computes and sets all its values, and is then ready to work.
func (g *CompletionGroup) init(rl *Instance) { func (g *CompletionGroup) init(rl *Readline) {
// Details common to all displays // Details common to all displays
g.checkCycle(rl) // Based on the number of groups given to the shell, allows cycling or not g.checkCycle(rl) // Based on the number of groups given to the shell, allows cycling or not
@ -70,7 +70,7 @@ func (g *CompletionGroup) init(rl *Instance) {
// updateTabFind - When searching through all completion groups (whether it be command history or not), // updateTabFind - When searching through all completion groups (whether it be command history or not),
// we ask each of them to filter its own items and return the results to the shell for aggregating them. // we ask each of them to filter its own items and return the results to the shell for aggregating them.
// The rx parameter is passed, as the shell already checked that the search pattern is valid. // The rx parameter is passed, as the shell already checked that the search pattern is valid.
func (g *CompletionGroup) updateTabFind(rl *Instance) { func (g *CompletionGroup) updateTabFind(rl *Readline) {
suggs := rl.Searcher(rl.search, g.Suggestions) suggs := rl.Searcher(rl.search, g.Suggestions)
// We perform filter right here, so we create a new completion group, and populate it with our results. // We perform filter right here, so we create a new completion group, and populate it with our results.
@ -97,7 +97,7 @@ func (g *CompletionGroup) updateTabFind(rl *Instance) {
} }
// checkCycle - Based on the number of groups given to the shell, allows cycling or not // checkCycle - Based on the number of groups given to the shell, allows cycling or not
func (g *CompletionGroup) checkCycle(rl *Instance) { func (g *CompletionGroup) checkCycle(rl *Readline) {
if len(rl.tcGroups) == 1 { if len(rl.tcGroups) == 1 {
g.allowCycle = true g.allowCycle = true
} }
@ -108,7 +108,7 @@ func (g *CompletionGroup) checkCycle(rl *Instance) {
} }
// checkMaxLength - Based on the number of groups given to the shell, check/set MaxLength defaults // checkMaxLength - Based on the number of groups given to the shell, check/set MaxLength defaults
func (g *CompletionGroup) checkMaxLength(rl *Instance) { func (g *CompletionGroup) checkMaxLength(rl *Readline) {
// This means the user forgot to set it // This means the user forgot to set it
if g.MaxLength == 0 { if g.MaxLength == 0 {
@ -147,7 +147,7 @@ func checkNilItems(groups []*CompletionGroup) (checked []*CompletionGroup) {
// writeCompletion - This function produces a formatted string containing all appropriate items // writeCompletion - This function produces a formatted string containing all appropriate items
// and according to display settings. This string is then appended to the main completion string. // and according to display settings. This string is then appended to the main completion string.
func (g *CompletionGroup) writeCompletion(rl *Instance) (comp string) { func (g *CompletionGroup) writeCompletion(rl *Readline) (comp string) {
// Avoids empty groups in suggestions // Avoids empty groups in suggestions
if len(g.Suggestions) == 0 { if len(g.Suggestions) == 0 {
@ -169,7 +169,7 @@ func (g *CompletionGroup) writeCompletion(rl *Instance) (comp string) {
// getCurrentCell - The completion groups computes the current cell value, // getCurrentCell - The completion groups computes the current cell value,
// depending on its display type and its different parameters // depending on its display type and its different parameters
func (g *CompletionGroup) getCurrentCell(rl *Instance) string { func (g *CompletionGroup) getCurrentCell(rl *Readline) string {
switch g.DisplayType { switch g.DisplayType {
case TabDisplayGrid: case TabDisplayGrid:

View File

@ -8,7 +8,7 @@ import (
// initList - List display details. Because of the way alternative completions // initList - List display details. Because of the way alternative completions
// are handled, MaxLength cannot be set when there are alternative completions. // are handled, MaxLength cannot be set when there are alternative completions.
func (g *CompletionGroup) initList(rl *Instance) { func (g *CompletionGroup) initList(rl *Readline) {
// We may only ever have two different // We may only ever have two different
// columns: (suggestions, and alternatives) // columns: (suggestions, and alternatives)
@ -53,7 +53,7 @@ func (g *CompletionGroup) initList(rl *Instance) {
// moveTabListHighlight - Moves the highlighting for currently selected completion item (list display) // moveTabListHighlight - Moves the highlighting for currently selected completion item (list display)
// We don't care about the x, because only can have 2 columns of selectable choices (--long and -s) // We don't care about the x, because only can have 2 columns of selectable choices (--long and -s)
func (g *CompletionGroup) moveTabListHighlight(rl *Instance, x, y int) (done bool, next bool) { func (g *CompletionGroup) moveTabListHighlight(rl *Readline, x, y int) (done bool, next bool) {
// We dont' pass to x, because not managed by callers // We dont' pass to x, because not managed by callers
g.tcPosY += x g.tcPosY += x
@ -153,7 +153,7 @@ func (g *CompletionGroup) moveTabListHighlight(rl *Instance, x, y int) (done boo
} }
// writeList - A list completion string // writeList - A list completion string
func (g *CompletionGroup) writeList(rl *Instance) (comp string) { func (g *CompletionGroup) writeList(rl *Readline) (comp string) {
// Print group title and adjust offset if there is one. // Print group title and adjust offset if there is one.
if g.Name != "" { if g.Name != "" {
@ -249,7 +249,7 @@ func (g *CompletionGroup) writeList(rl *Instance) (comp string) {
return return
} }
func (rl *Instance) getListPad() (pad int) { func (rl *Readline) getListPad() (pad int) {
for _, group := range rl.tcGroups { for _, group := range rl.tcGroups {
if group.DisplayType == TabDisplayList { if group.DisplayType == TabDisplayList {
for i := range group.Suggestions { for i := range group.Suggestions {

View File

@ -7,7 +7,7 @@ import (
// initMap - Map display details. Called each time we want to be sure to have // initMap - Map display details. Called each time we want to be sure to have
// a working completion group either immediately, or later on. Generally defered. // a working completion group either immediately, or later on. Generally defered.
func (g *CompletionGroup) initMap(rl *Instance) { func (g *CompletionGroup) initMap(rl *Readline) {
// We make the map anyway, especially if we need to use it later // We make the map anyway, especially if we need to use it later
if g.Descriptions == nil { if g.Descriptions == nil {
@ -35,7 +35,7 @@ func (g *CompletionGroup) initMap(rl *Instance) {
} }
// moveTabMapHighlight - Moves the highlighting for currently selected completion item (map display) // moveTabMapHighlight - Moves the highlighting for currently selected completion item (map display)
func (g *CompletionGroup) moveTabMapHighlight(rl *Instance, x, y int) (done bool, next bool) { func (g *CompletionGroup) moveTabMapHighlight(rl *Readline, x, y int) (done bool, next bool) {
g.tcPosY += x g.tcPosY += x
g.tcPosY += y g.tcPosY += y
@ -72,7 +72,7 @@ func (g *CompletionGroup) moveTabMapHighlight(rl *Instance, x, y int) (done bool
} }
// writeMap - A map or list completion string // writeMap - A map or list completion string
func (g *CompletionGroup) writeMap(rl *Instance) (comp string) { func (g *CompletionGroup) writeMap(rl *Readline) (comp string) {
if g.Name != "" { if g.Name != "" {
// Print group title (changes with line returns depending on type) // Print group title (changes with line returns depending on type)

View File

@ -1,7 +1,7 @@
package readline package readline
import ( import (
// "fmt" // "fmt"
"os" "os"
"regexp" "regexp"
"strconv" "strconv"
@ -28,7 +28,7 @@ func leftMost() []byte {
var rxRcvCursorPos = regexp.MustCompile("^\x1b([0-9]+);([0-9]+)R$") var rxRcvCursorPos = regexp.MustCompile("^\x1b([0-9]+);([0-9]+)R$")
func (rl *Instance) getCursorPos() (x int, y int) { func (rl *Readline) getCursorPos() (x int, y int) {
if !rl.EnableGetCursorPos { if !rl.EnableGetCursorPos {
return -1, -1 return -1, -1
} }
@ -143,7 +143,7 @@ func unhideCursor() {
print(seqUnhideCursor) print(seqUnhideCursor)
} }
func (rl *Instance) backspace(forward bool) { func (rl *Readline) backspace(forward bool) {
if len(rl.line) == 0 || rl.pos == 0 { if len(rl.line) == 0 || rl.pos == 0 {
return return
} }
@ -151,7 +151,7 @@ func (rl *Instance) backspace(forward bool) {
rl.deleteBackspace(forward) rl.deleteBackspace(forward)
} }
func (rl *Instance) moveCursorByAdjust(adjust int) { func (rl *Readline) moveCursorByAdjust(adjust int) {
switch { switch {
case adjust > 0: case adjust > 0:
rl.pos += adjust rl.pos += adjust

View File

@ -14,7 +14,7 @@ import (
) )
// writeTempFile - This function optionally accepts a filename (generally specified with an extension). // writeTempFile - This function optionally accepts a filename (generally specified with an extension).
func (rl *Instance) writeTempFile(content []byte, filename string) (string, error) { func (rl *Readline) writeTempFile(content []byte, filename string) (string, error) {
// The final path to the buffer on disk // The final path to the buffer on disk
var path string var path string

View File

@ -15,7 +15,7 @@ const defaultEditor = "vi"
// depending on the actions taken by the user within it (eg: x or q! in Vim) // depending on the actions taken by the user within it (eg: x or q! in Vim)
// The filename parameter can be used to pass a specific filename.ext pattern, // The filename parameter can be used to pass a specific filename.ext pattern,
// which might be useful if the editor has builtin filetype plugin functionality. // which might be useful if the editor has builtin filetype plugin functionality.
func (rl *Instance) StartEditorWithBuffer(multiline []rune, filename string) ([]rune, error) { func (rl *Readline) StartEditorWithBuffer(multiline []rune, filename string) ([]rune, error) {
name, err := rl.writeTempFile([]byte(string(multiline)), filename) name, err := rl.writeTempFile([]byte(string(multiline)), filename)
if err != nil { if err != nil {
return multiline, err return multiline, err

View File

@ -13,11 +13,11 @@ type EventReturn struct {
} }
// AddEvent registers a new keypress handler // AddEvent registers a new keypress handler
func (rl *Instance) AddEvent(keyPress string, callback func(string, []rune, int) *EventReturn) { func (rl *Readline) AddEvent(keyPress string, callback func(string, []rune, int) *EventReturn) {
rl.evtKeyPress[keyPress] = callback rl.evtKeyPress[keyPress] = callback
} }
// DelEvent deregisters an existing keypress handler // DelEvent deregisters an existing keypress handler
func (rl *Instance) DelEvent(keyPress string) { func (rl *Readline) DelEvent(keyPress string) {
delete(rl.evtKeyPress, keyPress) delete(rl.evtKeyPress, keyPress)
} }

View File

@ -1,7 +0,0 @@
// Package readline is a pure-Go re-imagining of the UNIX readline API
//
// This package is designed to be run independently from murex and at some
// point it will be separated into it's own git repository (at a stage when I
// am confident that murex will no longer be the primary driver for features,
// bugs or other code changes)
package readline

View File

@ -11,7 +11,7 @@ func (rl *Instance) SetHintText(s string) {
} }
*/ */
func (rl *Instance) getHintText() { func (rl *Readline) getHintText() {
if !rl.modeAutoFind && !rl.modeTabFind { if !rl.modeAutoFind && !rl.modeTabFind {
// Return if no hints provided by the user/engine // Return if no hints provided by the user/engine
@ -27,7 +27,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 *Readline) writeHintText() {
if len(rl.hintText) == 0 { if len(rl.hintText) == 0 {
//rl.hintY = 0 //rl.hintY = 0
return return
@ -43,7 +43,7 @@ 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)
@ -52,12 +52,12 @@ func (rl *Instance) writeHintText() {
} }
} }
func (rl *Instance) resetHintText() { func (rl *Readline) resetHintText() {
//rl.hintY = 0 //rl.hintY = 0
rl.hintText = []rune{} rl.hintText = []rune{}
} }
func (rl *Instance) insertHintText() { func (rl *Readline) insertHintText() {
if len(rl.hintText) != 0 { if len(rl.hintText) != 0 {
// fill in hint text // fill in hint text
rl.insert(rl.hintText) rl.insert(rl.hintText)

View File

@ -29,24 +29,24 @@ type History interface {
} }
// SetHistoryCtrlR - Set the history source triggered with Ctrl-r combination // SetHistoryCtrlR - Set the history source triggered with Ctrl-r combination
func (rl *Instance) SetHistoryCtrlR(name string, history History) { func (rl *Readline) SetHistoryCtrlR(name string, history History) {
rl.mainHistName = name rl.mainHistName = name
rl.mainHistory = history rl.mainHistory = history
} }
// GetHistoryCtrlR - Returns the history source triggered by Ctrl-r // GetHistoryCtrlR - Returns the history source triggered by Ctrl-r
func (rl *Instance) GetHistoryCtrlR() History { func (rl *Readline) GetHistoryCtrlR() History {
return rl.mainHistory return rl.mainHistory
} }
// SetHistoryAltR - Set the history source triggered with Alt-r combination // SetHistoryAltR - Set the history source triggered with Alt-r combination
func (rl *Instance) SetHistoryAltR(name string, history History) { func (rl *Readline) SetHistoryAltR(name string, history History) {
rl.altHistName = name rl.altHistName = name
rl.altHistory = history rl.altHistory = history
} }
// GetHistoryAltR - Returns the history source triggered by Alt-r // GetHistoryAltR - Returns the history source triggered by Alt-r
func (rl *Instance) GetHistoryAltR() History { func (rl *Readline) GetHistoryAltR() History {
return rl.altHistory return rl.altHistory
} }
@ -101,7 +101,7 @@ func (h *NullHistory) Dump() interface{} {
} }
// Browse historic lines: // Browse historic lines:
func (rl *Instance) walkHistory(i int) { func (rl *Readline) walkHistory(i int) {
var ( var (
old, new string old, new string
dedup bool dedup bool
@ -123,7 +123,7 @@ func (rl *Instance) walkHistory(i int) {
// When we are exiting the current line buffer to move around // When we are exiting the current line buffer to move around
// the history, we make buffer the current line // the history, we make buffer the current line
if rl.histOffset == 0 && rl.histOffset + i == 1 { if rl.histOffset == 0 && rl.histOffset+i == 1 {
rl.lineBuf = string(rl.line) rl.lineBuf = string(rl.line)
} }
@ -168,7 +168,7 @@ func (rl *Instance) walkHistory(i int) {
// completeHistory - Populates a CompletionGroup with history and returns it the shell // completeHistory - Populates a CompletionGroup with history and returns it the shell
// we populate only one group, so as to pass it to the main completion engine. // we populate only one group, so as to pass it to the main completion engine.
func (rl *Instance) completeHistory() (hist []*CompletionGroup) { func (rl *Readline) completeHistory() (hist []*CompletionGroup) {
hist = make([]*CompletionGroup, 1) hist = make([]*CompletionGroup, 1)
hist[0] = &CompletionGroup{ hist[0] = &CompletionGroup{

View File

@ -4,12 +4,12 @@ import "regexp"
// SetInfoText - a nasty function to force writing a new info text. It does not update helpers, it just renders // 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. // them, so the info will survive until the helpers (thus including the info) will be updated/recomputed.
func (rl *Instance) SetInfoText(s string) { func (rl *Readline) SetInfoText(s string) {
rl.infoText = []rune(s) rl.infoText = []rune(s)
rl.renderHelpers() rl.renderHelpers()
} }
func (rl *Instance) getInfoText() { func (rl *Readline) getInfoText() {
if !rl.modeAutoFind && !rl.modeTabFind { if !rl.modeAutoFind && !rl.modeTabFind {
// Return if no infos provided by the user/engine // Return if no infos provided by the user/engine
@ -25,7 +25,7 @@ func (rl *Instance) getInfoText() {
} }
// writeInfoText - only writes the info text and computes its offsets. // writeInfoText - only writes the info text and computes its offsets.
func (rl *Instance) writeInfoText() { func (rl *Readline) writeInfoText() {
if len(rl.infoText) == 0 { if len(rl.infoText) == 0 {
rl.infoY = 0 rl.infoY = 0
return return
@ -50,7 +50,7 @@ func (rl *Instance) writeInfoText() {
} }
} }
func (rl *Instance) resetInfoText() { func (rl *Readline) resetInfoText() {
rl.infoY = 0 rl.infoY = 0
rl.infoText = []rune{} rl.infoText = []rune{}
} }

View File

@ -12,7 +12,10 @@ import (
// Instance is used to encapsulate the parameter group and run time of any given // Instance is used to encapsulate the parameter group and run time of any given
// readline instance so that you can reuse the readline API for multiple entry // readline instance so that you can reuse the readline API for multiple entry
// captures without having to repeatedly unload configuration. // captures without having to repeatedly unload configuration.
type Instance struct {
// #type
// blah blah
type Readline struct {
// //
// Input Modes ------------------------------------------------------------------------------- // Input Modes -------------------------------------------------------------------------------
@ -213,8 +216,8 @@ type Instance struct {
} }
// NewInstance is used to create a readline instance and initialise it with sane defaults. // NewInstance is used to create a readline instance and initialise it with sane defaults.
func NewInstance() *Instance { func NewInstance() *Readline {
rl := new(Instance) rl := new(Readline)
// Prompt // Prompt
rl.Multiline = false rl.Multiline = false

View File

@ -6,7 +6,7 @@ import (
// When the DelayedSyntaxWorker gives us a new line, we need to check if there // When the DelayedSyntaxWorker gives us a new line, we need to check if there
// is any processing to be made, that all lines match in terms of content. // is any processing to be made, that all lines match in terms of content.
func (rl *Instance) updateLine(line []rune) { func (rl *Readline) updateLine(line []rune) {
if len(rl.currentComp) > 0 { if len(rl.currentComp) > 0 {
} else { } else {
@ -18,7 +18,7 @@ func (rl *Instance) updateLine(line []rune) {
// getLine - In many places we need the current line input. We either return the real line, // getLine - In many places we need the current line input. We either return the real line,
// or the one that includes the current completion candidate, if there is any. // or the one that includes the current completion candidate, if there is any.
func (rl *Instance) GetLine() []rune { func (rl *Readline) GetLine() []rune {
if len(rl.currentComp) > 0 { if len(rl.currentComp) > 0 {
return rl.lineComp return rl.lineComp
} }
@ -30,7 +30,7 @@ func (rl *Instance) GetLine() []rune {
// function is only ever called once, and after having moved back to prompt position // function is only ever called once, and after having moved back to prompt position
// and having printed the line: this is so that at any moment, everyone has the good // and having printed the line: this is so that at any moment, everyone has the good
// values for moving around, synchronized with the update input line. // values for moving around, synchronized with the update input line.
func (rl *Instance) echo() { func (rl *Readline) echo() {
// Then we print the prompt, and the line, // Then we print the prompt, and the line,
hideCursor() hideCursor()
@ -79,7 +79,7 @@ func (rl *Instance) echo() {
unhideCursor() unhideCursor()
} }
func (rl *Instance) insert(r []rune) { func (rl *Readline) insert(r []rune) {
for { for {
// I don't really understand why `0` is creaping in at the end of the // I don't really understand why `0` is creaping in at the end of the
// array but it only happens with unicode characters. // array but it only happens with unicode characters.
@ -112,11 +112,11 @@ func (rl *Instance) insert(r []rune) {
rl.updateHelpers() rl.updateHelpers()
} }
func (rl *Instance) Insert(t string) { func (rl *Readline) Insert(t string) {
rl.insert([]rune(t)) rl.insert([]rune(t))
} }
func (rl *Instance) deleteX() { func (rl *Readline) deleteX() {
switch { switch {
case len(rl.line) == 0: case len(rl.line) == 0:
return return
@ -134,7 +134,7 @@ func (rl *Instance) deleteX() {
rl.updateHelpers() rl.updateHelpers()
} }
func (rl *Instance) deleteBackspace(forward bool) { func (rl *Readline) deleteBackspace(forward bool) {
switch { switch {
case len(rl.line) == 0: case len(rl.line) == 0:
return return
@ -153,7 +153,7 @@ func (rl *Instance) deleteBackspace(forward bool) {
rl.updateHelpers() rl.updateHelpers()
} }
func (rl *Instance) clearLine() { func (rl *Readline) clearLine() {
if len(rl.line) == 0 { if len(rl.line) == 0 {
return return
} }
@ -179,21 +179,21 @@ func (rl *Instance) clearLine() {
rl.clearVirtualComp() rl.clearVirtualComp()
} }
func (rl *Instance) deleteToBeginning() { func (rl *Readline) deleteToBeginning() {
rl.resetVirtualComp(false) rl.resetVirtualComp(false)
// Keep the line length up until the cursor // Keep the line length up until the cursor
rl.line = rl.line[rl.pos:] rl.line = rl.line[rl.pos:]
rl.pos = 0 rl.pos = 0
} }
func (rl *Instance) deleteToEnd() { func (rl *Readline) deleteToEnd() {
rl.resetVirtualComp(false) rl.resetVirtualComp(false)
// Keep everything before the cursor // Keep everything before the cursor
rl.line = rl.line[:rl.pos] rl.line = rl.line[:rl.pos]
} }
// @TODO(Renzix): move to emacs sepecific file // @TODO(Renzix): move to emacs sepecific file
func (rl *Instance) emacsForwardWord(tokeniser tokeniser) (adjust int) { func (rl *Readline) emacsForwardWord(tokeniser tokeniser) (adjust int) {
split, index, pos := tokeniser(rl.line, rl.pos) split, index, pos := tokeniser(rl.line, rl.pos)
if len(split) == 0 { if len(split) == 0 {
return return
@ -214,7 +214,7 @@ func (rl *Instance) emacsForwardWord(tokeniser tokeniser) (adjust int) {
return return
} }
func (rl *Instance) emacsBackwardWord(tokeniser tokeniser) (adjust int) { func (rl *Readline) emacsBackwardWord(tokeniser tokeniser) (adjust int) {
split, index, pos := tokeniser(rl.line, rl.pos) split, index, pos := tokeniser(rl.line, rl.pos)
if len(split) == 0 { if len(split) == 0 {
return return

View File

@ -17,17 +17,17 @@ import (
var rlMetaKey = rt.StringValue("__readline") var rlMetaKey = rt.StringValue("__readline")
func (rl *Instance) luaLoader(rtm *rt.Runtime) (rt.Value, func()) { func (rl *Readline) luaLoader(rtm *rt.Runtime) (rt.Value, func()) {
rlMethods := rt.NewTable() rlMethods := rt.NewTable()
rlMethodss := map[string]util.LuaExport{ rlMethodss := map[string]util.LuaExport{
"deleteByAmount": {luaDeleteByAmount, 2, false}, "deleteByAmount": {rlDeleteByAmount, 2, false},
"getLine": {luaGetLine, 1, false}, "getLine": {rlGetLine, 1, false},
"getVimRegister": {luaGetRegister, 2, false}, "getVimRegister": {rlGetRegister, 2, false},
"insert": {luaInsert, 2, false}, "insert": {rlInsert, 2, false},
"read": {luaRead, 1, false}, "read": {rlRead, 1, false},
"readChar": {luaReadChar, 1, false}, "readChar": {rlReadChar, 1, false},
"setVimRegister": {luaSetRegister, 3, false}, "setVimRegister": {rlSetRegister, 3, false},
"log": {luaLog, 2, false}, "log": {rlLog, 2, false},
} }
util.SetExports(rtm, rlMethods, rlMethodss) util.SetExports(rtm, rlMethods, rlMethodss)
@ -48,7 +48,7 @@ func (rl *Instance) luaLoader(rtm *rt.Runtime) (rt.Value, func()) {
rtm.SetRegistry(rlMetaKey, rt.TableValue(rlMeta)) rtm.SetRegistry(rlMetaKey, rt.TableValue(rlMeta))
rlFuncs := map[string]util.LuaExport{ rlFuncs := map[string]util.LuaExport{
"new": {luaNew, 0, false}, "new": {rlNew, 0, false},
} }
luaRl := rt.NewTable() luaRl := rt.NewTable()
@ -57,14 +57,20 @@ func (rl *Instance) luaLoader(rtm *rt.Runtime) (rt.Value, func()) {
return rt.TableValue(luaRl), nil return rt.TableValue(luaRl), nil
} }
func luaNew(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // new() -> @Readline
// Creates a new readline instance.
func rlNew(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
rl := NewInstance() rl := NewInstance()
ud := rlUserData(t.Runtime, rl) ud := rlUserData(t.Runtime, rl)
return c.PushingNext1(t.Runtime, rt.UserDataValue(ud)), nil return c.PushingNext1(t.Runtime, rt.UserDataValue(ud)), nil
} }
func luaInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #member
// insert(text)
// Inserts text into the Hilbish command line.
// #param text string
func rlInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil { if err := c.CheckNArgs(2); err != nil {
return nil, err return nil, err
} }
@ -84,7 +90,10 @@ func luaInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
func luaRead(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #member
// read() -> string
// Reads input from the user.
func rlRead(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
} }
@ -105,11 +114,12 @@ func luaRead(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.StringValue(inp)), nil return c.PushingNext1(t.Runtime, rt.StringValue(inp)), nil
} }
// #member
// setVimRegister(register, text) // setVimRegister(register, text)
// Sets the vim register at `register` to hold the passed text. // Sets the vim register at `register` to hold the passed text.
// #param register string // #param register string
// #param text string // #param text string
func luaSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func rlSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(3); err != nil { if err := c.CheckNArgs(3); err != nil {
return nil, err return nil, err
} }
@ -134,10 +144,11 @@ func luaSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
// #member
// getVimRegister(register) -> string // getVimRegister(register) -> string
// Returns the text that is at the register. // Returns the text that is at the register.
// #param register string // #param register string
func luaGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func rlGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil { if err := c.CheckNArgs(2); err != nil {
return nil, err return nil, err
} }
@ -157,10 +168,11 @@ func luaGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
} }
// #member
// getLine() -> string // getLine() -> string
// Returns the current input line. // Returns the current input line.
// #returns string // #returns string
func luaGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func rlGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
} }
@ -175,9 +187,10 @@ func luaGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
} }
// #member
// getChar() -> string // getChar() -> string
// Reads a keystroke from the user. This is in a format of something like Ctrl-L. // Reads a keystroke from the user. This is in a format of something like Ctrl-L.
func luaReadChar(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func rlReadChar(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil { if err := c.Check1Arg(); err != nil {
return nil, err return nil, err
} }
@ -191,10 +204,11 @@ func luaReadChar(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
} }
// #member
// deleteByAmount(amount) // deleteByAmount(amount)
// Deletes characters in the line by the given amount. // Deletes characters in the line by the given amount.
// #param amount number // #param amount number
func luaDeleteByAmount(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func rlDeleteByAmount(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil { if err := c.CheckNArgs(2); err != nil {
return nil, err return nil, err
} }
@ -214,7 +228,10 @@ func luaDeleteByAmount(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
func luaLog(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { // #member
// log(text)
// Prints a message *before* the prompt without it being interrupted by user input.
func rlLog(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil { if err := c.CheckNArgs(2); err != nil {
return nil, err return nil, err
} }
@ -234,7 +251,7 @@ func luaLog(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
func rlArg(c *rt.GoCont, arg int) (*Instance, error) { func rlArg(c *rt.GoCont, arg int) (*Readline, error) {
j, ok := valueToRl(c.Arg(arg)) j, ok := valueToRl(c.Arg(arg))
if !ok { if !ok {
return nil, fmt.Errorf("#%d must be a readline", arg+1) return nil, fmt.Errorf("#%d must be a readline", arg+1)
@ -243,17 +260,17 @@ func rlArg(c *rt.GoCont, arg int) (*Instance, error) {
return j, nil return j, nil
} }
func valueToRl(val rt.Value) (*Instance, bool) { func valueToRl(val rt.Value) (*Readline, bool) {
u, ok := val.TryUserData() u, ok := val.TryUserData()
if !ok { if !ok {
return nil, false return nil, false
} }
j, ok := u.Value().(*Instance) j, ok := u.Value().(*Readline)
return j, ok return j, ok
} }
func rlUserData(rtm *rt.Runtime, rl *Instance) *rt.UserData { func rlUserData(rtm *rt.Runtime, rl *Readline) *rt.UserData {
rlMeta := rtm.Registry(rlMetaKey) rlMeta := rtm.Registry(rlMetaKey)
return rt.NewUserData(rl, rlMeta.AsTable()) return rt.NewUserData(rl, rlMeta.AsTable())
} }

View File

@ -8,20 +8,20 @@ import (
// SetPrompt will define the readline prompt string. // SetPrompt will define the readline prompt string.
// It also calculates the runes in the string as well as any non-printable escape codes. // It also calculates the runes in the string as well as any non-printable escape codes.
func (rl *Instance) SetPrompt(s string) { func (rl *Readline) SetPrompt(s string) {
rl.mainPrompt = s rl.mainPrompt = s
rl.computePrompt() rl.computePrompt()
} }
// SetRightPrompt sets the right prompt. // SetRightPrompt sets the right prompt.
func (rl *Instance) SetRightPrompt(s string) { func (rl *Readline) SetRightPrompt(s string) {
rl.rightPrompt = s + " " rl.rightPrompt = s + " "
rl.computePrompt() rl.computePrompt()
} }
// RefreshPromptLog - A simple function to print a string message (a log, or more broadly, // RefreshPromptLog - A simple function to print a string message (a log, or more broadly,
// an asynchronous event) without bothering the user, and by "pushing" the prompt below the message. // an asynchronous event) without bothering the user, and by "pushing" the prompt below the message.
func (rl *Instance) RefreshPromptLog(log string) (err error) { func (rl *Readline) 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 {
@ -73,7 +73,7 @@ func (rl *Instance) RefreshPromptLog(log string) (err error) {
} }
// RefreshPromptInPlace - Refreshes the prompt in the very same place he is. // RefreshPromptInPlace - Refreshes the prompt in the very same place he is.
func (rl *Instance) RefreshPromptInPlace(prompt string) (err error) { func (rl *Readline) RefreshPromptInPlace(prompt 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.
// Prompt data intependent // Prompt data intependent
if !rl.modeTabCompletion { if !rl.modeTabCompletion {
@ -117,7 +117,7 @@ func (rl *Instance) RefreshPromptInPlace(prompt string) (err error) {
// @prompt => If not nil (""), will use this prompt instead of the currently set prompt. // @prompt => If not nil (""), will use this prompt instead of the currently set prompt.
// @offset => Used to set the number of lines to go upward, before reprinting. Set to 0 if not used. // @offset => Used to set the number of lines to go upward, before reprinting. Set to 0 if not used.
// @clearLine => If true, will clean the current input line on the next refresh. // @clearLine => If true, will clean the current input line on the next refresh.
func (rl *Instance) RefreshPromptCustom(prompt string, offset int, clearLine bool) (err error) { func (rl *Readline) RefreshPromptCustom(prompt string, offset int, clearLine bool) (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 {
@ -166,7 +166,7 @@ func (rl *Instance) RefreshPromptCustom(prompt string, offset int, clearLine boo
// computePrompt - At any moment, returns an (1st or 2nd line) actualized prompt, // computePrompt - At any moment, returns an (1st or 2nd line) actualized prompt,
// considering all input mode parameters and prompt string values. // considering all input mode parameters and prompt string values.
func (rl *Instance) computePrompt() (prompt []rune) { func (rl *Readline) computePrompt() (prompt []rune) {
if rl.Multiline { if rl.Multiline {
if rl.MultilinePrompt != "" { if rl.MultilinePrompt != "" {
rl.realPrompt = []rune(rl.MultilinePrompt) rl.realPrompt = []rune(rl.MultilinePrompt)
@ -194,7 +194,7 @@ func (rl *Instance) computePrompt() (prompt []rune) {
return return
} }
func (rl *Instance) colorizeVimPrompt(p []rune) (cp []rune) { func (rl *Readline) colorizeVimPrompt(p []rune) (cp []rune) {
if rl.VimModeColorize { if rl.VimModeColorize {
return []rune(fmt.Sprintf("%s%s%s", BOLD, string(p), RESET)) return []rune(fmt.Sprintf("%s%s%s", BOLD, string(p), RESET))
} }
@ -211,8 +211,8 @@ func getRealLength(s string) (l int) {
return getWidth([]rune(stripped)) return getWidth([]rune(stripped))
} }
func (rl *Instance) echoRightPrompt() { func (rl *Readline) echoRightPrompt() {
if rl.fullX < GetTermWidth() - rl.rightPromptLen - 1 { if rl.fullX < GetTermWidth()-rl.rightPromptLen-1 {
moveCursorForwards(GetTermWidth()) moveCursorForwards(GetTermWidth())
moveCursorBackwards(rl.rightPromptLen) moveCursorBackwards(rl.rightPromptLen)
print(rl.rightPrompt) print(rl.rightPrompt)

View File

@ -2,16 +2,6 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package terminal provides support functions for dealing with terminals, as
// commonly found on UNIX systems.
//
// Putting a terminal into raw mode is the most common requirement:
//
// oldState, err := terminal.MakeRaw(0)
// if err != nil {
// panic(err)
// }
// defer terminal.Restore(0, oldState)
package readline package readline
import ( import (

View File

@ -2,18 +2,9 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build windows
// +build windows // +build windows
// Package terminal provides support functions for dealing with terminals, as
// commonly found on UNIX systems.
//
// Putting a terminal into raw mode is the most common requirement:
//
// oldState, err := terminal.MakeRaw(0)
// if err != nil {
// panic(err)
// }
// defer terminal.Restore(0, oldState)
package readline package readline
import ( import (
@ -70,4 +61,3 @@ func GetSize(fd int) (width, height int, err error) {
} }
return int(info.Size.X), int(info.Size.Y), nil return int(info.Size.X), int(info.Size.Y), nil
} }

View File

@ -13,7 +13,7 @@ var rxMultiline = regexp.MustCompile(`[\r\n]+`)
// Readline displays the readline prompt. // Readline displays the readline prompt.
// It will return a string (user entered data) or an error. // It will return a string (user entered data) or an error.
func (rl *Instance) Readline() (string, error) { func (rl *Readline) Readline() (string, error) {
fd := int(os.Stdin.Fd()) fd := int(os.Stdin.Fd())
state, err := MakeRaw(fd) state, err := MakeRaw(fd)
if err != nil { if err != nil {
@ -183,7 +183,7 @@ func (rl *Instance) Readline() (string, error) {
if rl.modeTabFind { if rl.modeTabFind {
rl.backspaceTabFind() rl.backspaceTabFind()
} else { } else {
if (rl.pos < len(rl.line)) { if rl.pos < len(rl.line) {
rl.deleteBackspace(true) rl.deleteBackspace(true)
} }
} }
@ -545,7 +545,7 @@ func (rl *Instance) Readline() (string, error) {
// editorInput is an unexported function used to determine what mode of text // editorInput is an unexported function used to determine what mode of text
// entry readline is currently configured for and then update the line entries // entry readline is currently configured for and then update the line entries
// accordingly. // accordingly.
func (rl *Instance) editorInput(r []rune) { func (rl *Readline) editorInput(r []rune) {
if len(r) == 0 { if len(r) == 0 {
return return
} }
@ -595,7 +595,7 @@ func (rl *Instance) editorInput(r []rune) {
// viEscape - In case th user is using Vim input, and the escape sequence has not // viEscape - In case th user is using Vim input, and the escape sequence has not
// been handled by other cases, we dispatch it to Vim and handle a few cases here. // been handled by other cases, we dispatch it to Vim and handle a few cases here.
func (rl *Instance) viEscape(r []rune) { func (rl *Readline) viEscape(r []rune) {
// Sometimes the escape sequence is interleaved with another one, // Sometimes the escape sequence is interleaved with another one,
// but key strokes might be in the wrong order, so we double check // but key strokes might be in the wrong order, so we double check
@ -611,7 +611,7 @@ func (rl *Instance) viEscape(r []rune) {
} }
} }
func (rl *Instance) escapeSeq(r []rune) { func (rl *Readline) escapeSeq(r []rune) {
switch string(r) { switch string(r) {
// Vim escape sequences & dispatching -------------------------------------------------------- // Vim escape sequences & dispatching --------------------------------------------------------
case string(charEscape): case string(charEscape):
@ -755,11 +755,11 @@ func (rl *Instance) escapeSeq(r []rune) {
rl.updateHelpers() rl.updateHelpers()
return return
case seqDelete,seqDelete2: case seqDelete, seqDelete2:
if rl.modeTabFind { if rl.modeTabFind {
rl.backspaceTabFind() rl.backspaceTabFind()
} else { } else {
if (rl.pos < len(rl.line)) { if rl.pos < len(rl.line) {
rl.deleteBackspace(true) rl.deleteBackspace(true)
} }
} }
@ -890,7 +890,7 @@ func (rl *Instance) escapeSeq(r []rune) {
} }
} }
func (rl *Instance) carridgeReturn() { func (rl *Readline) carridgeReturn() {
rl.moveCursorByAdjust(len(rl.line)) rl.moveCursorByAdjust(len(rl.line))
rl.updateHelpers() rl.updateHelpers()
rl.clearHelpers() rl.clearHelpers()
@ -924,7 +924,7 @@ func isMultiline(r []rune) bool {
return false return false
} }
func (rl *Instance) allowMultiline(data []byte) bool { func (rl *Readline) allowMultiline(data []byte) bool {
rl.clearHelpers() rl.clearHelpers()
printf("\r\nWARNING: %d bytes of multiline data was dumped into the shell!", len(data)) printf("\r\nWARNING: %d bytes of multiline data was dumped into the shell!", len(data))
for { for {

View File

@ -23,7 +23,7 @@ type registers struct {
mutex *sync.Mutex mutex *sync.Mutex
} }
func (rl *Instance) initRegisters() { func (rl *Readline) initRegisters() {
rl.registers = &registers{ rl.registers = &registers{
num: make(map[int][]rune, 10), num: make(map[int][]rune, 10),
alpha: make(map[string][]rune, 52), alpha: make(map[string][]rune, 52),
@ -36,7 +36,7 @@ func (rl *Instance) initRegisters() {
// the number of Vim iterations and we save the resulting string to the appropriate buffer. // the number of Vim iterations and we save the resulting string to the appropriate buffer.
// It's the same as saveToRegisterTokenize, but without the need to generate tokenized & // It's the same as saveToRegisterTokenize, but without the need to generate tokenized &
// cursor-pos-actualized versions of the input line. // cursor-pos-actualized versions of the input line.
func (rl *Instance) saveToRegister(adjust int) { func (rl *Readline) saveToRegister(adjust int) {
// Get the current cursor position and go the length specified. // Get the current cursor position and go the length specified.
var begin = rl.pos var begin = rl.pos
@ -66,7 +66,7 @@ func (rl *Instance) saveToRegister(adjust int) {
// saveToRegisterTokenize - Passing a function that will move around the line in the desired way, we get // saveToRegisterTokenize - Passing a function that will move around the line in the desired way, we get
// the number of Vim iterations and we save the resulting string to the appropriate buffer. Because we // the number of Vim iterations and we save the resulting string to the appropriate buffer. Because we
// need the cursor position to be really moved around between calls to the jumper, we also need the tokeniser. // need the cursor position to be really moved around between calls to the jumper, we also need the tokeniser.
func (rl *Instance) saveToRegisterTokenize(tokeniser tokeniser, jumper func(tokeniser) int, vii int) { func (rl *Readline) saveToRegisterTokenize(tokeniser tokeniser, jumper func(tokeniser) int, vii int) {
// The register is going to have to heavily manipulate the cursor position. // The register is going to have to heavily manipulate the cursor position.
// Remember the original one first, for the end. // Remember the original one first, for the end.
@ -104,11 +104,11 @@ func (rl *Instance) saveToRegisterTokenize(tokeniser tokeniser, jumper func(toke
// saveBufToRegister - Instead of computing the buffer ourselves based on an adjust, // saveBufToRegister - Instead of computing the buffer ourselves based on an adjust,
// let the caller pass directly this buffer, yet relying on the register system to // let the caller pass directly this buffer, yet relying on the register system to
// determine which register will store the buffer. // determine which register will store the buffer.
func (rl *Instance) saveBufToRegister(buffer []rune) { func (rl *Readline) saveBufToRegister(buffer []rune) {
rl.SetRegisterBuf(string(rl.registers.currentRegister), buffer) rl.SetRegisterBuf(string(rl.registers.currentRegister), buffer)
} }
func (rl *Instance) SetRegisterBuf(reg string, buffer []rune) { func (rl *Readline) SetRegisterBuf(reg string, buffer []rune) {
// We must make an immutable version of the buffer first. // We must make an immutable version of the buffer first.
buf := string(buffer) buf := string(buffer)
@ -141,7 +141,7 @@ func (rl *Instance) SetRegisterBuf(reg string, buffer []rune) {
// The user asked to paste a buffer onto the line, so we check from which register // The user asked to paste a buffer onto the line, so we check from which register
// we are supposed to select the buffer, and return it to the caller for insertion. // we are supposed to select the buffer, and return it to the caller for insertion.
func (rl *Instance) pasteFromRegister() (buffer []rune) { func (rl *Readline) pasteFromRegister() (buffer []rune) {
// When exiting this function the currently selected register is dropped, // When exiting this function the currently selected register is dropped,
defer rl.registers.resetRegister() defer rl.registers.resetRegister()
@ -155,7 +155,7 @@ func (rl *Instance) pasteFromRegister() (buffer []rune) {
return rl.GetFromRegister(activeRegister) return rl.GetFromRegister(activeRegister)
} }
func (rl *Instance) GetFromRegister(reg string) []rune { func (rl *Readline) GetFromRegister(reg string) []rune {
// Find the active register, and return its content. // Find the active register, and return its content.
num, err := strconv.Atoi(reg) num, err := strconv.Atoi(reg)
@ -264,7 +264,7 @@ 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 *Readline) completeRegisters() (groups []*CompletionGroup) {
// We set the info exceptionally // We set the info exceptionally
info := BLUE + "-- registers --" + RESET info := BLUE + "-- registers --" + RESET

View File

@ -2,7 +2,7 @@ package readline
// syntaxCompletion - applies syntax highlighting to the current input line. // syntaxCompletion - applies syntax highlighting to the current input line.
// nothing special to note here, nor any changes envisioned. // nothing special to note here, nor any changes envisioned.
func (rl *Instance) syntaxCompletion() { func (rl *Readline) syntaxCompletion() {
if rl.SyntaxCompleter == nil { if rl.SyntaxCompleter == nil {
return return
} }

View File

@ -2,13 +2,14 @@ package readline
import ( import (
"strings" "strings"
"github.com/rivo/uniseg" "github.com/rivo/uniseg"
) )
// insertCandidateVirtual - When a completion candidate is selected, we insert it virtually in the input line: // insertCandidateVirtual - When a completion candidate is selected, we insert it virtually in the input line:
// this will not trigger further firltering against the other candidates. Each time this function // this will not trigger further firltering against the other candidates. Each time this function
// is called, any previous candidate is dropped, after being used for moving the cursor around. // is called, any previous candidate is dropped, after being used for moving the cursor around.
func (rl *Instance) insertCandidateVirtual(candidate []rune) { func (rl *Readline) insertCandidateVirtual(candidate []rune) {
for { for {
// I don't really understand why `0` is creaping in at the end of the // I don't really understand why `0` is creaping in at the end of the
// array but it only happens with unicode characters. // array but it only happens with unicode characters.
@ -57,7 +58,7 @@ func (rl *Instance) insertCandidateVirtual(candidate []rune) {
// Insert the current completion candidate into the input line. // Insert the current completion candidate into the input line.
// This candidate might either be the currently selected one (white frame), // This candidate might either be the currently selected one (white frame),
// or the only candidate available, if the total number of candidates is 1. // or the only candidate available, if the total number of candidates is 1.
func (rl *Instance) insertCandidate() { func (rl *Readline) insertCandidate() {
cur := rl.getCurrentGroup() cur := rl.getCurrentGroup()
@ -83,7 +84,7 @@ func (rl *Instance) insertCandidate() {
} }
// updateVirtualComp - Either insert the current completion candidate virtually, or on the real line. // updateVirtualComp - Either insert the current completion candidate virtually, or on the real line.
func (rl *Instance) updateVirtualComp() { func (rl *Readline) updateVirtualComp() {
cur := rl.getCurrentGroup() cur := rl.getCurrentGroup()
if cur != nil { if cur != nil {
@ -118,7 +119,7 @@ func (rl *Instance) updateVirtualComp() {
// resetVirtualComp - This function is called before most of our readline key handlers, // resetVirtualComp - This function is called before most of our readline key handlers,
// and makes sure that the current completion (virtually inserted) is either inserted or dropped, // and makes sure that the current completion (virtually inserted) is either inserted or dropped,
// and that all related parameters are reinitialized. // and that all related parameters are reinitialized.
func (rl *Instance) resetVirtualComp(drop bool) { func (rl *Readline) resetVirtualComp(drop bool) {
// If we don't have a current virtual completion, there's nothing to do. // If we don't have a current virtual completion, there's nothing to do.
// IMPORTANT: this MUST be first, to avoid nil problems with empty comps. // IMPORTANT: this MUST be first, to avoid nil problems with empty comps.
@ -196,7 +197,7 @@ func trimTrailing(comp string) (trimmed string, hadSlash bool) {
} }
// viDeleteByAdjustVirtual - Same as viDeleteByAdjust, but for our virtually completed input line. // viDeleteByAdjustVirtual - Same as viDeleteByAdjust, but for our virtually completed input line.
func (rl *Instance) viDeleteByAdjustVirtual(adjust int) { func (rl *Readline) viDeleteByAdjustVirtual(adjust int) {
var ( var (
newLine []rune newLine []rune
backOne bool backOne bool
@ -235,7 +236,7 @@ func (rl *Instance) viDeleteByAdjustVirtual(adjust int) {
} }
// viJumpEVirtual - Same as viJumpE, but for our virtually completed input line. // viJumpEVirtual - Same as viJumpE, but for our virtually completed input line.
func (rl *Instance) viJumpEVirtual(tokeniser func([]rune, int) ([]string, int, int)) (adjust int) { func (rl *Readline) viJumpEVirtual(tokeniser func([]rune, int) ([]string, int, int)) (adjust int) {
split, index, pos := tokeniser(rl.lineComp, rl.pos) split, index, pos := tokeniser(rl.lineComp, rl.pos)
if len(split) == 0 { if len(split) == 0 {
return return
@ -258,7 +259,7 @@ func (rl *Instance) viJumpEVirtual(tokeniser func([]rune, int) ([]string, int, i
return return
} }
func (rl *Instance) deleteVirtual() { func (rl *Readline) deleteVirtual() {
switch { switch {
case len(rl.lineComp) == 0: case len(rl.lineComp) == 0:
return return
@ -274,7 +275,7 @@ func (rl *Instance) deleteVirtual() {
// We are done with the current virtual completion candidate. // We are done with the current virtual completion candidate.
// Get ready for the next one // Get ready for the next one
func (rl *Instance) clearVirtualComp() { func (rl *Readline) clearVirtualComp() {
rl.line = rl.lineComp rl.line = rl.lineComp
rl.currentComp = []rune{} rl.currentComp = []rune{}
rl.compAddSpace = false rl.compAddSpace = false

View File

@ -28,7 +28,7 @@ const (
// getTabCompletion - This root function sets up all completion items and engines, // getTabCompletion - This root function sets up all completion items and engines,
// dealing with all search and completion modes. But it does not perform printing. // dealing with all search and completion modes. But it does not perform printing.
func (rl *Instance) getTabCompletion() { func (rl *Readline) getTabCompletion() {
// Populate registers if requested. // Populate registers if requested.
if rl.modeAutoFind && rl.searchMode == RegisterFind { if rl.modeAutoFind && rl.searchMode == RegisterFind {
@ -53,7 +53,7 @@ func (rl *Instance) getTabCompletion() {
} }
// getRegisterCompletion - Populates and sets up completion for Vim registers. // getRegisterCompletion - Populates and sets up completion for Vim registers.
func (rl *Instance) getRegisterCompletion() { func (rl *Readline) getRegisterCompletion() {
rl.tcGroups = rl.completeRegisters() rl.tcGroups = rl.completeRegisters()
if len(rl.tcGroups) == 0 { if len(rl.tcGroups) == 0 {
@ -84,7 +84,7 @@ func (rl *Instance) getRegisterCompletion() {
} }
// getTabSearchCompletion - Populates and sets up completion for completion search. // getTabSearchCompletion - Populates and sets up completion for completion search.
func (rl *Instance) getTabSearchCompletion() { func (rl *Readline) getTabSearchCompletion() {
// Get completions from the engine, and make sure there is a current group. // Get completions from the engine, and make sure there is a current group.
rl.getCompletions() rl.getCompletions()
@ -94,7 +94,7 @@ func (rl *Instance) getTabSearchCompletion() {
rl.getCurrentGroup() rl.getCurrentGroup()
// Set the info for this completion mode // Set the info for this completion mode
rl.infoText = append([]rune("Completion search: " + UNDERLINE + BOLD), rl.tfLine...) rl.infoText = append([]rune("Completion search: "+UNDERLINE+BOLD), rl.tfLine...)
for _, g := range rl.tcGroups { for _, g := range rl.tcGroups {
g.updateTabFind(rl) g.updateTabFind(rl)
@ -107,7 +107,7 @@ func (rl *Instance) getTabSearchCompletion() {
} }
// getHistorySearchCompletion - Populates and sets up completion for command history search // getHistorySearchCompletion - Populates and sets up completion for command history search
func (rl *Instance) getHistorySearchCompletion() { func (rl *Readline) getHistorySearchCompletion() {
// Refresh full list each time // Refresh full list each time
rl.tcGroups = rl.completeHistory() rl.tcGroups = rl.completeHistory()
@ -142,7 +142,7 @@ func (rl *Instance) getHistorySearchCompletion() {
// getNormalCompletion - Populates and sets up completion for normal comp mode. // getNormalCompletion - Populates and sets up completion for normal comp mode.
// Will automatically cancel the completion mode if there are no candidates. // Will automatically cancel the completion mode if there are no candidates.
func (rl *Instance) getNormalCompletion() { func (rl *Readline) getNormalCompletion() {
// Get completions groups, pass delayedTabContext and check nils // Get completions groups, pass delayedTabContext and check nils
rl.getCompletions() rl.getCompletions()
@ -172,7 +172,7 @@ func (rl *Instance) getNormalCompletion() {
// getCompletions - Calls the completion engine/function to yield a list of 0 or more completion groups, // getCompletions - Calls the completion engine/function to yield a list of 0 or more completion groups,
// sets up a delayed tab context and passes it on to the tab completion engine function, and ensure no // sets up a delayed tab context and passes it on to the tab completion engine function, and ensure no
// nil groups/items will pass through. This function is called by different comp search/nav modes. // nil groups/items will pass through. This function is called by different comp search/nav modes.
func (rl *Instance) getCompletions() { func (rl *Readline) getCompletions() {
// If there is no wired tab completion engine, nothing we can do. // If there is no wired tab completion engine, nothing we can do.
if rl.TabCompleter == nil { if rl.TabCompleter == nil {
@ -214,7 +214,7 @@ func (rl *Instance) getCompletions() {
// moveTabCompletionHighlight - This function is in charge of // moveTabCompletionHighlight - This function is in charge of
// computing the new position in the current completions liste. // computing the new position in the current completions liste.
func (rl *Instance) moveTabCompletionHighlight(x, y int) { func (rl *Readline) moveTabCompletionHighlight(x, y int) {
rl.completionOpen = true rl.completionOpen = true
g := rl.getCurrentGroup() g := rl.getCurrentGroup()
@ -253,7 +253,7 @@ func (rl *Instance) moveTabCompletionHighlight(x, y int) {
} }
// writeTabCompletion - Prints all completion groups and their items // writeTabCompletion - Prints all completion groups and their items
func (rl *Instance) writeTabCompletion() { func (rl *Readline) writeTabCompletion() {
// The final completions string to print. // The final completions string to print.
var completions string var completions string
@ -289,7 +289,7 @@ func (rl *Instance) writeTabCompletion() {
// cropCompletions - When the user cycles through a completion list longer // cropCompletions - When the user cycles through a completion list longer
// than the console MaxTabCompleterRows value, we crop the completions string // than the console MaxTabCompleterRows value, we crop the completions string
// so that "global" cycling (across all groups) is printed correctly. // so that "global" cycling (across all groups) is printed correctly.
func (rl *Instance) cropCompletions(comps string) (cropped string, usedY int) { func (rl *Readline) cropCompletions(comps string) (cropped string, usedY int) {
// If we actually fit into the MaxTabCompleterRows, return the comps // If we actually fit into the MaxTabCompleterRows, return the comps
if rl.tcUsedY < rl.MaxTabCompleterRows { if rl.tcUsedY < rl.MaxTabCompleterRows {
@ -366,7 +366,7 @@ func (rl *Instance) cropCompletions(comps string) (cropped string, usedY int) {
return return
} }
func (rl *Instance) getAbsPos() int { func (rl *Readline) getAbsPos() int {
var prev int var prev int
var foundCurrent bool var foundCurrent bool
for _, grp := range rl.tcGroups { for _, grp := range rl.tcGroups {
@ -390,7 +390,7 @@ func (rl *Instance) getAbsPos() int {
// We pass a special subset of the current input line, so that // We pass a special subset of the current input line, so that
// completions are available no matter where the cursor is. // completions are available no matter where the cursor is.
func (rl *Instance) getCompletionLine() (line []rune, pos int) { func (rl *Readline) getCompletionLine() (line []rune, pos int) {
pos = rl.pos - len(rl.currentComp) pos = rl.pos - len(rl.currentComp)
if pos < 0 { if pos < 0 {
@ -409,7 +409,7 @@ func (rl *Instance) getCompletionLine() (line []rune, pos int) {
return return
} }
func (rl *Instance) getCurrentGroup() (group *CompletionGroup) { func (rl *Readline) getCurrentGroup() (group *CompletionGroup) {
for _, g := range rl.tcGroups { for _, g := range rl.tcGroups {
if g.isCurrent && len(g.Suggestions) > 0 { if g.isCurrent && len(g.Suggestions) > 0 {
return g return g
@ -431,7 +431,7 @@ func (rl *Instance) getCurrentGroup() (group *CompletionGroup) {
// cycleNextGroup - Finds either the first non-empty group, // cycleNextGroup - Finds either the first non-empty group,
// or the next non-empty group after the current one. // or the next non-empty group after the current one.
func (rl *Instance) cycleNextGroup() { func (rl *Readline) cycleNextGroup() {
for i, g := range rl.tcGroups { for i, g := range rl.tcGroups {
if g.isCurrent { if g.isCurrent {
g.isCurrent = false g.isCurrent = false
@ -452,7 +452,7 @@ func (rl *Instance) cycleNextGroup() {
} }
// cyclePreviousGroup - Same as cycleNextGroup but reverse // cyclePreviousGroup - Same as cycleNextGroup but reverse
func (rl *Instance) cyclePreviousGroup() { func (rl *Readline) cyclePreviousGroup() {
for i, g := range rl.tcGroups { for i, g := range rl.tcGroups {
if g.isCurrent { if g.isCurrent {
g.isCurrent = false g.isCurrent = false
@ -471,7 +471,7 @@ func (rl *Instance) cyclePreviousGroup() {
} }
// Check if we have a single completion candidate // Check if we have a single completion candidate
func (rl *Instance) hasOneCandidate() bool { func (rl *Readline) hasOneCandidate() bool {
if len(rl.tcGroups) == 0 { if len(rl.tcGroups) == 0 {
return false return false
} }
@ -509,7 +509,7 @@ func (rl *Instance) hasOneCandidate() bool {
// - The user-specified max completion length // - The user-specified max completion length
// - 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 *Readline) promptCompletionConfirm(sentence string) {
rl.infoText = []rune(sentence) rl.infoText = []rune(sentence)
rl.compConfirmWait = true rl.compConfirmWait = true
@ -518,7 +518,7 @@ func (rl *Instance) promptCompletionConfirm(sentence string) {
rl.renderHelpers() rl.renderHelpers()
} }
func (rl *Instance) getCompletionCount() (comps int, lines int, adjusted int) { func (rl *Readline) getCompletionCount() (comps int, lines int, adjusted int) {
for _, group := range rl.tcGroups { for _, group := range rl.tcGroups {
comps += len(group.Suggestions) comps += len(group.Suggestions)
// if group.Name != "" { // if group.Name != "" {
@ -535,7 +535,7 @@ func (rl *Instance) getCompletionCount() (comps int, lines int, adjusted int) {
return return
} }
func (rl *Instance) resetTabCompletion() { func (rl *Readline) resetTabCompletion() {
rl.modeTabCompletion = false rl.modeTabCompletion = false
rl.tabCompletionSelect = false rl.tabCompletionSelect = false
rl.compConfirmWait = false rl.compConfirmWait = false

View File

@ -12,7 +12,7 @@ const (
RegisterFind RegisterFind
) )
func (rl *Instance) backspaceTabFind() { func (rl *Readline) backspaceTabFind() {
if len(rl.tfLine) > 0 { if len(rl.tfLine) > 0 {
rl.tfLine = rl.tfLine[:len(rl.tfLine)-1] rl.tfLine = rl.tfLine[:len(rl.tfLine)-1]
} }
@ -21,7 +21,7 @@ func (rl *Instance) backspaceTabFind() {
// Filter and refresh (print) a list of completions. The caller should have reset // Filter and refresh (print) a list of completions. The caller should have reset
// the virtual completion system before, so that should not clash with this. // the virtual completion system before, so that should not clash with this.
func (rl *Instance) updateTabFind(r []rune) { func (rl *Readline) updateTabFind(r []rune) {
rl.tfLine = append(rl.tfLine, r...) rl.tfLine = append(rl.tfLine, r...)
@ -34,7 +34,7 @@ func (rl *Instance) updateTabFind(r []rune) {
rl.renderHelpers() rl.renderHelpers()
} }
func (rl *Instance) resetTabFind() { func (rl *Readline) resetTabFind() {
rl.modeTabFind = false rl.modeTabFind = false
// rl.modeAutoFind = false // Added, because otherwise it gets stuck on search completions // rl.modeAutoFind = false // Added, because otherwise it gets stuck on search completions

View File

@ -7,12 +7,12 @@ import (
// DelayedTabContext is a custom context interface for async updates to the tab completions // DelayedTabContext is a custom context interface for async updates to the tab completions
type DelayedTabContext struct { type DelayedTabContext struct {
rl *Instance rl *Readline
Context context.Context Context context.Context
cancel context.CancelFunc cancel context.CancelFunc
} }
func delayedSyntaxTimer(rl *Instance, i int64) { func delayedSyntaxTimer(rl *Readline, i int64) {
if rl.PasswordMask != 0 || rl.DelayedSyntaxWorker == nil { if rl.PasswordMask != 0 || rl.DelayedSyntaxWorker == nil {
return return
} }

View File

@ -5,7 +5,7 @@ type undoItem struct {
pos int pos int
} }
func (rl *Instance) undoAppendHistory() { func (rl *Readline) undoAppendHistory() {
defer func() { rl.viUndoSkipAppend = false }() defer func() { rl.viUndoSkipAppend = false }()
if rl.viUndoSkipAppend { if rl.viUndoSkipAppend {
@ -18,7 +18,7 @@ func (rl *Instance) undoAppendHistory() {
}) })
} }
func (rl *Instance) undoLast() { func (rl *Readline) undoLast() {
var undo undoItem var undo undoItem
for { for {
if len(rl.viUndoHistory) == 0 { if len(rl.viUndoHistory) == 0 {

View File

@ -10,7 +10,7 @@ import (
// 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 Infos 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 *Readline) updateHelpers() {
print(seqHideCursor) print(seqHideCursor)
// Load all Infos & completions before anything. // Load all Infos & completions before anything.
// Thus overwrites anything having been dirtily added/forced/modified, like rl.SetInfoText() // Thus overwrites anything having been dirtily added/forced/modified, like rl.SetInfoText()
@ -19,7 +19,9 @@ func (rl *Instance) updateHelpers() {
if rl.modeTabCompletion && !rl.completionOpen { if rl.modeTabCompletion && !rl.completionOpen {
rl.getTabCompletion() rl.getTabCompletion()
} else { } else {
if rl.completionOpen { rl.completionOpen = false } if rl.completionOpen {
rl.completionOpen = false
}
} }
// We clear everything // We clear everything
@ -49,7 +51,7 @@ func getWidth(x []rune) int {
} }
// Update reference should be called only once in a "loop" (not Readline(), but key control loop) // Update reference should be called only once in a "loop" (not Readline(), but key control loop)
func (rl *Instance) updateReferences() { func (rl *Readline) updateReferences() {
// We always need to work with clean data, // We always need to work with clean data,
// since we will have incrementers all around // since we will have incrementers all around
@ -103,7 +105,7 @@ func (rl *Instance) updateReferences() {
} }
} }
func (rl *Instance) resetHelpers() { func (rl *Readline) resetHelpers() {
rl.modeAutoFind = false rl.modeAutoFind = false
// Now reset all below-input helpers // Now reset all below-input helpers
@ -113,7 +115,7 @@ func (rl *Instance) resetHelpers() {
// clearHelpers - Clears everything: prompt, input, Infos & 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 *Readline) clearHelpers() {
// Now go down to the last line of input // Now go down to the last line of input
moveCursorDown(rl.fullY - rl.posY) moveCursorDown(rl.fullY - rl.posY)
@ -132,7 +134,7 @@ func (rl *Instance) clearHelpers() {
// renderHelpers - pritns all components (prompt, line, Infos & 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 *Readline) renderHelpers() {
// when the instance is in this state we want it to be "below" the user's // when the instance is in this state we want it to be "below" the user's
// input for it to be aligned properly // input for it to be aligned properly
@ -197,14 +199,14 @@ func (rl *Instance) renderHelpers() {
moveCursorForwards(rl.posX) moveCursorForwards(rl.posX)
} }
func (rl *Instance) bufprintF(format string, a ...any) { func (rl *Readline) bufprintF(format string, a ...any) {
fmt.Fprintf(rl.bufferedOut, format, a...) fmt.Fprintf(rl.bufferedOut, format, a...)
} }
func (rl *Instance) bufprint(text string) { func (rl *Readline) bufprint(text string) {
fmt.Fprint(rl.bufferedOut, text) fmt.Fprint(rl.bufferedOut, text)
} }
func (rl *Instance) bufflush() { func (rl *Readline) bufflush() {
rl.bufferedOut.Flush() rl.bufferedOut.Flush()
} }

View File

@ -34,6 +34,7 @@ var (
) )
type ViAction int type ViAction int
const ( const (
VimActionYank = iota VimActionYank = iota
VimActionPaste VimActionPaste
@ -52,7 +53,7 @@ var (
// vi - Apply a key to a Vi action. Note that as in the rest of the code, all cursor movements // vi - Apply a key to a Vi action. Note that as in the rest of the code, all cursor movements
// have been moved away, and only the rl.pos is adjusted: when echoing the input line, the shell // have been moved away, and only the rl.pos is adjusted: when echoing the input line, the shell
// will compute the new cursor pos accordingly. // will compute the new cursor pos accordingly.
func (rl *Instance) vi(r rune) { func (rl *Readline) vi(r rune) {
activeRegister := string(rl.registers.currentRegister) activeRegister := string(rl.registers.currentRegister)
// Check if we are in register mode. If yes, and for some characters, // Check if we are in register mode. If yes, and for some characters,
@ -384,7 +385,7 @@ func (rl *Instance) vi(r rune) {
} }
} }
func (rl *Instance) getViIterations() int { func (rl *Readline) getViIterations() int {
i, _ := strconv.Atoi(rl.viIteration) i, _ := strconv.Atoi(rl.viIteration)
if i < 1 { if i < 1 {
i = 1 i = 1
@ -393,7 +394,7 @@ func (rl *Instance) getViIterations() int {
return i return i
} }
func (rl *Instance) refreshVimStatus() { func (rl *Readline) refreshVimStatus() {
rl.ViModeCallback(rl.modeViMode) rl.ViModeCallback(rl.modeViMode)
rl.computePrompt() rl.computePrompt()
rl.updateHelpers() rl.updateHelpers()
@ -401,7 +402,7 @@ func (rl *Instance) refreshVimStatus() {
// viInfoMessage - lmorg's way of showing Vim status is to overwrite the info. // 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) viInfoMessage() { func (rl *Readline) viInfoMessage() {
switch rl.modeViMode { switch rl.modeViMode {
case VimKeys: case VimKeys:
rl.infoText = []rune("-- VIM KEYS -- (press `i` to return to normal editing mode)") rl.infoText = []rune("-- VIM KEYS -- (press `i` to return to normal editing mode)")
@ -421,7 +422,7 @@ func (rl *Instance) viInfoMessage() {
rl.renderHelpers() rl.renderHelpers()
} }
func (rl *Instance) viJumpB(tokeniser tokeniser) (adjust int) { func (rl *Readline) viJumpB(tokeniser tokeniser) (adjust int) {
split, index, pos := tokeniser(rl.line, rl.pos) split, index, pos := tokeniser(rl.line, rl.pos)
switch { switch {
case len(split) == 0: case len(split) == 0:
@ -436,7 +437,7 @@ func (rl *Instance) viJumpB(tokeniser tokeniser) (adjust int) {
return adjust * -1 return adjust * -1
} }
func (rl *Instance) viJumpE(tokeniser tokeniser) (adjust int) { func (rl *Readline) viJumpE(tokeniser tokeniser) (adjust int) {
split, index, pos := tokeniser(rl.line, rl.pos) split, index, pos := tokeniser(rl.line, rl.pos)
if len(split) == 0 { if len(split) == 0 {
return return
@ -459,7 +460,7 @@ func (rl *Instance) viJumpE(tokeniser tokeniser) (adjust int) {
return return
} }
func (rl *Instance) viJumpW(tokeniser tokeniser) (adjust int) { func (rl *Readline) viJumpW(tokeniser tokeniser) (adjust int) {
split, index, pos := tokeniser(rl.line, rl.pos) split, index, pos := tokeniser(rl.line, rl.pos)
switch { switch {
case len(split) == 0: case len(split) == 0:
@ -472,7 +473,7 @@ func (rl *Instance) viJumpW(tokeniser tokeniser) (adjust int) {
return return
} }
func (rl *Instance) viJumpPreviousBrace() (adjust int) { func (rl *Readline) viJumpPreviousBrace() (adjust int) {
if rl.pos == 0 { if rl.pos == 0 {
return 0 return 0
} }
@ -486,7 +487,7 @@ func (rl *Instance) viJumpPreviousBrace() (adjust int) {
return 0 return 0
} }
func (rl *Instance) viJumpNextBrace() (adjust int) { func (rl *Readline) viJumpNextBrace() (adjust int) {
if rl.pos >= len(rl.line)-1 { if rl.pos >= len(rl.line)-1 {
return 0 return 0
} }
@ -500,7 +501,7 @@ func (rl *Instance) viJumpNextBrace() (adjust int) {
return 0 return 0
} }
func (rl *Instance) viJumpBracket() (adjust int) { func (rl *Readline) viJumpBracket() (adjust int) {
split, index, pos := tokeniseBrackets(rl.line, rl.pos) split, index, pos := tokeniseBrackets(rl.line, rl.pos)
switch { switch {
case len(split) == 0: case len(split) == 0:

View File

@ -5,7 +5,7 @@ import (
) )
// vimDelete - // vimDelete -
func (rl *Instance) viDelete(r rune) { func (rl *Readline) viDelete(r rune) {
// We are allowed to type iterations after a delete ('d') command. // We are allowed to type iterations after a delete ('d') command.
// in which case we don't exit the delete mode. The next thing typed // in which case we don't exit the delete mode. The next thing typed
@ -91,7 +91,7 @@ func (rl *Instance) viDelete(r rune) {
} }
} }
func (rl *Instance) viDeleteByAdjust(adjust int) { func (rl *Readline) viDeleteByAdjust(adjust int) {
var ( var (
newLine []rune newLine []rune
backOne bool backOne bool
@ -142,11 +142,11 @@ func (rl *Instance) viDeleteByAdjust(adjust int) {
rl.updateHelpers() rl.updateHelpers()
} }
func (rl *Instance) DeleteByAmount(adjust int) { func (rl *Readline) DeleteByAmount(adjust int) {
rl.viDeleteByAdjust(adjust) rl.viDeleteByAdjust(adjust)
} }
func (rl *Instance) vimDeleteToken(r rune) bool { func (rl *Readline) vimDeleteToken(r rune) bool {
tokens, _, _ := tokeniseSplitSpaces(rl.line, 0) tokens, _, _ := tokeniseSplitSpaces(rl.line, 0)
pos := int(r) - 48 // convert ASCII to integer pos := int(r) - 48 // convert ASCII to integer
if pos > len(tokens) { if pos > len(tokens) {

65
rl.go
View File

@ -13,9 +13,10 @@ import (
) )
type lineReader struct { type lineReader struct {
rl *readline.Instance rl *readline.Readline
fileHist *fileHistory fileHist *fileHistory
} }
var hinter *rt.Closure var hinter *rt.Closure
var highlighter *rt.Closure var highlighter *rt.Closure
@ -54,25 +55,31 @@ func newLineReader(prompt string, noHist bool) *lineReader {
rl.ViModeCallback = func(mode readline.ViMode) { rl.ViModeCallback = func(mode readline.ViMode) {
modeStr := "" modeStr := ""
switch mode { switch mode {
case readline.VimKeys: modeStr = "normal" case readline.VimKeys:
case readline.VimInsert: modeStr = "insert" modeStr = "normal"
case readline.VimDelete: modeStr = "delete" case readline.VimInsert:
case readline.VimReplaceOnce, readline.VimReplaceMany: modeStr = "replace" modeStr = "insert"
case readline.VimDelete:
modeStr = "delete"
case readline.VimReplaceOnce, readline.VimReplaceMany:
modeStr = "replace"
} }
setVimMode(modeStr) setVimMode(modeStr)
} }
rl.ViActionCallback = func(action readline.ViAction, args []string) { rl.ViActionCallback = func(action readline.ViAction, args []string) {
actionStr := "" actionStr := ""
switch action { switch action {
case readline.VimActionPaste: actionStr = "paste" case readline.VimActionPaste:
case readline.VimActionYank: actionStr = "yank" actionStr = "paste"
case readline.VimActionYank:
actionStr = "yank"
} }
hooks.Emit("hilbish.vimAction", actionStr, args) hooks.Emit("hilbish.vimAction", actionStr, args)
} }
rl.HintText = func(line []rune, pos int) []rune { rl.HintText = func(line []rune, pos int) []rune {
hinter := hshMod.Get(rt.StringValue("hinter")) hinter := hshMod.Get(rt.StringValue("hinter"))
retVal, err := rt.Call1(l.MainThread(), hinter, retVal, err := rt.Call1(l.MainThread(), hinter,
rt.StringValue(string(line)), rt.IntValue(int64(pos))) rt.StringValue(string(line)), rt.IntValue(int64(pos)))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return []rune{} return []rune{}
@ -88,7 +95,7 @@ func newLineReader(prompt string, noHist bool) *lineReader {
rl.SyntaxHighlighter = func(line []rune) string { rl.SyntaxHighlighter = func(line []rune) string {
highlighter := hshMod.Get(rt.StringValue("highlighter")) highlighter := hshMod.Get(rt.StringValue("highlighter"))
retVal, err := rt.Call1(l.MainThread(), highlighter, retVal, err := rt.Call1(l.MainThread(), highlighter,
rt.StringValue(string(line))) rt.StringValue(string(line)))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return string(line) return string(line)
@ -105,7 +112,7 @@ func newLineReader(prompt string, noHist bool) *lineReader {
term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 2, false) term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 2, false)
compHandle := hshMod.Get(rt.StringValue("completion")).AsTable().Get(rt.StringValue("handler")) compHandle := hshMod.Get(rt.StringValue("completion")).AsTable().Get(rt.StringValue("handler"))
err := rt.Call(l.MainThread(), compHandle, []rt.Value{rt.StringValue(string(line)), err := rt.Call(l.MainThread(), compHandle, []rt.Value{rt.StringValue(string(line)),
rt.IntValue(int64(pos))}, term) rt.IntValue(int64(pos))}, term)
var compGroups []*readline.CompletionGroup var compGroups []*readline.CompletionGroup
if err != nil { if err != nil {
@ -175,11 +182,11 @@ func newLineReader(prompt string, noHist bool) *lineReader {
itemAliases[itemName] = itemAlias itemAliases[itemName] = itemAlias
} else if keytyp == rt.IntType { } else if keytyp == rt.IntType {
vlStr, ok := lval.TryString() vlStr, ok := lval.TryString()
if !ok { if !ok {
// TODO: error // TODO: error
return return
} }
items = append(items, vlStr) items = append(items, vlStr)
} else { } else {
// TODO: error // TODO: error
return return
@ -188,20 +195,22 @@ func newLineReader(prompt string, noHist bool) *lineReader {
var dispType readline.TabDisplayType var dispType readline.TabDisplayType
switch luaCompType.AsString() { switch luaCompType.AsString() {
case "grid": dispType = readline.TabDisplayGrid case "grid":
case "list": dispType = readline.TabDisplayList dispType = readline.TabDisplayGrid
case "list":
dispType = readline.TabDisplayList
// need special cases, will implement later // need special cases, will implement later
//case "map": dispType = readline.TabDisplayMap //case "map": dispType = readline.TabDisplayMap
} }
compGroups = append(compGroups, &readline.CompletionGroup{ compGroups = append(compGroups, &readline.CompletionGroup{
DisplayType: dispType, DisplayType: dispType,
Aliases: itemAliases, Aliases: itemAliases,
Descriptions: itemDescriptions, Descriptions: itemDescriptions,
ItemDisplays: itemDisplays, ItemDisplays: itemDisplays,
Suggestions: items, Suggestions: items,
TrimSlash: false, TrimSlash: false,
NoSpace: true, NoSpace: true,
}) })
}) })
@ -227,8 +236,8 @@ func (lr *lineReader) SetPrompt(p string) {
halfPrompt := strings.Split(p, "\n") halfPrompt := strings.Split(p, "\n")
if len(halfPrompt) > 1 { if len(halfPrompt) > 1 {
lr.rl.Multiline = true lr.rl.Multiline = true
lr.rl.SetPrompt(strings.Join(halfPrompt[:len(halfPrompt) - 1], "\n")) lr.rl.SetPrompt(strings.Join(halfPrompt[:len(halfPrompt)-1], "\n"))
lr.rl.MultilinePrompt = halfPrompt[len(halfPrompt) - 1:][0] lr.rl.MultilinePrompt = halfPrompt[len(halfPrompt)-1:][0]
} else { } else {
lr.rl.Multiline = false lr.rl.Multiline = false
lr.rl.MultilinePrompt = "" lr.rl.MultilinePrompt = ""
@ -265,11 +274,11 @@ func (lr *lineReader) Resize() {
// method of saving history. // method of saving history.
func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table { func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table {
lrLua := map[string]util.LuaExport{ lrLua := map[string]util.LuaExport{
"add": {lr.luaAddHistory, 1, false}, "add": {lr.luaAddHistory, 1, false},
"all": {lr.luaAllHistory, 0, false}, "all": {lr.luaAllHistory, 0, false},
"clear": {lr.luaClearHistory, 0, false}, "clear": {lr.luaClearHistory, 0, false},
"get": {lr.luaGetHistory, 1, false}, "get": {lr.luaGetHistory, 1, false},
"size": {lr.luaSize, 0, false}, "size": {lr.luaSize, 0, false},
} }
mod := rt.NewTable() mod := rt.NewTable()