http://qs1969.pair.com?node_id=11115029

ShainEdge has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to make a simple perl based notebook (for my own learning experience). Other then Adding new notes, and Deleting them, I would like to edit current notes.

Is there a way to insert a note text into the <STDIN> block, as if the person had typed it in, so that it can be edited before (entering) the new edit of the note?

So, for example:
The note "grocery list" already has "milk, butter, bread and eggs" stored.

Choosing the "E)dit note" and selecting the "grocery list" would print out the stored text in an editable format so that the user can make a change such as move to add ", peanut butter" after "bread" then enter the new text as the edited note.

It might be easier in Perl/Tk, but I was wondering if there is a solution in a command line version.

Replies are listed 'Best First'.
Re: pre-texted <STDIN>
by Corion (Patriarch) on Apr 04, 2020 at 10:53 UTC

    Unless you want to write your own text editor, just spawn the text editor your user wants, to edit their document, using Proc::InvokeEditor:

    use Proc::InvokeEditor; my $unedited_text = read_text_from_existing_note('note.txt'); my $edited_text = Proc::InvokeEditor->edit($unedited_text); write_text_to_existing_note('note.txt'); # update # or alternatively system($ENV{EDITOR}, '/tmp/note.txt') == 0 or warn "Couldn't launch editor '$ENV{EDITOR}': $!";
      That is an awesome response! Problem is with most programming languages is that you know there might be a simple solution, but without someone who has walked that walk before, sometimes the solution doesn't seem to have any way to get from here to there, in finding it. You know there is a package, but how do you know that "InvokeEditor" exists, much less that it does what you wanted it to.

        The CPAN search can help you find packages, and in this case, I saw when the module was released and have used it since.

Re: pre-texted <STDIN> with Term::ReadLine -- oneliner
by Discipulus (Canon) on Apr 04, 2020 at 11:48 UTC
    hello ShainEdge and welcome to the monastery and to the wonderful world of Perl!

    you can play with Term::ReadLine to accomplish your task. The following oneliner is a windows version and you have to force the underlaying readline module setting PERL_RL ENV var and also TERM one to be empty (not DUMB as cmd.exe IS DUMB ;)

    perl -MTerm::ReadLine -E "BEGIN{$ENV{PERL_RL}='Perl';$ENV{TERM}=''} $ +term = Term::ReadLine->new(); $txt='spaghetti,pomodoro,vino'; $txt=$t +erm->readline('edit:',$txt); say 'new:',$txt" edit:spaghetti,pomodoro,vino # you now edit the line erasing 'pomodor +o' and hit RETURN new:spaghetti, vino

    To adapt to Linux use single quotes around the oneliiner and double quotes inside it; you can probably remove the BEGIN block too.

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

      Ah! Thank you very much! I re-wrote it similar as a perl script, and it does what I was looking for it to do!

      I'm getting some non-critical errors in the script being run from the command line, based on it not being able to deal with the terminal size, but I should be able to get that sorted, if just dump the error messages to nul-space.

      "Unable to get Terminal Size. The Win32 GetConsoleScreenBufferInfo call didn't work. The COLUMNS and LINES environment variables didn't work. at C:/Strawberry/perl/vendor/lib/Term/ReadLine/readline.pm line 410.", in a Windows setting

      There is a great bunch of individuals here!

      Thank you for the greeting. This looks, so-far as I have read your script, what I was shooting for.

      I went to read the "Term::Readline" documentation, and I would not have guessed it was able to do as you have presented, reading that document. That is what I had mentioned in an earlier statment. If I were to just search for this, I could take days of looking at hints that it could do what I wanted, based on the name, but come away, after reading it, not knowing it was the proper package/function.

        > If I were to just search for this, I could take days of looking at hints that it could do what I wanted

        indeed! there is CPAN search but.. for all the remaining part there is perlmonks! ;)

        Nothing like the experience from other users (not speaking about me), infact the perl community is one its best plus.

        You can also find IO::Prompt an interesting solution (dunno atm if it can prefill input but you can adapt my example to it)

        L*

        PS changed the title for personal indexing matters

        There are no rules, there are no thumbs..
        Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: pre-texted <STDIN>
