Create a simple line editor

19

5

In: a string without line breaks*
Allow the user to edit and submit the line
Out: the modified string (optionally with a trailing linebreak)

The line editor must at minimum allow the user to:

  1. move a visible cursor left and right

  2. insert and/or overwrite characters at the cursor position

  3. remove characters at the cursor position

  4. submit the new string, i.e. cease editing cause the modified string to be returned/printed/displayed/saved… (with no other text)

Appreciated, but not required:

  • explanation of your code.

  • link to an online testing site that can demonstrate your program/function

  • an animated image demonstrating usage (TIO, for instance, does not allow interactivity)

Note:

  • key-bindings are suggestions only

  • GUI or visual styling is not required

Examples

In the following, the cursor is illustrated with _.

In: Just some text
Allow the user to edit:
Just some text_  User presses (left arrow key) nine times
Just ̲some text  User presses Del four times
Just ̲ text  User presses any
Just any_text  User presses Enter
Out: Just any text

In: Remove me
Allow the user to edit:
Remove me_  User presses Backspace nine times
_  User presses Enter
Out:  (empty string)


* To prevent trivial editor solutions, this must either be supplied via a different input method than the editing commands, or must be separated from them by a newline or similar.

Adám

Posted 2017-04-24T12:14:46.203

Reputation: 37 779

Do we have to use those exact keypresses for i/o? Also, can we create a file to save the text to? – Rɪᴋᴇʀ – 2017-04-24T13:11:09.363

1

@Riker key-bindings are suggestions only. Input and output methods follow PPCG defaults.

– Adám – 2017-04-24T13:14:01.147

Can the string contain newlines? And can a newline be used to "submit"? – Conor O'Brien – 2017-04-24T14:23:14.010

@ConorO'Brien No; *Simple line editor*. Yes; The editor must at minimum allow the user to: (...) 4. submit the new string – Adám – 2017-04-24T14:24:51.673

1It would be nice if you specified line in the actual body of the question, as titles on this site are not exactly specifications... – Conor O'Brien – 2017-04-24T14:26:49.140

@ConorO'Brien OK? – Adám – 2017-04-24T14:27:25.507

Is it needed to respond to the Enter key? Or can I bind my edit control directly to a variable? – sergiol – 2017-04-24T19:58:08.673

@sergiol The line editor must at minimum allow the user to (...) submit the new string. key-bindings are suggestions only. As long as there is a possibility for the user to signal that editing is done, it is fine. – Adám – 2017-04-24T20:18:29.437

So, Is my answer valid or not? – sergiol – 2017-04-24T20:19:02.677

@sergiol Can the user click the button to stop editing and let surrounding code continue? – Adám – 2017-04-24T20:21:23.770

Clicking the X button will exit from application. – sergiol – 2017-04-24T20:29:06.480

Let us continue this discussion in chat.

– Adám – 2017-04-24T20:29:38.603

A nice challenge but too vulnerable to abuse – Beta Decay – 2017-04-25T18:09:54.373

@BetaDecay I think I've managed to patch up the worst holes. Do you see any more? – Adám – 2017-04-25T18:10:35.587

I don't think so – Beta Decay – 2017-04-25T18:18:14.517

Bash, 2 bytes: vi (jk) – programmer5000 – 2017-07-24T17:43:19.943

@programmer5000 How do you submit? – Adám – 2017-07-24T17:45:21.280

@programmer5000 APL, 1 byte: – Adám – 2017-07-24T17:46:35.517

I think this is one case where banning builtins would've been a good idea. – 12Me21 – 2018-03-26T19:57:37.017

@12Me21 But which build-ins? Those that do the entire job? Half of it? Rather than hard-to-define bans, we need a better voting culture that promotes ingenuity – Adám – 2018-03-26T21:15:52.923

Answers

3

APL (Dyalog), 5 bytes

⍞←⍞⋄⍞

This is a tradfn, so to use it, do

∇a
⍞←⍞⋄⍞
∇

And then call it by using a, after which you supply the starting string, and then you can edit the string.

user41805

Posted 2017-04-24T12:14:46.203

