19
\$\begingroup\$

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.

\$\endgroup\$
20
  • \$\begingroup\$ Do we have to use those exact keypresses for i/o? Also, can we create a file to save the text to? \$\endgroup\$
    – Riker
    Apr 24, 2017 at 13:11
  • 1
    \$\begingroup\$ @Riker key-bindings are suggestions only. Input and output methods follow PPCG defaults. \$\endgroup\$
    – Adám
    Apr 24, 2017 at 13:14
  • \$\begingroup\$ Can the string contain newlines? And can a newline be used to "submit"? \$\endgroup\$ Apr 24, 2017 at 14:23
  • \$\begingroup\$ @ConorO'Brien No; Simple line editor. Yes; The editor must at minimum allow the user to: (...) 4. submit the new string \$\endgroup\$
    – Adám
    Apr 24, 2017 at 14:24
  • 1
    \$\begingroup\$ It would be nice if you specified line in the actual body of the question, as titles on this site are not exactly specifications... \$\endgroup\$ Apr 24, 2017 at 14:26

18 Answers 18

10
\$\begingroup\$

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 ..."))

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Save a byte by replacing "" with 0 or even s (so the user can still see the original text while editing - a neat feature). \$\endgroup\$
    – Adám
    Apr 24, 2017 at 13:35
5
\$\begingroup\$

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.)

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Doesn't work, for me, -bash: read: -i: invalid option? man says only ers are allowed flags. \$\endgroup\$
    – Riker
    Apr 24, 2017 at 13:31
  • \$\begingroup\$ Yeah, I was about to ask about -ei \$\endgroup\$
    – Adám
    Apr 24, 2017 at 13:31
  • \$\begingroup\$ -i was introduced in Bash 4.0 (released in February 2009) – “-i text Use TEXT as the initial text for Readline”. \$\endgroup\$
    – manatwork
    Apr 24, 2017 at 16:21
  • \$\begingroup\$ I'm wondering, would cat work? \$\endgroup\$ Apr 27, 2017 at 14:12
  • \$\begingroup\$ cat just awaits for incoming stream, for which the shell will provide no editing functionality. At least not by default. \$\endgroup\$
    – manatwork
    Apr 27, 2017 at 14:19
4
\$\begingroup\$

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.

\$\endgroup\$
4
\$\begingroup\$

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;
}
\$\endgroup\$
1
  • \$\begingroup\$ Can't you replace KEY_*by the respective integers to save some bytes? \$\endgroup\$
    – sergiol
    Mar 26, 2018 at 13:52
3
\$\begingroup\$

Bash + Vi/Vim, 14 bytes

echo $1>a;vi a

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

\$\endgroup\$
2
  • \$\begingroup\$ Maybe I am missing something, but does this meet the requirement of outputting the modified string? \$\endgroup\$ Apr 25, 2017 at 14:11
  • 1
    \$\begingroup\$ @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. \$\endgroup\$
    – Riker
    Apr 25, 2017 at 14:22
3
\$\begingroup\$

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>

\$\endgroup\$
4
  • 2
    \$\begingroup\$ you can remove the type attribut for the input Element \$\endgroup\$ Apr 24, 2017 at 12:34
  • \$\begingroup\$ You should probably use oninput instead. \$\endgroup\$ Apr 24, 2017 at 12:50
  • \$\begingroup\$ @SIGSEGV I don't think oninput fires when Enter is pressed \$\endgroup\$
    – Tom
    Apr 24, 2017 at 12:57
  • 1
    \$\begingroup\$ e.which==13?alert(t.value):0 -> e.which-13||alert(t.value) saves two bytes. \$\endgroup\$
    – Luke
    Apr 24, 2017 at 17:18
