Giter VIP home page Giter VIP logo

bestline's Introduction

Bestline

Library for interactive pseudoteletypewriter command sessions using ANSI Standard X3.64 control sequences

Bestline Demo Video GIF

Overview

This is a single-file no-dependencies C or C++ library that makes it as easy as possible to display a command prompt that asks the user for input. It supports Emacs editing shortcuts, history search, completion / hint callback, and utf-8 editing under a bsd-2 license.

Bestline is a fork of linenoise (a popular readline alternative) that fixes its bugs and adds the missing features while reducing binary footprint (surprisingly) by removing bloated dependencies, which means you can finally have a permissively-licensed command prompt w/ a 38kb footprint that's nearly as good as gnu readline.

$ CC="cc -s -static -Os -DNDEBUG" make
$ ls -hal bestline_example
-rwxr-xr-x 1 jart jart 38K Sep 19 21:41 bestline_example

Example

This example will save history to ~/.foo_history. It's 50kb when statically linked with Cosmopolitan Libc.

#include <stdio.h>
#include "bestline.h"
main() {
    char *line;
    while ((line = bestlineWithHistory("IN> ", "foo"))) {
        fputs("OUT> ", stdout);
        fputs(line, stdout);
        fputs("\n", stdout);
        free(line);
    }
}

Shortcuts

CTRL-E         END
CTRL-A         START
CTRL-B         BACK
CTRL-F         FORWARD
CTRL-L         CLEAR
CTRL-H         BACKSPACE
CTRL-D         DELETE
CTRL-Y         YANK
CTRL-D         EOF (IF EMPTY)
CTRL-N         NEXT HISTORY
CTRL-P         PREVIOUS HISTORY
CTRL-R         SEARCH HISTORY
CTRL-G         CANCEL SEARCH
ALT-<          BEGINNING OF HISTORY
ALT->          END OF HISTORY
ALT-F          FORWARD WORD
ALT-B          BACKWARD WORD
CTRL-ALT-F     FORWARD EXPR
CTRL-ALT-B     BACKWARD EXPR
ALT-RIGHT      FORWARD EXPR
ALT-LEFT       BACKWARD EXPR
CTRL-K         KILL LINE FORWARDS
CTRL-U         KILL LINE BACKWARDS
ALT-H          KILL WORD BACKWARDS
CTRL-W         KILL WORD BACKWARDS
CTRL-ALT-H     KILL WORD BACKWARDS
ALT-D          KILL WORD FORWARDS
ALT-Y          ROTATE KILL RING AND YANK AGAIN
ALT-\          SQUEEZE ADJACENT WHITESPACE
CTRL-T         TRANSPOSE
ALT-T          TRANSPOSE WORD
ALT-U          UPPERCASE WORD
ALT-L          LOWERCASE WORD
ALT-C          CAPITALIZE WORD
CTRL-C         INTERRUPT PROCESS
CTRL-Z         SUSPEND PROCESS
CTRL-\         QUIT PROCESS
CTRL-S         PAUSE OUTPUT
CTRL-Q         UNPAUSE OUTPUT (IF PAUSED)
CTRL-Q         ESCAPED INSERT
CTRL-SPACE     SET MARK
CTRL-X CTRL-X  GOTO MARK
CTRL-Z         SUSPEND PROCESS

ProTip

Remap CAPS LOCK to CTRL.

Requirements

You have to use an ANSI UTF-8 terminal that supports VT100 codes.

Changes

Here's what we've changed compared to linenoise:

  • Remove bell
  • Add kill ring
  • Fix flickering
  • Add UTF-8 editing
  • Add CTRL-R search
  • React to terminal resizing
  • Don't generate .data section
  • Support terminal flow control
  • Make history loading 10x faster
  • Make multiline mode the only mode
  • Support unlimited input line length
  • Accommodate O_NONBLOCK file descriptors
  • Restore raw mode on process foregrounding
  • Make source code compatible with C++ compilers
  • Fix corruption issues by using generalized parsing
  • Implement nearly all GNU readline editing shortcuts
  • Remove heavyweight dependencies like printf/sprintf
  • Remove ISIG→^C→EAGAIN hack and catch signals properly
  • Support running on Windows in MinTTY or CMD.EXE on Win10+
  • Support diacritics, русский, Ελληνικά, 漢字, 仮名, 한글

Readability