Reputation: 16 320

8

JavaScript (ES6), 15 14 bytes

I don't understand why this is getting so many upvotes!

s=>prompt(s,s)

Saved a byte thanks to Adám's suggestion that I display the original input in the prompt.


Try It

f=
s=>prompt(s,s)
console.log(f("Edit this ..."))

Shaggy

Posted 2017-04-24T12:14:46.203

Reputation: 24 623

1Save a byte by replacing "" with 0 or even s (so the user can still see the original text while editing - a neat feature). – Adám – 2017-04-24T13:35:26.243

5

Bash 4.x, 25 characters

read -ei "$*" t
echo "$t"

Sample run:

bash-4.3$ bash interactive.sh hello world
hello golfing world
hello golfing world

(Line 2 above was the interactive editing, line 3 the output of resulted text.)

manatwork

Posted 2017-04-24T12:14:46.203

Reputation: 17 865

1Doesn't work, for me, -bash: read: -i: invalid option? man says only ers are allowed flags. – Rɪᴋᴇʀ – 2017-04-24T13:31:38.067

Yeah, I was about to ask about -ei – Adám – 2017-04-24T13:31:53.737

-i was introduced in Bash 4.0 (released in February 2009) – “-i text Use TEXT as the initial text for Readline”. – manatwork – 2017-04-24T16:21:45.147

I'm wondering, would cat work? – Matthew Roh – 2017-04-27T14:12:22.373

cat just awaits for incoming stream, for which the shell will provide no editing functionality. At least not by default. – manatwork – 2017-04-27T14:19:13.957

3

HTML + JavaScript (ES6), 77 66 64 bytes

HTML

<input id=t

JavaScript

onkeyup=e=>e.which-13||alert(t.value);f=a=>t.value=a;

Saved 10 bytes thanks to Jörg Hülsermann and 2 bytes thanks to Luke.

onkeyup=e=>e.which-13||alert(t.value);f=a=>t.value=a;

f("Remove Me");
<input id=t>

Tom

Posted 2017-04-24T12:14:46.203

Reputation: 3 078

2you can remove the type attribut for the input Element – Jörg Hülsermann – 2017-04-24T12:34:44.947

You should probably use oninput instead. – Matthew Roh – 2017-04-24T12:50:44.813

@SIGSEGV I don't think oninput fires when Enter is pressed – Tom – 2017-04-24T12:57:52.830

1e.which==13?alert(t.value):0 -> e.which-13||alert(t.value) saves two bytes. – Luke – 2017-04-24T17:18:56.983

3

Bash + Vi/Vim, 14 bytes

echo $1>a;vi a

vi is aliased to vim on macOS, I don't know about other OSes.

Rɪᴋᴇʀ

Posted 2017-04-24T12:14:46.203

Reputation: 7 410

Maybe I am missing something, but does this meet the requirement of outputting the modified string? – Grayson Kent – 2017-04-25T14:11:48.493

1@GraysonKent you can save and quit by hitting :wq! or :x, so I think so. OP is a bit unclear on what qualifies as that. – Rɪᴋᴇʀ – 2017-04-25T14:22:43.017

3

Python 2, 275 200 bytes

Not a winner, but here it is:

from msvcrt import*
s=list(input())[::-1]
c=i=0
def p(a):print''.join(a)[::-1]
while'\r'!=c:p(s[:i]+['<']+s[i:]);c=getch();x=c!='\b';exec["s[i:i+1-x]=c*x","i=(i-1+2*(c<'\\t'))%-~len(s)"][x*' '>c]
p(s)

Explanation:

It works by reversing the input (with [::-1]), and excluding and inserting characters in that reversed input so that the cursor does not have to move. Reverses it again when printing.

Usage:

[Tab] key to move Right
[Ctrl+C] to move Left
[Backspace] to erase
[Return] to finish editing
Any other key, will be inserted into text

Example:

Using OP's example

In: Just some text
Just some text>
Just some> text User presses Ctrl+C five times
Just > text  User presses Backspace four times
Just any> text  User presses any
Just any> text  User presses Enter
Out: Just any text

