[erlang-questions] how search and search_quit are handled by user_drv?
Fred Hebert
mononcqc@REDACTED
Tue May 26 14:46:31 CEST 2015
On 05/21, Leo Liu wrote:
>Press key C-r and then TAB in the eshell one can observe the prompt
>changes to history `search' and back to normal again.
>
>I try to understand how it works in details but my understanding does
>not match what I observe above.
>
>The following is what I think should have happened where `|' indicates
>the cursor and indented lines are messages the user_drv process receives
>which cause the prompt to change from state 1 to 5.
>
Hi,
I implemented the search functionality so I'm in a good position to
explain how it works.
The thing to understand about the Erlang shell is how it is being split
up [1]:
- user.erl (or a tty program) ends up hadnling stdio
- user_sup.erl handles initialization of the whole system
- user_drv handles translation between the internal Erlang IO protocol
and the driver (tty) stuff. It also handles job management (^G) and
choosing which shell is active.
- group.erl has an instance for each individual job/shell, does line
buffering (until they're ready to be evaluated), handles up/down
arrows (to switch lines), and so on.
- edlin.erl handles each individual *line* for cursor movement,
character deletion, and so on. It also handles things like
interpreting keyboard shortcuts (^A, ^H, etc.)
- shell.erl handles evaluation of Erlang code
So when functionality like ^r search gets implemented, it needs to work
both with group.erl (to cycle through lines, buffer the input, etc.) and
edlin.erl (to interpret ^R and ^S). The stuff in user_drv takes place at
an earlier time, and shell.erl at a latter time; both aren't connected
to search.
The other thing to understand is that while vi and vim are the most
known modal editors in the wild, the Erlang shell uses similar modes to
understand how to handle its stuff internally, even through emacs mode;
as such, <Esc>+B (backwards word) is represented in two characters: $\e
(escape), which switches the shell in 'meta' mode, and 'B', which is
interpreted as a special sequence under that mode rather than regular
ASCII input.
So the implementation works a bit as follows:
1. When ^R (backward search) or ^S (forward search) is seen in group.erl
and we're in the normal mode, we store the current line prompt ('1>')
and create a new one, then set edlin into the 'search' mode[2]
2. The search mode is enabled in edlin, which will handle interpreting
what to do in there. Edlin has a character mapping table[3] that lets
search interpret which mode the shell should be in. This means special
sequences to mean quitting search mode, ways to specify whether to look
back up or down again (^S or ^R has been pressed a second time), or
whether the character is just valid. This portion is really about
selecting the right *mode*.
3. The mapping is used to switch on operations [4] and identify proper
transformations to the visual representation of the buffer than needs to
be done [5]. This is because there's a difference between what we have
as an internal state, and how we represent it. This bit is about
*representation*.
4. Whenever the shell is in search mode, group.erl sends its data
through edlin, handles the result, and looks at characters that came in,
the current line buffer, and performs a search on each line, either
backwards or forwards according to the search direction [6]. It then
takes whatever line it has found (if any) and sends it to the driver to
output (bypassing or adding to what edlin had done before) to get the
right *representation* for the user.
This bit is tricky because while we display old user text in the line,
this text never makes it to edlin.erl -- what we do is tell the driver
"show this", while edlin holds the current search buffer in memory. The
trick here is that group.erl stores the current line stack position
(where in the list of line is the cursor?) so that user search doesn't
conflict with what we show on screen.
5. Whenever the user is satisfied with a result, they press <Enter>,
which edlin interprets as the 'search_found' state; group.erl interprets
this to fetch the current line buffer (which should be in a position
where a satisfactory entry was found!), copy it in the current topmost
position in the line buffer, and revert the prompt to normal with the
input ready to go [7].
6. If on the other hand the search is cancelled, the shell falls back
into edit mode, but at the current stack position [8] to replicate
existing bash/zsh behaviour. This lets you edit a result you had found
previously, in place.
7. Any other special meta modes (like search_meta_left_sq_bracket, which
handles fancy escape sequences while in shell mode) are tunneled through
to edlin because they have nothing to do with multiline business
group.erl handles, and everything to do with mode switching.
This is all rather complex, but respects (as well as possible) the
previously established separation of concerns of the Erlang shell
between line editing and buffer management.
Regards,
Fred.
[1]: http://ferd.ca/repl-a-bit-more-and-less-than-that.html
[2]:
https://github.com/erlang/otp/blob/maint/lib/stdlib/src/edlin.erl#L234
[3]:
https://github.com/erlang/otp/blob/maint/lib/stdlib/src/edlin.erl#L234
[4]:
https://github.com/erlang/otp/blob/maint/lib/stdlib/src/edlin.erl#L131
[5]:
https://github.com/erlang/otp/blob/maint/lib/stdlib/src/edlin.erl#L268-L318
[6]:
https://github.com/erlang/otp/blob/maint/lib/kernel/src/group.erl#L581-L605
[7]:
https://github.com/erlang/otp/blob/maint/lib/kernel/src/group.erl#L558-L564
[8]:
https://github.com/erlang/otp/blob/maint/lib/kernel/src/group.erl#L565-L580
More information about the erlang-questions
mailing list