Code Monkey home page Code Monkey logo

noline's Introduction

Pipeline Crates.io API reference

noline

Noline is an IO-agnostic #[no_std] line editor providing robust line editing for any system. The core functionality is IO-free, so it can be adapted to any system be it embedded, async, async embedded, WASM or IPoAC (IP over Avian Carriers).

Features:

  • IO-free
  • Minimal dependencies
  • No allocation needed - Both heap-based and static buffers are provided
  • UTF-8 support
  • Emacs keybindings
  • Line history

Possible future features:

  • Auto-completion and hints

The API should be considered experimental and will change in the future.

The core implementation consists of a state machie taking bytes as input and yielding iterators over byte slices. Because this is done without any IO, Noline can be adapted to work on any platform.

Noline comes with multiple implemenations:

  • [sync_editor::Editor] – Editor for synchronous IO with the following wrapper:
    • [sync_io::IO] – IO wrapper for [embedded_io::Read] and [embedded_io::Write]
  • [async_editor::Editor] - Editor for asynchronous IO with the following wrapper:
    • [async_io::IO] – IO wrapper for [embedded_io_async::Read] and [embedded_io_async::Write]

Editors can be built using [builder::EditorBuilder].

Example

use noline::{builder::EditorBuilder, sync_io::std_sync::StdIOWrapper, sync_io::IO};
use std::fmt::Write;
use std::io;
use termion::raw::IntoRawMode;

fn main() {
    let _stdout = io::stdout().into_raw_mode().unwrap();
    let prompt = "> ";

    let mut io = IO::<StdIOWrapper>::new(StdIOWrapper::new());

    let mut editor = EditorBuilder::new_unbounded()
        .with_unbounded_history()
        .build_sync(&mut io)
        .unwrap();

    while let Ok(line) = editor.readline(prompt, &mut io) {
        writeln!(io, "Read: '{}'", line).unwrap();
    }
}

For more details, see docs.

Usage

Add this to your Cargo.toml:

[dependencies]
noline = "0.3.0"

License

MPL-2.0

noline's People

Contributors

eivindbergem avatar gwenn avatar m5p3nc3r avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

noline's Issues

Std IO Mangled

I intend to use noline in a WASM context to talk to xterm.js, but I thought I would write out the logic and debug using sync::std::IO first.

This is approximately my code.

    let io = IO::new(io::stdin(), io::stdout());

    let mut editor = EditorBuilder::new_static::<100>()
        .with_static_history::<20>()
        .build_sync(&mut io)
        .unwrap();
    
    //editor.load_history(/* with some strings */);
    
    loop {
        let cmd = editor.readline("$ ", &mut io);
        
        dbg!(cmd);
    }

It looks to me like flush is not working for some reason, I'm seeing escape codes after the editor is initialized (^[[24;1R^[[24;86R) and the prompt isn't printed until after I've hit enter. After that the prompt isn't printed at all, it just mangles the input and produces garbage. It is clear that some escapes are being interpreted by the emulator.

I've tried three different emulators, Gnome terminal, alacritty and xterm. Essentially the same behavior in all three.

MVP

  • Basic line editing
    • UTF-8
    • Navigation (left, right, emacs bindings, etc)
    • Unknown input handling (bell?)
    • Line wrapping (sidescroll?)
  • Implementations
    • std+sync
    • std+async
    • embedded+sync
    • embedded+async
  • Examples
    • std+sync
    • std+async-tokio
    • no_std+uart
  • Documentation
    • Doc strings
    • Readme
  • Github workflows
  • Cargo publish #2

Implement history

Features:

  • Browse history with up-down arrows and ctrl-p/cntrl-n
  • Support for both static and dynamic buffer
  • Search with ctrl-r

Shall noline offer a way to print lines while readlining?

Shall it be possible to build chat-like UI where CLI app may display events while waiting for user input? Or to implement a tool like rlwrap(1)?

This requires at least triggering a redraw of the prompt and the buffer, so cannot be easily done without noline's support.

"Half duplex" mode of interaction with user seem to plague many readline-like libraries for Rust.

no_std examples fails after `usbd-hid` update

error[E0277]: the trait bound `usbd_hid_descriptors::MainItemKind: From<std::string::String>` is not satisfied
   --> /home/eivind.bergem/.cargo/registry/src/index.crates.io-6f17d22bba15001f/usbd-hid-macros-0.6.0/src/spec.rs:512:43
    |
