movingtomars / liner Goto Github PK
View Code? Open in Web Editor NEWA readline-like library in Rust.
License: MIT License
A readline-like library in Rust.
License: MIT License
deny processing of inputs by boolean result, basically:
use liner::{Event, Context};
use termion::event::Key;
fn main() {
let mut line = Context::new();
line.read_line("$ ", &mut |Event {editor, kind}| -> bool {
match kind {
EventKind::BeforeKey(Key::Char(c)) => !c.is_whitespace(),
_ => true
}
});
}
output:
./target/release/liner-test
$
# <SPACE> <SPACE> <SPACE> <SPACE>
$
# <SPACE> <SPACE> <SPACE> a <SPACE> b <SPACE> c <SPACE>
$ abc
i accomplished this currently, and terribly with Editor::undo
.
I've noticed for a while now that liner has a bug where if a command exceeds a certain number of characters, it begins shifting the current line upwards by one line each time a key is entered until it is at the top of the terminal.
Right now, we pass closures to the Context/Editor to process Events.
Would something like this be better?
pub trait EventHandler {
fn handle_before_key_press(&mut self, key: Key) {}
fn handle_after_key_press(&mut self, key: Key) {}
...
}
If you wouldn't mind, I'd like to see a bytes()
iterator for the Buffer
structure. Perhaps even a to_string()
and as_bytes()
method too. Basically, when writing data to an output, it needs to be in a raw &[u8]
format.
Currently, compiling liner emits several warnings, which should be fixed.
Hey @MovingtoMars, please bump the version and cargo publish
so we can get the Ctrl-C implementation from crates.io
Bash has a functionality to search in the history, triggered by the ctrl-r key combination.
https://www.gnu.org/software/bash/manual/html_node/Readline-vi-Mode.html
Not sure how desired this feature is.
Fish has this nice feature of suggesting commands from history without pressing Ctrl+R every time:
https://fishshell.com/docs/current/tutorial.html#tut_autosuggestions
Would you like to have something like this implemented in liner?
So I currently have a limited form of multiline comments / commands in the Ion shell which works great for scripts, but for the REPL there is no means of going back and editing previous lines that have been entered, but not yet submitted for completion. Does liner currently support this feature in some way?
I came from the redox-os project which uses liner in ion, and I tried to run set -o vi and found this.
Im working on this thing, I forked it and I think I fixed it: :crossed_fingers: . You probably should check my work because I have no idea how this project works.
My fork is here: https://github.com/liamnaddell/liner
Should I pr?
I also "added" two more tests that use ctrl+[ instead of Esc
echo '\
one'
produces similar behavior to #45, the suggestion is repeatedly printed, creating new lines
liner doesn't move the cursor to the next line when the end of a line is reached. Instead it stays on the current line for an extra character, then moves to the second character of the next line. This can be seen while typing at the end of a line, or while moving character by character across line boundaries with the arrow keys.
It would be nice if liner could automatically unindent by a specified number of characters when specific keywords are found, such as else
and end
, in order to replicate the nice feature found in Fish.
I think handling keyboard signals correctly is a key feature for liner to have, but don't know what work is required.
The active repository is at https://gitlab.redox-os.org/redox-os/liner
Dependent on ticki/termion#46
in fish, ctrl+w doesn't kill the entire word (string of non-whitespace), it kills up to the closest slash. requesting this feature.
I have this
pub fn read_enumerated(message: &str, choices: &[&str], default_idx: usize) -> Result<String> {
let mut ctx = Context::new();
let mut idx: usize = default_idx;
let buff = choices[idx].to_owned();
let hints: HashMap<char, usize> = choices.iter()
.enumerate()
.map(|(idx, choice)| (choice.chars().next().unwrap(), idx))
.into_iter()
.collect();
ctx.read_line_with_init_buffer(format!("{} (TAB or arrows to select {:?}): ",
message,
choices),
&mut |Event { editor, kind }| {
if let EventKind::AfterKey(key) = kind {
match key {
// hitting enter should still work as expected
Key::Char('\n') => return,
// change to the next choice, wrap around to the 1st choice
Key::Left | Key::Down | Key::Char('\t') => {
idx = if idx == 0 { choices.len() - 1 } else { idx - 1 }
}
// change to the previous choice, wrap around to the last choice
Key::Right | Key::Up => {
idx = if idx + 1 == choices.len() { 0 } else { idx + 1 }
}
// change to the choice matching the initial char
Key::Char(key) => {
if hints.contains_key(&key) {
idx = hints[&key];
}
}
// anything other than tab, arrows or hints should do nothing
_ => {},
}
let new_choice: Vec<char> = choices[idx].chars().collect();
editor.move_cursor_to_end_of_line().unwrap();
editor.delete_all_before_cursor().unwrap();
editor.insert_chars_after_cursor(&new_choice).unwrap();
}
},
buff)?;
Ok(choices[idx].to_owned())
}
When I use it with 0.4.1, though, instead of re-drawing the line in place, is like some extraneous new lines are being introduced. I am trying to track this down, if I can figure it out, I will submit a PR. I thought you might have an idea or at least be able to reproduce using my code.
when scrolling through the history, i'd like to skip duplicate entries
Because there's a Go library also named liner, I feel that this library should have a name change.
I have a cli I am writing that saves some data to file based on an initial run. Re-running loads that file, it would be nice to present the saved values for editing. Other than re-implementing some internal functions of Context, I cannot see a way to do this really. If I could pass my own buffer into an additional read_line method, that would very nicely do it. I'll see if I can come up with a quick PR that demonstrates what I mean.
in bash, <Esc>.
, and <Alt>.
insert the last argument of the previous command; multiple presses go further into the history. requesting this feature.
I've been asked if we could implement the ability to customize tab auto-completions to not just complete an entire command all at once when tabbed, but to also allow completing by words, one word per tab.
I've a question regarding the usage of liner within the Ion shell.
I've wired the context history to the completer here, but tab completions won't function after the first word is typed. Is there a way to go about having the completer work with multiple words typed in the terminal?
For example echo foo | ./target/debug/liner_test
gives:
thread 'main' panicked at 'called
Result::unwrap()
on anErr
value: Os { code: 25, kind: Other, message: "Inappropriate ioctl for device" }', libcore/result.rs:916:5
The problematic unwrap is at src/context.rs:95, which calls let stdout = stdout().into_raw_mode().unwrap();
This issue also causes redox-os/ion to panic with a redirected stdin.
See also: https://github.com/ticki/termion/issues/39
If you start liner_test
and type ls ..<tab>
, the program crashes with a panic, due to calling unwrap() on None.
redox-os/ion#283
https://github.com/MovingtoMars/liner/blob/master/src/editor.rs#L121
For my use case, at least, hard coding to zero breaks my code. I'll try to pull together a PR that works with my code, based on the changes in 0.4.1.
This is not good for my use case, where I need to have an editor alongside some static text. (E.g. for editing a field)
Could this be changed to clear::CurrentLine
or clear::UntilNewline
?
If so, I'll open a PR.
Getting a StringError("Unable to set Termios attribute.")
panic from this specific line when attempting to run Ion in one thread, while tokio_signal is reading Ctrl+C signals in another thread.
Right now, the keybindings are hardcoded in. They should be remappable. This should be sorted out at the same time as #6 ideally.
I would like to use liner to capture usernames, followed by passwords. Currently the change required would be to not echo characters as they are received, or perhaps to use a mask char.
A mask char may be easier to implement, as it would not invalidate any of the editing interfaces
Something else I've noticed with completions in the Ion shell is that if your attempt to cd to a directory starting with the ~
character. Is there a way tto either supply the expanded form to liner or have liner expand it?
Could be nice for things like Ion syntax highlighting.
Would need some changes to Buffer
.
Something's running out of control and causing serious issues ( https://github.com/redox-os/ion/issues/415 ) when the history file is somewhat large (~1000 lines). Might possibly be an issue in the recent history file commit that wasn't detected by the tests? I'm not sure yet. History file continually gets larger and larger until an OOM error occurs.
I will be investigating this.
I'm assuming that the main reason that there currently is no windows support for liner
is that the termion
dependency has no windows support either.
However, there is an open issue here to add that functionality.
Once that is merged, It would be great if windows support could be added here as well.
I have a liner
fork here that uses a termion
fork in order to add this support. I haven't turned it into a PR because I think some more work may be necessary. For example, it seems to work fine on the windows CMD
shell, but starting my application using the git Bash
shell results in an infinite list of:
[Err] Error { repr: Custom(Custom { kind: Other, error: StringError("Unable to get the terminal size.") }) }
However, it can easily be tested in a project using liner by using my github fork as a dependency:
liner = { git = "https://github.com/jjpe/liner", branch = "windows" }
if i implicit cd to both ~/dir
and ~/dir/
, when i press ~/di<Tab>
i see both of those completions presented; i would expect ~/dir/
to be completed automatically.
edit: this is in ion shell
read_line
never exits when the last char on the line is a backslash. It would be useful to at least be able to overwrite the handle_newline
, or better even, to be able to specify when to end a line.
Maybe this is meant this way, but if the history-file has exactly max_file_size
lines and another is pushed, the file is cleared completely and the newly pushed is its only entry.
The in memory-history works as expected (drops the oldest, but keeps the rest).
How to reproduce (quick and dirty):
$ touch test
$ for x in $(seq 1000); do echo $x >> test; done
$ cat test
This gives 1000 entries.
Compile and run:
extern crate liner;
use liner::Context;
fn main() {
let mut con = Context::new();
con.history.set_file_name(Some("test".to_string()));
con.history.load_history();
let command = "blabla";
con.history.push(command.into());
con.history.commit_history();
}
and then
$ cat test
again. Result only "blabla" will be in the history-file.
https://github.com/redox-os/ion/issues/601
Would be really nice to complete filenames in subdirectories:
e.g. b/c <tab>
would expand to bin/cargo
if unique, or show options for expansion
Am working on a pull request for this.
There is currently no way to store a history and load it from a file.
possible sources of inspiration:
ion from redox
readline written in Go
If this is a desired feature in this project, I will work on it.
Nothing important -- just an API convenience suggestion.
Using rustc 1.37.0 (eae3437df 2019-08-13), compiling liner v0.4.4 leads to this message:
warning[E0503]: cannot use `self.key_bindings` because it was mutably borrowed
--> /home/ngirard/.cargo/registry/src/github.com-1ecc6299db9ec823/liner-0.4.4/src/context.rs:98:17
|
96 | let ed = try!(Editor::new_with_init_buffer(stdout, prompt, self, buffer));
| ---- borrow of `*self` occurs here
97 | match self.key_bindings {
98 | KeyBindings::Emacs => Self::handle_keys(keymap::Emacs::new(ed), handler),
| ^^^^^^^^^^^^^^^^^^ use of borrowed `*self`
99 | KeyBindings::Vi => Self::handle_keys(keymap::Vi::new(ed), handler),
| -- borrow later used here
|
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
= note: for more information, try `rustc --explain E0729`
It'd be nice to have some "contributing" page with TODO list or something similar.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.