3
\$\begingroup\$

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',
\$\endgroup\$
6
  • 6
    \$\begingroup\$ +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. \$\endgroup\$
    – Adám
    Apr 25, 2017 at 13:03
  • \$\begingroup\$ Can't you save bytes by using other keys for left and right? \$\endgroup\$
    – Adám
    Apr 25, 2017 at 13:08
  • \$\begingroup\$ I could assume characters as - and + to do so, but the editor would be unable to accept those characters then \$\endgroup\$ Apr 25, 2017 at 13:11
  • \$\begingroup\$ How about \t and \v? \$\endgroup\$
    – Adám
    Apr 25, 2017 at 13:14
  • 1
    \$\begingroup\$ That's the user's problem, no? Control+K, maybe? \$\endgroup\$
    – Adám
    Apr 25, 2017 at 13:17
2
\$\begingroup\$

VBScript, 23 40 bytes

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

enter image description here

\$\endgroup\$
0
2
\$\begingroup\$

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

\$\endgroup\$
2
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ 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. \$\endgroup\$
    – manatwork
    Apr 26, 2017 at 11:30
1
\$\begingroup\$

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

\$\endgroup\$
4
  • \$\begingroup\$ Should I assume this works? Maybe you can add an explanation, or link, or animation? \$\endgroup\$
    – Adám
    Apr 24, 2017 at 12:50
  • \$\begingroup\$ @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 \$\endgroup\$ Apr 24, 2017 at 13:09
  • \$\begingroup\$ Input = test></input><script>alert("Hi");//some malicious code\n</script> \$\endgroup\$ Apr 24, 2017 at 13:56
  • \$\begingroup\$ @RomanGräf Bad Boy. Yes it is better to filter Javascript \$\endgroup\$ Apr 24, 2017 at 14:06
1
\$\begingroup\$

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

\$\endgroup\$
4
  • \$\begingroup\$ Can you explain this one? \$\endgroup\$
    – Adám
    Apr 24, 2017 at 20:41
  • 1
    \$\begingroup\$ I can add images of it running on a Windows command line. \$\endgroup\$
    – sergiol
    Apr 24, 2017 at 20:42
  • \$\begingroup\$ @Adám: Do you still want me to explain what the code is doing or are the images enough? \$\endgroup\$
    – sergiol
    Apr 25, 2017 at 8:37
  • \$\begingroup\$ This is enough. \$\endgroup\$
    – Adám
    Apr 25, 2017 at 15:17
1
\$\begingroup\$

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

\$\endgroup\$
2
  • \$\begingroup\$ @Adám I misread that part. I've updated the answer. \$\endgroup\$ Apr 25, 2017 at 16:25
  • \$\begingroup\$ Now it is good. \$\endgroup\$
    – Adám
    Apr 25, 2017 at 18:20
1
\$\begingroup\$

Excel VBA Immediate Window Command - 22 bytes

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

enter image description here

\$\endgroup\$
5
  • 2
    \$\begingroup\$ This 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,...). \$\endgroup\$
    – Adám
    Apr 26, 2017 at 12:36
  • \$\begingroup\$ inputbox(0,,"Hi") saves a byte \$\endgroup\$ Apr 26, 2017 at 12:38
  • \$\begingroup\$ @Adám Thanks. Fixed now it's reading from cell A1 \$\endgroup\$
    – Rohan
    Apr 27, 2017 at 5:37
  • \$\begingroup\$ 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. \$\endgroup\$
    – Adám
    Apr 27, 2017 at 5:40
  • \$\begingroup\$ It goes where it came from cell A1 \$\endgroup\$
    – Rohan
    Apr 27, 2017 at 5:42
1
\$\begingroup\$

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)
\$\endgroup\$
0
\$\begingroup\$

ZX Spectrum BASIC, 7 bytes

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

INPUT a$: PRINT a$
\$\endgroup\$
2
  • 1
    \$\begingroup\$ How does it get the initial value of a$? \$\endgroup\$
    – Adám
    Apr 25, 2017 at 14:41
  • \$\begingroup\$ @Adám it gets the initial value of a$ by keyboard input; a$ will be over-written with the INPUT keyword. \$\endgroup\$ Jun 20, 2019 at 11:42
0
\$\begingroup\$

Windows command interpreter, 16 bytes

set/pa=
echo %a%

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

\$\endgroup\$
0
\$\begingroup\$

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

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.