512 |             self.set_item(name, item_kind.into(), settings, bits, quirks);
    |                                           ^^^^ the trait `From<std::string::String>` is not implemented for `usbd_hid_descriptors::MainItemKind`, which is required by `std::string::String: Into<_>`
    |
    = help: the trait `From<&str>` is implemented for `usbd_hid_descriptors::MainItemKind`
    = help: for that trait implementation, expected `&str`, found `std::string::String`
    = note: required for `std::string::String` to implement `Into<usbd_hid_descriptors::MainItemKind>`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `usbd-hid-macros` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...

See twitchyliquid64/usbd-hid#72 for more info.

noline under IRQ, not polling

Hi Guys

I have the polling USB Serial version of noline working fine, but I need to have it working under IRQ as I am building a multi-function device with USB-HID and Serial (acting as a debug/status port).

Do you have an example of using noline with no_std under IRQ? Or are there known issues with this mode of usage?

Implementing embassy async Editor

Hi Guys

I am looking at reimplementing the no_sync::Editor that is currently based on Tokio (which requires the std crate) to make use of embassy async functions.

I have hit a couple of early problems with my efforts because of some of the visibility decisions made in noline. It may be that I am trying to do this in the wrong way, in which case some pointers would be appreciated, but can I ask why

  • noline::terminal::Terminal is marked pub(crate)
  • noline::history::get_history_entries is marked pub(crate)
    These two design decisions makes it impossible for me to create a noline Editor for Embassy outside of the noline crate.

CarriageReturn or LineFeed

Hi

I am having issues when using base64 to stream data through noline. If I use a command under a unix like system (macos)

base64 -b64 -i <file>

the line endings are LF not CR, so I don't get any lines read from the input stream with noline.

I must admit I'm no expert in the dark arts of line endings, and the differences between OS's views of the LF/CRLF, but if I change the core::Line::advance to:

-CarriageReturn => {
+CarriageReturn | LineFeed => {
    if self.buffer.len() > 0 {
        let _ = self.nav.history.add_entry(self.buffer.as_str());
    }

    self.generate_output(Done)
}

I am able to process normal input from stdin, but also piped input from a unix command.

Is this a sensible change, or am I mis-understanding the problem>

Add support for grapheme clusters

Currently, noline makes the faulty assumption that one unicode symbol equals one character. The correct unit here is the grapheme cluster which occupies on column.

Support for grapheme clusters can be implemented using unicode-segmentation which happens to be #[no_std].

Unexpected ParserError

I'm having some issues trying to use this crate to make an interaction for my no_std project.
I tried implementing my own IO type (for my environment), but always panic when building the editor:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParserError', src/main.rs:9:10
stack backtrace:
   0: rust_begin_unwind
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/panicking.rs:142:14
   2: core::result::unwrap_failed
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/result.rs:1814:5
   3: core::result::Result<T,E>::unwrap
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/result.rs:1107:23
   4: testkey::main
             at ./src/main.rs:6:22
   5: core::ops::function::FnOnce::call_once
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/ops/function.rs:248:5

here is my code:

// main.rs
use noline::builder::EditorBuilder;
use std::io::Write;

fn main() {
    let mut io = Console::new();
    let mut editor = EditorBuilder::new_unbounded()
        .with_unbounded_history()
        .build_sync(&mut io)
        .unwrap();
    loop {
        if let Ok(line) = editor.readline("> ", &mut io) {
            write!(io, "Read: '{}'\n\r", line).unwrap();
        } else {
            break;
        }
    }
}

pub struct Console(Vec<u8>);

impl Console {
    pub fn new() -> Self {
        let vec = Vec::from([b'a', b'b', b'c']);
        Self(vec)
    }
}

impl noline::sync::Read for Console {
    type Error = core::convert::Infallible;

    fn read(&mut self) -> Result<u8, Self::Error> {
        if let Some(byte) = self.0.last() {
            return Ok(*byte);
        } else {
            panic!("should panic here");
        }
    }
}

impl noline::sync::Write for Console {
    type Error = std::io::Error;

    fn write(&mut self, word: &[u8]) -> Result<(), Self::Error> {
        std::io::Write::write(&mut std::io::stdout(), word).map(|_| ())
    }

    fn flush(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

impl Write for Console {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        std::io::Write::write(&mut std::io::stdout(), buf)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        std::io::stdout().flush()
    }
}

Did I write something wrong? It looks like everything works fine

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.