diff --git a/CHANGELOG.md b/CHANGELOG.md
index b80824b0..8e82fdd7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,13 @@
## Unreleased
### Added
- Forward/Right arrow key will fill in hint text (#327)
+- The readline library adds the ability to create custom instances of the Hilbish
+line editor. Now, `hilbish.editor` has been changed to a readline instance, instead of just being a table of a few functions to access it.
+This means the colon operator is now the *preferred* way of accessing its functions,
+and the dot operator will cause errors in 3.0.
+Example: `hilbish.editor.getLine()` should be changed to `hilbish.editor:getLine()`
+before 3.0
+- Added the `hilbish.editor:read` and `hilbish.editor:log(text)` functions.
### Changed
- Documentation for Lunacolors has been improved, with more information added.
- Values returned by bait hooks will be passed to the `throw` caller
diff --git a/api.go b/api.go
index 8800f723..3d0ba262 100644
--- a/api.go
+++ b/api.go
@@ -121,9 +121,6 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
timersModule := timers.loader(rtm)
mod.Set(rt.StringValue("timers"), rt.TableValue(timersModule))
- editorModule := editorLoader(rtm)
- mod.Set(rt.StringValue("editor"), rt.TableValue(editorModule))
-
versionModule := rt.NewTable()
util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch))
util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion()))
diff --git a/cmd/docgen/docgen.go b/cmd/docgen/docgen.go
index 1521e0e0..4961a648 100644
--- a/cmd/docgen/docgen.go
+++ b/cmd/docgen/docgen.go
@@ -2,14 +2,14 @@ package main
import (
"fmt"
- "path/filepath"
"go/ast"
"go/doc"
"go/parser"
"go/token"
+ "os"
+ "path/filepath"
"regexp"
"strings"
- "os"
"sync"
md "github.com/atsushinee/go-markdown-generator/doc"
@@ -27,49 +27,49 @@ menu:
`
type emmyPiece struct {
- DocPiece *docPiece
+ DocPiece *docPiece
Annotations []string
- Params []string // we only need to know param name to put in function
- FuncName string
+ Params []string // we only need to know param name to put in function
+ FuncName string
}
type module struct {
- Types []docPiece
- Docs []docPiece
- Fields []docPiece
- Properties []docPiece
+ Types []docPiece
+ Docs []docPiece
+ Fields []docPiece
+ Properties []docPiece
ShortDescription string
- Description string
- ParentModule string
- HasInterfaces bool
- HasTypes bool
+ Description string
+ ParentModule string
+ HasInterfaces bool
+ HasTypes bool
}
-type param struct{
+type param struct {
Name string
Type string
- Doc []string
+ Doc []string
}
type docPiece struct {
- Doc []string
- FuncSig string
- FuncName string
- Interfacing string
+ Doc []string
+ FuncSig string
+ FuncName string
+ Interfacing string
ParentModule string
- GoFuncName string
- IsInterface bool
- IsMember bool
- IsType bool
- Fields []docPiece
- Properties []docPiece
- Params []param
- Tags map[string][]tag
+ GoFuncName string
+ IsInterface bool
+ IsMember bool
+ IsType bool
+ Fields []docPiece
+ Properties []docPiece
+ Params []param
+ Tags map[string][]tag
}
type tag struct {
- id string
- fields []string
+ id string
+ fields []string
startIdx int
}
@@ -78,13 +78,14 @@ var interfaceDocs = make(map[string]module)
var emmyDocs = make(map[string][]emmyPiece)
var typeTable = make(map[string][]string) // [0] = parentMod, [1] = interfaces
var prefix = map[string]string{
- "main": "hl",
- "hilbish": "hl",
- "fs": "f",
+ "main": "hl",
+ "hilbish": "hl",
+ "fs": "f",
"commander": "c",
- "bait": "b",
- "terminal": "term",
- "snail": "snail",
+ "bait": "b",
+ "terminal": "term",
+ "snail": "snail",
+ "readline": "rl",
}
func getTagsAndDocs(docs string) (map[string][]tag, []string) {
@@ -109,7 +110,7 @@ func getTagsAndDocs(docs string) (map[string][]tag, []string) {
} else {
if tagParts[0] == "example" {
exampleIdx := tags["example"][0].startIdx
- exampleCode := pts[exampleIdx+1:idx]
+ exampleCode := pts[exampleIdx+1 : idx]
tags["example"][0].fields = exampleCode
parts = strings.Split(strings.Replace(strings.Join(parts, "\n"), strings.TrimPrefix(strings.Join(exampleCode, "\n"), "#example\n"), "", -1), "\n")
@@ -121,7 +122,7 @@ func getTagsAndDocs(docs string) (map[string][]tag, []string) {
fleds = tagParts[2:]
}
tags[tagParts[0]] = append(tags[tagParts[0]], tag{
- id: tagParts[1],
+ id: tagParts[1],
fields: fleds,
})
}
@@ -138,7 +139,7 @@ func docPieceTag(tagName string, tags map[string][]tag) []docPiece {
for _, tag := range tags[tagName] {
dps = append(dps, docPiece{
FuncName: tag.id,
- Doc: tag.fields,
+ Doc: tag.fields,
})
}
@@ -169,16 +170,16 @@ func setupDocType(mod string, typ *doc.Type) *docPiece {
if strings.HasPrefix(d, "---") {
// TODO: document types in lua
/*
- emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
- emmyLinePieces := strings.Split(emmyLine, " ")
- emmyType := emmyLinePieces[0]
- if emmyType == "@param" {
- em.Params = append(em.Params, emmyLinePieces[1])
- }
- if emmyType == "@vararg" {
- em.Params = append(em.Params, "...") // add vararg
- }
- em.Annotations = append(em.Annotations, d)
+ emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
+ emmyLinePieces := strings.Split(emmyLine, " ")
+ emmyType := emmyLinePieces[0]
+ if emmyType == "@param" {
+ em.Params = append(em.Params, emmyLinePieces[1])
+ }
+ if emmyType == "@vararg" {
+ em.Params = append(em.Params, "...") // add vararg
+ }
+ em.Annotations = append(em.Annotations, d)
*/
} else {
typeDoc = append(typeDoc, d)
@@ -191,16 +192,16 @@ func setupDocType(mod string, typ *doc.Type) *docPiece {
}
parentMod := mod
dps := &docPiece{
- Doc: typeDoc,
- FuncName: typeName,
- Interfacing: interfaces,
- IsInterface: inInterface,
- IsMember: isMember,
- IsType: true,
+ Doc: typeDoc,
+ FuncName: typeName,
+ Interfacing: interfaces,
+ IsInterface: inInterface,
+ IsMember: isMember,
+ IsType: true,
ParentModule: parentMod,
- Fields: fields,
- Properties: properties,
- Tags: tags,
+ Fields: fields,
+ Properties: properties,
+ Tags: tags,
}
typeTable[strings.ToLower(typeName)] = []string{parentMod, interfaces}
@@ -221,6 +222,10 @@ func setupDoc(mod string, fun *doc.Func) *docPiece {
goto start
}
+ if prefix[mod] == "" {
+ return nil
+ }
+
if (!strings.HasPrefix(fun.Name, prefix[mod]) && tags["interface"] == nil) || (strings.ToLower(fun.Name) == "loader" && tags["interface"] == nil) {
return nil
}
@@ -248,7 +253,7 @@ start:
params[i] = param{
Name: p.id,
Type: p.fields[0],
- Doc: p.fields[1:],
+ Doc: p.fields[1:],
}
}
}
@@ -279,24 +284,24 @@ start:
parentMod = mod
}
dps := &docPiece{
- Doc: funcdoc,
- FuncSig: funcsig,
- FuncName: funcName,
- Interfacing: interfaces,
- GoFuncName: strings.ToLower(fun.Name),
- IsInterface: inInterface,
- IsMember: isMember,
+ Doc: funcdoc,
+ FuncSig: funcsig,
+ FuncName: funcName,
+ Interfacing: interfaces,
+ GoFuncName: strings.ToLower(fun.Name),
+ IsInterface: inInterface,
+ IsMember: isMember,
ParentModule: parentMod,
- Fields: fields,
- Properties: properties,
- Params: params,
- Tags: tags,
+ Fields: fields,
+ Properties: properties,
+ Params: params,
+ Tags: tags,
}
if strings.HasSuffix(dps.GoFuncName, strings.ToLower("loader")) {
dps.Doc = parts
}
em.DocPiece = dps
-
+
emmyDocs[mod] = append(emmyDocs[mod], em)
return dps
}
@@ -326,11 +331,11 @@ provided by Hilbish.
os.Mkdir("emmyLuaDocs", 0777)
dirs := []string{"./", "./util"}
- filepath.Walk("golibs/", func (path string, info os.FileInfo, err error) error {
+ filepath.Walk("golibs/", func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
return nil
}
- dirs = append(dirs, "./" + path)
+ dirs = append(dirs, "./"+path)
return nil
})
@@ -445,13 +450,13 @@ provided by Hilbish.
docs[mod] = newDoc
} else {
docs[mod] = module{
- Types: filteredTypePieces,
- Docs: filteredPieces,
+ Types: filteredTypePieces,
+ Docs: filteredPieces,
ShortDescription: shortDesc,
- Description: strings.Join(desc, "\n"),
- HasInterfaces: hasInterfaces,
- Properties: docPieceTag("property", tags),
- Fields: docPieceTag("field", tags),
+ Description: strings.Join(desc, "\n"),
+ HasInterfaces: hasInterfaces,
+ Properties: docPieceTag("property", tags),
+ Fields: docPieceTag("field", tags),
}
}
}
@@ -466,7 +471,7 @@ provided by Hilbish.
for mod, v := range docs {
docPath := "docs/api/" + mod + ".md"
if v.HasInterfaces {
- os.Mkdir("docs/api/" + mod, 0777)
+ os.Mkdir("docs/api/"+mod, 0777)
os.Remove(docPath) // remove old doc path if it exists
docPath = "docs/api/" + mod + "/_index.md"
}
@@ -519,11 +524,11 @@ provided by Hilbish.
continue
}
- mdTable.SetContent(i - diff, 0, fmt.Sprintf(`%s`, dps.FuncName, dps.FuncSig))
+ mdTable.SetContent(i-diff, 0, fmt.Sprintf(`%s`, dps.FuncName, dps.FuncSig))
if len(dps.Doc) == 0 {
fmt.Printf("WARNING! Function %s on module %s has no documentation!\n", dps.FuncName, modname)
} else {
- mdTable.SetContent(i - diff, 1, dps.Doc[0])
+ mdTable.SetContent(i-diff, 1, dps.Doc[0])
}
}
f.WriteString(mdTable.String())
@@ -537,7 +542,6 @@ provided by Hilbish.
mdTable.SetTitle(0, "")
mdTable.SetTitle(1, "")
-
for i, dps := range modu.Fields {
mdTable.SetContent(i, 0, dps.FuncName)
mdTable.SetContent(i, 1, strings.Join(dps.Doc, " "))
@@ -552,7 +556,6 @@ provided by Hilbish.
mdTable.SetTitle(0, "")
mdTable.SetTitle(1, "")
-
for i, dps := range modu.Properties {
mdTable.SetContent(i, 0, dps.FuncName)
mdTable.SetContent(i, 1, strings.Join(dps.Doc, " "))
@@ -570,7 +573,7 @@ provided by Hilbish.
continue
}
f.WriteString(fmt.Sprintf("
", dps.FuncName))
- htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(modname + "." + dps.FuncSig, "<", `\<`, -1), func(typ string) string {
+ htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(modname+"."+dps.FuncSig, "<", `\<`, -1), func(typ string) string {
typName := typ[1:]
typLookup := typeTable[strings.ToLower(typName)]
ifaces := typLookup[0] + "." + typLookup[1] + "/"
@@ -657,7 +660,7 @@ provided by Hilbish.
typName := regexp.MustCompile(`\w+`).FindString(typ[1:])
typLookup := typeTable[strings.ToLower(typName)]
fmt.Printf("%+q, \n", typLookup)
- linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s/#%s", typLookup[0], typLookup[0] + "." + typLookup[1], strings.ToLower(typName))
+ linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s/#%s", typLookup[0], typLookup[0]+"."+typLookup[1], strings.ToLower(typName))
return fmt.Sprintf(`
%s`, linkedTyp, typName)
})
f.WriteString(fmt.Sprintf("#### %s\n", htmlSig))
diff --git a/docs/api/hilbish/hilbish.editor.md b/docs/api/hilbish/hilbish.editor.md
deleted file mode 100644
index 6dac64b8..00000000
--- a/docs/api/hilbish/hilbish.editor.md
+++ /dev/null
@@ -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
-|||
-|----|----|
-|
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.|
-|
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.|
-
-
-
-
-hilbish.editor.deleteByAmount(amount)
-
-
-
-
-
-Deletes characters in the line by the given amount.
-
-#### Parameters
-`number` **`amount`**
-
-
-
-
-
-
-
-hilbish.editor.getLine() -> string
-
-
-
-
-
-Returns the current input line.
-
-#### Parameters
-This function has no parameters.
-
-
-
-
-
-hilbish.editor.getVimRegister(register) -> string
-
-
-
-
-
-Returns the text that is at the register.
-
-#### Parameters
-`string` **`register`**
-
-
-
-
-
-
-
-hilbish.editor.insert(text)
-
-
-
-
-
-Inserts text into the Hilbish command line.
-
-#### Parameters
-`string` **`text`**
-
-
-
-
-
-
-
-hilbish.editor.getChar() -> string
-
-
-
-
-
-Reads a keystroke from the user. This is in a format of something like Ctrl-L.
-
-#### Parameters
-This function has no parameters.
-
-
-
-
-
-hilbish.editor.setVimRegister(register, text)
-
-
-
-
-
-Sets the vim register at `register` to hold the passed text.
-
-#### Parameters
-`string` **`register`**
-
-
-`string` **`text`**
-
-
-
-
diff --git a/docs/api/readline.md b/docs/api/readline.md
new file mode 100644
index 00000000..dfa4568e
--- /dev/null
+++ b/docs/api/readline.md
@@ -0,0 +1,66 @@
+---
+title: Module readline
+description: line reader library
+layout: doc
+menu:
+ docs:
+ parent: "API"
+---
+
+## Introduction
+The readline module is responsible for reading 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,
+syntax highlighting, everything. The global Hilbish readline instance
+is usable at `hilbish.editor`.
+
+## Functions
+|||
+|----|----|
+|
new() -> @Readline|Creates a new readline instance.|
+
+
+
+
+readline.new() -> Readline
+
+
+
+
+
+Creates a new readline instance.
+
+#### Parameters
+This function has no parameters.
+
+
+## Types
+
+
+## Readline
+
+### Methods
+#### 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.
+
diff --git a/editor.go b/editor.go
deleted file mode 100644
index 2c04f255..00000000
--- a/editor.go
+++ /dev/null
@@ -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
-}
diff --git a/emmyLuaDocs/hilbish.lua b/emmyLuaDocs/hilbish.lua
index a2935bba..1e9e0d46 100644
--- a/emmyLuaDocs/hilbish.lua
+++ b/emmyLuaDocs/hilbish.lua
@@ -7,24 +7,6 @@ local hilbish = {}
--- @param cmd string
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.
--- This function is meant to be used as a helper in a command completion handler.
---
diff --git a/emmyLuaDocs/readline.lua b/emmyLuaDocs/readline.lua
new file mode 100644
index 00000000..a7fc8059
--- /dev/null
+++ b/emmyLuaDocs/readline.lua
@@ -0,0 +1,32 @@
+--- @meta
+
+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
diff --git a/go.mod b/go.mod
index cc88c8ef..23bd7f69 100644
--- a/go.mod
+++ b/go.mod
@@ -30,7 +30,7 @@ require (
replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20240815163633-562273e09b73
-replace github.com/maxlandon/readline => ./readline
+replace github.com/maxlandon/readline => ./golibs/readline
replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10
diff --git a/readline/CHANGES.md b/golibs/readline/CHANGES.md
similarity index 100%
rename from readline/CHANGES.md
rename to golibs/readline/CHANGES.md
diff --git a/readline/LICENSE b/golibs/readline/LICENSE
similarity index 100%
rename from readline/LICENSE
rename to golibs/readline/LICENSE
diff --git a/readline/README.md b/golibs/readline/README.md
similarity index 100%
rename from readline/README.md
rename to golibs/readline/README.md
diff --git a/readline/codes.go b/golibs/readline/codes.go
similarity index 68%
rename from readline/codes.go
rename to golibs/readline/codes.go
index 28a9e604..380b35d7 100644
--- a/readline/codes.go
+++ b/golibs/readline/codes.go
@@ -54,14 +54,14 @@ var (
seqCtrlDelete2 = string([]byte{27, 91, 77})
seqAltDelete = string([]byte{27, 91, 51, 59, 51, 126})
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})
seqAltD = string([]byte{27, 100})
seqAltF = string([]byte{27, 102})
seqAltR = string([]byte{27, 114}) // Used for alternative history
seqAltBackspace = string([]byte{27, 127})
seqPageUp = string([]byte{27, 91, 53, 126})
- seqPageDown = string([]byte{27, 91, 54, 126})
+ seqPageDown = string([]byte{27, 91, 54, 126})
)
const (
@@ -76,7 +76,7 @@ const (
seqCursorTopLeft = "\x1b[H" // Clears screen and places cursor on top-left
seqGetCursorPos = "\x1b6n" // response: "\x1b{Line};{Column}R"
- seqHideCursor = "\x1b[?25l"
+ seqHideCursor = "\x1b[?25l"
seqUnhideCursor = "\x1b[?25h"
seqCtrlLeftArrow = "\x1b[1;5D"
@@ -143,55 +143,94 @@ const (
// TODO: return whether its actually a sequence or not
// 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)
i, _ := os.Stdin.Read(b)
r := []rune(string(b))
s := string(r[:i])
switch b[0] {
- case charCtrlA: return "Ctrl-A"
- case charCtrlB: return "Ctrl-B"
- case charCtrlC: return "Ctrl-C"
- case charEOF: return "Ctrl-D"
- case charCtrlE: return "Ctrl-E"
- case charCtrlF: return "Ctrl-F"
- case charCtrlG: return "Ctrl-G"
- case charBackspace, charBackspace2: return "Backspace"
- case charTab: return "Tab"
- case charCtrlK: return "Ctrl-K"
- case charCtrlL: return "Ctrl-L"
- case charCtrlN: return "Ctrl-N"
- case charCtrlO: return "Ctrl-O"
- case charCtrlP: return "Ctrl-P"
- case charCtrlQ: return "Ctrl-Q"
- case charCtrlR: return "Ctrl-R"
- case charCtrlS: return "Ctrl-S"
- case charCtrlT: return "Ctrl-T"
- case charCtrlU: return "Ctrl-U"
- case charCtrlV: return "Ctrl-V"
- 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"
- }
+ case charCtrlA:
+ return "Ctrl-A"
+ case charCtrlB:
+ return "Ctrl-B"
+ case charCtrlC:
+ return "Ctrl-C"
+ case charEOF:
+ return "Ctrl-D"
+ case charCtrlE:
+ return "Ctrl-E"
+ case charCtrlF:
+ return "Ctrl-F"
+ case charCtrlG:
+ return "Ctrl-G"
+ case charBackspace, charBackspace2:
+ return "Backspace"
+ case charTab:
+ return "Tab"
+ case charCtrlK:
+ return "Ctrl-K"
+ case charCtrlL:
+ return "Ctrl-L"
+ case charCtrlN:
+ return "Ctrl-N"
+ case charCtrlO:
+ return "Ctrl-O"
+ case charCtrlP:
+ return "Ctrl-P"
+ case charCtrlQ:
+ return "Ctrl-Q"
+ case charCtrlR:
+ return "Ctrl-R"
+ case charCtrlS:
+ return "Ctrl-S"
+ case charCtrlT:
+ return "Ctrl-T"
+ case charCtrlU:
+ return "Ctrl-U"
+ case charCtrlV:
+ return "Ctrl-V"
+ 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
diff --git a/readline/comp-grid.go b/golibs/readline/comp-grid.go
similarity index 92%
rename from readline/comp-grid.go
rename to golibs/readline/comp-grid.go
index c198bdbd..0d523773 100644
--- a/readline/comp-grid.go
+++ b/golibs/readline/comp-grid.go
@@ -4,12 +4,13 @@ import (
"fmt"
"strconv"
"strings"
+
"github.com/rivo/uniseg"
-)
+)
// 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.
-func (g *CompletionGroup) initGrid(rl *Instance) {
+func (g *CompletionGroup) initGrid(rl *Readline) {
// Compute size of each completion item box
tcMaxLength := 1
@@ -44,7 +45,7 @@ func (g *CompletionGroup) initGrid(rl *Instance) {
}
// 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.tcPosY += y
@@ -96,7 +97,7 @@ func (g *CompletionGroup) moveTabGridHighlight(rl *Instance, x, y int) (done boo
}
// 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 g.Name != "" {
@@ -127,9 +128,9 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {
sugg := g.Suggestions[i]
if len(sugg) > GetTermWidth() {
- sugg = sugg[:GetTermWidth() - 4] + "..."
+ sugg = sugg[:GetTermWidth()-4] + "..."
}
- formatStr := "%-"+cellWidth+"s%s "
+ formatStr := "%-" + cellWidth + "s%s "
if g.tcMaxX == 1 {
formatStr = "%s%s"
}
diff --git a/readline/comp-group.go b/golibs/readline/comp-group.go
similarity index 95%
rename from readline/comp-group.go
rename to golibs/readline/comp-group.go
index 74b528a3..f6019fd0 100644
--- a/readline/comp-group.go
+++ b/golibs/readline/comp-group.go
@@ -49,7 +49,7 @@ type CompletionGroup struct {
}
// 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
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),
// 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.
-func (g *CompletionGroup) updateTabFind(rl *Instance) {
+func (g *CompletionGroup) updateTabFind(rl *Readline) {
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.
@@ -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
-func (g *CompletionGroup) checkCycle(rl *Instance) {
+func (g *CompletionGroup) checkCycle(rl *Readline) {
if len(rl.tcGroups) == 1 {
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
-func (g *CompletionGroup) checkMaxLength(rl *Instance) {
+func (g *CompletionGroup) checkMaxLength(rl *Readline) {
// This means the user forgot to set it
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
// 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
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,
// 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 {
case TabDisplayGrid:
diff --git a/readline/comp-list.go b/golibs/readline/comp-list.go
similarity index 96%
rename from readline/comp-list.go
rename to golibs/readline/comp-list.go
index 403cf5d1..bb34b49b 100644
--- a/readline/comp-list.go
+++ b/golibs/readline/comp-list.go
@@ -8,7 +8,7 @@ import (
// initList - List display details. Because of the way 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
// 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)
// 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
g.tcPosY += x
@@ -153,7 +153,7 @@ func (g *CompletionGroup) moveTabListHighlight(rl *Instance, x, y int) (done boo
}
// 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.
if g.Name != "" {
@@ -249,7 +249,7 @@ func (g *CompletionGroup) writeList(rl *Instance) (comp string) {
return
}
-func (rl *Instance) getListPad() (pad int) {
+func (rl *Readline) getListPad() (pad int) {
for _, group := range rl.tcGroups {
if group.DisplayType == TabDisplayList {
for i := range group.Suggestions {
diff --git a/readline/comp-map.go b/golibs/readline/comp-map.go
similarity index 94%
rename from readline/comp-map.go
rename to golibs/readline/comp-map.go
index ec985ff8..65cf2035 100644
--- a/readline/comp-map.go
+++ b/golibs/readline/comp-map.go
@@ -7,7 +7,7 @@ import (
// 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.
-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
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)
-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 += y
@@ -72,7 +72,7 @@ func (g *CompletionGroup) moveTabMapHighlight(rl *Instance, x, y int) (done bool
}
// 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 != "" {
// Print group title (changes with line returns depending on type)
diff --git a/readline/cursor.go b/golibs/readline/cursor.go
similarity index 94%
rename from readline/cursor.go
rename to golibs/readline/cursor.go
index 9d68a5a2..8128d733 100644
--- a/readline/cursor.go
+++ b/golibs/readline/cursor.go
@@ -1,7 +1,7 @@
package readline
import (
-// "fmt"
+ // "fmt"
"os"
"regexp"
"strconv"
@@ -28,7 +28,7 @@ func leftMost() []byte {
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 {
return -1, -1
}
@@ -143,7 +143,7 @@ func unhideCursor() {
print(seqUnhideCursor)
}
-func (rl *Instance) backspace(forward bool) {
+func (rl *Readline) backspace(forward bool) {
if len(rl.line) == 0 || rl.pos == 0 {
return
}
@@ -151,7 +151,7 @@ func (rl *Instance) backspace(forward bool) {
rl.deleteBackspace(forward)
}
-func (rl *Instance) moveCursorByAdjust(adjust int) {
+func (rl *Readline) moveCursorByAdjust(adjust int) {
switch {
case adjust > 0:
rl.pos += adjust
diff --git a/readline/editor.go b/golibs/readline/editor.go
similarity index 96%
rename from readline/editor.go
rename to golibs/readline/editor.go
index 41a24db3..186e066b 100644
--- a/readline/editor.go
+++ b/golibs/readline/editor.go
@@ -14,7 +14,7 @@ import (
)
// 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
var path string
diff --git a/readline/editor_plan9.go b/golibs/readline/editor_plan9.go
similarity index 60%
rename from readline/editor_plan9.go
rename to golibs/readline/editor_plan9.go
index e6789f15..c1ab1e9d 100644
--- a/readline/editor_plan9.go
+++ b/golibs/readline/editor_plan9.go
@@ -1,9 +1,10 @@
+//go:build plan9
// +build plan9
package readline
import "errors"
-func (rl *Instance) launchEditor(multiline []rune) ([]rune, error) {
+func (rl *Readline) launchEditor(multiline []rune) ([]rune, error) {
return rl.line, errors.New("Not currently supported on Plan 9")
}
diff --git a/readline/editor_unix.go b/golibs/readline/editor_unix.go
similarity index 94%
rename from readline/editor_unix.go
rename to golibs/readline/editor_unix.go
index c103b257..5a5b3e00 100644
--- a/readline/editor_unix.go
+++ b/golibs/readline/editor_unix.go
@@ -15,7 +15,7 @@ const defaultEditor = "vi"
// 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,
// 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)
if err != nil {
return multiline, err
diff --git a/readline/editor_windows.go b/golibs/readline/editor_windows.go
similarity index 77%
rename from readline/editor_windows.go
rename to golibs/readline/editor_windows.go
index c1972dde..a5530e14 100644
--- a/readline/editor_windows.go
+++ b/golibs/readline/editor_windows.go
@@ -5,6 +5,6 @@ package readline
import "errors"
// StartEditorWithBuffer - Not implemented on Windows platforms.
-func (rl *Instance) StartEditorWithBuffer(multiline []rune, filename string) ([]rune, error) {
+func (rl *Readline) StartEditorWithBuffer(multiline []rune, filename string) ([]rune, error) {
return rl.line, errors.New("Not currently supported on Windows")
}
diff --git a/readline/errors.go b/golibs/readline/errors.go
similarity index 100%
rename from readline/errors.go
rename to golibs/readline/errors.go
diff --git a/readline/events.go b/golibs/readline/events.go
similarity index 83%
rename from readline/events.go
rename to golibs/readline/events.go
index 5d630769..6b687283 100644
--- a/readline/events.go
+++ b/golibs/readline/events.go
@@ -13,11 +13,11 @@ type EventReturn struct {
}
// 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
}
// DelEvent deregisters an existing keypress handler
-func (rl *Instance) DelEvent(keyPress string) {
+func (rl *Readline) DelEvent(keyPress string) {
delete(rl.evtKeyPress, keyPress)
}
diff --git a/readline/go.mod b/golibs/readline/go.mod
similarity index 100%
rename from readline/go.mod
rename to golibs/readline/go.mod
diff --git a/readline/go.sum b/golibs/readline/go.sum
similarity index 100%
rename from readline/go.sum
rename to golibs/readline/go.sum
diff --git a/readline/hint.go b/golibs/readline/hint.go
similarity index 86%
rename from readline/hint.go
rename to golibs/readline/hint.go
index d0c54fe0..2dc7a29d 100644
--- a/readline/hint.go
+++ b/golibs/readline/hint.go
@@ -5,13 +5,13 @@ import "regexp"
// SetHintText - a nasty function to force writing a new hint text. It does not update helpers, it just renders
// them, so the hint will survive until the helpers (thus including the hint) will be updated/recomputed.
/*
-func (rl *Instance) SetHintText(s string) {
+func (rl *Readline) SetHintText(s string) {
rl.hintText = []rune(s)
rl.renderHelpers()
}
*/
-func (rl *Instance) getHintText() {
+func (rl *Readline) getHintText() {
if !rl.modeAutoFind && !rl.modeTabFind {
// 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.
-func (rl *Instance) writeHintText() {
+func (rl *Readline) writeHintText() {
if len(rl.hintText) == 0 {
//rl.hintY = 0
return
@@ -43,7 +43,7 @@ func (rl *Instance) writeHintText() {
wrapped, hintLen := WrapText(string(rl.hintText), width)
offset += hintLen
-// rl.hintY = offset
+ // rl.hintY = offset
hintText := string(wrapped)
@@ -52,12 +52,12 @@ func (rl *Instance) writeHintText() {
}
}
-func (rl *Instance) resetHintText() {
+func (rl *Readline) resetHintText() {
//rl.hintY = 0
rl.hintText = []rune{}
}
-func (rl *Instance) insertHintText() {
+func (rl *Readline) insertHintText() {
if len(rl.hintText) != 0 {
// fill in hint text
rl.insert(rl.hintText)
diff --git a/readline/history.go b/golibs/readline/history.go
similarity index 93%
rename from readline/history.go
rename to golibs/readline/history.go
index 0c87a623..9e862614 100644
--- a/readline/history.go
+++ b/golibs/readline/history.go
@@ -29,24 +29,24 @@ type History interface {
}
// 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.mainHistory = history
}
// GetHistoryCtrlR - Returns the history source triggered by Ctrl-r
-func (rl *Instance) GetHistoryCtrlR() History {
+func (rl *Readline) GetHistoryCtrlR() History {
return rl.mainHistory
}
// 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.altHistory = history
}
// GetHistoryAltR - Returns the history source triggered by Alt-r
-func (rl *Instance) GetHistoryAltR() History {
+func (rl *Readline) GetHistoryAltR() History {
return rl.altHistory
}
@@ -101,7 +101,7 @@ func (h *NullHistory) Dump() interface{} {
}
// Browse historic lines:
-func (rl *Instance) walkHistory(i int) {
+func (rl *Readline) walkHistory(i int) {
var (
old, new string
dedup bool
@@ -123,7 +123,7 @@ func (rl *Instance) walkHistory(i int) {
// When we are exiting the current line buffer to move around
// 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)
}
@@ -168,7 +168,7 @@ func (rl *Instance) walkHistory(i int) {
// 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.
-func (rl *Instance) completeHistory() (hist []*CompletionGroup) {
+func (rl *Readline) completeHistory() (hist []*CompletionGroup) {
hist = make([]*CompletionGroup, 1)
hist[0] = &CompletionGroup{
diff --git a/readline/info.go b/golibs/readline/info.go
similarity index 89%
rename from readline/info.go
rename to golibs/readline/info.go
index 269157d8..ac77297f 100644
--- a/readline/info.go
+++ b/golibs/readline/info.go
@@ -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
// 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.renderHelpers()
}
-func (rl *Instance) getInfoText() {
+func (rl *Readline) getInfoText() {
if !rl.modeAutoFind && !rl.modeTabFind {
// 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.
-func (rl *Instance) writeInfoText() {
+func (rl *Readline) writeInfoText() {
if len(rl.infoText) == 0 {
rl.infoY = 0
return
@@ -50,7 +50,7 @@ func (rl *Instance) writeInfoText() {
}
}
-func (rl *Instance) resetInfoText() {
+func (rl *Readline) resetInfoText() {
rl.infoY = 0
rl.infoText = []rune{}
}
diff --git a/readline/instance.go b/golibs/readline/instance.go
similarity index 90%
rename from readline/instance.go
rename to golibs/readline/instance.go
index 3f52bed5..ef12a3ff 100644
--- a/readline/instance.go
+++ b/golibs/readline/instance.go
@@ -5,12 +5,16 @@ import (
"os"
"regexp"
"sync"
+
+ "github.com/arnodel/golua/lib/packagelib"
)
// 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
// captures without having to repeatedly unload configuration.
-type Instance struct {
+
+// #type
+type Readline struct {
//
// Input Modes -------------------------------------------------------------------------------
@@ -31,13 +35,13 @@ type Instance struct {
Multiline bool // If set to true, the shell will have a two-line prompt.
MultilinePrompt string // If multiline is true, this is the content of the 2nd line.
- mainPrompt string // If multiline true, the full prompt string / If false, the 1st line of the prompt
- rightPrompt string
- rightPromptLen int
- realPrompt []rune // The prompt that is actually on the same line as the beginning of the input line.
- defaultPrompt []rune
- promptLen int
- stillOnRefresh bool // True if some logs have printed asynchronously since last loop. Check refresh prompt funcs
+ mainPrompt string // If multiline true, the full prompt string / If false, the 1st line of the prompt
+ rightPrompt string
+ rightPromptLen int
+ realPrompt []rune // The prompt that is actually on the same line as the beginning of the input line.
+ defaultPrompt []rune
+ promptLen int
+ stillOnRefresh bool // True if some logs have printed asynchronously since last loop. Check refresh prompt funcs
//
// Input Line ---------------------------------------------------------------------------------
@@ -114,9 +118,9 @@ type Instance struct {
searchMode FindMode // Used for varying hints, and underlying functions called
regexSearch *regexp.Regexp // Holds the current search regex match
search string
- mainHist bool // Which history stdin do we want
- histInfo []rune // We store a piece of hist info, for dual history sources
- Searcher func(string, []string) []string
+ mainHist bool // Which history stdin do we want
+ histInfo []rune // We store a piece of hist info, for dual history sources
+ Searcher func(string, []string) []string
//
// History -----------------------------------------------------------------------------------
@@ -200,17 +204,19 @@ type Instance struct {
// concurency
mutex sync.Mutex
- ViModeCallback func(ViMode)
+ ViModeCallback func(ViMode)
ViActionCallback func(ViAction, []string)
RawInputCallback func([]rune) // called on all input
bufferedOut *bufio.Writer
+
+ Loader packagelib.Loader
}
// NewInstance is used to create a readline instance and initialise it with sane defaults.
-func NewInstance() *Instance {
- rl := new(Instance)
+func NewInstance() *Readline {
+ rl := new(Readline)
// Prompt
rl.Multiline = false
@@ -245,7 +251,9 @@ func NewInstance() *Instance {
}
for _, hay := range haystack {
- if rl.regexSearch == nil { continue }
+ if rl.regexSearch == nil {
+ continue
+ }
if rl.regexSearch.MatchString(hay) {
suggs = append(suggs, hay)
}
@@ -256,6 +264,11 @@ func NewInstance() *Instance {
rl.bufferedOut = bufio.NewWriter(os.Stdout)
+ rl.Loader = packagelib.Loader{
+ Name: "readline",
+ Load: rl.luaLoader,
+ }
+
// Registers
rl.initRegisters()
diff --git a/readline/line.go b/golibs/readline/line.go
similarity index 90%
rename from readline/line.go
rename to golibs/readline/line.go
index 3069ad86..3466e92f 100644
--- a/readline/line.go
+++ b/golibs/readline/line.go
@@ -6,7 +6,7 @@ import (
// 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.
-func (rl *Instance) updateLine(line []rune) {
+func (rl *Readline) updateLine(line []rune) {
if len(rl.currentComp) > 0 {
} 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,
// 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 {
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
// 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.
-func (rl *Instance) echo() {
+func (rl *Readline) echo() {
// Then we print the prompt, and the line,
hideCursor()
@@ -79,7 +79,7 @@ func (rl *Instance) echo() {
unhideCursor()
}
-func (rl *Instance) insert(r []rune) {
+func (rl *Readline) insert(r []rune) {
for {
// I don't really understand why `0` is creaping in at the end of the
// array but it only happens with unicode characters.
@@ -112,11 +112,11 @@ func (rl *Instance) insert(r []rune) {
rl.updateHelpers()
}
-func (rl *Instance) Insert(t string) {
+func (rl *Readline) Insert(t string) {
rl.insert([]rune(t))
}
-func (rl *Instance) deleteX() {
+func (rl *Readline) deleteX() {
switch {
case len(rl.line) == 0:
return
@@ -134,7 +134,7 @@ func (rl *Instance) deleteX() {
rl.updateHelpers()
}
-func (rl *Instance) deleteBackspace(forward bool) {
+func (rl *Readline) deleteBackspace(forward bool) {
switch {
case len(rl.line) == 0:
return
@@ -153,7 +153,7 @@ func (rl *Instance) deleteBackspace(forward bool) {
rl.updateHelpers()
}
-func (rl *Instance) clearLine() {
+func (rl *Readline) clearLine() {
if len(rl.line) == 0 {
return
}
@@ -179,21 +179,21 @@ func (rl *Instance) clearLine() {
rl.clearVirtualComp()
}
-func (rl *Instance) deleteToBeginning() {
+func (rl *Readline) deleteToBeginning() {
rl.resetVirtualComp(false)
// Keep the line length up until the cursor
rl.line = rl.line[rl.pos:]
rl.pos = 0
}
-func (rl *Instance) deleteToEnd() {
+func (rl *Readline) deleteToEnd() {
rl.resetVirtualComp(false)
// Keep everything before the cursor
rl.line = rl.line[:rl.pos]
}
// @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)
if len(split) == 0 {
return
@@ -214,7 +214,7 @@ func (rl *Instance) emacsForwardWord(tokeniser tokeniser) (adjust int) {
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)
if len(split) == 0 {
return
diff --git a/readline/line_test.go b/golibs/readline/line_test.go
similarity index 100%
rename from readline/line_test.go
rename to golibs/readline/line_test.go
diff --git a/golibs/readline/lua.go b/golibs/readline/lua.go
new file mode 100644
index 00000000..9efbc48f
--- /dev/null
+++ b/golibs/readline/lua.go
@@ -0,0 +1,276 @@
+// line reader library
+// The readline module is responsible for reading 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,
+// syntax highlighting, everything. The global Hilbish readline instance
+// is usable at `hilbish.editor`.
+package readline
+
+import (
+ "fmt"
+ "io"
+
+ "hilbish/util"
+
+ rt "github.com/arnodel/golua/runtime"
+)
+
+var rlMetaKey = rt.StringValue("__readline")
+
+func (rl *Readline) luaLoader(rtm *rt.Runtime) (rt.Value, func()) {
+ rlMethods := rt.NewTable()
+ rlMethodss := map[string]util.LuaExport{
+ "deleteByAmount": {rlDeleteByAmount, 2, false},
+ "getLine": {rlGetLine, 1, false},
+ "getVimRegister": {rlGetRegister, 2, false},
+ "insert": {rlInsert, 2, false},
+ "read": {rlRead, 1, false},
+ "readChar": {rlReadChar, 1, false},
+ "setVimRegister": {rlSetRegister, 3, false},
+ "log": {rlLog, 2, false},
+ }
+ util.SetExports(rtm, rlMethods, rlMethodss)
+
+ rlMeta := rt.NewTable()
+ rlIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
+ _, err := rlArg(c, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ arg := c.Arg(1)
+ val := rlMethods.Get(arg)
+
+ return c.PushingNext1(t.Runtime, val), nil
+ }
+
+ rlMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(rlIndex, "__index", 2, false)))
+ rtm.SetRegistry(rlMetaKey, rt.TableValue(rlMeta))
+
+ rlFuncs := map[string]util.LuaExport{
+ "new": {rlNew, 0, false},
+ }
+
+ luaRl := rt.NewTable()
+ util.SetExports(rtm, luaRl, rlFuncs)
+
+ return rt.TableValue(luaRl), nil
+}
+
+// new() -> @Readline
+// Creates a new readline instance.
+func rlNew(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
+ rl := NewInstance()
+ ud := rlUserData(t.Runtime, rl)
+
+ return c.PushingNext1(t.Runtime, rt.UserDataValue(ud)), nil
+}
+
+// #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 {
+ return nil, err
+ }
+
+ rl, err := rlArg(c, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ text, err := c.StringArg(1)
+ if err != nil {
+ return nil, err
+ }
+
+ rl.insert([]rune(text))
+
+ return c.Next(), nil
+}
+
+// #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 {
+ return nil, err
+ }
+
+ rl, err := rlArg(c, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ inp, err := rl.Readline()
+ if err == EOF {
+ fmt.Println("")
+ return nil, io.EOF
+ } else if err != nil {
+ return nil, err
+ }
+
+ return c.PushingNext1(t.Runtime, rt.StringValue(inp)), nil
+}
+
+// #member
+// setVimRegister(register, text)
+// Sets the vim register at `register` to hold the passed text.
+// #param register string
+// #param text string
+func rlSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
+ if err := c.CheckNArgs(3); err != nil {
+ return nil, err
+ }
+
+ rl, err := rlArg(c, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ register, err := c.StringArg(1)
+ if err != nil {
+ return nil, err
+ }
+
+ text, err := c.StringArg(2)
+ if err != nil {
+ return nil, err
+ }
+
+ rl.SetRegisterBuf(register, []rune(text))
+
+ return c.Next(), nil
+}
+
+// #member
+// getVimRegister(register) -> string
+// Returns the text that is at the register.
+// #param register string
+func rlGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
+ if err := c.CheckNArgs(2); err != nil {
+ return nil, err
+ }
+
+ rl, err := rlArg(c, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ register, err := c.StringArg(1)
+ if err != nil {
+ return nil, err
+ }
+
+ buf := rl.GetFromRegister(register)
+
+ return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
+}
+
+// #member
+// getLine() -> string
+// Returns the current input line.
+// #returns string
+func rlGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
+ if err := c.Check1Arg(); err != nil {
+ return nil, err
+ }
+
+ rl, err := rlArg(c, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ buf := rl.GetLine()
+
+ return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
+}
+
+// #member
+// getChar() -> string
+// Reads a keystroke from the user. This is in a format of something like Ctrl-L.
+func rlReadChar(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
+ if err := c.Check1Arg(); err != nil {
+ return nil, err
+ }
+
+ rl, err := rlArg(c, 0)
+ if err != nil {
+ return nil, err
+ }
+ buf := rl.ReadChar()
+
+ return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
+}
+
+// #member
+// deleteByAmount(amount)
+// Deletes characters in the line by the given amount.
+// #param amount number
+func rlDeleteByAmount(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
+ if err := c.CheckNArgs(2); err != nil {
+ return nil, err
+ }
+
+ rl, err := rlArg(c, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ amount, err := c.IntArg(1)
+ if err != nil {
+ return nil, err
+ }
+
+ rl.DeleteByAmount(int(amount))
+
+ return c.Next(), nil
+}
+
+// #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 {
+ return nil, err
+ }
+
+ rl, err := rlArg(c, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ logText, err := c.StringArg(1)
+ if err != nil {
+ return nil, err
+ }
+
+ rl.RefreshPromptLog(logText)
+
+ return c.Next(), nil
+}
+
+func rlArg(c *rt.GoCont, arg int) (*Readline, error) {
+ j, ok := valueToRl(c.Arg(arg))
+ if !ok {
+ return nil, fmt.Errorf("#%d must be a readline", arg+1)
+ }
+
+ return j, nil
+}
+
+func valueToRl(val rt.Value) (*Readline, bool) {
+ u, ok := val.TryUserData()
+ if !ok {
+ return nil, false
+ }
+
+ j, ok := u.Value().(*Readline)
+ return j, ok
+}
+
+func rlUserData(rtm *rt.Runtime, rl *Readline) *rt.UserData {
+ rlMeta := rtm.Registry(rlMetaKey)
+ return rt.NewUserData(rl, rlMeta.AsTable())
+}
diff --git a/readline/prompt.go b/golibs/readline/prompt.go
similarity index 91%
rename from readline/prompt.go
rename to golibs/readline/prompt.go
index d141cd63..09af1623 100644
--- a/readline/prompt.go
+++ b/golibs/readline/prompt.go
@@ -8,20 +8,20 @@ import (
// SetPrompt will define the readline prompt string.
// 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.computePrompt()
}
// SetRightPrompt sets the right prompt.
-func (rl *Instance) SetRightPrompt(s string) {
+func (rl *Readline) SetRightPrompt(s string) {
rl.rightPrompt = s + " "
rl.computePrompt()
}
// 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.
-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.
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.
-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.
// Prompt data intependent
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.
// @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.
-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.
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,
// 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.MultilinePrompt != "" {
rl.realPrompt = []rune(rl.MultilinePrompt)
@@ -190,11 +190,11 @@ func (rl *Instance) computePrompt() (prompt []rune) {
// Strip color escapes
rl.promptLen = getRealLength(string(rl.realPrompt))
rl.rightPromptLen = getRealLength(string(rl.rightPrompt))
-
+
return
}
-func (rl *Instance) colorizeVimPrompt(p []rune) (cp []rune) {
+func (rl *Readline) colorizeVimPrompt(p []rune) (cp []rune) {
if rl.VimModeColorize {
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))
}
-func (rl *Instance) echoRightPrompt() {
- if rl.fullX < GetTermWidth() - rl.rightPromptLen - 1 {
+func (rl *Readline) echoRightPrompt() {
+ if rl.fullX < GetTermWidth()-rl.rightPromptLen-1 {
moveCursorForwards(GetTermWidth())
moveCursorBackwards(rl.rightPromptLen)
print(rl.rightPrompt)
diff --git a/readline/raw_bsd.go b/golibs/readline/raw_bsd.go
similarity index 100%
rename from readline/raw_bsd.go
rename to golibs/readline/raw_bsd.go
diff --git a/readline/raw_linux.go b/golibs/readline/raw_linux.go
similarity index 100%
rename from readline/raw_linux.go
rename to golibs/readline/raw_linux.go
diff --git a/readline/raw_plan9.go b/golibs/readline/raw_plan9.go
similarity index 81%
rename from readline/raw_plan9.go
rename to golibs/readline/raw_plan9.go
index 2736629f..583d61d1 100644
--- a/readline/raw_plan9.go
+++ b/golibs/readline/raw_plan9.go
@@ -2,16 +2,6 @@
// Use of this source code is governed by a BSD-style
// 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
import (
diff --git a/readline/raw_solaris.go b/golibs/readline/raw_solaris.go
similarity index 100%
rename from readline/raw_solaris.go
rename to golibs/readline/raw_solaris.go
diff --git a/readline/raw_unix.go b/golibs/readline/raw_unix.go
similarity index 100%
rename from readline/raw_unix.go
rename to golibs/readline/raw_unix.go
diff --git a/readline/raw_windows.go b/golibs/readline/raw_windows.go
similarity index 85%
rename from readline/raw_windows.go
rename to golibs/readline/raw_windows.go
index 50d29f37..6e8c61cb 100644
--- a/readline/raw_windows.go
+++ b/golibs/readline/raw_windows.go
@@ -2,18 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go: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
import (
@@ -70,4 +61,3 @@ func GetSize(fd int) (width, height int, err error) {
}
return int(info.Size.X), int(info.Size.Y), nil
}
-
diff --git a/readline/readline.go b/golibs/readline/readline.go
similarity index 98%
rename from readline/readline.go
rename to golibs/readline/readline.go
index 7282071b..889a2f5f 100644
--- a/readline/readline.go
+++ b/golibs/readline/readline.go
@@ -13,7 +13,7 @@ var rxMultiline = regexp.MustCompile(`[\r\n]+`)
// Readline displays the readline prompt.
// 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())
state, err := MakeRaw(fd)
if err != nil {
@@ -183,7 +183,7 @@ func (rl *Instance) Readline() (string, error) {
if rl.modeTabFind {
rl.backspaceTabFind()
} else {
- if (rl.pos < len(rl.line)) {
+ if rl.pos < len(rl.line) {
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
// entry readline is currently configured for and then update the line entries
// accordingly.
-func (rl *Instance) editorInput(r []rune) {
+func (rl *Readline) editorInput(r []rune) {
if len(r) == 0 {
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
// 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,
// 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) {
// Vim escape sequences & dispatching --------------------------------------------------------
case string(charEscape):
@@ -755,11 +755,11 @@ func (rl *Instance) escapeSeq(r []rune) {
rl.updateHelpers()
return
- case seqDelete,seqDelete2:
+ case seqDelete, seqDelete2:
if rl.modeTabFind {
rl.backspaceTabFind()
} else {
- if (rl.pos < len(rl.line)) {
+ if rl.pos < len(rl.line) {
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.updateHelpers()
rl.clearHelpers()
@@ -924,7 +924,7 @@ func isMultiline(r []rune) bool {
return false
}
-func (rl *Instance) allowMultiline(data []byte) bool {
+func (rl *Readline) allowMultiline(data []byte) bool {
rl.clearHelpers()
printf("\r\nWARNING: %d bytes of multiline data was dumped into the shell!", len(data))
for {
diff --git a/readline/register.go b/golibs/readline/register.go
similarity index 95%
rename from readline/register.go
rename to golibs/readline/register.go
index 2edf1fab..a20fbcad 100644
--- a/readline/register.go
+++ b/golibs/readline/register.go
@@ -23,7 +23,7 @@ type registers struct {
mutex *sync.Mutex
}
-func (rl *Instance) initRegisters() {
+func (rl *Readline) initRegisters() {
rl.registers = ®isters{
num: make(map[int][]rune, 10),
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.
// It's the same as saveToRegisterTokenize, but without the need to generate tokenized &
// 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.
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
// 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.
-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.
// 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,
// let the caller pass directly this buffer, yet relying on the register system to
// 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)
}
-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.
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
// 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,
defer rl.registers.resetRegister()
@@ -155,7 +155,7 @@ func (rl *Instance) pasteFromRegister() (buffer []rune) {
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.
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.
-func (rl *Instance) completeRegisters() (groups []*CompletionGroup) {
+func (rl *Readline) completeRegisters() (groups []*CompletionGroup) {
// We set the info exceptionally
info := BLUE + "-- registers --" + RESET
diff --git a/readline/syntax.go b/golibs/readline/syntax.go
similarity index 90%
rename from readline/syntax.go
rename to golibs/readline/syntax.go
index a64becef..388d9da9 100644
--- a/readline/syntax.go
+++ b/golibs/readline/syntax.go
@@ -2,7 +2,7 @@ package readline
// syntaxCompletion - applies syntax highlighting to the current input line.
// nothing special to note here, nor any changes envisioned.
-func (rl *Instance) syntaxCompletion() {
+func (rl *Readline) syntaxCompletion() {
if rl.SyntaxCompleter == nil {
return
}
diff --git a/readline/tab-virtual.go b/golibs/readline/tab-virtual.go
similarity index 95%
rename from readline/tab-virtual.go
rename to golibs/readline/tab-virtual.go
index d1e1d764..e8d95138 100644
--- a/readline/tab-virtual.go
+++ b/golibs/readline/tab-virtual.go
@@ -2,13 +2,14 @@ package readline
import (
"strings"
+
"github.com/rivo/uniseg"
)
// 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
// 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 {
// I don't really understand why `0` is creaping in at the end of the
// 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.
// This candidate might either be the currently selected one (white frame),
// or the only candidate available, if the total number of candidates is 1.
-func (rl *Instance) insertCandidate() {
+func (rl *Readline) insertCandidate() {
cur := rl.getCurrentGroup()
@@ -83,7 +84,7 @@ func (rl *Instance) insertCandidate() {
}
// updateVirtualComp - Either insert the current completion candidate virtually, or on the real line.
-func (rl *Instance) updateVirtualComp() {
+func (rl *Readline) updateVirtualComp() {
cur := rl.getCurrentGroup()
if cur != nil {
@@ -118,7 +119,7 @@ func (rl *Instance) updateVirtualComp() {
// 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 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.
// 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.
-func (rl *Instance) viDeleteByAdjustVirtual(adjust int) {
+func (rl *Readline) viDeleteByAdjustVirtual(adjust int) {
var (
newLine []rune
backOne bool
@@ -235,7 +236,7 @@ func (rl *Instance) viDeleteByAdjustVirtual(adjust int) {
}
// 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)
if len(split) == 0 {
return
@@ -258,7 +259,7 @@ func (rl *Instance) viJumpEVirtual(tokeniser func([]rune, int) ([]string, int, i
return
}
-func (rl *Instance) deleteVirtual() {
+func (rl *Readline) deleteVirtual() {
switch {
case len(rl.lineComp) == 0:
return
@@ -274,7 +275,7 @@ func (rl *Instance) deleteVirtual() {
// We are done with the current virtual completion candidate.
// Get ready for the next one
-func (rl *Instance) clearVirtualComp() {
+func (rl *Readline) clearVirtualComp() {
rl.line = rl.lineComp
rl.currentComp = []rune{}
rl.compAddSpace = false
diff --git a/readline/tab.go b/golibs/readline/tab.go
similarity index 93%
rename from readline/tab.go
rename to golibs/readline/tab.go
index f2cc1408..04b9e2bd 100644
--- a/readline/tab.go
+++ b/golibs/readline/tab.go
@@ -28,7 +28,7 @@ const (
// getTabCompletion - This root function sets up all completion items and engines,
// 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.
if rl.modeAutoFind && rl.searchMode == RegisterFind {
@@ -53,7 +53,7 @@ func (rl *Instance) getTabCompletion() {
}
// getRegisterCompletion - Populates and sets up completion for Vim registers.
-func (rl *Instance) getRegisterCompletion() {
+func (rl *Readline) getRegisterCompletion() {
rl.tcGroups = rl.completeRegisters()
if len(rl.tcGroups) == 0 {
@@ -84,7 +84,7 @@ func (rl *Instance) getRegisterCompletion() {
}
// 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.
rl.getCompletions()
@@ -94,7 +94,7 @@ func (rl *Instance) getTabSearchCompletion() {
rl.getCurrentGroup()
// 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 {
g.updateTabFind(rl)
@@ -107,7 +107,7 @@ func (rl *Instance) getTabSearchCompletion() {
}
// getHistorySearchCompletion - Populates and sets up completion for command history search
-func (rl *Instance) getHistorySearchCompletion() {
+func (rl *Readline) getHistorySearchCompletion() {
// Refresh full list each time
rl.tcGroups = rl.completeHistory()
@@ -142,7 +142,7 @@ func (rl *Instance) getHistorySearchCompletion() {
// getNormalCompletion - Populates and sets up completion for normal comp mode.
// 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
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,
// 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.
-func (rl *Instance) getCompletions() {
+func (rl *Readline) getCompletions() {
// If there is no wired tab completion engine, nothing we can do.
if rl.TabCompleter == nil {
@@ -214,7 +214,7 @@ func (rl *Instance) getCompletions() {
// moveTabCompletionHighlight - This function is in charge of
// 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
g := rl.getCurrentGroup()
@@ -253,7 +253,7 @@ func (rl *Instance) moveTabCompletionHighlight(x, y int) {
}
// writeTabCompletion - Prints all completion groups and their items
-func (rl *Instance) writeTabCompletion() {
+func (rl *Readline) writeTabCompletion() {
// The final completions string to print.
var completions string
@@ -289,7 +289,7 @@ func (rl *Instance) writeTabCompletion() {
// cropCompletions - When the user cycles through a completion list longer
// than the console MaxTabCompleterRows value, we crop the completions string
// 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 rl.tcUsedY < rl.MaxTabCompleterRows {
@@ -366,7 +366,7 @@ func (rl *Instance) cropCompletions(comps string) (cropped string, usedY int) {
return
}
-func (rl *Instance) getAbsPos() int {
+func (rl *Readline) getAbsPos() int {
var prev int
var foundCurrent bool
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
// 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)
if pos < 0 {
@@ -409,7 +409,7 @@ func (rl *Instance) getCompletionLine() (line []rune, pos int) {
return
}
-func (rl *Instance) getCurrentGroup() (group *CompletionGroup) {
+func (rl *Readline) getCurrentGroup() (group *CompletionGroup) {
for _, g := range rl.tcGroups {
if g.isCurrent && len(g.Suggestions) > 0 {
return g
@@ -431,7 +431,7 @@ func (rl *Instance) getCurrentGroup() (group *CompletionGroup) {
// cycleNextGroup - Finds either the first non-empty group,
// or the next non-empty group after the current one.
-func (rl *Instance) cycleNextGroup() {
+func (rl *Readline) cycleNextGroup() {
for i, g := range rl.tcGroups {
if g.isCurrent {
g.isCurrent = false
@@ -452,7 +452,7 @@ func (rl *Instance) cycleNextGroup() {
}
// cyclePreviousGroup - Same as cycleNextGroup but reverse
-func (rl *Instance) cyclePreviousGroup() {
+func (rl *Readline) cyclePreviousGroup() {
for i, g := range rl.tcGroups {
if g.isCurrent {
g.isCurrent = false
@@ -471,7 +471,7 @@ func (rl *Instance) cyclePreviousGroup() {
}
// Check if we have a single completion candidate
-func (rl *Instance) hasOneCandidate() bool {
+func (rl *Readline) hasOneCandidate() bool {
if len(rl.tcGroups) == 0 {
return false
}
@@ -509,7 +509,7 @@ func (rl *Instance) hasOneCandidate() bool {
// - The user-specified max completion length
// - The terminal lengh
// 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.compConfirmWait = true
@@ -518,7 +518,7 @@ func (rl *Instance) promptCompletionConfirm(sentence string) {
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 {
comps += len(group.Suggestions)
// if group.Name != "" {
@@ -535,7 +535,7 @@ func (rl *Instance) getCompletionCount() (comps int, lines int, adjusted int) {
return
}
-func (rl *Instance) resetTabCompletion() {
+func (rl *Readline) resetTabCompletion() {
rl.modeTabCompletion = false
rl.tabCompletionSelect = false
rl.compConfirmWait = false
diff --git a/readline/tabfind.go b/golibs/readline/tabfind.go
similarity index 90%
rename from readline/tabfind.go
rename to golibs/readline/tabfind.go
index 3e463126..9d6aba91 100644
--- a/readline/tabfind.go
+++ b/golibs/readline/tabfind.go
@@ -12,7 +12,7 @@ const (
RegisterFind
)
-func (rl *Instance) backspaceTabFind() {
+func (rl *Readline) backspaceTabFind() {
if len(rl.tfLine) > 0 {
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
// 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...)
@@ -34,7 +34,7 @@ func (rl *Instance) updateTabFind(r []rune) {
rl.renderHelpers()
}
-func (rl *Instance) resetTabFind() {
+func (rl *Readline) resetTabFind() {
rl.modeTabFind = false
// rl.modeAutoFind = false // Added, because otherwise it gets stuck on search completions
diff --git a/readline/term.go b/golibs/readline/term.go
similarity index 100%
rename from readline/term.go
rename to golibs/readline/term.go
diff --git a/readline/timer.go b/golibs/readline/timer.go
similarity index 98%
rename from readline/timer.go
rename to golibs/readline/timer.go
index 76eab23a..0e5f9f0f 100644
--- a/readline/timer.go
+++ b/golibs/readline/timer.go
@@ -7,12 +7,12 @@ import (
// DelayedTabContext is a custom context interface for async updates to the tab completions
type DelayedTabContext struct {
- rl *Instance
+ rl *Readline
Context context.Context
cancel context.CancelFunc
}
-func delayedSyntaxTimer(rl *Instance, i int64) {
+func delayedSyntaxTimer(rl *Readline, i int64) {
if rl.PasswordMask != 0 || rl.DelayedSyntaxWorker == nil {
return
}
diff --git a/readline/tokenise.go b/golibs/readline/tokenise.go
similarity index 100%
rename from readline/tokenise.go
rename to golibs/readline/tokenise.go
diff --git a/readline/tui-effects.go b/golibs/readline/tui-effects.go
similarity index 100%
rename from readline/tui-effects.go
rename to golibs/readline/tui-effects.go
diff --git a/readline/undo.go b/golibs/readline/undo.go
similarity index 89%
rename from readline/undo.go
rename to golibs/readline/undo.go
index 9b5e55a1..6d8d5bd2 100644
--- a/readline/undo.go
+++ b/golibs/readline/undo.go
@@ -5,7 +5,7 @@ type undoItem struct {
pos int
}
-func (rl *Instance) undoAppendHistory() {
+func (rl *Readline) undoAppendHistory() {
defer func() { rl.viUndoSkipAppend = false }()
if rl.viUndoSkipAppend {
@@ -18,7 +18,7 @@ func (rl *Instance) undoAppendHistory() {
})
}
-func (rl *Instance) undoLast() {
+func (rl *Readline) undoLast() {
var undo undoItem
for {
if len(rl.viUndoHistory) == 0 {
diff --git a/readline/update.go b/golibs/readline/update.go
similarity index 93%
rename from readline/update.go
rename to golibs/readline/update.go
index 0538aadc..59f41a6f 100644
--- a/readline/update.go
+++ b/golibs/readline/update.go
@@ -10,7 +10,7 @@ import (
// updateHelpers is a key part of the whole refresh process:
// it should coordinate reprinting the input line, any Infos and completions
// and manage to get back to the current (computed) cursor coordinates
-func (rl *Instance) updateHelpers() {
+func (rl *Readline) updateHelpers() {
print(seqHideCursor)
// Load all Infos & completions before anything.
// 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 {
rl.getTabCompletion()
} else {
- if rl.completionOpen { rl.completionOpen = false }
+ if rl.completionOpen {
+ rl.completionOpen = false
+ }
}
// 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)
-func (rl *Instance) updateReferences() {
+func (rl *Readline) updateReferences() {
// We always need to work with clean data,
// 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
// Now reset all below-input helpers
@@ -113,7 +115,7 @@ func (rl *Instance) resetHelpers() {
// clearHelpers - Clears everything: prompt, input, Infos & comps,
// and comes back at the prompt.
-func (rl *Instance) clearHelpers() {
+func (rl *Readline) clearHelpers() {
// Now go down to the last line of input
moveCursorDown(rl.fullY - rl.posY)
@@ -132,7 +134,7 @@ func (rl *Instance) clearHelpers() {
// renderHelpers - pritns all components (prompt, line, Infos & comps)
// and replaces the cursor to its current position. This function never
// 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
// input for it to be aligned properly
@@ -197,14 +199,14 @@ func (rl *Instance) renderHelpers() {
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...)
}
-func (rl *Instance) bufprint(text string) {
+func (rl *Readline) bufprint(text string) {
fmt.Fprint(rl.bufferedOut, text)
}
-func (rl *Instance) bufflush() {
+func (rl *Readline) bufflush() {
rl.bufferedOut.Flush()
}
diff --git a/readline/vim.go b/golibs/readline/vim.go
similarity index 95%
rename from readline/vim.go
rename to golibs/readline/vim.go
index d496705c..5e2477a5 100644
--- a/readline/vim.go
+++ b/golibs/readline/vim.go
@@ -34,6 +34,7 @@ var (
)
type ViAction int
+
const (
VimActionYank = iota
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
// 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.
-func (rl *Instance) vi(r rune) {
+func (rl *Readline) vi(r rune) {
activeRegister := string(rl.registers.currentRegister)
// 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)
if i < 1 {
i = 1
@@ -393,7 +394,7 @@ func (rl *Instance) getViIterations() int {
return i
}
-func (rl *Instance) refreshVimStatus() {
+func (rl *Readline) refreshVimStatus() {
rl.ViModeCallback(rl.modeViMode)
rl.computePrompt()
rl.updateHelpers()
@@ -401,7 +402,7 @@ func (rl *Instance) refreshVimStatus() {
// 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.
-func (rl *Instance) viInfoMessage() {
+func (rl *Readline) viInfoMessage() {
switch rl.modeViMode {
case VimKeys:
rl.infoText = []rune("-- VIM KEYS -- (press `i` to return to normal editing mode)")
@@ -421,7 +422,7 @@ func (rl *Instance) viInfoMessage() {
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)
switch {
case len(split) == 0:
@@ -436,7 +437,7 @@ func (rl *Instance) viJumpB(tokeniser tokeniser) (adjust int) {
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)
if len(split) == 0 {
return
@@ -459,7 +460,7 @@ func (rl *Instance) viJumpE(tokeniser tokeniser) (adjust int) {
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)
switch {
case len(split) == 0:
@@ -472,7 +473,7 @@ func (rl *Instance) viJumpW(tokeniser tokeniser) (adjust int) {
return
}
-func (rl *Instance) viJumpPreviousBrace() (adjust int) {
+func (rl *Readline) viJumpPreviousBrace() (adjust int) {
if rl.pos == 0 {
return 0
}
@@ -486,7 +487,7 @@ func (rl *Instance) viJumpPreviousBrace() (adjust int) {
return 0
}
-func (rl *Instance) viJumpNextBrace() (adjust int) {
+func (rl *Readline) viJumpNextBrace() (adjust int) {
if rl.pos >= len(rl.line)-1 {
return 0
}
@@ -500,7 +501,7 @@ func (rl *Instance) viJumpNextBrace() (adjust int) {
return 0
}
-func (rl *Instance) viJumpBracket() (adjust int) {
+func (rl *Readline) viJumpBracket() (adjust int) {
split, index, pos := tokeniseBrackets(rl.line, rl.pos)
switch {
case len(split) == 0:
diff --git a/readline/vimdelete.go b/golibs/readline/vimdelete.go
similarity index 95%
rename from readline/vimdelete.go
rename to golibs/readline/vimdelete.go
index f5c1806c..54573d28 100644
--- a/readline/vimdelete.go
+++ b/golibs/readline/vimdelete.go
@@ -5,7 +5,7 @@ import (
)
// vimDelete -
-func (rl *Instance) viDelete(r rune) {
+func (rl *Readline) viDelete(r rune) {
// 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
@@ -91,7 +91,7 @@ func (rl *Instance) viDelete(r rune) {
}
}
-func (rl *Instance) viDeleteByAdjust(adjust int) {
+func (rl *Readline) viDeleteByAdjust(adjust int) {
var (
newLine []rune
backOne bool
@@ -142,11 +142,11 @@ func (rl *Instance) viDeleteByAdjust(adjust int) {
rl.updateHelpers()
}
-func (rl *Instance) DeleteByAmount(adjust int) {
+func (rl *Readline) DeleteByAmount(adjust int) {
rl.viDeleteByAdjust(adjust)
}
-func (rl *Instance) vimDeleteToken(r rune) bool {
+func (rl *Readline) vimDeleteToken(r rune) bool {
tokens, _, _ := tokeniseSplitSpaces(rl.line, 0)
pos := int(r) - 48 // convert ASCII to integer
if pos > len(tokens) {
diff --git a/readline/wrap.go b/golibs/readline/wrap.go
similarity index 100%
rename from readline/wrap.go
rename to golibs/readline/wrap.go
diff --git a/lua.go b/lua.go
index 3a28f096..3a51eaaf 100644
--- a/lua.go
+++ b/lua.go
@@ -80,6 +80,7 @@ func loadLibs(r *rt.Runtime) {
cmds = commander.New(r)
lib.LoadLibs(r, cmds.Loader)
+ lib.LoadLibs(l, lr.rl.Loader)
}
func yarnloadLibs(r *rt.Runtime) {
@@ -89,6 +90,13 @@ func yarnloadLibs(r *rt.Runtime) {
lib.LoadAll(r)
lib.LoadLibs(r, hilbishLoader)
+ lib.LoadLibs(r, hooks.Loader)
+ lib.LoadLibs(r, fs.Loader)
+ lib.LoadLibs(r, terminal.Loader)
+ lib.LoadLibs(r, snail.Loader)
+ lib.LoadLibs(r, cmds.Loader)
+ lib.LoadLibs(l, lr.rl.Loader)
+
}
func runConfig(confpath string) {
diff --git a/nature/editor.lua b/nature/editor.lua
new file mode 100644
index 00000000..002d3409
--- /dev/null
+++ b/nature/editor.lua
@@ -0,0 +1,28 @@
+local readline = require 'readline'
+
+local editor = readline.new()
+local editorMt = {}
+
+hilbish.editor = {}
+
+local function contains(search, needle)
+ for _, p in ipairs(search) do
+ if p == needle then
+ return true
+ end
+ end
+
+ return false
+end
+
+function editorMt.__index(_, key)
+ if contains({'deleteByAmount', 'getVimRegister', 'getLine', 'insert', 'readChar', 'setVimRegister'}, key) then
+ --editor:log 'The calling method of this function has changed. Please use the colon to call this hilbish.editor function.'
+ end
+
+ return function(...)
+ return editor[key](editor, ...)
+ end
+end
+
+setmetatable(hilbish.editor, editorMt)
diff --git a/nature/init.lua b/nature/init.lua
index 9b75a271..4f973f33 100644
--- a/nature/init.lua
+++ b/nature/init.lua
@@ -28,6 +28,7 @@ require 'nature.vim'
require 'nature.runner'
require 'nature.hummingbird'
require 'nature.abbr'
+require 'nature.editor'
local shlvl = tonumber(os.getenv 'SHLVL')
if shlvl ~= nil then
diff --git a/readline/godoc.go b/readline/godoc.go
deleted file mode 100644
index ecea2bd5..00000000
--- a/readline/godoc.go
+++ /dev/null
@@ -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
diff --git a/readline/raw/LICENSE b/readline/raw/LICENSE
deleted file mode 100644
index 261eeb9e..00000000
--- a/readline/raw/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/readline/raw/README.md b/readline/raw/README.md
deleted file mode 100644
index 2ef9aadb..00000000
--- a/readline/raw/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# raw
-
-This command exists here purely as a lazy feature for me to scan key presses
-for their corresponding escape codes. It is a useful dev tool for rationalizing
-what is happening in the different terminal emulators (since documentation
-regarding what escape codes they send can often be non-existent and some of the
-more exotic key combinations or modern keyboard functions can have multiple
-published standards.
-
-This package is not imported by `readline` and is not required as part of `readline`
\ No newline at end of file
diff --git a/readline/raw/main.go b/readline/raw/main.go
deleted file mode 100644
index eff84406..00000000
--- a/readline/raw/main.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package main
-
-import (
- "fmt"
- "os"
-
- "github.com/maxlandon/readline"
-)
-
-func main() {
- readline.MakeRaw(int(os.Stdin.Fd()))
-
- for {
- b := make([]byte, 1024)
- i, err := os.Stdin.Read(b)
- if err != nil {
- panic(err)
- }
-
- fmt.Println(b[:i])
- }
-}
diff --git a/rl.go b/rl.go
index b431ad7d..e316967e 100644
--- a/rl.go
+++ b/rl.go
@@ -13,9 +13,10 @@ import (
)
type lineReader struct {
- rl *readline.Instance
+ rl *readline.Readline
fileHist *fileHistory
}
+
var hinter *rt.Closure
var highlighter *rt.Closure
@@ -54,58 +55,64 @@ func newLineReader(prompt string, noHist bool) *lineReader {
rl.ViModeCallback = func(mode readline.ViMode) {
modeStr := ""
switch mode {
- case readline.VimKeys: modeStr = "normal"
- case readline.VimInsert: modeStr = "insert"
- case readline.VimDelete: modeStr = "delete"
- case readline.VimReplaceOnce, readline.VimReplaceMany: modeStr = "replace"
+ case readline.VimKeys:
+ modeStr = "normal"
+ case readline.VimInsert:
+ modeStr = "insert"
+ case readline.VimDelete:
+ modeStr = "delete"
+ case readline.VimReplaceOnce, readline.VimReplaceMany:
+ modeStr = "replace"
}
setVimMode(modeStr)
}
rl.ViActionCallback = func(action readline.ViAction, args []string) {
actionStr := ""
switch action {
- case readline.VimActionPaste: actionStr = "paste"
- case readline.VimActionYank: actionStr = "yank"
+ case readline.VimActionPaste:
+ actionStr = "paste"
+ case readline.VimActionYank:
+ actionStr = "yank"
}
hooks.Emit("hilbish.vimAction", actionStr, args)
}
rl.HintText = func(line []rune, pos int) []rune {
hinter := hshMod.Get(rt.StringValue("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 {
fmt.Println(err)
return []rune{}
}
-
+
hintText := ""
if luaStr, ok := retVal.TryString(); ok {
hintText = luaStr
}
-
+
return []rune(hintText)
}
rl.SyntaxHighlighter = func(line []rune) string {
highlighter := hshMod.Get(rt.StringValue("highlighter"))
retVal, err := rt.Call1(l.MainThread(), highlighter,
- rt.StringValue(string(line)))
+ rt.StringValue(string(line)))
if err != nil {
fmt.Println(err)
return string(line)
}
-
+
highlighted := ""
if luaStr, ok := retVal.TryString(); ok {
highlighted = luaStr
}
-
+
return highlighted
}
rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) {
term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 2, false)
compHandle := hshMod.Get(rt.StringValue("completion")).AsTable().Get(rt.StringValue("handler"))
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
if err != nil {
@@ -175,11 +182,11 @@ func newLineReader(prompt string, noHist bool) *lineReader {
itemAliases[itemName] = itemAlias
} else if keytyp == rt.IntType {
vlStr, ok := lval.TryString()
- if !ok {
- // TODO: error
- return
- }
- items = append(items, vlStr)
+ if !ok {
+ // TODO: error
+ return
+ }
+ items = append(items, vlStr)
} else {
// TODO: error
return
@@ -188,20 +195,22 @@ func newLineReader(prompt string, noHist bool) *lineReader {
var dispType readline.TabDisplayType
switch luaCompType.AsString() {
- case "grid": dispType = readline.TabDisplayGrid
- case "list": dispType = readline.TabDisplayList
+ case "grid":
+ dispType = readline.TabDisplayGrid
+ case "list":
+ dispType = readline.TabDisplayList
// need special cases, will implement later
//case "map": dispType = readline.TabDisplayMap
}
compGroups = append(compGroups, &readline.CompletionGroup{
- DisplayType: dispType,
- Aliases: itemAliases,
+ DisplayType: dispType,
+ Aliases: itemAliases,
Descriptions: itemDescriptions,
ItemDisplays: itemDisplays,
- Suggestions: items,
- TrimSlash: false,
- NoSpace: true,
+ Suggestions: items,
+ TrimSlash: false,
+ NoSpace: true,
})
})
@@ -227,8 +236,8 @@ func (lr *lineReader) SetPrompt(p string) {
halfPrompt := strings.Split(p, "\n")
if len(halfPrompt) > 1 {
lr.rl.Multiline = true
- lr.rl.SetPrompt(strings.Join(halfPrompt[:len(halfPrompt) - 1], "\n"))
- lr.rl.MultilinePrompt = halfPrompt[len(halfPrompt) - 1:][0]
+ lr.rl.SetPrompt(strings.Join(halfPrompt[:len(halfPrompt)-1], "\n"))
+ lr.rl.MultilinePrompt = halfPrompt[len(halfPrompt)-1:][0]
} else {
lr.rl.Multiline = false
lr.rl.MultilinePrompt = ""
@@ -260,16 +269,16 @@ func (lr *lineReader) Resize() {
// #interface history
// command history
-// The history interface deals with command history.
+// The history interface deals with command history.
// This includes the ability to override functions to change the main
// method of saving history.
func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table {
lrLua := map[string]util.LuaExport{
- "add": {lr.luaAddHistory, 1, false},
- "all": {lr.luaAllHistory, 0, false},
+ "add": {lr.luaAddHistory, 1, false},
+ "all": {lr.luaAllHistory, 0, false},
"clear": {lr.luaClearHistory, 0, false},
- "get": {lr.luaGetHistory, 1, false},
- "size": {lr.luaSize, 0, false},
+ "get": {lr.luaGetHistory, 1, false},
+ "size": {lr.luaSize, 0, false},
}
mod := rt.NewTable()