Inline editor version:

If you want the text to be editted inline, append ,'\r', at the end of the print statement:

def p(a):print''.join(a)[::-1],'\r',

Felipe Nardi Batista

Posted 2017-04-24T12:14:46.203

Reputation: 2 345

6+1 Very nice. First answer which actually implements a real editor, as opposed to relying on pre-existing functionality. I was considering posting another challenge to do just this. – Adám – 2017-04-25T13:03:06.043

Can't you save bytes by using other keys for left and right? – Adám – 2017-04-25T13:08:08.907

I could assume characters as - and + to do so, but the editor would be unable to accept those characters then – Felipe Nardi Batista – 2017-04-25T13:11:36.310

How about \t and \v? – Adám – 2017-04-25T13:14:22.147

how do i use \v? ctrl+v? – Felipe Nardi Batista – 2017-04-25T13:15:22.547

1That's the user's problem, no? Control+K, maybe? – Adám – 2017-04-25T13:17:39.690

3

C + NCURSES, 573 bytes

#include <curses.h>
i;j;k;s;c;p;int main(a,b)char**b;{char*q;char t[999];if(a&&(q=b[1]))while(*q)t[s++]=*q++;i=s;initscr();noecho();keypad(stdscr,1);do{for(j=0;j<i;j++)addch(t[j]);addch('|');for(j=i;t[j];j++)addch(t[j]);c=getch();switch(c){case KEY_LEFT:if(i)i--;break;case KEY_RIGHT:if(i<s)i++;break;case 8:case 127:case KEY_BACKSPACE:if(i){for(k=i-1;k<s;k++)t[k]=t[k+1];i--;s--;}break;case KEY_DC:if(i<s){for(k=i;k<s;k++)t[k]=t[k+1];s--;}break;default:if(c>31&c<127){for(k=s;k>i;k--)t[k]=t[k-1];t[i]=c;i++;s++;}}clear();}while(c!=10);printw(t);getch();endwin();return 0;}

Test

  • Compile & Run with input Just some text.

enter image description here

enter image description here

  • Press Left-Arrow button nine times.

enter image description here

  • Press Delete button four times.

enter image description here

  • Press a then n then y.

enter image description here

  • Press Enter to terminate.

enter image description here

Detailed

#include <curses.h>

int main(int argc, char ** argv)
{
    char*q = 0;
    char t[999] = {0};
    int j = 0, k = 0;
    int i = 0; // cursor before first char
    int s = 0; // current size
    int c = 0; // current input
    int p = 0;

    // copy first command-line argument
    if(argc>0 && (q=argv[1]))while(*q)t[s++]=*q++; i=s;

    initscr(); // initiate NCURSES
    noecho(); // input does not echo on entry
    keypad(stdscr,TRUE); // handle all input

    do
    {
        // print current content with cursor
        for(j=0;j<i;j++) addch(t[j]);
        addch('|'); for(j=i;t[j];j++) addch(t[j]);

//      printw("\n\n> key %d pressed",c); // debug

        c = getch(); // read next char

        switch(c)
        {
            case KEY_LEFT: // left arrow; cursor left
            if(i > 0) i--;
            break;

            case KEY_RIGHT: // right arrow; cursor right
            if(i < s) i++;
            break;

            case 8: // backspace; remove previous char
            case 127:
            case KEY_BACKSPACE:
            if(i > 0)
            {
                for(k=i-1; k<s; k++) t[k]=t[k+1];
                i--;s--;
            }
            break;

            case KEY_DC: // delete; remove next char
            if(i < s)
            {
                for(k=i; k<s; k++) t[k]=t[k+1];
                s--;
            }
            break;

            default: // none of the above

            if(c > 31 && c < 127) // printable char
            {
                for(k=s; k>i; k--) t[k]=t[k-1];
                t[i] = c;i++;s++;
            }
        }

        clear(); // clear screen
        if(c == 10) break;
    }
    while(c);

    addch('\n');
    printw(t); // print final result
    getch(); // wait for any input
    endwin();
    return 0;
}