This codebase aims to follow in Antirez's tradition of writing beautiful programs, that solve extremely difficult technical problems in the most elegant way possible. The original Linenoise source code is sort of like an old Delorean where it's simple and beautiful, but has a lot of things broken about it that need to be fixed, which gives you plenty of reasons to sit down and fix things to fully appreciate its beauty.

There are, however, some differences in style. Antirez generally optimizes for fewer lines of code even if it makes the binary footprint larger and with poor edge case handling and cultural biases presumably to preserve its accessibility and value as an educational tool. For example, one of the biggest issues with Linenoise, was that pressing the wrong key on the keyboard would mess with the state and garble input since it didn't actually parse ansi codes or even multibyte characters.

While this project has addressed many of Linenoise's shortcomings, we've sought to do it in a way that carries on the Antirez tradition of simple elegant hackable code. It is our hope that should you find opportunities for improvement in this codebase that you'll find it equally pleasurable to work with.

Portability

Bestline is written in portable ANSI C99 that conforms to POSIX. We recommend using Cosmopolitan Libc since it produces binaries that work on any operating system including Windows.

Portability across terminals is achieved because literally everything these days supports VT100 control codes which were standardized by ANSI back in the 1970's. This library ignores platform-specific norms for multibyte encoding and it also ignores antiquated terminal capability databases. Libraries like ncurses were designed to reduce bandwidth on 300 bit per second modems. They're bloated and huge because they needed to implement workarounds to all the "incompatible by design" engineering practices used by terminal platforms in the 70's in 80's.

Corporate America has long since moved on to making GUI platforms incompatible instead. Even the Windows command prompt supports VT100 and XTERM sequences these days. Seriously. It's 2021 and everyone in the world finally agrees on UTF-8 and ANSI VT100 style command sequences. That's why bestline is now, for the first time in history, able to offer you a fully featured experience using simple bloat-free code.

Contributing

We'd love to accept your pull requests! Please send an email beforehand to Justine Tunney [email protected] saying that you intend to assign her the copyright to the changes you contribute to bestline.

Please do not contribute changes that have #ifdef statements. We don't care if MSVC printed a warning, and we won't accept Windows torture code since Windows compatibility can be abstracted by Cosmopolitan Libc which does C on Windows better than Windows does considering how there's about ten different incompatible libc implementations, provided by the Windows platform, and they have a history of doing things like adding telemetry.

License

Bestline is released under the 2-clause BSD license. You have the freedom to copy the Bestline source code into your codebase, but you have to keep the license notice at the top of the file. You also have the freedom to distribute your app as a closed-source binary, but you have to embed the copyright notice in the executable. We've added an .ident assembly directive to the top of the source code file which should automatically take care of binary notice compliance.

The BSD-2 License

Copyright 2018-2021 Justine Tunney <[email protected]>
Copyright 2010-2016 Salvatore Sanfilippo <[email protected]>
Copyright 2010-2013 Pieter Noordhuis <[email protected]>

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

 *  Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

 *  Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Donations

Please consider tipping the author at https://github.com/sponsors/jart since she needs your support in order to keep going building cool tools and libraries that serve the public interest. So if you like what you've seen and want to encourage more, please consider granting recognition.

bestline's People

Contributors

alexey-milovidov avatar dimkr avatar jart avatar melchizedek6809 avatar mourinaruto avatar mrdomino avatar octetta avatar sirdaev avatar xoich 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  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

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

bestline's Issues

Interrupt handling seems broken

Hi,

this might just be my misunderstanding of the way this library deals with interrupts, but the current behaviour seems a bit broken.
When one presses Ctrl+C, I would expect bestline to do some cleanup and then re-raise the interrupt for the calling function.

This is not what happens at all.

Currently, when pressing Ctrl+C, the library seemingly does nothing, though it "eats" the next key press (meaning, the next key press seems to be ignored, but after that everything works as expected again). Only when pressing Ctrl+C twice, the interrupt is propagated properly and errno is set to EINTR.

After looking at the source code and seeing that there are signal handlers in place, I tried to track down the issue and I found this in bestline.c:1360:

rc = read(fd,&c,1);

For some reason, if the program receives an interrupt during this read call, it does not call the interrupt handler or set errno. Instead, read sets c to 3 (ETX). I'm honestly not sure what this means or if it is intended, but I was able to get the expected behaviour by simply calling the interrupt handler manually in this case.

rc = read(fd,&c,1);
if (c == 3){
    bestlineOnInt(SIGINT);
}

