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\$
  • \$\begingroup\$ Do we have to use those exact keypresses for i/o? Also, can we create a file to save the text to? \$\endgroup\$ – Rɪᴋᴇʀ Apr 24 '17 at 13:11
  • 1
    \$\begingroup\$ @Riker key-bindings are suggestions only. Input and output methods follow PPCG defaults. \$\endgroup\$ – Adám Apr 24 '17 at 13:14
  • \$\begingroup\$ Can the string contain newlines? And can a newline be used to "submit"? \$\endgroup\$ – Conor O'Brien Apr 24 '17 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 '17 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\$ – Conor O'Brien Apr 24 '17 at 14:26

18 Answers 18

3
\$\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\$
8
\$\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
    \$\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 '17 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\$
  • 1
    \$\begingroup\$ Doesn't work, for me, -bash: read: -i: invalid option? man says only ers are allowed flags. \$\endgroup\$ – Rɪᴋᴇʀ Apr 24 '17 at 13:31
  • \$\begingroup\$ Yeah, I was about to ask about -ei \$\endgroup\$ – Adám Apr 24 '17 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 '17 at 16:21
  • \$\begingroup\$ I'm wondering, would cat work? \$\endgroup\$ – Matthew Roh Apr 27 '17 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 '17 at 14:19
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\$
  • \$\begingroup\$ Maybe I am missing something, but does this meet the requirement of outputting the modified string? \$\endgroup\$ – Grayson Kent Apr 25 '17 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\$ – Rɪᴋᴇʀ Apr 25 '17 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\$
  • 2
    \$\begingroup\$ you can remove the type attribut for the input Element \$\endgroup\$ – Jörg Hülsermann Apr 24 '17 at 12:34
  • \$\begingroup\$ You should probably use oninput instead. \$\endgroup\$ – Matthew Roh Apr 24 '17 at 12:50
  • \$\begingroup\$ @SIGSEGV I don't think oninput fires when Enter is pressed \$\endgroup\$ – Tom Apr 24 '17 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 '17 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
    \$\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 '17 at 13:03
  • \$\begingroup\$ Can't you save bytes by using other keys for left and right? \$\endgroup\$ – Adám Apr 25 '17 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\$ – Felipe Nardi Batista Apr 25 '17 at 13:11
  • \$\begingroup\$ How about \t and \v? \$\endgroup\$ – Adám Apr 25 '17 at 13:14
  • 1
    \$\begingroup\$ That's the user's problem, no? Control+K, maybe? \$\endgroup\$ – Adám Apr 25 '17 at 13:17
3
\$\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\$
  • \$\begingroup\$ Can't you replace KEY_*by the respective integers to save some bytes? \$\endgroup\$ – sergiol Mar 26 '18 at 13:52
2
\$\begingroup\$

VBScript, 23 40 bytes

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

enter image description here

\$\endgroup\$
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\$
  • \$\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 '17 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\$
  • \$\begingroup\$ Should I assume this works? Maybe you can add an explanation, or link, or animation? \$\endgroup\$ – Adám Apr 24 '17 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\$ – Jörg Hülsermann Apr 24 '17 at 13:09
  • \$\begingroup\$ Input = test></input><script>alert("Hi");//some malicious code\n</script> \$\endgroup\$ – Roman Gräf Apr 24 '17 at 13:56
  • \$\begingroup\$ @RomanGräf Bad Boy. Yes it is better to filter Javascript \$\endgroup\$ – Jörg Hülsermann Apr 24 '17 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\$
  • \$\begingroup\$ Can you explain this one? \$\endgroup\$ – Adám Apr 24 '17 at 20:41
  • 1
    \$\begingroup\$ I can add images of it running on a Windows command line. \$\endgroup\$ – sergiol Apr 24 '17 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 '17 at 8:37
  • \$\begingroup\$ This is enough. \$\endgroup\$ – Adám Apr 25 '17 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\$
  • \$\begingroup\$ @Adám I misread that part. I've updated the answer. \$\endgroup\$ – Engineer Toast Apr 25 '17 at 16:25
  • \$\begingroup\$ Now it is good. \$\endgroup\$ – Adám Apr 25 '17 at 18:20
1
\$\begingroup\$

Excel VBA Immediate Window Command - 22 bytes

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

enter image description here

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

ZX Spectrum BASIC, 7 bytes

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

INPUT a$: PRINT a$
\$\endgroup\$
  • 1
    \$\begingroup\$ How does it get the initial value of a$? \$\endgroup\$ – Adám Apr 25 '17 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\$ – Shaun Bebbers Jun 20 at 11:42
0
\$\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\$

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, privacy policy and cookie policy

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