Khaled.K

Posted 2017-04-24T12:14:46.203

Reputation: 1 435

Can't you replace KEY_*by the respective integers to save some bytes? – sergiol – 2018-03-26T13:52:34.053

2

VBScript, 23 40 bytes

MsgBox InputBox("",,Wscript.Arguments(0))

enter image description here

Johan du Toit

Posted 2017-04-24T12:14:46.203

Reputation: 1 524

2

Ruby, 9 19 22 84 bytes

->s{r=Readline;r.pre_input_hook=->{r.insert_text s;r.redisplay};r.readline}

This creates a Readline pre input hook that inserts the text s and then redisplays. After this, irb gets messed up so make sure to run this from a file. Run as a lambda, it takes the input string as an argument and returns the output string.

puts Readline.readline

This uses the Readline library to perform line editing. My previous answer only allowed backspaces.

puts gets

This is really, really self explanatory.

Edit: I have been asked for an explanation. This is equivalent to puts(gets). gets inputs a string with a line editor. puts outputs it.

dkudriavtsev

Posted 2017-04-24T12:14:46.203

Reputation: 5 781

Chain the method calls: r.insert_text(s).redisplay and remove the following ;. BTW, as I understand our policy, you should mention that your code runs in irb, because otherwise you would need to require Readline in your code. – manatwork – 2017-04-26T11:30:55.320

2

C#, 53 bytes

s=>{SendKeys.SendWait(s);return Console.ReadLine();};

Where s is a string to modify and the output is the new value.

SendKeys.SendWait: Sends the given keys to the active application, and then waits for the messages to be processed.

or 74 bytes if we are not in a Windows Forms context:

s=>{System.Windows.Forms.SendKeys.SendWait(s);return Console.ReadLine();};

gif demo

aloisdg moving to codidact.com

Posted 2017-04-24T12:14:46.203

Reputation: 1 767

1

PHP + HTML, 26 Bytes

<input value=<?=$_GET[0]?>

The Browser adds automatically the closing tag

$_GET Using a url like http://example.com?0=input as Input Creates in a HTML <input value=input

And this is the output for the string input

<input value=input

Jörg Hülsermann

Posted 2017-04-24T12:14:46.203

Reputation: 13 026

Should I assume this works? Maybe you can add an explanation, or link, or animation? – Adám – 2017-04-24T12:50:31.187

@Adám Yes you can assume this Maybe you will it improve and set the attribute autofocus to have the cursor at the beginning in it. Other HTML elements you can also edit if you set the attribute contenteditable – Jörg Hülsermann – 2017-04-24T13:09:30.513

Input = test></input><script>alert("Hi");//some malicious code\n</script> – Roman Gräf – 2017-04-24T13:56:40.243

@RomanGräf Bad Boy. Yes it is better to filter Javascript – Jörg Hülsermann – 2017-04-24T14:06:14.920

1

Tcl, 17

puts [gets stdin]

Online interpreters just suck to demonstrate it, then I showcase some images from a Windows command shell:

Test case 1

enter image description hereenter image description hereenter image description hereenter image description hereenter image description here

Test case 2

enter image description hereenter image description here enter image description here

sergiol

Posted 2017-04-24T12:14:46.203

Reputation: 3 055

Can you explain this one? – Adám – 2017-04-24T20:41:08.170

1I can add images of it running on a Windows command line. – sergiol – 2017-04-24T20:42:30.950

@Adám: Do you still want me to explain what the code is doing or are the images enough? – sergiol – 2017-04-25T08:37:16.943

This is enough. – Adám – 2017-04-25T15:17:17.623

1

AHK, 32 bytes

InputBox,s,,,,,,,,,,%1%
Send,%s%

InputBox stores whatever is typed as the variable s and gives a starting prompt of the variable 1 which is the first passed parameter.
Send sends keystrokes to the current window. In this case, it'll be the contents of s.
MsgBox was an option but, for golfing, Send is 2 bytes shorter.

Animation

Engineer Toast

Posted 2017-04-24T12:14:46.203

Reputation: 5 769