While this works for me right now, I doubt this is a robust fix.

Am I on the right track here, or is the current behaviour intended?

Thanks in advance.

Would like a way to turn off parenthesis matching.

I have a rather odd syntax I want to support using bestline, that uses unmatched parenthesis.

I've disabled this locally by just forcing IsBalanced() to return 1.

Should this be a runtime option?

If there's interest, I can dig further on a more elegant way to handle.

Support for something like "readNextKey()"?

This library, as all your work, looks awesome!

I am pretty sure it should be possible to add a function readNextKey() so that it would return the "logical key" that the user pressed: k, S, 4, Enter, F5, Page-Up, Ctrl-C, Alt-D, Ctrl-Alt-L, etc. This would require decoding the escape sequences associated to keys such as Page-Up (which is something that is at least partially done today, right?), and might require adding defines / enums for the possible key values.

Do you think something like this could be useful?

interest in vi mode?

I'm looking for a GNU readline alternative, mostly to make sure https://www.oilshell.org/ doesn't become too tied to that interface (which is very hairy).

oilshell/oil#460

I did some research and it seems like the only line editors that support vi mode (set -o vi) are GNU readline and libedit (derived from NetBSD with few docs). It looks like it is a few thousand lines of logic in each.

I wonder if this is in scope for bestline? I looked at the code a little and maybe it can be done with an alternative bestlineEdit()? The main difference is that vi is modal and hitting escape brings you into edit mode, but I think that could be handled in such a wrapper.

~/src/readline-8.1$ wc -l vi*.c
  875 vi_keymap.c
 2408 vi_mode.c
 3283 total
~/src/libedit-20210910-3.1/src$ wc -l vi*.c
 1160 vi.c
  774 vis.c
 1934 total

minimal docs on vi mode: https://tiswww.case.edu/php/chet/readline/rluserman.html#SEC22

help of building shared library

Hi jart, thanks for the excellent work of both cosmopolitan and bestline.

Recently, I need a cross-platform readline replacement for python since it simply not working on windows, and with some researching I found this repo.

I initially tried to compile bestline.c to libbestline.so, with GNU gcc and it's std lib, I could get it work, but it is not really portable since it rely on libc and several libs at runtime, see:
image

Then I tried to compile it with cosmopolitan which I consider it features a portable libc runtime, I tried the following build command:

libbestline.so: bestline.c crt.o ape.o ape.lds cosmopolitan.a cosmopolitan.h
        gcc -g -Os -fPIC -shared -fno-pie -no-pie -mno-red-zone -nostdlib -nostdinc -fno-omit-frame-pointer -pg -mnop-\
mcount -o $@ bestline.c -Wl,--gc-sections -fuse-ld=bfd -Wl,-T,ape.lds -include cosmopolitan.h crt.o ape.o cosmopolitan\
.a

Unfortunatelly, it produces a ELF 64-bit LSB executable instead of a shared object:
image
what I want is to build is a dynamic library with ldd comand shows not a dynamic executable.
Could you send some help to me? I would be very appreciate for that.
thank you in advance :D

Minor spelling and grammar issues in README

A few suggestions to improve the README; feel free to incorporate any of these if you'd like (or not at all):

footprint (surprisingly) by removing bloated dependencies which means

dependencies, which means

- Support diacratics, русский, Ελληνικά, **人, 日本語, 한국인

diacratics → diacritics

There are however some differences in style. Antirez generally optimizes

There are, however, some differences [...]

300 bit per second modems. They're bloated and huge because the needed

"the needed" → "they needed"

日本 (Japan) should be replaced with 日本語 (Japanese language) in the list of supported languages

One of the lines in the README:

- Support diacratics, русский, Ελληνικά, **人, 한국인, 日本

uses 日本 in a list of supported languages to mean "Japanese". However, 日本 (Nihon) is Japan (the country), whereas what you probably intended to use here is 日本語 (Nihongo) which is the Japanese language.

Sources:

Cannot build: compatibility with glibc?