by hippo (Bishop) on Apr 04, 2020 at 10:54 UTC

    Do you mean something like running $ENV{VISUAL} on a buffer? In which case, yes, that should be fairly simple.

      I will have to look closer into this to see what this even means. Thanks!
Re: pre-texted <STDIN>
by bart (Canon) on Apr 04, 2020 at 12:15 UTC
    I'm going to take your title as a question, irrespective of any interpretation of what you really want to achieve. Can you instert extra text, say, from a text file, into the perl input via STDIN? Well, yes you can.

    If you set @ARGV to the paths of a number of files, while(<>) will read the files instead of STDIN. But add "-" (that's a minus sign) as a file namen, and perl will also read from normal STDIN.

    In summary: the following snippet will read a saved text file and input from STDIN.

    @ARGV = ('grocery_list.txt', '-'); while(<>) { print " * $_"; }

    This will read from STDIN until you somehow terminate it: read from a finite pipe, or enter ctrl-D or ctrl-Z interactively, depending on the platform.

      Not exactly. What I want to do, within the perl program, is take a $string, insert it into a STDIN-like function, have it user editable, and then save it back as a $string.

      So, what we are looking at is: $string = "this is a list". Insert that string in a real-time user-editable buffer. Then, have that edited string returned... such as "this is an 'edited' list." now in the $string variable

        So you're talking about the console, not about STDIN.

        The module RL can, theoretically, do that; see insert_test and stuff_char.

        However, I've not yet succeeded in installing it using CPAN on Windows, as it's missing "readline.h", so I'd have to get a port of "readline" (GNU?) to Windows first. MinGW might have it, but that's about all I know.

Re: pre-texted <STDIN>
by tybalt89 (Monsignor) on Apr 05, 2020 at 17:12 UTC

    Just as an excuse to play around with single line ANSI control sequences, I came up with this. It runs in an xterm on ArchLinux ( mostly because that's all I have to play with ).

    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11115029 use warnings; my $newline = promptandreviseline( "Previous Value : ", 'previous line +' ); print "The New Line is: $newline\n"; ###################################################################### use Term::ReadKey; use Data::Dump qw( pp ); sub promptandreviseline { my ($prompt, $line) = @_; $line //= ''; $line =~ tr/\t\n/ /; local $| = 1; my $startcolumn = 1 + length $prompt; print "\e[1G$prompt"; my $cursor = length $line; my $pos = $startcolumn + $cursor; print "\e[${startcolumn}G\e[K$line\e[${pos}G"; my $more = 1; ReadMode 'raw'; while( $more ) { sysread STDIN, my $input, 4096 or last; for my $key ( $input =~ /\e[^\e]+|./gs ) { if( $key =~ /^[\cc\e]\z/ ) { $more = 0 } # ^c escape elsif( $key =~ /^[\n\r]\z/ ) { print "\n"; $line .= "\n"; $more = 0 } elsif( $key =~ /^[ -~]$/ ) { substr $line, $cursor++, 0, $key } +# char elsif( $key eq "\b" ) # backspace { $cursor > 0 and substr $line, --$cursor, 1, ''; } elsif( $key eq "\e[3~" ) # Delete { $cursor < length($line) and substr $line, $cursor, 1, ''; } elsif( $key eq "\e[D" ) { $cursor > 0 and $cursor-- } # left arr +ow elsif( $key eq "\e[C" ) { $cursor < length($line) and ++$cursor +} # right elsif( $key eq "\e[F" ) { $cursor = length($line) } # End elsif( $key eq "\e[H" ) { $cursor = 0 } # Home else # invalid, show sequence { my $seq = pp $key; substr $line, $cursor, 0, $seq; $cursor += length $seq; } $pos = $startcolumn + $cursor; $more and print "\e[${startcolumn}G$line\e[K\e[${pos}G"; } } ReadMode 'restore'; return $line; }