@Adám I misread that part. I've updated the answer. – Engineer Toast – 2017-04-25T16:25:51.760

Now it is good. – Adám – 2017-04-25T18:20:03.790

1

Excel VBA Immediate Window Command - 22 bytes

[a1]=inputbox(0,,[a1])

enter image description here

Rohan

Posted 2017-04-24T12:14:46.203

Reputation: 31

2This is only a partial solution, as your initial value ("Hi") is hard-coded. The initial value must be read from somewhere (a cell or a prompt or a file...) and the final value must be put somewhere (a cell, a messagebox, a file,...). – Adám – 2017-04-26T12:36:57.350

inputbox(0,,"Hi") saves a byte – Felipe Nardi Batista – 2017-04-26T12:38:24.563

@Adám Thanks. Fixed now it's reading from cell A1 – Rohan – 2017-04-27T05:37:07.207

I think you need [a1]=inputbox(0,,[a1]) or something, otherwise, where does a go? Storing in a variable is not a valid output method.

– Adám – 2017-04-27T05:40:29.893

It goes where it came from cell A1 – Rohan – 2017-04-27T05:42:54.953

0

ZX Spectrum BASIC, 7 bytes

Trivial, included for completeness (INPUT and PRINT are one byte tokens each).

INPUT a$: PRINT a$

Radovan Garabík

Posted 2017-04-24T12:14:46.203

Reputation: 437

1How does it get the initial value of a$? – Adám – 2017-04-25T14:41:44.443

@Adám it gets the initial value of a$ by keyboard input; a$ will be over-written with the INPUT keyword. – Shaun Bebbers – 2019-06-20T11:42:08.840

0

SmileBASIC, 138 bytes

DEF E S@L
WAIT
B=BUTTON(1)C=C-(B==4&&C)+(B>7&&C<LEN(S))I$=INKEY$()IF"\u0008"<I$THEN B=I$<"\u000E"S=SUBST$(S,C,B,I$*!B)?S?" "*C;1IF"\u0008"!=I$GOTO@L
END

Creates a function E with 1 argument and 0 outputs. (Output is displayed in the console)

The escaped characters should be the actual symbols, but they wouldn't show up here.

Controls:

Normal keys: Insert a character before the cursor.
Enter: Delete the character at the cursor.
Backspace: Submit.
D-pad left: Move cursor left.
All buttons except up, down, and left on the d-pad: Move cursor right.

Inserting/deleting characters is backwards so it's very annoying to use (but should still fulfill the requirements).

Just some text
1             
'(press right 5 times)
Just some text
     1
'(press enter 4 times)
Just  text
     1
'(press a)
Just a text
     1
'(press right)
Just a text
      1
'(...)
Just any text
       1
'(press backspace)

12Me21

Posted 2017-04-24T12:14:46.203

Reputation: 6 110

0

Windows command interpreter, 16 bytes

set/pa=
echo %a%

This is very trivial; the command interpreter is trivially a "line editor".

user85052

Posted 2017-04-24T12:14:46.203

Reputation:

0

Commodore BASIC (C64Mini, C64/128, VIC-20 etc...) 179 tokenized BASIC bytes

This should be typed in business mode (upper/lower case characters)

 0a$="Just some text":?"{SHIFT+CLR/HOME}"a$"_":fOi=0to1step0:getk$:on-(k$<>"")goS2:goS1:nE
 1?"{CTRL+N}{CLR/HOME}"a$"_  ";:return
 2ifasc(k$)<>20thena$=a$+k$:on-(asc(k$)=13)goS4:return
 3if-(len(a$))thena$=leF(a$,len(a$)-1):goS4:return
 4?"{ARROW LEFT}{ARROW LEFT}{ARROW LEFT}  ":return

Allows basic text editing + delete + new line. Maximum size of a$ like all strings in Commodore BASIC is 255 characters, so any more than that will crash the program. I will find a way to do > 255 characters if that is necessary.

Simple type-em-up simulator

Simple type-em-up simulator in action

Shaun Bebbers

Posted 2017-04-24T12:14:46.203

Reputation: 1 814