milovidov@milovidov-desktop:~/work/bestline$ clang++ --version
clang version 11.0.0 (https://github.com/llvm/llvm-project.git f5df584a5077e726ad851ccfe8496deda3e5ef07)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/bin

milovidov@milovidov-desktop:~/work/bestline$ CC=clang make    
clang    -c -o bestline.o bestline.c
In file included from bestline.c:122:
In file included from /usr/include/termios.h:25:
/usr/include/features.h:314:53: error: invalid token at start of a preprocessor expression
     || (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 1)       \
                                                    ^
/usr/include/features.h:319:48: error: invalid token at start of a preprocessor expression
#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 2 || defined _XOPEN_SOURCE
                                               ^
bestline.c:213:25: warning: tentative definition of variable with internal linkage has incomplete non-array type 'struct sigaction' [-Wtentative-definition-incomplete-type]
static struct sigaction orig_int;
                        ^
bestline.c:213:15: note: forward declaration of 'struct sigaction'
static struct sigaction orig_int;
              ^
bestline.c:214:25: warning: tentative definition of variable with internal linkage has incomplete non-array type 'struct sigaction' [-Wtentative-definition-incomplete-type]
static struct sigaction orig_quit;
                        ^
bestline.c:213:15: note: forward declaration of 'struct sigaction'
static struct sigaction orig_int;
              ^
bestline.c:215:25: warning: tentative definition of variable with internal linkage has incomplete non-array type 'struct sigaction' [-Wtentative-definition-incomplete-type]
static struct sigaction orig_cont;
                        ^
bestline.c:213:15: note: forward declaration of 'struct sigaction'
static struct sigaction orig_int;
              ^
bestline.c:216:25: warning: tentative definition of variable with internal linkage has incomplete non-array type 'struct sigaction' [-Wtentative-definition-incomplete-type]
static struct sigaction orig_winch;
                        ^
bestline.c:213:15: note: forward declaration of 'struct sigaction'
static struct sigaction orig_int;
              ^
bestline.c:1107:15: warning: implicit declaration of function 'getdelim' is invalid in C99 [-Wimplicit-function-declaration]
    if ((rc = getdelim(&p, &c, '\n', f)) != EOF) {
              ^
bestline.c:1652:22: error: variable has incomplete type 'struct sigaction'
    struct sigaction sa;
                     ^
bestline.c:213:15: note: forward declaration of 'struct sigaction'
static struct sigaction orig_int;
              ^
bestline.c:1665:13: warning: implicit declaration of function 'sigemptyset' is invalid in C99 [-Wimplicit-function-declaration]
            sigemptyset(&sa.sa_mask);
            ^
bestline.c:1666:13: warning: implicit declaration of function 'sigaction' is invalid in C99 [-Wimplicit-function-declaration]
            sigaction(SIGCONT,&sa,&orig_cont);
            ^
bestline.c:1681:9: warning: implicit declaration of function 'sigaction' is invalid in C99 [-Wimplicit-function-declaration]
        sigaction(SIGCONT,&orig_cont,0);
        ^
bestline.c:1875:43: warning: implicitly declaring library function 'strdup' with type 'char *(const char *)' [-Wimplicit-function-declaration]
    history[historylen - 1 - l->hindex] = strdup(l->buf);
                                          ^
bestline.c:1875:43: note: include the header <string.h> or explicitly provide a declaration for 'strdup'
bestline.c:2023:26: warning: implicitly declaring library function 'strndup' with type 'char *(const char *, unsigned long)' [-Wimplicit-function-declaration]
        ring.p[ring.i] = strndup(p, n);
                         ^
bestline.c:2023:26: note: include the header <string.h> or explicitly provide a declaration for 'strndup'
bestline.c:2742:5: error: unknown type name 'sigset_t'; did you mean '__sigset_t'?
    sigset_t omask;
    ^~~~~~~~
    __sigset_t                                                                                                                                                                                                                                        
/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h:8:3: note: '__sigset_t' declared here
} __sigset_t;
  ^
bestline.c:2744:22: error: variable has incomplete type 'struct sigaction'
    struct sigaction sa;
                     ^
bestline.c:213:15: note: forward declaration of 'struct sigaction'
static struct sigaction orig_int;
              ^
bestline.c:2748:5: warning: implicit declaration of function 'sigemptyset' is invalid in C99 [-Wimplicit-function-declaration]
    sigemptyset(&sa.sa_mask);
    ^
bestline.c:2749:5: warning: implicit declaration of function 'sigaddset' is invalid in C99 [-Wimplicit-function-declaration]
    sigaddset(&sa.sa_mask,SIGINT);
    ^
bestline.c:2751:5: warning: implicit declaration of function 'sigprocmask' is invalid in C99 [-Wimplicit-function-declaration]
    sigprocmask(SIG_BLOCK,&sa.sa_mask,&omask);
    ^
bestline.c:2751:17: error: use of undeclared identifier 'SIG_BLOCK'
    sigprocmask(SIG_BLOCK,&sa.sa_mask,&omask);
                ^
bestline.c:2752:19: error: use of undeclared identifier 'SA_NODEFER'
    sa.sa_flags = SA_NODEFER;
                  ^
bestline.c:2754:5: warning: implicit declaration of function 'sigaction' is invalid in C99 [-Wimplicit-function-declaration]
    sigaction(SIGINT,&sa,&orig_int);
    ^
bestline.c:2758:21: error: use of undeclared identifier 'SIG_UNBLOCK'
        sigprocmask(SIG_UNBLOCK,&sa.sa_mask,0);
                    ^
bestline.c:2766:17: error: use of undeclared identifier 'SIG_SETMASK'
    sigprocmask(SIG_SETMASK,&omask,0);
                ^
bestline.c:2797:18: warning: implicit declaration of function 'fileno' is invalid in C99 [-Wimplicit-function-declaration]
    if ((!isatty(fileno(stdin)) ||
                 ^
bestline.c:213:25: error: tentative definition has type 'struct sigaction' that is never completed
static struct sigaction orig_int;
                        ^
bestline.c:213:15: note: forward declaration of 'struct sigaction'
static struct sigaction orig_int;
              ^
bestline.c:214:25: error: tentative definition has type 'struct sigaction' that is never completed
static struct sigaction orig_quit;
                        ^
bestline.c:213:15: note: forward declaration of 'struct sigaction'
static struct sigaction orig_int;
              ^
bestline.c:215:25: error: tentative definition has type 'struct sigaction' that is never completed
static struct sigaction orig_cont;
                        ^
bestline.c:213:15: note: forward declaration of 'struct sigaction'
static struct sigaction orig_int;
              ^
bestline.c:216:25: error: tentative definition has type 'struct sigaction' that is never completed
static struct sigaction orig_winch;
                        ^
bestline.c:213:15: note: forward declaration of 'struct sigaction'
static struct sigaction orig_int;
              ^
15 warnings and 13 errors generated.
make: *** [<builtin>: bestline.o] Error 1
milovidov@milovidov-desktop:~/work/bestline$ ldd --version
ldd (Ubuntu GLIBC 2.31-0ubuntu9.2) 2.31
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

feature request: readline-style transponse

In readline and editline (and zle and emacs), CTRL-T transposes two letters as in bestline, but with the following addition: if cursor is at eol, swap the two last letters:

> ab<CTRL-T>
> ba

readline also implements this for ALT-T to transpose the last two words:

$ foo bar<ALT-T>
$ bar foo

last column is unused

readline and editline use the last column of the terminal before going to the next line, but bestline puts a space there, is this intentional?

it looks confusing because it could also be read as a space

2021-10-31-150728_312x299_scrot

Issues with long lines

Just compiled bestline_example and tried its Unicode-handling, because I looking for a replacement of a hacked version of linenoise with partial Unicode support.

The following was run in a terminal of size 80x24 in a gnome terminal window on Ubuntu 21.10.

$ echo $TERM
xterm-256color

The initial prompt is hello> , which is fine.
Pasting the string 💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አይከሰስ። into bestline also works fine.
Now when pasting the same string 💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አይከሰስ። again, the line would be longer than 80 characters, so bestline wraps the line (correctly).
However, on its way to there bestline prints the complete input line once again for each overflow character.

Example ("x" denotes cursor position):
Initial:

hello> x

After pasting the string once:

hello> 💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አይከሰስ።x

After pasting the string twice:

hello> 💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አይከሰስ።💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አ
hello> 💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አይከሰስ።💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አ
hello> 💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አይከሰስ።💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አ
hello> 💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አይከሰስ።💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አ
hello> 💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አይከሰስ።💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አ
ይከሰስ።x

This should instead be just

hello> 💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አይከሰስ።💩🍺🌧 ⛈ 🌩 ⚡🔥💥🌨 "ሰማይ አይታረስ ንጉሥ አ
ይከሰስ።x

As a follow-up, I was trying to go down in history (pressing the down arrow key), which brings up the last used line.
It provides the correct line, but pressing it several times moves everything else up even one line. This shouldn't be the case because when there is room to display the last full history entry, nothing should be moved around.

Thanks for the amazing work on bestline! IMO a modern linenoise successor is much needed!

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.