greadline
A module for the
V programming language
Version 0.5
Installation
$ v install edam.greadline
Usage
import edam.greadline
fn main() {
text := greadline.readline('Enter text> ') or { return }
println("You typed: ${text}")
}
History File
To add history, simply load the file and that will set the filename used in subsequent calls, such as writing history, which you will want to defer to happen at program termination:
greadline.history_file_read(filename)!
defer {
greadline.history_file_write() or {}
}
Note: it is not an error when
history_file_read()
os.exists()
You can set a sensible limit on the size of the history file. This will
immediately truncate the history file (if it exceeds the limit) and will affect
subsequent calls to
history_file_write()
history_file_append()
greadline.set_history_file_limit(1000)!
Initialisation
GNU readline will initialise with the user's (or system) readline init file
anyway. But in addition you can use
greadline.read_init_file(filename)
Alternatively, use
greadline.parse_and_bind(line)
Completion
"Tab completion" can be controlled by providing a completion function. The completion function is passed the word at (or before) the cursor and returns all possible completions.
greadline.set_completion_fn(fn(word string) []string {
return commands.filter(it.starts_with(word))
})
Completion can also be set back to the default (i.e., filenames) or turned off completely.
greadline.set_completion_default()
greadline.set_completion_off()
Multiple Prompts
For different prompts, you may want different histories and completion rules.
TODO
Manipulating Line Buffer
In addition to the builtin named functions in GNU readline (available via key bindings), you can add your own named functions and set key bindings for them, so that the user can trigger them while entering text.
greadline.add_bindable_fn('star-first-chars', star_first_n_chars)
greadline.parse_and_bind('Control-g: star-first-chars')!
In your bindable function, you can then modify the line input buffer in various ways.
pos := greadline.point() // get current cursor position
greadline.set_point(5) // set cursor position
greadline.insert_text("hi") // insert 2 chars at the cursor
greadline.delete_text(5, 7) // delete 2 chars at buffer offset 5
length := greadline.length() // get input text length
mark := greadline.mark() or { -1 } // get mark (selection offset) if set
greadline.set_mark(0) // set mark (selection) to offset 0
greadline.clear_mark() // clear mark (unselect)
Or you can modify the whole input line buffer in one go and readine will try to preserve point (the cursor position). Here is the function we added above...
fn star_first_n_chars(count int, _ greadline.Key) bool {
line := greadline.line_buffer() // get the input line
count_ := math.min(math.max(count, 0), line.len)
line_ := '*'.repeat(count_) + line[count_..line.len]
greadline.set_line_buffer(line_) // set the new input line
return true // no error
}
Notes on bindable functions:
-
count
is a an argument that the user can pass to the function while editing the line (like in Emacs) and it defaults to 1. It is typically used as a "repeat count". - The second argument (type
Key
) should not be used. (It will be added in the future, but the definition of Key
will change.) - Returns false on error.
Development
Testing the module
$ v test .
Changes
0.1 - Initial verison
0.2 - Support more of API, bug fixes, API consistency, more tests
0.3 - Added completion and custom bindable functions, bug fixes
0.4 - Fixed file name
0.5 - Fixed warnings
Licence
Copyright (C) 2023 Tim Marston [email protected]