How to erase a line in terminal on any keypress with readline in Haskell? - haskell

I am using the Haskell bindings to readline library for reading input from the user.
What I want to achieve is to offer a prefilled value that can be submitted by just hitting the return key, but if the user starts typing, I would like to erase the prefilled text automatically. (So that user does not have to care about erasing prefilled value.)
I think it could be done by using the rl_getc_function hook. However, the Haskell bindings do not expose this function.
Is there some way how to get the desired behaviour?

Finally I figured out a way to do it, but it is not nice and feels like a bit of a hack.
It works by rebinding all keys to a custom function that first erases the line, then resets all these handlers to actually insert the character again. In order not to lose the triggering keypress it is pushed back to input.
main :: IO ()
main = do
-- create a copy of current keymap
keymap <- getKeymap >>= copyKeymap
-- bind (hopefully all) regular keys to a helper function
forM_ (map toEnum [32..256]) $ \key ->
bindKey key $ \ _ _ -> do
-- delete all text on line
getEnd >>= deleteText 0
-- set cursor to start of line
setPoint 0
redisplay
-- reset keymap to the original one
setKeymap keymap
-- push pressed key back to input
setPendingInput key
return 0
setStartupHook (Just $ insertText "prefilled text")
readline "prompt> "
The major pain point of this is determining which keys to rebind. I used a scientific method of trial and error: the lower bound is space (anything less than that is non-printable, may have some special function and I don't want to mess with it). The upper bound is set to 256 – anything more resulted in segfault, using lesser number left the function not working on some unicode input.
Even with this range, it might not work if the user starts typing something in CJK. It definitely does not work correctly if the user types something by using a dead key.

Related

Understanding Haskell's Laziness

I was reading: Haskell interact function
So I tried
interact (unlines . map (show . length) . lines)
And it worked as I expected. I type something, press enter, then I get the length printed at the prompt.
So then I wanted to try make it simply repeat what I typed, so I tried
interact (unlines . map id . lines)
But now it repeats every character I type in. Why is that? I thought the trick was in the lines followed by unlines - but it's clearly not. lines "a" produces ["a"], so how come in the first function when I start typing my input, it doesn't just immediately give "1" as the output? There's clearly something I misunderstand about "Finding the length of a string is not like this -- the whole string must be known before any output can be produced."
The fact that lines "a" produces ["a"] does not mean that if you are currently entering a, that lines just processes the input to a list ["a"]. You should see the input as a (possibly) infinite list of characters. In case the prompt is waiting for user input, it is thus "blocking" on the next input.
That however does not mean that functions like lines can not partially resolve the result already. lines has been implemented in a lazy manner such that it processes the stream of characters, and each time when it sees a new line character, it starts emitting the next element. This thus means that lines could process an infinite sequence of characters into an infinite list of lines.
If you use length :: Foldable f => f a -> Int however, then this requires the list to be evaluated (not the elements of the list however). So that means length will only emit an answer from the moment lines starts emitting the next item.
You can use seq (and variants) to force the evaluation of a term before a certain action is done. For example seq :: a -> b -> b will evaluate the first parameter to Weak Head Normal Form (WHNF), and then return the second parameter.
Based on seq, other functions have been constructed, like seqList :: [a] -> [a] in the Data.Lists module of the lists package.
We can use this to postpone evaluation until the first line is known, like:
-- will echo full lines
import Data.Lists(seqList)
interact (unlines . map (\x -> seqList x x) . lines)
This is to do with lazy evaluation. I'll try to explain this in as intuitive a way as possible.
When you write interact (unlines . map (show . length) . lines), every time a character is input, we don't actually know what the next output character can be until you press enter. So, you get the behaviour you expected.
However, at every point in interact (unlines . map id . lines) = interact id, every time you enter a character, it's guaranteed that that character is included in the output. So, if you input a character, that character is also output immediately.
This is one of the reasons that the word "lazy" is a bit of a misnomer. It's true that Haskell will only evaluate something when it needs to, but the flipside of that is that when it needs to, it'll do so as soon as possible. Here Haskell needs to evaluate the output since you want to print it, so it evaluates it as much as it can—one character at a time—ironically making it seem eager!
More specifically, interact isn't intended for real time user input—it's intended for file input, in which you pipe a file into an executable with bash. It should be run something like this:
$ runhaskell Interactor.hs < my_big_file.txt > list_of_lengths.txt
If you want line-by-line buffering, you'll probably have to do it manually, unless you want to 'trick' the compiler as Willem does. Here's some very simple code that works as you expect—but note that it has no exit state unlike interact, which will terminate at the EOF.
main = do
ln <- getLine -- Buffers until you press enter
putStrLn ln -- Print the line we just got
main -- Loop forever

Can anything done in a Haskell script be reproduced in a GHCi session?

I want to run the function
act :: IO(Char, Char)
act = do x <- getChar
getChar
y <- getChar
return (x,y)
interactively in a GHCi session. I've seen elsewhere that you can define a function in a session by using the semi-colon to replace a line-break. However, when I write
act :: IO(Char, Char); act = do x <- getChar; getChar; y <- getChar; return (x,y)
it doesn't compile, saying
parse error on input ‘;’
I've elsewhere seen that :{ ... }: can be used for multiple line commands, but typing
:{ act :: IO(Char, Char)
and then hitting enter causes an error--perhaps I'm misunderstanding how to use them.
Besides just getting this particular case to work, is there a generic way of taking code that would run in a Haskell script and making it run in an interactive session?
You can't just insert semicolons to replace each line break. Doing stuff on one line means opting out of the layout rule, so you have to insert your own semicolons and braces. This means you need to know where those braces and semicolons would be required without the layout rule. For this case in particular, each do block needs braces around the whole block, and semicolons between each operation. The layout rule normally inserts these for you based on indentation.
So to write this specific example on one line, you can do this:
let act :: IO(Char, Char); act = do {x <- getChar; getChar; y <- getChar; return (x,y)}
On a new enough version of ghci you can omit the let as well.
For simple enough do blocks you might even get away with omitting the braces. In your example there's only one place the { and } could possibly go, and so GHCI inserts them even when you do everything on one line. But for an expression with multiple do blocks or other multiline constructs, you will need to insert them explicitly if you want them on one line.
On the broader question:
Besides just getting this particular case to work, is there a generic way of taking code that would run in a Haskell script and making it run in an interactive session?
The closest thing I know of is using the multiline delimiters, ":{ and :} (each on a single line of its own)". They can handle almost anything you can throw at them. They can't handle imports (GHCi does support the full import syntax, but each import must be on its own in a line) and pragmas (the only alternative is :set, which also need a line all of its own), which means you can't help but separate them from the rest of the code and enter them beforehand.
(You can always save the code somewhere and load the file with :l, and that will often turn out to be the more convenient option. Still, I have a soft spot for :{ and :} -- if I want no more than trying out half a dozen lines of impromptu code with no context, I tend to open a text editor window, write the little snippet and paste it directly in GHCi.)

EasyMotion repeat

I've replaced my fFtT completely with EasyMotion's equivalent and I've found it to be adequate in most cases except when I need to repeat the last motion with text objects. For example, dot command following ct or cf don't work the way they're supposed to. Is there a way make this work somehow, or do I have to resort to mapping the original ftFT for cases like this?
I try to be bold, without testing and say, NO, it cannot be repeated.
you typed some magic key (for example, f . Default <leader><leader>f), triggered easyMotion, try to move to letter x. but on your current screen, there are 10 x after your cursor. Then you typed c to move to the right one. now you try to type dot . to repeat it. how easyMotion know which x you want to go to next?

How to provide parameter in vim macro

Is it possible to provide some parameter when recording a macro in vim via prompt or some other ways?
Edit:
I have such code:
foo
bar
And I would like to surround each with ruby block:
expect do
foo
end.to raise_error(CustomClass)
expect do
foo
end.to raise_error(OtherCustomClass)
So, it is easy to create a macro that will result with:
expect do
foo
end.to raise_error()
expect do
foo
end.to raise_error()
But it will be nice to have prompt that will be used to set raise_error method parameter. In each use of macro this parameter will be different.
While I agree with everyone else that if you need this feature, you're probably going about things inefficiently, it is possible to insert a variable text string into a document as part of a macro. The trick is to store the text you want to use in your macro in a register.
yank some text into a named register, for example "xyw to yank a word into the x register
record your macro, qq, when you want to place the variable text, put it, for example "xp to put the text in the x register into the document where the cursor is
now, when you play your q macro, #q, it will use whatever is currently in the x register, so you can yank different text into the x register, play your q macro, and the newly yanked text will be used.
If you are talking about recording a macro with qx...q, this is not possible.
However you could still do : :let #y = substitute(#x, 'old_pattern', 'replacement', 'g') and then use #y.
You could also define a function:
function Play(myArg)
execute 'normal sequence_of_characters' . a:myArg . 'other_sequence_of_characters'
endfunction
call Play('foo')
Very particularly in the OP's situation, where you really only have precisely two variable pieces of content, I find the easiest method to be a bastardisation of #mkomitee's approach above.
Instead of manually saving the two ‘parameters’ into registers before each usage of the macro, I prefer to type the “first parameter,” visual-select it, evaluate the macro, then type the “second parameter.” To achieve this, I start the macro with a deletion command (a simple d, assuming you're always going to invoke the macro in visual-mode, for instance); then finish it with a command that switches to insert mode (like c or i), and finally, while still in insert mode, a Ctrl-O q to cause the macro to also leave Vim in insert mode when it's done.
As a slightly simple example, if the two “parameters” are single words, here's the keystrokes to create (and then invoke) a macro to manipulate widget.snog() to a parameterised widgetFoo.fooSnog(bar):
foob qq "zdw — we're now recording to the q register, with the first ‘argument’ in z
‸
"aP — prefix-paste from a fixed register used elsewhere in the document
widget.snog()‸
^ea␣Ctrl-rEscb~hx — paste the first arg, and capitalize
widget‸Foo.snog()
2w~b"zP — capitalize existing word, then paste the first arg again
widgetFoo.fo‸oSnog()
$Ctrl-Oq — move to the last position, enter insert-mode, and end the macro
widgetFoo.fooSnog(‸)
After finishing the first instance with bar, we can now use it several times:
obazEscb — set up our first ‘argument’,
widgetFoo.fooSnog(bar)
‸baz
#qquuxEsc — invoke the macro, and finish with the second one
widgetFoo.fooSnog(bar)
widgetBaz.bazSnog(quux‸)
ocorgeEscb##graultEsc — repeat a third time
widgetFoo.fooSnog(bar)
widgetBaz.bazSnog(quux)
widgetCorge.corgeSnog(grault‸)
ogarplyEscb##waldoEsc — … and so on
widgetFoo.fooSnog(bar)
widgetBaz.bazSnog(quux)
widgetCorge.corgeSnog(grault)
widgetGarply.garplySnog(waldo‸)
Of course, it looks laborious, typed out in such a long fashion — but it's surprisingly few key-strokes in practice, and very easy to train into your fingers.
tl;dr: type the first argument; enter macro-recording before deleting it into a register; manipulate your text as desired; then leave vim in insert-mode at the position of the second argument with Ctrl-Oq.
If you need to generate a code, which is the case, the best way for this is to use vim snippets. You can configure snippet to put cursor where you need when you [tab].

Enclosing the function call in another function call (retval as parameter)

Having this LOC:
printf("%s (%d)\t(%d)\t%d-%d\t", meta_scanner_token_name($ret['major']), $ret['major'], (string)$ret['dirty'], $ret['start_line'], $ret['minor']);
What is the fastest way in terms of key strokes to enclose the call to meta_scanner_token_name in another function call to foo, yelding:
printf("%s (%d)\t(%d)\t%d-%d\t", foo(meta_scanner_token_name($ret['major'])), $ret['major'], (string)$ret['dirty'], $ret['start_line'], $ret['minor']);
given that
first scenario: my cursor is on 'm' at the beginning of the function?
second scenario: my cursor is somewhere on meta_scanner_token_name?
va)oB would select the entire line, and ys%) would enclose only the m, resulting in:
... (m)eta_sca...
Please answer to both scenarios.
(I am using spf13-vim with default settings except some visual changes, if that has any relevance)
ifoo(<Esc> then f)i)<Esc>
bifoo(<Esc> then f)i)<Esc>
but I'm still a Vim noob
-- EDIT --
I see "Surrounding.vim" is a modified version of "Surround.vim" if it's compatible with Surround you can do:
Scenario 1
vt,sffoo<CR>
vt, to select everything until the first ,
s to launch Surround.vim
f to instruct Surround to input a "function"
foo the identifier
<CR> Enter key.
That's 6 keystrokes not including typing foo which — I think — can't really be avoided.
Scenario 2
bvt,sffoo<CR>
It's the same as scenario 1 except that you type b first to go back to the first letter of meta_scanner_token_name.
Using normal vim you could do this (prefix with b for scenario 2)
`cf)foo()<esc>P`
If your vim plugins add the closing paren for you, you can drop that from the sequence. Depending on where it leaves your cursor, you might need to use p instead of P.

Resources