Packages

nerves_system_br

1.23.0
1.34.0 1.33.9 1.33.8 1.33.7 1.33.6 1.33.5 1.33.4 1.33.3 1.33.2 1.33.1 1.33.0 1.32.5 1.32.4 1.32.3 1.32.2 1.32.1 1.32.0 1.31.7 1.31.6 1.31.5 1.31.4 1.31.3 1.31.2 1.31.1 1.31.0 1.30.1 1.30.0 1.29.3 1.29.2 1.29.1 1.29.0 1.28.3 1.28.2 1.28.1 1.28.0 1.27.3 1.27.2 1.27.1 1.27.0 1.26.1 1.26.0 1.25.3 1.25.2 1.25.1 1.25.0 1.24.1 1.24.0 1.23.3 1.23.2 1.23.1 1.23.0 1.22.8 1.22.7 1.22.6 1.22.5 1.22.4 1.22.3 1.22.2 1.22.1 1.22.0 1.21.6 1.21.5 1.21.4 1.21.3 1.21.2 1.21.1 1.21.0 1.20.6 1.20.5 1.20.4 1.20.3 1.20.2 1.20.1 1.20.0 1.19.1 1.19.0 1.18.6 1.18.5 1.18.4 1.18.3 1.18.2 1.18.1 1.18.0 1.17.4 1.17.3 1.17.2 1.17.1 1.17.0 1.16.5 1.16.4 1.16.3 1.16.2 1.16.1 1.16.0 1.15.2 1.15.1 1.15.0 1.14.5 1.14.4 1.14.3 1.14.2 1.14.1 1.14.0 1.13.8 1.13.7 1.13.6 1.13.5 1.13.4 1.13.3 1.13.2 1.13.1 1.13.0 1.12.4 1.12.3 1.12.2 1.12.1 1.12.0 1.12.0-rc.0 1.11.4 1.11.3 1.11.2 1.11.1 1.11.0 1.10.3 1.10.2 1.10.1 1.10.0 1.9.5 1.9.4 1.9.3 1.9.2 1.9.1 1.9.0 1.8.6 1.8.5 1.8.4 1.8.3 1.8.2 1.8.1 1.8.0 1.7.3 1.7.2 1.7.1 1.7.0 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.6 1.5.5 1.5.4 1.5.3 1.5.2 1.5.1 1.5.0 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.2 1.2.1 1.2.0 1.1.0 1.0.1 1.0.0 1.0.0-rc.4 1.0.0-rc.3 1.0.0-rc.2 1.0.0-rc.1 1.0.0-rc.0 0.17.0 0.16.4 0.16.3 0.16.2 0.16.1-2017-11 0.16.0-2017-11 0.15.1 0.15.0 0.14.1 0.14.0 0.13.9 0.13.8 0.13.7 0.13.6 0.13.5 0.13.4 retired 0.13.3 0.13.2 0.13.0 0.12.1 0.12.0 0.11.1 0.11.0 0.10.1 0.10.0 0.9.4 0.9.3 0.9.2 0.9.1 0.9.0 0.8.2 0.8.1 0.8.0 0.7.0 0.6.1 0.6.0 0.5.2 0.5.1 0.5.0 0.4.2 0.4.1

Nerves System BR - Buildroot based build platform for Nerves Systems

Current section

Files

Jump to
nerves_system_br patches buildroot 0015-erlang-revert-multiline-editing-for-OTP-26.0.patch
Raw

patches/buildroot/0015-erlang-revert-multiline-editing-for-OTP-26.0.patch

From 18468e69e463b69117ce1423ff9c90aad4e60eeb Mon Sep 17 00:00:00 2001
From: Frank Hunleth <fhunleth@troodon-software.com>
Date: Mon, 5 Jun 2023 14:51:35 -0400
Subject: [PATCH] erlang: revert multiline editing for OTP 26.0
---
.../26.0/0003-Revert-multiline-editing.patch | 2185 +++++++++++++++++
1 file changed, 2185 insertions(+)
create mode 100644 package/erlang/26.0/0003-Revert-multiline-editing.patch
diff --git a/package/erlang/26.0/0003-Revert-multiline-editing.patch b/package/erlang/26.0/0003-Revert-multiline-editing.patch
new file mode 100644
index 0000000000..4fc0415fa6
--- /dev/null
+++ b/package/erlang/26.0/0003-Revert-multiline-editing.patch
@@ -0,0 +1,2185 @@
+From 19fe6e1ba9b679c0903fb6815800ffa9787050aa Mon Sep 17 00:00:00 2001
+From: Frank Hunleth <fhunleth@troodon-software.com>
+Date: Mon, 5 Jun 2023 08:57:03 -0400
+Subject: [PATCH] Revert multiline editing
+
+This reverts commit 16139b6a35944583b4f1ef05c7698bfe2f771c7a, reversing
+changes made to 8305dd5e2b962ada3403a6fc0c600367afdfec5e.
+---
+ lib/kernel/src/group.erl | 348 +++++------
+ lib/kernel/src/prim_tty.erl | 380 ++----------
+ lib/kernel/src/user_drv.erl | 48 +-
+ lib/kernel/test/interactive_shell_SUITE.erl | 150 +----
+ lib/stdlib/src/edlin.erl | 602 ++++++++------------
+ 5 files changed, 487 insertions(+), 1041 deletions(-)
+
+diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl
+index ca7251d7ec..311c2f3c5e 100644
+--- a/lib/kernel/src/group.erl
++++ b/lib/kernel/src/group.erl
+@@ -483,56 +483,50 @@ get_chars_n(Prompt, M, F, Xa, Drv, Shell, Buf, Encoding) ->
+ Pbs = prompt_bytes(Prompt, Encoding),
+ case get(echo) of
+ true ->
+- get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, start, [], Encoding);
++ get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, start, Encoding);
+ false ->
+ get_chars_n_loop(Pbs, M, F, Xa, Drv, Shell, Buf, start, Encoding)
+ end.
+
+ get_chars_line(Prompt, M, F, Xa, Drv, Shell, Buf, Encoding) ->
+ Pbs = prompt_bytes(Prompt, Encoding),
+- get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, start, [], Encoding).
++ get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, start, Encoding).
+
+-get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf0, State, LineCont0, Encoding) ->
++get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf0, State, Encoding) ->
+ Result = case get(echo) of
+- true ->
+- get_line(Buf0, Pbs, LineCont0, Drv, Shell, Encoding);
+- false ->
+- %% get_line_echo_off only deals with lists
+- %% and does not need encoding...
+- get_line_echo_off(Buf0, Pbs, Drv, Shell)
+- end,
++ true ->
++ get_line(Buf0, Pbs, Drv, Shell, Encoding);
++ false ->
++ % get_line_echo_off only deals with lists
++ % and does not need encoding...
++ get_line_echo_off(Buf0, Pbs, Drv, Shell)
++ end,
+ case Result of
+- {done,LineCont1,Buf} ->
+- get_chars_apply(Pbs, M, F, Xa, Drv, Shell, Buf, State, LineCont1, Encoding);
+-
+- interrupted ->
+- {error,{error,interrupted},[]};
+- terminated ->
+- {exit,terminated}
++ {done,Line,Buf} ->
++ get_chars_apply(Pbs, M, F, Xa, Drv, Shell, Buf, State, Line, Encoding);
++ interrupted ->
++ {error,{error,interrupted},[]};
++ terminated ->
++ {exit,terminated}
+ end.
+
+-get_chars_apply(Pbs, M, F, Xa, Drv, Shell, Buf, State0, LineCont, Encoding) ->
+- %% multi line support means that we should not keep the state
+- %% but we need to keep it for oldshell mode
+- {State, Line} = case get(echo) of
+- true -> {start, edlin:current_line(LineCont)};
+- false -> {State0, LineCont}
+- end,
+- case catch M:F(State, cast(Line,get(read_mode), Encoding), Encoding, Xa) of
++get_chars_apply(Pbs, M, F, Xa, Drv, Shell, Buf, State0, Line, Encoding) ->
++ case catch M:F(State0, cast(Line,get(read_mode), Encoding), Encoding, Xa) of
+ {stop,Result,eof} ->
+ {ok,Result,eof};
+ {stop,Result,Rest} ->
+- _ = case {M,F} of
+- {io_lib, get_until} ->
+- save_line_buffer(string:trim(Line, both)++"\n", get_lines(new_stack(get(line_buffer))));
+- _ ->
+- skip
+- end,
+- {ok,Result,append(Rest, Buf, Encoding)};
++ case {M,F} of
++ {io_lib, get_until} ->
++ save_line_buffer(Line, get_lines(new_stack(get(line_buffer)))),
++ {ok,Result,append(Rest, Buf, Encoding)};
++ _ ->
++ {ok,Result,append(Rest, Buf, Encoding)}
++ end;
+ {'EXIT',_} ->
+ {error,{error,err_func(M, F, Xa)},[]};
+ State1 ->
+- get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, State1, LineCont, Encoding)
++ save_line_buffer(Line, get_lines(new_stack(get(line_buffer)))),
++ get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, State1, Encoding)
+ end.
+
+ get_chars_n_loop(Pbs, M, F, Xa, Drv, Shell, Buf0, State, Encoding) ->
+@@ -564,73 +558,65 @@ err_func(_, F, _) ->
+ %% Get a line with eventual line editing. Handle other io requests
+ %% while getting line.
+ %% Returns:
+-%% {done,LineChars,RestChars}
+-%% interrupted
+-get_line(Chars, Pbs, Cont, Drv, Shell, Encoding) ->
+- {more_chars,Cont1,Rs} = case Cont of
+- [] -> edlin:start(Pbs);
+- _ -> edlin:start(Pbs, Cont)
+- end,
++%% {done,LineChars,RestChars}
++%% interrupted
++
++get_line(Chars, Pbs, Drv, Shell, Encoding) ->
++ {more_chars,Cont,Rs} = edlin:start(Pbs),
+ send_drv_reqs(Drv, Rs),
+- get_line1(edlin:edit_line(Chars, Cont1), Drv, Shell, new_stack(get(line_buffer)),
+- Encoding).
++ get_line1(edlin:edit_line(Chars, Cont), Drv, Shell, new_stack(get(line_buffer)),
++ Encoding).
+
+-get_line1({done, Cont, Rest, Rs}, Drv, _Shell, _Ls, _Encoding) ->
++get_line1({done,Line,Rest,Rs}, Drv, _Shell, _Ls, _Encoding) ->
+ send_drv_reqs(Drv, Rs),
+- {done, Cont, Rest};
++ {done,Line,Rest};
+ get_line1({undefined,{_A, Mode, Char}, _Cs, Cont, Rs}, Drv, Shell, Ls0, Encoding)
+- when Mode =:= none, Char =:= $\^O;
+- Mode =:= meta, Char =:= $o ->
++ when ((Mode =:= none) and (Char =:= $\^O)) ->
+ send_drv_reqs(Drv, Rs),
+- Buffer = edlin:current_line(Cont),
++ Buffer = edlin:current_chars(Cont),
+ send_drv(Drv, {open_editor, Buffer}),
+ receive
+ {Drv, {editor_data, Cs}} ->
+- send_drv_reqs(Drv, edlin:erase_line()),
++ send_drv_reqs(Drv, edlin:erase_line(Cont)),
+ {more_chars,NewCont,NewRs} = edlin:start(edlin:prompt(Cont)),
+ send_drv_reqs(Drv, NewRs),
+ get_line1(edlin:edit_line(Cs, NewCont), Drv, Shell, Ls0, Encoding)
+ end;
+-%% Move Up, Down in History: Ctrl+P, Ctrl+N
+ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Shell, Ls0, Encoding)
+- when Mode =:= none, Char =:= $\^P;
+- Mode =:= meta_left_sq_bracket, Char =:= $A ->
++ when ((Mode =:= none) and (Char =:= $\^P))
++ or ((Mode =:= meta_left_sq_bracket) and (Char =:= $A)) ->
+ send_drv_reqs(Drv, Rs),
+ case up_stack(save_line(Ls0, edlin:current_line(Cont))) of
+- {none,_Ls} ->
+- send_drv(Drv, beep),
+- get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls0, Encoding);
+- {Lcs,Ls} ->
+- send_drv_reqs(Drv, edlin:erase_line()),
+- {more_chars,Ncont,Nrs} = edlin:start(edlin:prompt(Cont)),
+- send_drv_reqs(Drv, Nrs),
+- get_line1(edlin:edit_line1(string:to_graphemes(lists:sublist(Lcs,
+- 1,
+- length(Lcs)-1)),
+- Ncont),
+- Drv,
+- Shell,
+- Ls, Encoding)
++ {none,_Ls} ->
++ send_drv(Drv, beep),
++ get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls0, Encoding);
++ {Lcs,Ls} ->
++ send_drv_reqs(Drv, edlin:erase_line(Cont)),
++ {more_chars,Ncont,Nrs} = edlin:start(edlin:prompt(Cont)),
++ send_drv_reqs(Drv, Nrs),
++ get_line1(edlin:edit_line1(lists:sublist(Lcs, 1, length(Lcs)-1),
++ Ncont),
++ Drv,
++ Shell,
++ Ls, Encoding)
+ end;
+ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Shell, Ls0, Encoding)
+- when Mode =:= none, Char =:= $\^N;
+- Mode =:= meta_left_sq_bracket, Char =:= $B ->
++ when ((Mode =:= none) and (Char =:= $\^N))
++ or ((Mode =:= meta_left_sq_bracket) and (Char =:= $B)) ->
+ send_drv_reqs(Drv, Rs),
+ case down_stack(save_line(Ls0, edlin:current_line(Cont))) of
+- {none,_Ls} ->
+- send_drv(Drv, beep),
+- get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls0, Encoding);
+- {Lcs,Ls} ->
+- send_drv_reqs(Drv, edlin:erase_line()),
+- {more_chars,Ncont,Nrs} = edlin:start(edlin:prompt(Cont)),
+- send_drv_reqs(Drv, Nrs),
+- get_line1(edlin:edit_line1(string:to_graphemes(lists:sublist(Lcs,
+- 1,
+- length(Lcs)-1)),
+- Ncont),
+- Drv,
+- Shell,
+- Ls, Encoding)
++ {none,_Ls} ->
++ send_drv(Drv, beep),
++ get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls0, Encoding);
++ {Lcs,Ls} ->
++ send_drv_reqs(Drv, edlin:erase_line(Cont)),
++ {more_chars,Ncont,Nrs} = edlin:start(edlin:prompt(Cont)),
++ send_drv_reqs(Drv, Nrs),
++ get_line1(edlin:edit_line1(lists:sublist(Lcs, 1, length(Lcs)-1),
++ Ncont),
++ Drv,
++ Shell,
++ Ls, Encoding)
+ end;
+ %% ^R = backward search, ^S = forward search.
+ %% Search is tricky to implement and does a lot of back-and-forth
+@@ -643,13 +629,15 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Shell, Ls0, Encoding)
+ %% the regular ones (none, meta_left_sq_bracket) and handle special
+ %% cases of history search.
+ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Shell, Ls, Encoding)
+- when Mode =:= none, Char =:= $\^R ->
++ when ((Mode =:= none) and (Char =:= $\^R)) ->
+ send_drv_reqs(Drv, Rs),
+ %% drop current line, move to search mode. We store the current
+ %% prompt ('N>') and substitute it with the search prompt.
+- put(search_quit_prompt, Cont),
+- Pbs = prompt_bytes("\033[;1;4msearch:\033[0m ", Encoding),
+- {more_chars,Ncont,_Nrs} = edlin:start(Pbs, search),
++ send_drv_reqs(Drv, edlin:erase_line(Cont)),
++ put(search_quit_prompt, edlin:prompt(Cont)),
++ Pbs = prompt_bytes("(search)`': ", Encoding),
++ {more_chars,Ncont,Nrs} = edlin:start(Pbs, search),
++ send_drv_reqs(Drv, Nrs),
+ get_line1(edlin:edit_line1(Cs, Ncont), Drv, Shell, Ls, Encoding);
+ get_line1({Expand, Before, Cs0, Cont,Rs}, Drv, Shell, Ls0, Encoding)
+ when Expand =:= expand; Expand =:= expand_full ->
+@@ -696,98 +684,66 @@ get_line1({Expand, Before, Cs0, Cont,Rs}, Drv, Shell, Ls0, Encoding)
+ _ ->
+ %% If there are more results than fit on
+ %% screen we expand above
+- send_drv_reqs(Drv, [{put_chars_keep_state, unicode, NlMatchStr},redraw_prompt]),
++ send_drv(Drv, {put_chars, unicode, NlMatchStr}),
+ [$\e, $l | Cs1]
+ end
+ end;
+ false ->
+- send_drv(Drv, {put_chars_keep_state, unicode, NlMatchStr}),
++ send_drv(Drv, {put_chars, unicode, NlMatchStr}),
+ [$\e, $l | Cs1]
+ end
+ end,
+ get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls0, Encoding);
+-get_line1({undefined, {_, search_quit, _}, _Cs, _Cont={line, P, Line, none}, Rs}, Drv, Shell, Ls, Encoding) ->
+- get_line1({more_chars, {line, P, Line, search_quit}, Rs}, Drv, Shell, Ls, Encoding);
+ get_line1({undefined,_Char,Cs,Cont,Rs}, Drv, Shell, Ls, Encoding) ->
+ send_drv_reqs(Drv, Rs),
+ send_drv(Drv, beep),
+ get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls, Encoding);
+ %% The search item was found and accepted (new line entered on the exact
+ %% result found)
+-get_line1({_What,{line,_,_Drv,search_found},Rs}, Drv, Shell, Ls0, Encoding) ->
+- SearchResult = get(search_result),
+- LineCont = case SearchResult of
+- [] -> {[],{[],[]},[]};
+- _ -> [Last| LB] = lists:reverse(SearchResult),
+- {LB, {lists:reverse(Last),[]},[]}
+- end,
+- Prompt = edlin:prompt(get(search_quit_prompt)),
+- send_drv_reqs(Drv, Rs),
+- send_drv_reqs(Drv, edlin:erase_line()),
+- send_drv_reqs(Drv, edlin:redraw_line({line, Prompt, LineCont, none})),
+- put(search_result, []),
+- %% TODO, do even need to save it, won't it be saved by handling {done...}?
+- Ls = save_line(new_stack(get_lines(Ls0)), edlin:current_line({line, edlin:prompt(get(search_quit_prompt)), LineCont, none})),
+- get_line1({done, LineCont, "\n", Rs}, Drv, Shell, Ls, Encoding);
++get_line1({_What,Cont={line,_Prompt,_Chars,search_found},Rs}, Drv, Shell, Ls0, Encoding) ->
++ Line = edlin:current_line(Cont),
++ %% this may create duplicate entries.
++ Ls = save_line(new_stack(get_lines(Ls0)), Line),
++ get_line1({done, Line, "", Rs}, Drv, Shell, Ls, Encoding);
+ %% The search mode has been exited, but the user wants to remain in line
+ %% editing mode wherever that was, but editing the search result.
+-get_line1({What,{line,_,_,search_quit},Rs}, Drv, Shell, Ls, Encoding) ->
++get_line1({What,Cont={line,_Prompt,_Chars,search_quit},Rs}, Drv, Shell, Ls, Encoding) ->
++ Line = edlin:current_chars(Cont),
+ %% Load back the old prompt with the correct line number.
+- case edlin:prompt(get(search_quit_prompt)) of
+- Prompt -> % redraw the line and keep going with the same stack position
+- SearchResult = get(search_result),
+- L = case SearchResult of
+- [] -> {[],{[],[]},[]};
+- _ -> [Last|LB] = lists:reverse(SearchResult),
+- {LB, {lists:reverse(Last), []}, []}
+- end,
+- NCont = {line,Prompt,L,none},
+- put(search_result, []),
+- send_drv_reqs(Drv, [delete_line|Rs]),
+- send_drv_reqs(Drv, edlin:redraw_line(NCont)),
+- get_line1({What, NCont ,[]}, Drv, Shell, pad_stack(Ls), Encoding)
++ case get(search_quit_prompt) of
++ undefined -> % should not happen. Fallback.
++ LsFallback = save_line(new_stack(get_lines(Ls)), Line),
++ get_line1({done, "\n", Line, Rs}, Drv, Shell, LsFallback, Encoding);
++ Prompt -> % redraw the line and keep going with the same stack position
++ NCont = {line,Prompt,{lists:reverse(Line),[]},none},
++ send_drv_reqs(Drv, Rs),
++ send_drv_reqs(Drv, edlin:erase_line(Cont)),
++ send_drv_reqs(Drv, edlin:redraw_line(NCont)),
++ get_line1({What, NCont ,[]}, Drv, Shell, pad_stack(Ls), Encoding)
+ end;
+-get_line1({What,_Cont={line,_,_,search_cancel},Rs}, Drv, Shell, Ls, Encoding) ->
+- NCont = get(search_quit_prompt),
+- put(search_result, []),
+- send_drv_reqs(Drv, [delete_line|Rs]),
+- send_drv_reqs(Drv, edlin:redraw_line(NCont)),
+- get_line1({What, NCont, []}, Drv, Shell, Ls, Encoding);
+ %% Search mode is entered.
+-get_line1({What,{line,Prompt,{_,{RevCmd0,_},_},search},_Rs},
+- Drv, Shell, Ls0, Encoding) ->
++get_line1({What,{line,Prompt,{RevCmd0,_Aft},search},Rs},
++ Drv, Shell, Ls0, Encoding) ->
++ send_drv_reqs(Drv, Rs),
+ %% Figure out search direction. ^S and ^R are returned through edlin
+ %% whenever we received a search while being already in search mode.
+ {Search, Ls1, RevCmd} = case RevCmd0 of
+- [$\^S|RevCmd1] ->
+- {fun search_down_stack/2, Ls0, RevCmd1};
+- [$\^R|RevCmd1] ->
+- {fun search_up_stack/2, Ls0, RevCmd1};
+- _ -> % new search, rewind stack for a proper search.
+- {fun search_up_stack/2, new_stack(get_lines(Ls0)), RevCmd0}
+- end,
++ [$\^S|RevCmd1] ->
++ {fun search_down_stack/2, Ls0, RevCmd1};
++ [$\^R|RevCmd1] ->
++ {fun search_up_stack/2, Ls0, RevCmd1};
++ _ -> % new search, rewind stack for a proper search.
++ {fun search_up_stack/2, new_stack(get_lines(Ls0)), RevCmd0}
++ end,
+ Cmd = lists:reverse(RevCmd),
+ {Ls, NewStack} = case Search(Ls1, Cmd) of
+- {none, Ls2} ->
+- send_drv(Drv, beep),
+- put(search_result, []),
+- send_drv(Drv, delete_line),
+- send_drv(Drv, {put_chars, unicode, unicode:characters_to_binary(Prompt++Cmd)}),
+- {Ls2, {[],{RevCmd, []},[]}};
+- {Line, Ls2} -> % found. Complete the output edlin couldn't have done.
+- Lines = string:split(string:to_graphemes(Line), "\n", all),
+- Output = if length(Lines) > 5 ->
+- [A,B,C,D,E|_]=Lines,
+- (["\n " ++ Line1 || Line1 <- [A,B,C,D,E]] ++
+- [io_lib:format("~n ... (~w lines omitted)",[length(Lines)-5])]);
+- true -> ["\n " ++ Line1 || Line1 <- Lines]
+- end,
+- put(search_result, Lines),
+- send_drv(Drv, delete_line),
+- send_drv(Drv, {put_chars, unicode, unicode:characters_to_binary(Prompt++Cmd)}),
+- send_drv(Drv, {put_expand_no_trim, unicode, unicode:characters_to_binary(Output)}),
+- {Ls2, {[],{RevCmd, []},[]}}
+- end,
++ {none, Ls2} ->
++ send_drv(Drv, beep),
++ {Ls2, {RevCmd, "': "}};
++ {Line, Ls2} -> % found. Complete the output edlin couldn't have done.
++ send_drv_reqs(Drv, [{put_chars, Encoding, Line}]),
++ {Ls2, {RevCmd, "': "++Line}}
++ end,
+ Cont = {line,Prompt,NewStack,search},
+ more_data(What, Cont, Drv, Shell, Ls, Encoding);
+ get_line1({What,Cont0,Rs}, Drv, Shell, Ls, Encoding) ->
+@@ -796,32 +752,29 @@ get_line1({What,Cont0,Rs}, Drv, Shell, Ls, Encoding) ->
+
+ more_data(What, Cont0, Drv, Shell, Ls, Encoding) ->
+ receive
+- {Drv, activate} ->
+- send_drv_reqs(Drv, edlin:redraw_line(Cont0)),
+- more_data(What, Cont0, Drv, Shell, Ls, Encoding);
+- {Drv,{data,Cs}} ->
+- get_line1(edlin:edit_line(Cs, Cont0), Drv, Shell, Ls, Encoding);
+- {Drv,eof} ->
+- get_line1(edlin:edit_line(eof, Cont0), Drv, Shell, Ls, Encoding);
+- {io_request,From,ReplyAs,Req} when is_pid(From) ->
+- {more_chars,Cont,_More} = edlin:edit_line([], Cont0),
+- send_drv_reqs(Drv, edlin:erase_line()),
+- io_request(Req, From, ReplyAs, Drv, Shell, []), %WRONG!!!
+- send_drv_reqs(Drv, edlin:redraw_line(Cont)),
+- get_line1({more_chars,Cont,[]}, Drv, Shell, Ls, Encoding);
++ {Drv,{data,Cs}} ->
++ get_line1(edlin:edit_line(Cs, Cont0), Drv, Shell, Ls, Encoding);
++ {Drv,eof} ->
++ get_line1(edlin:edit_line(eof, Cont0), Drv, Shell, Ls, Encoding);
++ {io_request,From,ReplyAs,Req} when is_pid(From) ->
++ {more_chars,Cont,_More} = edlin:edit_line([], Cont0),
++ send_drv_reqs(Drv, edlin:erase_line(Cont)),
++ io_request(Req, From, ReplyAs, Drv, Shell, []), %WRONG!!!
++ send_drv_reqs(Drv, edlin:redraw_line(Cont)),
++ get_line1({more_chars,Cont,[]}, Drv, Shell, Ls, Encoding);
+ {reply,{From,ReplyAs},Reply} ->
+ %% We take care of replies from puts here as well
+ io_reply(From, ReplyAs, Reply),
+ more_data(What, Cont0, Drv, Shell, Ls, Encoding);
+- {'EXIT',Drv,interrupt} ->
+- interrupted;
+- {'EXIT',Drv,_} ->
+- terminated;
+- {'EXIT',Shell,R} ->
+- exit(R)
++ {'EXIT',Drv,interrupt} ->
++ interrupted;
++ {'EXIT',Drv,_} ->
++ terminated;
++ {'EXIT',Shell,R} ->
++ exit(R)
+ after
+- get_line_timeout(What)->
+- get_line1(edlin:edit_line([], Cont0), Drv, Shell, Ls, Encoding)
++ get_line_timeout(What)->
++ get_line1(edlin:edit_line([], Cont0), Drv, Shell, Ls, Encoding)
+ end.
+
+ get_line_echo_off(Chars, Pbs, Drv, Shell) ->
+@@ -861,21 +814,21 @@ get_chars_echo_off1(Drv, Shell) ->
+ receive
+ {Drv, {data, Cs}} ->
+ Cs;
+- {Drv, eof} ->
++ {Drv, eof} ->
+ eof;
+- {io_request,From,ReplyAs,Req} when is_pid(From) ->
+- io_request(Req, From, ReplyAs, Drv, Shell, []),
+- get_chars_echo_off1(Drv, Shell);
++ {io_request,From,ReplyAs,Req} when is_pid(From) ->
++ io_request(Req, From, ReplyAs, Drv, Shell, []),
++ get_chars_echo_off1(Drv, Shell);
+ {reply,{From,ReplyAs},Reply} when From =/= undefined ->
+ %% We take care of replies from puts here as well
+ io_reply(From, ReplyAs, Reply),
+ get_chars_echo_off1(Drv, Shell);
+- {'EXIT',Drv,interrupt} ->
+- interrupted;
+- {'EXIT',Drv,_} ->
+- terminated;
+- {'EXIT',Shell,R} ->
+- exit(R)
++ {'EXIT',Drv,interrupt} ->
++ interrupted;
++ {'EXIT',Drv,_} ->
++ terminated;
++ {'EXIT',Shell,R} ->
++ exit(R)
+ end.
+
+ %% We support line editing for the ICANON mode except the following
+@@ -956,8 +909,8 @@ get_all_lines({stack, U, {}, []}) ->
+ U;
+ get_all_lines({stack, U, {}, D}) ->
+ case lists:reverse(D, U) of
+- ["\n"|Lines] -> Lines;
+- Lines -> Lines
++ ["\n"|Lines] -> Lines;
++ Lines -> Lines
+ end;
+ get_all_lines({stack, U, L, D}) ->
+ get_all_lines({stack, U, {}, [L|D]}).
+@@ -981,22 +934,22 @@ save_line_buffer(Lines) ->
+
+ search_up_stack(Stack, Substr) ->
+ case up_stack(Stack) of
+- {none,NewStack} -> {none,NewStack};
+- {L, NewStack} ->
++ {none,NewStack} -> {none,NewStack};
++ {L, NewStack} ->
+ case string:find(L, Substr) of
+ nomatch -> search_up_stack(NewStack, Substr);
+ _ -> {string:trim(L, trailing, "$\n"), NewStack}
+- end
++ end
+ end.
+
+ search_down_stack(Stack, Substr) ->
+ case down_stack(Stack) of
+- {none,NewStack} -> {none,NewStack};
+- {L, NewStack} ->
+- case string:find(L, Substr) of
+- nomatch -> search_down_stack(NewStack, Substr);
+- _ -> {string:trim(L, trailing, "$\n"), NewStack}
+- end
++ {none,NewStack} -> {none,NewStack};
++ {L, NewStack} ->
++ case string:find(L, Substr) of
++ nomatch -> search_down_stack(NewStack, Substr);
++ _ -> {string:trim(L, trailing, "$\n"), NewStack}
++ end
+ end.
+
+
+@@ -1010,15 +963,16 @@ get_password1({Chars,[]}, Drv, Shell) ->
+ {Drv,{data,Cs}} ->
+ get_password1(edit_password(Cs,Chars),Drv,Shell);
+ {io_request,From,ReplyAs,Req} when is_pid(From) ->
++ %send_drv_reqs(Drv, [{delete_chars, -length(Pbs)}]),
+ io_request(Req, From, ReplyAs, Drv, Shell, []), %WRONG!!!
+ %% I guess the reason the above line is wrong is that Buf is
+ %% set to []. But do we expect anything but plain output?
+
+- get_password1({Chars, []}, Drv, Shell);
++ get_password1({Chars, []}, Drv, Shell);
+ {reply,{From,ReplyAs},Reply} ->
+ %% We take care of replies from puts here as well
+ io_reply(From, ReplyAs, Reply),
+- get_password1({Chars, []}, Drv, Shell);
++ get_password1({Chars, []},Drv, Shell);
+ {'EXIT',Drv,interrupt} ->
+ interrupted;
+ {'EXIT',Drv,_} ->
+diff --git a/lib/kernel/src/prim_tty.erl b/lib/kernel/src/prim_tty.erl
+index a03e746cc9..4ef10c752d 100644
+--- a/lib/kernel/src/prim_tty.erl
++++ b/lib/kernel/src/prim_tty.erl
+@@ -136,8 +136,6 @@
+ writer,
+ options,
+ unicode,
+- lines_before = [], %% All lines before the current line in reverse order
+- lines_after = [], %% All lines after the current line.
+ buffer_before = [], %% Current line before cursor in reverse
+ buffer_after = [], %% Current line after cursor not in reverse
+ buffer_expand, %% Characters in expand buffer
+@@ -168,20 +166,10 @@
+ -type request() ::
+ {putc_raw, binary()} |
+ {putc, unicode:unicode_binary()} |
+- {putc_keep_state, unicode:unicode_binary()} |
+ {expand, unicode:unicode_binary()} |
+- {expand_with_trim, unicode:unicode_binary()} |
+ {insert, unicode:unicode_binary()} |
+ {delete, integer()} |
+- delete_after_cursor |
+- delete_line |
+- redraw_prompt |
+- {redraw_prompt, string(), string(), tuple()} |
+- redraw_prompt_pre_deleted |
+- new_prompt |
+ {move, integer()} |
+- {move_line, integer()} |
+- {move_combo, integer(), integer(), integer()} |
+ clear |
+ beep.
+ -opaque state() :: #state{}.
+@@ -288,9 +276,9 @@ init(State, {unix,_}) ->
+ %% See https://www.gnu.org/software/termutils/manual/termcap-1.3/html_mono/termcap.html#SEC23
+ %% for a list of all possible termcap capabilities
+ Clear = case tgetstr("clear") of
+- {ok, C} -> C;
+- false -> (#state{})#state.clear
+- end,
++ {ok, C} -> C;
++ false -> (#state{})#state.clear
++ end,
+ Cols = case tgetnum("co") of
+ {ok, Cs} -> Cs;
+ _ -> (#state{})#state.cols
+@@ -333,7 +321,7 @@ init(State, {unix,_}) ->
+ {ok, <<"\e[6n">> = U7} ->
+ %% User 7 should contain the codes for getting
+ %% cursor position.
+- %% User 6 should contain how to parse the reply
++ % User 6 should contain how to parse the reply
+ {ok, <<"\e[%i%d;%dR">>} = tgetstr("u6"),
+ <<"\e[6n">> = U7;
+ false -> (#state{})#state.position
+@@ -547,62 +535,26 @@ handle_request(State = #state{ options = #{ tty := false } }, Request) ->
+ _Ignore ->
+ {<<>>, State}
+ end;
+-handle_request(State, {redraw_prompt, Pbs, Pbs2, {LB, {Bef, Aft}, LA}}) ->
+- {ClearLine, Cleared} = handle_request(State, delete_line),
+- CL = lists:reverse(Bef,Aft),
+- Text = Pbs ++ lists:flatten(lists:join("\n"++Pbs2, lists:reverse(LB)++[CL|LA])),
+- Moves = if LA /= [] ->
+- [Last|_] = lists:reverse(LA),
+- {move_combo, -logical(Last), length(LA), logical(Bef)};
+- true ->
+- {move, -logical(Aft)}
+- end,
+- {_, InsertedText} = handle_request(Cleared, {insert, unicode:characters_to_binary(Text)}),
+- {_, Moved} = handle_request(InsertedText, Moves),
+- {Redraw, NewState} = handle_request(Moved, redraw_prompt_pre_deleted),
+- {[ClearLine, Redraw], NewState};
+-handle_request(State, redraw_prompt) ->
+- {ClearLine, _} = handle_request(State, delete_line),
+- {Redraw, NewState} = handle_request(State, redraw_prompt_pre_deleted),
+- {[ClearLine, Redraw], NewState};
+-handle_request(State = #state{unicode = U, cols = W}, redraw_prompt_pre_deleted) ->
+- {Movement, TextInView} = in_view(State),
+- {_, NewPrompt} = handle_request(State, new_prompt),
+- {Redraw, RedrawState} = insert_buf(NewPrompt#state{xn = false}, unicode:characters_to_binary(TextInView)),
+- {Output, _} = case State#state.buffer_expand of
+- undefined ->
+- {[encode(Redraw, U), xnfix(RedrawState, RedrawState#state.buffer_before), Movement], RedrawState};
+- BufferExpand ->
+- BBCols = cols(State#state.buffer_before, U),
+- End = BBCols + cols(State#state.buffer_after,U),
+- {ExpandBuffer, NewState} = insert_buf(RedrawState#state{ buffer_expand = [] }, iolist_to_binary(BufferExpand)),
+- BECols = cols(W, End, NewState#state.buffer_expand, U),
+- MoveToEnd = move_cursor(RedrawState, BECols, End),
+- {[encode(Redraw,U),encode(ExpandBuffer, U), MoveToEnd, Movement], RedrawState}
+-
+- end,
+- {Output, State};
+ %% Clear the expand buffer after the cursor when we handle any request.
+-handle_request(State = #state{ buffer_expand = Expand, unicode = U}, Request)
++handle_request(State = #state{ buffer_expand = Expand, unicode = U }, Request)
+ when Expand =/= undefined ->
+- {Redraw, NoExpandState} = handle_request(State#state{ buffer_expand = undefined }, redraw_prompt),
+- {Output, NewState} = handle_request(NoExpandState#state{ buffer_expand = undefined }, Request),
+- {[encode(Redraw, U), encode(Output, U)], NewState};
+-handle_request(State, new_prompt) ->
+- {"", State#state{buffer_before = [],
+- buffer_after = [],
+- lines_before = [],
+- lines_after = []}};
++ BBCols = cols(State#state.buffer_before, U),
++ BACols = cols(State#state.buffer_after, U),
++ ClearExpand = [move_cursor(State, BBCols, BBCols + BACols),
++ State#state.delete_after_cursor,
++ move_cursor(State, BBCols + BACols, BBCols)],
++ {Output, NewState} = handle_request(State#state{ buffer_expand = undefined }, Request),
++ {[ClearExpand, encode(Output, U)], NewState};
+ %% Print characters in the expandbuffer after the cursor
+-handle_request(State, {expand, Expand}) ->
+- handle_request(State#state{buffer_expand = Expand}, redraw_prompt);
+-handle_request(State, {expand_with_trim, Binary}) ->
+- handle_request(State,
+- {expand, iolist_to_binary(["\r\n",string:trim(Binary, both)])});
+-%% putc_keep_state prints Binary and keeps the current prompt unchanged
+-handle_request(State = #state{ unicode = U }, {putc_keep_state, Binary}) ->
+- {PutBuffer, _NewState} = insert_buf(State, Binary),
+- {encode(PutBuffer, U), State};
++handle_request(State = #state{ unicode = U }, {expand, Binary}) ->
++ BBCols = cols(State#state.buffer_before, U),
++ BACols = cols(State#state.buffer_after, U),
++ Expand = iolist_to_binary(["\r\n",string:trim(Binary, both)]),
++ MoveToEnd = move_cursor(State, BBCols, BBCols + BACols),
++ {ExpandBuffer, NewState} = insert_buf(State#state{ buffer_expand = [] }, Expand),
++ BECols = cols(NewState#state.cols, BBCols + BACols, NewState#state.buffer_expand, U),
++ MoveToOrig = move_cursor(State, BECols, BBCols),
++ {[MoveToEnd, encode(ExpandBuffer, U), MoveToOrig], NewState};
+ %% putc prints Binary and overwrites any existing characters
+ handle_request(State = #state{ unicode = U }, {putc, Binary}) ->
+ %% Todo should handle invalid unicode?
+@@ -611,136 +563,36 @@ handle_request(State = #state{ unicode = U }, {putc, Binary}) ->
+ {encode(PutBuffer, U), NewState};
+ true ->
+ %% Delete any overwritten characters after current the cursor
+- OldLength = logical(State#state.buffer_before) + lists:sum([logical(L) || L <- State#state.lines_before]),
+- NewLength = logical(NewState#state.buffer_before) + lists:sum([logical(L) || L <- NewState#state.lines_before]),
++ OldLength = logical(State#state.buffer_before),
++ NewLength = logical(NewState#state.buffer_before),
+ {_, _, _, NewBA} = split(NewLength - OldLength, NewState#state.buffer_after, U),
+ {encode(PutBuffer, U), NewState#state{ buffer_after = NewBA }}
+ end;
+ handle_request(State, {putc_raw, Binary}) ->
+ handle_request(State, {putc, unicode:characters_to_binary(Binary, latin1)});
+-handle_request(State = #state{}, delete_after_cursor) ->
+- {[State#state.delete_after_cursor],
+- State#state{buffer_after = [],
+- lines_after = []}};
+-handle_request(State = #state{unicode = U, cols = W, buffer_before = Bef,
+- lines_before = LinesBefore,
+- lines_after = _LinesAfter}, delete_line) ->
+- MoveToBeg = move_cursor(State, cols_multiline(Bef, LinesBefore, W, U), 0),
+- {[MoveToBeg, State#state.delete_after_cursor],
+- State#state{buffer_before = [],
+- buffer_after = [],
+- lines_before = [],
+- lines_after = []}};
+-handle_request(State = #state{ unicode = U, cols = W }, {delete, N}) when N > 0 ->
++handle_request(State = #state{ unicode = U }, {delete, N}) when N > 0 ->
+ {_DelNum, DelCols, _, NewBA} = split(N, State#state.buffer_after, U),
+ BBCols = cols(State#state.buffer_before, U),
+- BACols = cols(State#state.buffer_after, U),
+ NewBACols = cols(NewBA, U),
+- Output = [encode(NewBA, U),
+- lists:duplicate(DelCols, $\s),
+- xnfix(State, BBCols + NewBACols + DelCols),
+- move_cursor(State,
+- BBCols + NewBACols + DelCols,
+- BBCols)],
+- NewState0 = State#state{ buffer_after = NewBA },
+- if State#state.lines_after =/= [], (BBCols + BACols-N) rem W =:= 0 ->
+- {Delete, _} = handle_request(State, delete_line),
+- {Redraw, NewState1} = handle_request(NewState0, redraw_prompt_pre_deleted),
+- {[Delete, Redraw], NewState1};
+- true ->
+- {Output, NewState0}
+- end;
+-handle_request(State = #state{ unicode = U, cols = W }, {delete, N}) when N < 0 ->
++ {[encode(NewBA, U),
++ lists:duplicate(DelCols, $\s),
++ xnfix(State, BBCols + NewBACols + DelCols),
++ move_cursor(State,
++ BBCols + NewBACols + DelCols,
++ BBCols)],
++ State#state{ buffer_after = NewBA }};
++handle_request(State = #state{ unicode = U }, {delete, N}) when N < 0 ->
+ {_DelNum, DelCols, _, NewBB} = split(-N, State#state.buffer_before, U),
+- BBCols = cols(State#state.buffer_before, U),
+- BACols = cols(State#state.buffer_after, U),
+ NewBBCols = cols(NewBB, U),
+- Output = [move_cursor(State, NewBBCols + DelCols, NewBBCols),
+- encode(State#state.buffer_after,U),
+- lists:duplicate(DelCols, $\s),
+- xnfix(State, NewBBCols + BACols + DelCols),
+- move_cursor(State, NewBBCols + BACols + DelCols, NewBBCols)],
+- NewState0 = State#state{ buffer_before = NewBB },
+- if State#state.lines_after =/= [], (BBCols+BACols+N) rem W =:= 0 ->
+- {Delete, _} = handle_request(State, delete_line),
+- {Redraw, NewState1} = handle_request(NewState0, redraw_prompt_pre_deleted),
+- {[Delete, Redraw], NewState1};
+- true ->
+- {Output, NewState0}
+- end;
++ BACols = cols(State#state.buffer_after, U),
++ {[move_cursor(State, NewBBCols + DelCols, NewBBCols),
++ encode(State#state.buffer_after,U),
++ lists:duplicate(DelCols, $\s),
++ xnfix(State, NewBBCols + BACols + DelCols),
++ move_cursor(State, NewBBCols + BACols + DelCols, NewBBCols)],
++ State#state{ buffer_before = NewBB } };
+ handle_request(State, {delete, 0}) ->
+ {"",State};
+-%% {move_combo, before_line_movement, line_movement, after_line_movement}
+-%% Many of the move operations comes in threes, this is a helper to make
+-%% movement a little bit easier. We move to the beginning of
+-%% the line before switching line and then move to the right column on
+-%% the next line.
+-handle_request(State, {move_combo, V1, L, V2}) ->
+- {Moves1, NewState1} = handle_request(State, {move, V1}),
+- {Moves2, NewState2} = handle_request(NewState1, {move_line, L}),
+- {Moves3, NewState3} = handle_request(NewState2, {move, V2}),
+- {Moves1 ++ Moves2 ++ Moves3, NewState3};
+-handle_request(State = #state{ cols = W,
+- rows = R,
+- unicode = U,
+- buffer_before = Bef,
+- buffer_after = Aft,
+- lines_before = LinesBefore,
+- lines_after = LinesAfter},
+- {move_line, L}) when L < 0, length(LinesBefore) >= -L ->
+- {LinesJumped, [B|NewLinesBefore]} = lists:split(-L -1, LinesBefore),
+- PrevLinesCols = cols_multiline([B|LinesJumped], W, U),
+- N_Cols = min(cols(Bef, U), cols(B, U)),
+- {_, _, NewBB, NewBA} = split_cols(N_Cols, B, U),
+- Moves = move_cursor(State, PrevLinesCols, 0),
+- CL = lists:reverse(Bef,Aft),
+- NewLinesAfter = lists:reverse([CL|LinesJumped], LinesAfter),
+- NewState = State#state{buffer_before = NewBB,
+- buffer_after = NewBA,
+- lines_before = NewLinesBefore,
+- lines_after = NewLinesAfter},
+- RowsInView = cols_multiline([B,CL|LinesBefore], W, U) div W,
+- Output = if
+- %% When we move up and the view is "full"
+- RowsInView >= R ->
+- {Movement, TextInView} = in_view(NewState),
+- {ClearLine, Cleared} = handle_request(State, delete_line),
+- {Redraw, _} = handle_request(Cleared, {insert, unicode:characters_to_binary(TextInView)}),
+- [ClearLine, Redraw, Movement];
+- true -> Moves
+- end,
+- {Output, NewState};
+-handle_request(State = #state{ cols = W,
+- rows = R,
+- unicode = U,
+- buffer_before = Bef,
+- buffer_after = Aft,
+- lines_before = LinesBefore,
+- lines_after = LinesAfter},
+- {move_line, L}) when L > 0, length(LinesAfter) >= L ->
+- {LinesJumped, [A|NewLinesAfter]} = lists:split(L - 1, LinesAfter),
+- NextLinesCols = cols_multiline([(Bef++Aft)|LinesJumped], W, U),
+- N_Cols = min(cols(Bef, U), cols(A, U)),
+- {_, _, NewBB, NewBA} = split_cols(N_Cols, A, U),
+- Moves = move_cursor(State, 0, NextLinesCols),
+- CL = lists:reverse(Bef, Aft),
+- NewLinesBefore = lists:reverse([CL|LinesJumped],LinesBefore),
+- NewState = State#state{buffer_before = NewBB,
+- buffer_after = NewBA,
+- lines_before = NewLinesBefore,
+- lines_after = NewLinesAfter},
+- RowsInView = cols_multiline([A|NewLinesBefore], W, U) div W,
+- Output = if
+- RowsInView >= R ->
+- {Movement, TextInView} = in_view(NewState),
+- {ClearLine, Cleared} = handle_request(State, delete_line),
+- {Redraw, _} = handle_request(Cleared, {insert, unicode:characters_to_binary(TextInView)}),
+- [ClearLine, Redraw, Movement];
+- true -> Moves
+- end,
+- {Output, NewState};
+-handle_request(State, {move_line, _}) ->
+- {"", State};
+ handle_request(State = #state{ unicode = U }, {move, N}) when N < 0 ->
+ {_DelNum, DelCols, NewBA, NewBB} = split(-N, State#state.buffer_before, U),
+ NewBBCols = cols(NewBB, U),
+@@ -750,57 +602,29 @@ handle_request(State = #state{ unicode = U }, {move, N}) when N < 0 ->
+ handle_request(State = #state{ unicode = U }, {move, N}) when N > 0 ->
+ {_DelNum, DelCols, NewBB, NewBA} = split(N, State#state.buffer_after, U),
+ BBCols = cols(State#state.buffer_before, U),
+- Moves = move_cursor(State, BBCols, BBCols + DelCols),
+- {Moves, State#state{ buffer_after = NewBA,
+- buffer_before = NewBB ++ State#state.buffer_before} };
++ {move_cursor(State, BBCols, BBCols + DelCols),
++ State#state{ buffer_after = NewBA,
++ buffer_before = NewBB ++ State#state.buffer_before} };
+ handle_request(State, {move, 0}) ->
+ {"",State};
+-handle_request(State = #state{cols = W, xn = OrigXn, unicode = U,lines_after = LinesAfter}, {insert, Chars}) ->
++handle_request(State = #state{ xn = OrigXn, unicode = U }, {insert, Chars}) ->
+ {InsertBuffer, NewState0} = insert_buf(State#state{ xn = false }, Chars),
+- NewState1 = NewState0#state{ xn = OrigXn },
+- NewBBCols = cols(NewState1#state.buffer_before, U),
+- NewBACols = cols(NewState1#state.buffer_after, U),
+- Output = [ encode(InsertBuffer, U),
+- encode(NewState1#state.buffer_after, U),
+- xnfix(State, NewBBCols + NewBACols),
+- move_cursor(State, NewBBCols + NewBACols, NewBBCols) ],
+- if LinesAfter =:= []; (NewBBCols + NewBACols) rem W =:= 0 ->
+- {Output, NewState1};
+- true ->
+- {Delete, _} = handle_request(State, delete_line),
+- {Redraw, NewState2} = handle_request(NewState1, redraw_prompt_pre_deleted),
+- {[Delete, Redraw,""], NewState2}
+- end;
++ NewState = NewState0#state{ xn = OrigXn },
++ BBCols = cols(NewState#state.buffer_before, U),
++ BACols = cols(NewState#state.buffer_after, U),
++ {[ encode(InsertBuffer, U),
++ encode(NewState#state.buffer_after, U),
++ xnfix(State, BBCols + BACols),
++ move_cursor(State, BBCols + BACols, BBCols) ],
++ NewState};
+ handle_request(State, beep) ->
+ {<<7>>, State};
+ handle_request(State, clear) ->
+- {State#state.clear, State#state{buffer_before = [],
+- buffer_after = [],
+- lines_before = [],
+- lines_after = []}};
++ {State#state.clear, State};
+ handle_request(State, Req) ->
+ erlang:display({unhandled_request, Req}),
+ {"", State}.
+
+-%% Split the buffer after N cols
+-%% Returns the number of characters deleted, and the column length (N)
+-%% of those characters.
+-split_cols(N_Cols, Buff, Unicode) ->
+- split_cols(N_Cols, Buff, [], 0, 0, Unicode).
+-split_cols(N, [SkipChars | T], Acc, Cnt, Cols, Unicode) when is_binary(SkipChars) ->
+- split_cols(N, T, [SkipChars | Acc], Cnt, Cols, Unicode);
+-split_cols(0, Buff, Acc, Chars, Cols, _Unicode) ->
+- {Chars, Cols, Acc, Buff};
+-split_cols(N, _Buff, _Acc, _Chars, _Cols, _Unicode) when N < 0 ->
+- error;
+-split_cols(_N, [], Acc, Chars, Cols, _Unicode) ->
+- {Chars, Cols, Acc, []};
+-split_cols(N, [Char | T], Acc, Cnt, Cols, Unicode) when is_integer(Char) ->
+- split_cols(N - npwcwidth(Char), T, [Char | Acc], Cnt + 1, Cols + npwcwidth(Char, Unicode), Unicode);
+-split_cols(N, [Chars | T], Acc, Cnt, Cols, Unicode) when is_list(Chars) ->
+- split_cols(N - length(Chars), T, [Chars | Acc],
+- Cnt + length(Chars), Cols + cols(Chars, Unicode), Unicode).
+-
+ %% Split the buffer after N logical characters returning
+ %% the number of real characters deleted and the column length
+ %% of those characters
+@@ -811,7 +635,7 @@ split(0, Buff, Acc, Chars, Cols, _Unicode) ->
+ ?dbg({?FUNCTION_NAME, {Chars, Cols, Acc, Buff}}),
+ {Chars, Cols, Acc, Buff};
+ split(N, _Buff, _Acc, _Chars, _Cols, _Unicode) when N < 0 ->
+- error;
++ ok = N;
+ split(_N, [], Acc, Chars, Cols, _Unicode) ->
+ {Chars, Cols, Acc, []};
+ split(N, [Char | T], Acc, Cnt, Cols, Unicode) when is_integer(Char) ->
+@@ -861,77 +685,6 @@ move(left, #state{ left = Left }, N) ->
+ move(right, #state{ right = Right }, N) ->
+ lists:duplicate(N, Right).
+
+-in_view(#state{lines_after = LinesAfter, buffer_before = Bef, buffer_after = Aft, lines_before = LinesBefore, rows=R, cols=W, unicode=U, buffer_expand = BufferExpand} = State) ->
+- BufferExpandLines = case BufferExpand of
+- undefined -> [];
+- _ -> string:split(erlang:binary_to_list(BufferExpand), "\r\n", all)
+- end,
+- ExpandRows = (cols_multiline(BufferExpandLines, W, U) div W),
+- InputBeforeRows = (cols_multiline(LinesBefore, W, U) div W),
+- InputRows = (cols_multiline([Bef ++ Aft], W, U) div W),
+- InputAfterRows = (cols_multiline(LinesAfter, W, U) div W),
+- %% Dont print lines after if we have expansion rows
+- SumRows = InputBeforeRows+ InputRows + ExpandRows + InputAfterRows,
+- if SumRows > R ->
+- RowsLeftAfterInputRows = R - InputRows,
+- RowsLeftAfterExpandRows = RowsLeftAfterInputRows - ExpandRows,
+- RowsLeftAfterInputBeforeRows = RowsLeftAfterExpandRows - InputBeforeRows,
+- Cols1 = max(0,W*max(RowsLeftAfterInputBeforeRows, RowsLeftAfterExpandRows)),
+- {_, LBAfter, _, {_, LBAHalf}} = split_cols_multiline(Cols1, LinesBefore, U, W),
+- LBAfter0 = case LBAHalf of [] -> LBAfter;
+- _ -> [LBAHalf|LBAfter]
+- end,
+-
+- RowsLeftAfterInputAfterRows = RowsLeftAfterInputBeforeRows - InputAfterRows,
+- LAInViewLines = case BufferExpandLines of
+- [] ->
+- %% We must remove one line extra, since we may have an xnfix at the end which will
+- %% adds one extra line, so for consistency always remove one line
+- Cols2 = max(0,W*max(RowsLeftAfterInputAfterRows, RowsLeftAfterInputBeforeRows)-W),
+- {_, LABefore, _, {LABHalf, _}} = split_cols_multiline(Cols2, LinesAfter, U, W),
+- case LABHalf of [] -> LABefore;
+- _ -> [LABHalf|LABefore]
+- end;
+- _ ->
+- []
+- end,
+- LAInView = lists:flatten(["\n"++LA||LA<-lists:reverse(LAInViewLines)]),
+- LBInView = lists:flatten([LB++"\n"||LB<-LBAfter0]),
+- Text = LBInView ++ lists:reverse(Bef,Aft) ++ LAInView,
+- Movement = move_cursor(State,
+- cols_after_cursor(State#state{lines_after = LAInViewLines++[lists:reverse(Bef, Aft)]}),
+- cols(Bef,U)),
+- {Movement, Text};
+-
+- true ->
+- %% Everything fits in the current window, just output everything
+- Movement = move_cursor(State, cols_after_cursor(State#state{lines_after = lists:reverse(LinesAfter)++[lists:reverse(Bef, Aft)]}), cols(Bef,U)),
+- Text = lists:flatten([LB++"\n"||LB<-lists:reverse(LinesBefore)]) ++
+- lists:reverse(Bef,Aft) ++ lists:flatten(["\n"++LA||LA<-LinesAfter]),
+- {Movement, Text}
+- end.
+-cols_after_cursor(#state{lines_after=[LAST|LinesAfter],cols=W, unicode=U}) ->
+- cols_multiline(LAST, LinesAfter, W, U).
+-split_cols_multiline(Cols, Lines, U, W) ->
+- split_cols_multiline(Cols, Lines, U, W, 0, []).
+-split_cols_multiline(0, Lines, _U, _W, ColsAcc, AccBefore) ->
+- {ColsAcc, AccBefore, Lines, {[],[]}};
+-split_cols_multiline(_Cols, [], _U, _W, ColsAcc, AccBefore) ->
+- {ColsAcc, AccBefore, [], {[],[]}};
+-split_cols_multiline(Cols, [L|Lines], U, W, ColsAcc, AccBefore) ->
+- case cols(L, U) > Cols of
+- true ->
+- {_, _, LB, LA} = split_cols(Cols, L, U),
+- {ColsAcc+Cols, AccBefore, Lines, {lists:reverse(LB), LA}};
+- _ ->
+- Cols2 = (((cols(L,U)-1) div W)*W+W),
+- split_cols_multiline(Cols-Cols2, Lines, U, W, ColsAcc+Cols2, [L|AccBefore])
+- end.
+-cols_multiline(Lines, W, U) ->
+- cols_multiline("", Lines, W, U).
+-cols_multiline(ExtraCols, Lines, W, U) ->
+- cols(ExtraCols, U) + lists:sum([((cols(LB,U)-1) div W)*W + W || LB <- Lines]).
+-
+ cols([],_Unicode) ->
+ 0;
+ cols([Char | T], Unicode) when is_integer(Char) ->
+@@ -975,7 +728,6 @@ npwcwidthstring(String) ->
+ _ ->
+ npwcwidth($\e) + npwcwidthstring(Rest)
+ end;
+- [H|Rest] when is_list(H)-> lists:sum([npwcwidth(A)||A<-H]) + npwcwidthstring(Rest);
+ [H|Rest] -> npwcwidth(H) + npwcwidthstring(Rest)
+ end.
+
+@@ -1027,7 +779,7 @@ characters_to_output(Chars) ->
+ (Char) ->
+ Char
+ end, Chars)
+- )
++ )
+ end.
+ characters_to_buffer(Chars) ->
+ lists:flatmap(
+@@ -1063,12 +815,12 @@ insert_buf(State, Bin, LineAcc, Acc) ->
+ insert_buf(State, AnsiRest, [{ansi, Ansi} | LineAcc], Acc);
+ _ ->
+ insert_buf(State, Rest, [$\e | LineAcc], Acc)
+- end;
++ end;
+ {Ansi, AnsiRest} ->
+ %% We include the graphics ansi sequences in the
+ %% buffer that we step over
+ insert_buf(State, AnsiRest, [Ansi | LineAcc], Acc)
+- end;
++ end;
+ [NLCR | Rest] when NLCR =:= $\n; NLCR =:= $\r ->
+ Tail =
+ if NLCR =:= $\n ->
+@@ -1077,22 +829,10 @@ insert_buf(State, Bin, LineAcc, Acc) ->
+ <<$\r>>
+ end,
+ if State#state.buffer_expand =:= undefined ->
+- CurrentLine = lists:reverse(State#state.buffer_before),
+- LinesBefore = State#state.lines_before,
+- LinesBefore1 =
+- case {CurrentLine, LineAcc} of
+- {[], []} ->
+- LinesBefore;
+- {[],_} ->
+- [lists:reverse(LineAcc)|LinesBefore];
+- {_,_} ->
+- [CurrentLine++lists:reverse(LineAcc)|LinesBefore]
+- end,
+- insert_buf(State#state{ buffer_before = [],
+- buffer_after = State#state.buffer_after,
+- lines_before=LinesBefore1},
+- Rest, [], [Acc, [characters_to_output(lists:reverse(LineAcc)), Tail]]);
+- true -> insert_buf(State, Rest, [binary_to_list(Tail) | LineAcc], Acc)
++ insert_buf(State#state{ buffer_before = [], buffer_after = [] }, Rest, [],
++ [Acc, [characters_to_output(lists:reverse(LineAcc)), Tail]]);
++ true ->
++ insert_buf(State, Rest, [binary_to_list(Tail) | LineAcc], Acc)
+ end;
+ [Cluster | Rest] when is_list(Cluster) ->
+ insert_buf(State, Rest, [Cluster | LineAcc], Acc);
+diff --git a/lib/kernel/src/user_drv.erl b/lib/kernel/src/user_drv.erl
+index 25ebcbdd68..fc1a4434fb 100644
+--- a/lib/kernel/src/user_drv.erl
++++ b/lib/kernel/src/user_drv.erl
+@@ -52,25 +52,14 @@
+ %% Same as put_chars/3, but sends Reply to From when the characters are
+ %% guaranteed to have been written to the terminal
+ {put_chars_sync, unicode, binary(), {From :: pid(), Reply :: term()}} |
+- %% Put text in expansion area
+- {put_expand} |
+- {put_expand_no_trim} |
+ %% Move the cursor X characters left or right (negative is left)
+ {move_rel, -32768..32767} |
+- %% Move the cursor Y rows up or down (negative is up)
+- {move_line, -32768..32767} |
+- %% Move combo, helper to simplify some move operations
+- {move_combo, -32768..32767, -32768..32767, -32768..32767} |
+ %% Insert characters at current cursor position moving any
+ %% characters after the cursor.
+ {insert_chars, unicode, binary()} |
+ %% Delete X chars before or after the cursor adjusting any test remaining
+ %% to the right of the cursor.
+ {delete_chars, -32768..32767} |
+- %% Deletes the current prompt and expression
+- delete_line |
+- %% Delete after the cursor
+- delete_after_cursor |
+ %% Trigger a terminal "bell"
+ beep |
+ %% Clears the screen
+@@ -78,12 +67,7 @@
+ %% Execute multiple request() actions
+ {requests, [request()]} |
+ %% Open external editor
+- {open_editor, string()} |
+- %% Redraws the current prompt and expression
+- redraw_prompt |
+- {redraw_prompt, string(), string(), tuple()} |
+- %% Clears the state, not touching the characters
+- new_prompt.
++ {open_editor, string()}.
+
+ -export_type([message/0]).
+ -export([start/0, start/1, start_shell/0, start_shell/1, whereis_group/0]).
+@@ -314,6 +298,7 @@ init_remote_shell(State, Node, {M, F, A}) ->
+ end.
+
+ init_local_shell(State, InitialShell) ->
++
+ Slogan =
+ case application:get_env(
+ stdlib, shell_slogan,
+@@ -610,9 +595,8 @@ switch_loop(internal, {line, Line}, State) ->
+ {ok, Groups} ->
+ Curr = gr_cur_pid(Groups),
+ put(current_group, Curr),
+- Curr ! {self(), activate},
+ {next_state, server,
+- State#state{ current_group = Curr, groups = Groups }};
++ State#state{ current_group = Curr, groups = Groups } };
+ {retry, Requests} ->
+ {keep_state, State#state{ tty = io_requests(Requests, State#state.tty) },
+ {next_event, internal, line}};
+@@ -633,7 +617,7 @@ switch_loop(internal, {line, Line}, State) ->
+ end;
+ switch_loop(info,{ReadHandle,{data,Cs}}, {Cont, #state{ read = ReadHandle } = State}) ->
+ case edlin:edit_line(unicode:characters_to_list(Cs), Cont) of
+- {done,{[Line],_,_},_Rest, Rs} ->
++ {done,Line,_Rest, Rs} ->
+ {keep_state, State#state{ tty = io_requests(Rs, State#state.tty) },
+ {next_event, internal, {line, Line}}};
+ {undefined,_Char,MoreCs,NewCont,Rs} ->
+@@ -775,18 +759,6 @@ group_opts() ->
+ {term(), reference(), prim_tty:state()}.
+ io_request({requests,Rs}, TTY) ->
+ {noreply, io_requests(Rs, TTY)};
+-io_request(redraw_prompt, TTY) ->
+- write(prim_tty:handle_request(TTY, redraw_prompt));
+-io_request({redraw_prompt, Pbs, Pbs2, LineState}, TTY) ->
+- write(prim_tty:handle_request(TTY, {redraw_prompt, Pbs, Pbs2, LineState}));
+-io_request(new_prompt, TTY) ->
+- write(prim_tty:handle_request(TTY, new_prompt));
+-io_request(delete_after_cursor, TTY) ->
+- write(prim_tty:handle_request(TTY, delete_after_cursor));
+-io_request(delete_line, TTY) ->
+- write(prim_tty:handle_request(TTY, delete_line));
+-io_request({put_chars_keep_state, unicode, Chars}, TTY) ->
+- write(prim_tty:handle_request(TTY, {putc_keep_state, unicode:characters_to_binary(Chars)}));
+ io_request({put_chars, unicode, Chars}, TTY) ->
+ write(prim_tty:handle_request(TTY, {putc, unicode:characters_to_binary(Chars)}));
+ io_request({put_chars_sync, unicode, Chars, Reply}, TTY) ->
+@@ -798,15 +770,9 @@ io_request({put_chars_sync, latin1, Chars, Reply}, TTY) ->
+ {ok, MonitorRef} = prim_tty:write(NewTTY, Output, self()),
+ {Reply, MonitorRef, NewTTY};
+ io_request({put_expand, unicode, Chars}, TTY) ->
+- write(prim_tty:handle_request(TTY, {expand_with_trim, unicode:characters_to_binary(Chars)}));
+-io_request({put_expand_no_trim, unicode, Chars}, TTY) ->
+ write(prim_tty:handle_request(TTY, {expand, unicode:characters_to_binary(Chars)}));
+ io_request({move_rel, N}, TTY) ->
+ write(prim_tty:handle_request(TTY, {move, N}));
+-io_request({move_line, R}, TTY) ->
+- write(prim_tty:handle_request(TTY, {move_line, R}));
+-io_request({move_combo, V1, R, V2}, TTY) ->
+- write(prim_tty:handle_request(TTY, {move_combo, V1, R, V2}));
+ io_request({insert_chars, unicode, Chars}, TTY) ->
+ write(prim_tty:handle_request(TTY, {insert, unicode:characters_to_binary(Chars)}));
+ io_request({delete_chars, N}, TTY) ->
+@@ -824,12 +790,6 @@ io_requests([{insert_chars, unicode, C1},{insert_chars, unicode, C2}|Rs], TTY) -
+ io_requests([{insert_chars, unicode, [C1,C2]}|Rs], TTY);
+ io_requests([{put_chars, unicode, C1},{put_chars, unicode, C2}|Rs], TTY) ->
+ io_requests([{put_chars, unicode, [C1,C2]}|Rs], TTY);
+-io_requests([{move_rel, N}, {move_line, R}, {move_rel, M}|Rs], TTY) ->
+- io_requests([{move_combo, N, R, M}|Rs], TTY);
+-io_requests([{move_rel, N}, {move_line, R}|Rs], TTY) ->
+- io_requests([{move_combo, N, R, 0}|Rs], TTY);
+-io_requests([{move_line, R}, {move_rel, M}|Rs], TTY) ->
+- io_requests([{move_combo, 0, R, M}|Rs], TTY);
+ io_requests([R|Rs], TTY) ->
+ {noreply, NewTTY} = io_request(R, TTY),
+ io_requests(Rs, NewTTY);
+diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl
+index b413800661..04f4143ea9 100644
+--- a/lib/kernel/test/interactive_shell_SUITE.erl
++++ b/lib/kernel/test/interactive_shell_SUITE.erl
+@@ -46,9 +46,9 @@
+ shell_history_custom/1, shell_history_custom_errors/1,
+ job_control_remote_noshell/1,ctrl_keys/1,
+ get_columns_and_rows_escript/1,
+- shell_navigation/1, shell_multiline_navigation/1, shell_xnfix/1, shell_delete/1,
++ shell_navigation/1, shell_xnfix/1, shell_delete/1,
+ shell_transpose/1, shell_search/1, shell_insert/1,
+- shell_update_window/1, shell_small_window_multiline_navigation/1, shell_huge_input/1,
++ shell_update_window/1, shell_huge_input/1,
+ shell_invalid_unicode/1, shell_support_ansi_input/1,
+ shell_invalid_ansi/1, shell_suspend/1, shell_full_queue/1,
+ shell_unicode_wrap/1, shell_delete_unicode_wrap/1,
+@@ -66,7 +66,7 @@
+ -export([load/0, add/1]).
+ %% For custom prompt testing
+ -export([prompt/1]).
+--record(tmux, {peer, node, name, orig_location }).
++
+ suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,3}}].
+@@ -124,9 +124,9 @@ groups() ->
+ ]},
+ {tty_latin1,[],[{group,tty_tests}]},
+ {tty_tests, [parallel],
+- [shell_navigation, shell_multiline_navigation, shell_xnfix, shell_delete,
++ [shell_navigation, shell_xnfix, shell_delete,
+ shell_transpose, shell_search, shell_insert,
+- shell_update_window, shell_small_window_multiline_navigation, shell_huge_input,
++ shell_update_window, shell_huge_input,
+ shell_support_ansi_input,
+ shell_standard_error_nlcr,
+ shell_expand_location_above,
+@@ -404,68 +404,7 @@ shell_navigation(Config) ->
+ after
+ stop_tty(Term)
+ end.
+-shell_multiline_navigation(Config) ->
+- Term = start_tty(Config),
+
+- try
+- [begin
+- check_location(Term, {0, 0}),
+- send_tty(Term,"{aaa,"),
+- check_location(Term, {0,width("{aaa,")}),
+- send_tty(Term,"\n'b"++U++"b',"),
+- check_location(Term, {0, width("'b"++U++"b',")}),
+- send_tty(Term,"\nccc}"),
+- check_location(Term, {-2, 0}), %% Check that cursor jump backward (blink)
+- timer:sleep(1000), %% Wait for cursor to jump back
+- check_location(Term, {0, width("ccc}")}),
+- send_tty(Term,"Home"),
+- check_location(Term, {0, 0}),
+- send_tty(Term,"End"),
+- check_location(Term, {0, width("ccc}")}),
+- send_tty(Term,"Left"),
+- check_location(Term, {0, width("ccc")}),
+- send_tty(Term,"C-Left"),
+- check_location(Term, {0, 0}),
+- send_tty(Term,"C-Left"),
+- check_location(Term, {-1, width("'b"++U++"b',")}),
+- send_tty(Term,"C-Left"),
+- check_location(Term, {-1, 0}),
+- %send_tty(Term,"C-Left"),
+- %check_location(Term, {-1, 0}),
+- %send_tty(Term,"C-Right"),
+- %check_location(Term, {-1, 1}),
+- send_tty(Term,"C-Right"),
+- check_location(Term, {-1, width("'b"++U++"b'")}),
+- send_tty(Term,"C-Up"),
+- check_location(Term, {-2, width("{aaa,")}),
+- send_tty(Term,"C-Down"),
+- send_tty(Term,"C-Down"),
+- check_location(Term, {0, width("ccc}")}),
+- send_tty(Term,"Left"),
+- send_tty(Term,"C-Up"),
+- check_location(Term, {-1, width("'b"++U)}),
+- send_tty(Term,"M-<"),
+- check_location(Term, {-2, 0}),
+- send_tty(Term,"M->"),
+- send_tty(Term,"Left"),
+- check_location(Term, {0,width("ccc")}),
+- send_tty(Term,"Enter"),
+- send_tty(Term,"Right"),
+- check_location(Term, {0,0}),
+- send_tty(Term,"C-h"), % Backspace
+- check_location(Term, {-1,width("ccc}")}),
+- send_tty(Term,"Left"),
+- send_tty(Term,"M-Enter"),
+- send_tty(Term,"Right"),
+- check_location(Term, {0,1}),
+- send_tty(Term,"M-c"),
+- check_location(Term, {-3,0}),
+- send_tty(Term,"{'"++U++"',\n\n\nworks}.\n")
+- end || U <- hard_unicode()],
+- ok
+- after
+- stop_tty(Term)
+- end.
+ shell_clear(Config) ->
+
+ Term = start_tty(Config),
+@@ -788,7 +727,9 @@ shell_transpose(Config) ->
+ end.
+
+ shell_search(C) ->
++
+ Term = start_tty(C),
++ {_Row, Cols} = get_location(Term),
+
+ try
+ send_tty(Term,"a"),
+@@ -802,22 +743,20 @@ shell_search(C) ->
+ send_tty(Term,"Enter"),
+ check_location(Term, {0, 0}),
+ send_tty(Term,"C-r"),
+- check_content(Term, "search:\\s*\n\\s*'a😀'."),
++ check_location(Term, {0, - Cols + width(C, "(search)`': 'a😀'.") }),
+ send_tty(Term,"C-a"),
+- check_location(Term, {-1, width(C, "'a😀'.")}),
++ check_location(Term, {0, width(C, "'a😀'.")}),
+ send_tty(Term,"Enter"),
+ send_tty(Term,"C-r"),
+- check_content(Term, "search:\\s*\n\\s*'a😀'."),
++ check_location(Term, {0, - Cols + width(C, "(search)`': 'a😀'.") }),
+ send_tty(Term,"a"),
+- check_content(Term, "search: a\\s*\n\\s*'a😀'."),
++ check_location(Term, {0, - Cols + width(C, "(search)`a': 'a😀'.") }),
+ send_tty(Term,"C-r"),
+- check_content(Term, "search: a\\s*\n\\s*a."),
++ check_location(Term, {0, - Cols + width(C, "(search)`a': a.") }),
+ send_tty(Term,"BSpace"),
+- check_content(Term, "search:\\s*\n\\s*'a😀'."),
++ check_location(Term, {0, - Cols + width(C, "(search)`': 'a😀'.") }),
+ send_tty(Term,"BSpace"),
+- check_content(Term, "search:\\s*\n\\s*'a😀'."),
+- send_tty(Term,"M-c"),
+- check_location(Term, {-1, 0}),
++ check_location(Term, {0, - Cols + width(C, "(search)`': 'a😀'.") }),
+ ok
+ after
+ stop_tty(Term),
+@@ -872,49 +811,7 @@ shell_update_window(Config) ->
+ after
+ stop_tty(Term)
+ end.
+-shell_small_window_multiline_navigation(Config) ->
+- Term0 = start_tty(Config),
+- tmux(["resize-window -t ",tty_name(Term0)," -x ",30, " -y ", 6]),
+- {Row, Col} = get_location(Term0),
+- Term = Term0#tmux{orig_location = {Row, Col}},
+- Text = ("xbcdefghijklmabcdefghijklm\n"++
+- "abcdefghijkl\n"++
+- "abcdefghijklmabcdefghijklm\n"++
+- "abcdefghijklmabcdefghijklx"),
+- try
+- send_tty(Term,Text),
+- check_location(Term, {0, -4}),
+- send_tty(Term,"Home"),
+- check_location(Term, {-1, 0}),
+- send_tty(Term, "C-Up"),
+- check_location(Term, {-2, 0}),
+- send_tty(Term, "C-Down"),
+- check_location(Term, {-1, 0}),
+- send_tty(Term, "Left"),
+- check_location(Term, {-1, -4}),
+- send_tty(Term, "Right"),
+- check_location(Term, {-1, 0}),
+- send_tty(Term, "\e[1;4A"),
+- check_location(Term, {-5, 0}),
+- check_content(Term,"xbc"),
+- send_tty(Term, "\e[1;4B"),
+- check_location(Term, {0, -4}),
+- check_content(Term,"klx"),
+- send_tty(Term, " sets:is_e\t"),
+- check_content(Term,"is_element"),
+- check_content(Term,"is_empty"),
+- check_location(Term, {-3, 6}),
+- send_tty(Term, "C-Up"),
+- send_tty(Term,"Home"),
+- check_location(Term, {-2, 0}),
+- send_tty(Term, "sets:is_e\t"),
+- check_content(Term,"is_element"),
+- check_content(Term,"is_empty"),
+- check_location(Term, {-4, 9}),
+- ok
+- after
+- stop_tty(Term)
+- end.
++
+ shell_huge_input(Config) ->
+ Term = start_tty(Config),
+
+@@ -1071,7 +968,7 @@ shell_expand_location_below(Config) ->
+ send_stdin(Term, "\t"),
+ %% The expansion does not fit on screen, verify that
+ %% expand above mode is used
+- check_content(fun() -> get_content(Term, "-S -7") end,
++ check_content(fun() -> get_content(Term, "-S -5") end,
+ "3> long_module:" ++ FunctionName ++ "\nfunctions"),
+ check_content(Term, "3> long_module:" ++ FunctionName ++ "$"),
+
+@@ -1166,19 +1063,18 @@ external_editor(Config) ->
+ tmux(["resize-window -t ",tty_name(Term)," -x 80"]),
+ send_tty(Term,"os:putenv(\"EDITOR\",\"nano\").\n"),
+ send_tty(Term, "\"some text with\nnewline in it\""),
+- check_content(Term,"3> \"some text with\\s*\n.+\\s*newline in it\""),
++ check_content(Term,"3> \"some text with\\s*\n.+3>\\s*newline in it\""),
+ send_tty(Term, "C-O"),
+ check_content(Term,"GNU nano [\\d.]+"),
+- check_content(Term,"\"some text with\\s*\n\\s*newline in it\""),
+- send_tty(Term, "Right"),
++ check_content(Term,"newline in it\""),
+ send_tty(Term, "still"),
+ send_tty(Term, "Enter"),
+ send_tty(Term, "C-O"), %% save in nano
+ send_tty(Term, "Enter"),
+ send_tty(Term, "C-X"), %% quit in nano
+- check_content(Term,"3> \"still\\s*\n\\s*.+\\s*some text with\\s*\n.+\\s*newline in it\""),
++ check_content(Term,"still\n.+3> newline in it\""),
+ send_tty(Term,".\n"),
+- check_content(Term,"\\Q\"still\\nsome text with\\nnewline in it\"\\E"),
++ check_content(Term,"\\Q\"some text with\\nstill\\nnewline in it\"\\E"),
+ ok
+ after
+ stop_tty(Term),
+@@ -1484,6 +1380,8 @@ npwcwidth(CP) ->
+ end
+ end.
+
++-record(tmux, {peer, node, name, orig_location }).
++
+ tmux([Cmd|_] = Command) when is_list(Cmd) ->
+ tmux(lists:concat(Command));
+ tmux(Command) ->
+@@ -2227,13 +2125,15 @@ test_remote_job_control(Node) ->
+ {expect, "Unknown job"},
+ {expect, " --> $"},
+ {putline, "c 1"},
++ {expect, "\r\n"},
++ {putline, ""},
+ {expect, "\\Q("++RemNode++"@\\E[^)]*\\)[12]> $"},
+ {putdata, "\^g"},
+ {expect, " --> $"},
+ {putline, "j"},
+ {expect, "1[*] {shell,start,\\[init]}"},
+ {putline, "c"},
+- {expect, "\\Q("++RemNode++"@\\E[^)]*\\)[123]> $"},
++ {expect, "\r\n"},
+ {sleep, 100},
+ {putline, "35."},
+ {expect, "\\Q("++RemNode++"@\\E[^)]*\\)[123]> $"}
+diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl
+index 88283a54c9..b015479b9f 100644
+--- a/lib/stdlib/src/edlin.erl
++++ b/lib/stdlib/src/edlin.erl
+@@ -23,9 +23,10 @@
+ %% About Latin-1 characters: see the beginning of erl_scan.erl.
+
+ -export([init/0,init/1,start/1,start/2,edit_line/2,prefix_arg/1]).
+--export([erase_line/0,erase_inp/1,redraw_line/1]).
++-export([erase_line/1,erase_inp/1,redraw_line/1]).
+ -export([length_before/1,length_after/1,prompt/1]).
+ -export([current_line/1, current_chars/1]).
++%%-export([expand/1]).
+
+ -export([edit_line1/2]).
+
+@@ -71,105 +72,80 @@ start(Pbs) ->
+
+ %% Only two modes used: 'none' and 'search'. Other modes can be
+ %% handled inline through specific character handling.
+-start(Pbs, {_,{_,_},_}=Cont) ->
+- Rs1 = erase_line(),
+- Rs2 = redraw(Pbs, Cont, Rs1),
+- Rs3 = reverse(Rs2),
+- {more_chars,{line,Pbs,Cont,none},Rs3};
+-
+ start(Pbs, Mode) ->
+- {more_chars,{line,Pbs,{[],{[],[]},[]},Mode},[new_prompt, {put_chars,unicode,Pbs}]}.
++ {more_chars,{line,Pbs,{[],[]},Mode},[{put_chars,unicode,Pbs}]}.
+
+-edit_line(Cs, {line,P,L,{blink,N_Rs}}) ->
+- edit(Cs, P, L, none, N_Rs);
++edit_line(Cs, {line,P,L,{blink,N}}) ->
++ edit(Cs, P, L, none, [{move_rel,N}]);
+ edit_line(Cs, {line,P,L,M}) ->
+ edit(Cs, P, L, M, []).
+
+-edit_line1(Cs, {line,P,L,{blink,N_Rs}}) ->
+- edit(Cs, P, L, none, N_Rs);
+-edit_line1(Cs, {line,P,{B,{[],[]},A},none}) ->
+- [CurrentLine|Lines] = [string:to_graphemes(Line) || Line <- reverse(string:split(Cs, "\n",all))],
+- Cont = {Lines ++ B,{reverse(CurrentLine),[]},A},
+- Rs = reverse(redraw(P, Cont, [])),
+- %erlang:display({P, Cont, Cs, CurrentLine}),
+- {more_chars, {line,P,Cont,none},[delete_line|Rs]};
++edit_line1(Cs, {line,P,L,{blink,N}}) ->
++ edit(Cs, P, L, none, [{move_rel,N}]);
++edit_line1(Cs, {line,P,{[],[]},none}) ->
++ {more_chars, {line,P,{string:reverse(Cs),[]},none},[{put_chars, unicode, Cs}]};
+ edit_line1(Cs, {line,P,L,M}) ->
+ edit(Cs, P, L, M, []).
+
+ edit([C|Cs], P, Line, {blink,_}, [_|Rs]) -> %Remove blink here
+ edit([C|Cs], P, Line, none, Rs);
+-edit([C|Cs], P, {LB, {Bef,Aft}, LA}=MultiLine, Prefix, Rs0) ->
++edit([C|Cs], P, {Bef,Aft}, Prefix, Rs0) ->
+ case key_map(C, Prefix) of
+ meta ->
+- edit(Cs, P, MultiLine, meta, Rs0);
++ edit(Cs, P, {Bef,Aft}, meta, Rs0);
+ meta_o ->
+- edit(Cs, P, MultiLine, meta_o, Rs0);
++ edit(Cs, P, {Bef,Aft}, meta_o, Rs0);
+ meta_csi ->
+- edit(Cs, P, MultiLine, meta_csi, Rs0);
++ edit(Cs, P, {Bef,Aft}, meta_csi, Rs0);
+ meta_meta ->
+- edit(Cs, P, MultiLine, meta_meta, Rs0);
++ edit(Cs, P, {Bef,Aft}, meta_meta, Rs0);
+ {csi, _} = Csi ->
+- edit(Cs, P, MultiLine, Csi, Rs0);
++ edit(Cs, P, {Bef,Aft}, Csi, Rs0);
+ meta_left_sq_bracket ->
+- edit(Cs, P, MultiLine, meta_left_sq_bracket, Rs0);
++ edit(Cs, P, {Bef,Aft}, meta_left_sq_bracket, Rs0);
+ search_meta ->
+- edit(Cs, P, MultiLine, search_meta, Rs0);
++ edit(Cs, P, {Bef,Aft}, search_meta, Rs0);
+ search_meta_left_sq_bracket ->
+- edit(Cs, P, MultiLine, search_meta_left_sq_bracket, Rs0);
++ edit(Cs, P, {Bef,Aft}, search_meta_left_sq_bracket, Rs0);
+ ctlx ->
+- edit(Cs, P, MultiLine, ctlx, Rs0);
++ edit(Cs, P, {Bef,Aft}, ctlx, Rs0);
+ new_line ->
+- case Bef of
+- [] -> edit(Cs, P, MultiLine, none, Rs0);
+- _ -> MultiLine1 = {[lists:reverse(Bef)|LB],{[],Aft},LA},
+- edit(Cs, P, MultiLine1, none, redraw(P, MultiLine1, Rs0))
+- end;
+- new_line_finish ->
+- [Last|LAR]=LA1 = lists:reverse([lists:reverse(Bef,Aft)|LA]),
+- MultiLine1 = {LA1 ++ LB,{[],[]},[]},
+- % Move to end and redraw
+- Rs1 = redraw(P, {LAR ++ LB, {lists:reverse(Last), []},[]}, Rs0),
+- {done, MultiLine1, Cs, reverse(Rs1, [{insert_chars, unicode, "\n"}])};
++ {done, get_line(Bef, Aft ++ "\n"), Cs,
++ reverse(Rs0, [{move_rel,cp_len(Aft)},{put_chars,unicode,"\n"}])};
+ redraw_line ->
+- Rs1 = erase_line(Rs0),
+- Rs = redraw(P, MultiLine, Rs1),
+- edit(Cs, P, MultiLine, none, Rs);
++ Rs1 = erase(P, Bef, Aft, Rs0),
++ Rs = redraw(P, Bef, Aft, Rs1),
++ edit(Cs, P, {Bef,Aft}, none, Rs);
+ clear ->
+- Rs = redraw(P, MultiLine, [clear|Rs0]),
+- edit(Cs, P, MultiLine, none, Rs);
+- tab_expand ->
+- {expand, chars_before(MultiLine), Cs,
+- {line, P, MultiLine, tab_expand},
+- reverse(Rs0)};
++ Rs = redraw(P, Bef, Aft, [clear | Rs0]),
++ edit(Cs, P, {Bef,Aft}, none, Rs);
++ tab_expand ->
++ {expand, Bef, Cs,
++ {line, P, {Bef, Aft}, tab_expand},
++ reverse(Rs0)};
+ tab_expand_full ->
+- {expand_full, chars_before(MultiLine), Cs,
+- {line, P, MultiLine, tab_expand},
+- reverse(Rs0)};
++ {expand_full, Bef, Cs,
++ {line, P, {Bef, Aft}, tab_expand},
++ reverse(Rs0)};
+ {undefined,C} ->
+- {undefined,{none,Prefix,C},Cs,{line,P,MultiLine,none},
++ {undefined,{none,Prefix,C},Cs,{line,P,{Bef,Aft},none},
+ reverse(Rs0)};
+ Op ->
+- case do_op(Op, MultiLine, Rs0) of
+- {blink,N,MultiLine1,Rs} ->
+- edit(Cs, P, MultiLine1, {blink,N}, Rs);
+- {redraw, MultiLine1, Rs} ->
+- edit(Cs, P, MultiLine1, none, redraw(P, MultiLine1, Rs));
+- {MultiLine1, Rs, Mode} -> % allow custom modes from do_op
+- edit(Cs, P, MultiLine1, Mode, Rs);
+- {MultiLine1,Rs} ->
+- edit(Cs, P, MultiLine1, none, Rs)
++ case do_op(Op, Bef, Aft, Rs0) of
++ {blink,N,Line,Rs} ->
++ edit(Cs, P, Line, {blink,N}, Rs);
++ {Line, Rs, Mode} -> % allow custom modes from do_op
++ edit(Cs, P, Line, Mode, Rs);
++ {Line,Rs} ->
++ edit(Cs, P, Line, none, Rs)
+ end
+ end;
+ edit([], P, L, {blink,N}, Rs) ->
+- {blink,{line,P,L, {blink,N}},reverse(Rs)};
++ {blink,{line,P,L,{blink,N}},reverse(Rs)};
+ edit([], P, L, Prefix, Rs) ->
+ {more_chars,{line,P,L,Prefix},reverse(Rs)};
+-edit(eof, _, {_,{Bef,Aft0},LA} = L, _, Rs) ->
+- Aft1 = case LA of
+- [Last|_] -> Last;
+- _ -> Aft0
+- end,
+- {done,L,[],reverse(Rs, [{move_combo,-cp_len(Bef), length(LA), cp_len(Aft1)}])}.
++edit(eof, _, {Bef,Aft}, _, Rs) ->
++ {done,get_line(Bef, Aft),[],reverse(Rs, [{move_rel,cp_len(Aft)}])}.
+
+ %% %% Assumes that arg is a string
+ %% %% Horizontal whitespace only.
+@@ -207,8 +183,8 @@ key_map($\t, tab_expand) -> tab_expand_full;
+ key_map(C, tab_expand) -> key_map(C, none);
+ key_map($\^K, none) -> kill_line;
+ key_map($\^L, none) -> clear;
+-key_map($\n, none) -> new_line_finish;
+-key_map($\r, none) -> new_line_finish;
++key_map($\n, none) -> new_line;
++key_map($\r, none) -> new_line;
+ key_map($\^T, none) -> transpose_char;
+ key_map($\^U, none) -> ctlu;
+ key_map($\^], none) -> auto_blink;
+@@ -232,16 +208,11 @@ key_map($L, meta) -> redraw_line;
+ key_map($T, meta) -> transpose_word;
+ key_map($Y, meta) -> yank_pop;
+ key_map($b, meta) -> backward_word;
+-key_map($c, meta) -> clear_line;
+ key_map($d, meta) -> kill_word;
+ key_map($f, meta) -> forward_word;
+ key_map($l, meta) -> redraw_line;
+ key_map($t, meta) -> transpose_word;
+ key_map($y, meta) -> yank_pop;
+-key_map($<, meta) -> beginning_of_expression;
+-key_map($>, meta) -> end_of_expression;
+-key_map($\n, meta) -> new_line;
+-key_map($\r, meta) -> new_line;
+ key_map($O, meta) -> meta_o;
+ key_map($H, meta_o) -> beginning_of_line;
+ key_map($F, meta_o) -> end_of_line;
+@@ -252,7 +223,7 @@ key_map($H, meta_left_sq_bracket) -> beginning_of_line;
+ key_map($F, meta_left_sq_bracket) -> end_of_line;
+ key_map($D, meta_left_sq_bracket) -> backward_char;
+ key_map($C, meta_left_sq_bracket) -> forward_char;
+-% support a few <CTRL/ALT>+<CURSOR> combinations...
++% support a few <CTRL>+<CURSOR LEFT|RIGHT> combinations...
+ % - forward: \e\e[C, \e[5C, \e[1;5C
+ % - backward: \e\e[D, \e[5D, \e[1;5D
+ key_map($\e, meta) -> meta_meta;
+@@ -261,31 +232,14 @@ key_map($C, meta_csi) -> forward_word;
+ key_map($D, meta_csi) -> backward_word;
+ key_map($1, meta_left_sq_bracket) -> {csi, "1"};
+ key_map($3, meta_left_sq_bracket) -> {csi, "3"};
+-key_map($C, {csi, "3"}) -> forward_word;
+-key_map($D, {csi, "3"}) -> backward_word;
+-key_map($~, {csi, "3"}) -> forward_delete_char;
+ key_map($5, meta_left_sq_bracket) -> {csi, "5"};
+-key_map($C, {csi, "5"}) -> forward_word;
+-key_map($D, {csi, "5"}) -> backward_word;
+-key_map($;, {csi, "1"}) -> {csi, "1;"};
+-key_map($3, {csi, "1;"}) -> {csi, "1;3"};
+-key_map($C, {csi, "1;3"}) -> forward_word;
+-key_map($D, {csi, "1;3"}) -> backward_word;
+-key_map($A, {csi, "1;3"}) -> backward_line;
+-key_map($B, {csi, "1;3"}) -> forward_line;
+-key_map($4, {csi, "1;"}) -> {csi, "1;4"};
+-key_map($A, {csi, "1;4"}) -> beginning_of_expression;
+-key_map($B, {csi, "1;4"}) -> end_of_expression;
+ key_map($5, {csi, "1;"}) -> {csi, "1;5"};
++key_map($~, {csi, "3"}) -> forward_delete_char;
++key_map($C, {csi, "5"}) -> forward_word;
+ key_map($C, {csi, "1;5"}) -> forward_word;
++key_map($D, {csi, "5"}) -> backward_word;
+ key_map($D, {csi, "1;5"}) -> backward_word;
+-key_map($A, {csi, "1;5"}) -> backward_line;
+-key_map($B, {csi, "1;5"}) -> forward_line;
+-
+-
+-
+-
+-
++key_map($;, {csi, "1"}) -> {csi, "1;"};
+ key_map(C, none) when C >= $\s ->
+ {insert,C};
+ %% for search, we need smarter line handling and so
+@@ -310,8 +264,6 @@ key_map($\^], search) -> {search, search_quit};
+ key_map($\^X, search) -> {search, search_quit};
+ key_map($\^Y, search) -> {search, search_quit};
+ key_map($\e, search) -> search_meta;
+-key_map($c, search_meta) -> {search, search_cancel};
+-key_map($C, search_meta) -> {search, search_cancel};
+ key_map($[, search_meta) -> search_meta_left_sq_bracket;
+ key_map(_, search_meta) -> {search, search_quit};
+ key_map(_C, search_meta_left_sq_bracket) -> {search, search_quit};
+@@ -320,19 +272,19 @@ key_map(C, _) -> {undefined,C}.
+
+ %% do_op(Action, Before, After, Requests)
+ %% Before and After are of lists of type string:grapheme_cluster()
+-do_op({insert,C}, {LB,{[],[]},LA}, Rs) ->
+- {{LB,{[C],[]},LA},[{insert_chars, unicode,[C]}|Rs]};
+-do_op({insert,C}, {LB,{[Bef|Bef0], []},LA}, Rs) ->
++do_op({insert,C}, [], [], Rs) ->
++ {{[C],[]},[{put_chars, unicode,[C]}|Rs]};
++do_op({insert,C}, [Bef|Bef0], [], Rs) ->
+ case string:to_graphemes([Bef,C]) of
+- [GC] -> {{LB,{[GC|Bef0],[]},LA},[{insert_chars, unicode,[C]}|Rs]};
+- _ -> {{LB,{[C,Bef|Bef0],[]},LA},[{insert_chars, unicode,[C]}|Rs]}
++ [GC] -> {{[GC|Bef0],[]},[{put_chars, unicode,[C]}|Rs]};
++ _ -> {{[C,Bef|Bef0],[]},[{put_chars, unicode,[C]}|Rs]}
+ end;
+-do_op({insert,C}, {LB,{[], Aft},LA}, Rs) ->
+- {{LB,{[C],Aft},LA},[{insert_chars, unicode,[C]}|Rs]};
+-do_op({insert,C}, {LB,{[Bef|Bef0], Aft},LA}, Rs) ->
++do_op({insert,C}, [], Aft, Rs) ->
++ {{[C],Aft},[{insert_chars, unicode,[C]}|Rs]};
++do_op({insert,C}, [Bef|Bef0], Aft, Rs) ->
+ case string:to_graphemes([Bef,C]) of
+- [GC] -> {{LB,{[GC|Bef0],Aft},LA},[{insert_chars, unicode,[C]}|Rs]};
+- _ -> {{LB,{[C,Bef|Bef0],Aft},LA},[{insert_chars, unicode,[C]}|Rs]}
++ [GC] -> {{[GC|Bef0],Aft},[{insert_chars, unicode,[C]}|Rs]};
++ _ -> {{[C,Bef|Bef0],Aft},[{insert_chars, unicode,[C]}|Rs]}
+ end;
+ %% Search mode prompt always looks like (search)`$TERMS': $RESULT.
+ %% the {insert_search, _} handlings allow to share this implementation
+@@ -343,168 +295,127 @@ do_op({insert,C}, {LB,{[Bef|Bef0], Aft},LA}, Rs) ->
+ %% search mode), we can use the Bef and Aft variables to hold each
+ %% part of the line. Bef takes charge of "(search)`$TERMS" and Aft
+ %% takes charge of "': $RESULT".
+-%%
+-%% Since multiline support the search mode prompt always looks like:
+-%% search: $TERMS
+-%% $ResultLine1
+-%% $ResultLine2
+-do_op({insert_search, C}, {LB,{Bef, []},LA}, Rs) ->
+- {{LB, {[C|Bef],[]}, LA},
+- [{insert_chars, unicode, [C]}, delete_after_cursor | Rs], search};
+-do_op({insert_search, C}, {LB,{Bef, _Aft},LA}, Rs) ->
+- {{LB, {[C|Bef],[]}, LA},
+- [{insert_chars, unicode, [C]}, delete_after_cursor | Rs],
++do_op({insert_search, C}, Bef, [], Rs) ->
++ Aft="': ",
++ {{[C|Bef],Aft},
++ [{insert_chars, unicode, [C]++Aft}, {delete_chars,-3} | Rs],
++ search};
++do_op({insert_search, C}, Bef, Aft, Rs) ->
++ Offset= cp_len(Aft),
++ NAft = "': ",
++ {{[C|Bef],NAft},
++ [{insert_chars, unicode, [C]++NAft}, {delete_chars,-Offset} | Rs],
+ search};
+-do_op({search, backward_delete_char}, {LB,{[_|Bef], Aft},LA}, Rs) ->
++do_op({search, backward_delete_char}, [_|Bef], Aft, Rs) ->
+ Offset= cp_len(Aft)+1,
+- {{LB, {Bef,Aft}, LA},
+- [{insert_chars, unicode, Aft}, {delete_chars,-Offset}|Rs],
++ NAft = "': ",
++ {{Bef,NAft},
++ [{insert_chars, unicode, NAft}, {delete_chars,-Offset}|Rs],
+ search};
+-do_op({search, backward_delete_char}, {LB,{[], Aft},LA}, Rs) ->
+- {{LB, {[],Aft}, LA}, [{insert_chars, unicode, Aft}, {delete_chars,-cp_len(Aft)}|Rs], search};
+-do_op({search, skip_up}, {_,{Bef, Aft},_}, Rs) ->
++do_op({search, backward_delete_char}, [], Aft, Rs) ->
++ NAft="': ",
++ {{[],NAft}, [{insert_chars, unicode, NAft}, {delete_chars,-cp_len(Aft)}|Rs], search};
++do_op({search, skip_up}, Bef, Aft, Rs) ->
+ Offset= cp_len(Aft),
+- {{[],{[$\^R|Bef],Aft},[]}, % we insert ^R as a flag to whoever called us
+- [{insert_chars, unicode, Aft}, {delete_chars,-Offset}|Rs],
++ NAft = "': ",
++ {{[$\^R|Bef],NAft}, % we insert ^R as a flag to whoever called us
++ [{insert_chars, unicode, NAft}, {delete_chars,-Offset}|Rs],
+ search};
+-do_op({search, skip_down}, {_,{Bef, Aft},_LA}, Rs) ->
++do_op({search, skip_down}, Bef, Aft, Rs) ->
+ Offset= cp_len(Aft),
+- {{[],{[$\^S|Bef],Aft},[]}, % we insert ^S as a flag to whoever called us
+- [{insert_chars, unicode, Aft}, {delete_chars,-Offset}|Rs],
++ NAft = "': ",
++ {{[$\^S|Bef],NAft}, % we insert ^S as a flag to whoever called us
++ [{insert_chars, unicode, NAft}, {delete_chars,-Offset}|Rs],
+ search};
+-do_op({search, search_found}, {_,{_Bef, Aft},LA}, Rs) ->
+- {{[],{[],Aft},LA}, Rs, search_found};
+-do_op({search, search_quit}, {_,{_Bef, Aft},LA}, Rs) ->
+- {{[],{[],Aft},LA}, Rs, search_quit};
+-do_op({search, search_cancel}, _, Rs) ->
+- {{[],{[],[]},[]}, Rs, search_cancel};
++do_op({search, search_found}, _Bef, Aft, Rs) ->
++ "': "++NAft = Aft,
++ {{[],NAft},
++ [{put_chars, unicode, "\n"}, {move_rel,-cp_len(Aft)} | Rs],
++ search_found};
++do_op({search, search_quit}, _Bef, Aft, Rs) ->
++ "': "++NAft = Aft,
++ {{[],NAft},
++ [{put_chars, unicode, "\n"}, {move_rel,-cp_len(Aft)} | Rs],
++ search_quit};
+ %% do blink after $$
+-do_op({blink,C,M}, {_,{[$$,$$|_], _},_} = MultiLine, Rs) ->
+- blink(over_paren(chars_before(MultiLine), C, M), C, MultiLine, Rs);
++do_op({blink,C,M}, Bef=[$$,$$|_], Aft, Rs) ->
++ N = over_paren(Bef, C, M),
++ {blink,N+1,{[C|Bef],Aft},[{move_rel,-(N+1)},{insert_chars, unicode,[C]}|Rs]};
+ %% don't blink after a $
+-do_op({blink,C,_}, {_,{[$$|_], _},_} = MultiLine, Rs) ->
+- do_op({insert,C}, MultiLine, Rs);
+-do_op({blink,C,M}, MultiLine, Rs) ->
+- blink(over_paren(chars_before(MultiLine), C, M), C, MultiLine, Rs);
+-do_op(auto_blink, MultiLine, Rs) ->
+- blink(over_paren_auto(chars_before(MultiLine)), MultiLine, Rs);
+-do_op(forward_delete_char, {LB,{Bef, []},[NextLine|LA]}, Rs) ->
+- NewLine = {LB, {Bef, NextLine}, LA},
+- {redraw, NewLine, Rs};
+-do_op(forward_delete_char, {LB,{Bef, [GC|Aft]},LA}, Rs) ->
+- {{LB, {Bef,Aft}, LA},[{delete_chars,gc_len(GC)}|Rs]};
+-do_op(backward_delete_char, {[PrevLine|LB],{[], Aft},LA}, Rs) ->
+- NewLine = {LB, {lists:reverse(PrevLine), Aft}, LA},
+- {redraw, NewLine,Rs};
+-do_op(backward_delete_char, {LB,{[GC|Bef], Aft},LA}, Rs) ->
+- {{LB, {Bef,Aft}, LA},[{delete_chars,-gc_len(GC)}|Rs]};
+-do_op(transpose_char, {LB,{[C1,C2|Bef], []},LA}, Rs) ->
++do_op({blink,C,_}, Bef=[$$|_], Aft, Rs) ->
++ do_op({insert,C}, Bef, Aft, Rs);
++do_op({blink,C,M}, Bef, Aft, Rs) ->
++ case over_paren(Bef, C, M) of
++ beep ->
++ {{[C|Bef], Aft}, [beep,{insert_chars, unicode, [C]}|Rs]};
++ N -> {blink,N+1,{[C|Bef],Aft},
++ [{move_rel,-(N+1)},{insert_chars, unicode,[C]}|Rs]}
++ end;
++do_op(auto_blink, Bef, Aft, Rs) ->
++ case over_paren_auto(Bef) of
++ {N, Paren} ->
++ {blink,N+1,
++ {[Paren|Bef], Aft},[{move_rel,-(N+1)},{insert_chars, unicode,[Paren]}|Rs]};
++ % N is likely 0
++ N -> {blink,N+1,{Bef,Aft},
++ [{move_rel,-(N+1)}|Rs]}
++ end;
++do_op(forward_delete_char, Bef, [GC|Aft], Rs) ->
++ {{Bef,Aft},[{delete_chars,gc_len(GC)}|Rs]};
++do_op(backward_delete_char, [GC|Bef], Aft, Rs) ->
++ {{Bef,Aft},[{delete_chars,-gc_len(GC)}|Rs]};
++do_op(transpose_char, [C1,C2|Bef], [], Rs) ->
+ Len = gc_len(C1)+gc_len(C2),
+- {{LB, {[C2,C1|Bef],[]}, LA},[{put_chars, unicode,[C1,C2]},{move_rel,-Len}|Rs]};
+-do_op(transpose_char, {LB,{[C2|Bef], [C1|Aft]},LA}, Rs) ->
++ {{[C2,C1|Bef],[]},[{put_chars, unicode,[C1,C2]},{move_rel,-Len}|Rs]};
++do_op(transpose_char, [C2|Bef], [C1|Aft], Rs) ->
+ Len = gc_len(C2),
+- {{LB, {[C2,C1|Bef],Aft}, LA},[{put_chars, unicode,[C1,C2]},{move_rel,-Len}|Rs]};
+-do_op(kill_word, {LB,{Bef, Aft0},LA}, Rs) ->
++ {{[C2,C1|Bef],Aft},[{put_chars, unicode,[C1,C2]},{move_rel,-Len}|Rs]};
++do_op(kill_word, Bef, Aft0, Rs) ->
+ {Aft1,Kill0,N0} = over_non_word(Aft0, [], 0),
+ {Aft,Kill,N} = over_word(Aft1, Kill0, N0),
+ put(kill_buffer, reverse(Kill)),
+- {{LB, {Bef,Aft}, LA},[{delete_chars,N}|Rs]};
+-do_op(backward_kill_word, {LB,{Bef0, Aft},LA}, Rs) ->
++ {{Bef,Aft},[{delete_chars,N}|Rs]};
++do_op(backward_kill_word, Bef0, Aft, Rs) ->
+ {Bef1,Kill0,N0} = over_non_word(Bef0, [], 0),
+ {Bef,Kill,N} = over_word(Bef1, Kill0, N0),
+ put(kill_buffer, Kill),
+- {{LB,{Bef,Aft},LA},[{delete_chars,-N}|Rs]};
+-do_op(kill_line, {LB, {Bef, Aft}, LA}, Rs) ->
++ {{Bef,Aft},[{delete_chars,-N}|Rs]};
++do_op(kill_line, Bef, Aft, Rs) ->
+ put(kill_buffer, Aft),
+- {{LB, {Bef,[]}, LA},[{delete_chars,cp_len(Aft)}|Rs]};
+-do_op(clear_line, _, Rs) ->
+- {redraw, {[], {[],[]},[]}, Rs};
+-do_op(yank, {LB,{Bef, []},LA}, Rs) ->
++ {{Bef,[]},[{delete_chars,cp_len(Aft)}|Rs]};
++do_op(yank, Bef, [], Rs) ->
+ Kill = get(kill_buffer),
+- {{LB, {reverse(Kill, Bef),[]}, LA},[{put_chars, unicode,Kill}|Rs]};
+-do_op(yank, {LB,{Bef, Aft},LA}, Rs) ->
++ {{reverse(Kill, Bef),[]},[{put_chars, unicode,Kill}|Rs]};
++do_op(yank, Bef, Aft, Rs) ->
+ Kill = get(kill_buffer),
+- {{LB, {reverse(Kill, Bef),Aft}, LA},[{insert_chars, unicode,Kill}|Rs]};
+-do_op(forward_line, {_,_,[]} = MultiLine, Rs) ->
+- {MultiLine, Rs};
+-do_op(forward_line, {LB,{Bef, Aft},[AL|LA]}, Rs) ->
+- CL = lists:reverse(Bef, Aft),
+- CursorPos = min(length(Bef), length(AL)),
+- {Bef1, Aft1} = lists:split(CursorPos, AL),
+- {{[CL|LB], {lists:reverse(Bef1), Aft1}, LA}, [{move_combo, -cp_len(Bef), 1, cp_len(Bef1)}|Rs]};
+-do_op(backward_line, {[], _, _} = MultiLine, Rs) ->
+- {MultiLine, Rs};
+-do_op(backward_line, {[BL|LB],{Bef, Aft},LA}, Rs) ->
+- CL = lists:reverse(Bef, Aft),
+- CursorPos = min(length(Bef), length(BL)),
+- {Bef1, Aft1} = lists:split(CursorPos, BL),
+- {{LB, {lists:reverse(Bef1), Aft1}, [CL|LA]},[{move_combo, -cp_len(Bef), -1, cp_len(Bef1)}|Rs]};
+-do_op(forward_char, {LB,{Bef, []}, [AL|LA]}, Rs) ->
+- {{[lists:reverse(Bef)|LB],{[], string:to_graphemes(AL)}, LA}, [{move_combo, -cp_len(Bef), 1, 0}|Rs]};
+-do_op(forward_char, {LB,{Bef, [C|Aft]},LA}, Rs) ->
+- {{LB,{[C|Bef],Aft},LA},[{move_rel,gc_len(C)}|Rs]};
+-do_op(backward_char, {[BL|LB],{[], Aft},LA}, Rs) ->
+- {{LB,{lists:reverse(string:to_graphemes(BL)), []}, [Aft|LA]}, [{move_combo, 0, -1, cp_len(BL)}|Rs]};
+-do_op(backward_char, {LB,{[C|Bef], Aft},LA}, Rs) ->
+- {{LB, {Bef,[C|Aft]}, LA},[{move_rel,-gc_len(C)}|Rs]};
+-do_op(forward_word, {LB,{Bef0, []},[NextLine|LA]}, Rs) ->
+- {{[reverse(Bef0)|LB], {[], NextLine}, LA},[{move_combo, -cp_len(Bef0), 1, 0}|Rs]};
+-do_op(forward_word, {LB,{Bef0, Aft0},LA}, Rs) ->
++ {{reverse(Kill, Bef),Aft},[{insert_chars, unicode,Kill}|Rs]};
++do_op(forward_char, Bef, [C|Aft], Rs) ->
++ {{[C|Bef],Aft},[{move_rel,gc_len(C)}|Rs]};
++do_op(backward_char, [C|Bef], Aft, Rs) ->
++ {{Bef,[C|Aft]},[{move_rel,-gc_len(C)}|Rs]};
++do_op(forward_word, Bef0, Aft0, Rs) ->
+ {Aft1,Bef1,N0} = over_non_word(Aft0, Bef0, 0),
+- {Aft, Bef, N} = over_word(Aft1, Bef1, N0),
+- {{LB, {Bef,Aft}, LA},[{move_rel,N}|Rs]};
+-do_op(backward_word, {[PrevLine|LB],{[], Aft0},LA}, Rs) ->
+- {{LB, {reverse(PrevLine), []}, [Aft0|LA]},[{move_combo, 0, -1, cp_len(PrevLine)}|Rs]};
+-do_op(backward_word, {LB,{Bef0, Aft0},LA}, Rs) ->
++ {Aft,Bef,N} = over_word(Aft1, Bef1, N0),
++ {{Bef,Aft},[{move_rel,N}|Rs]};
++do_op(backward_word, Bef0, Aft0, Rs) ->
+ {Bef1,Aft1,N0} = over_non_word(Bef0, Aft0, 0),
+ {Bef,Aft,N} = over_word(Bef1, Aft1, N0),
+- {{LB, {Bef,Aft}, LA},[{move_rel,-N}|Rs]};
+-do_op(beginning_of_expression, {[],{[], Aft},LA}, Rs) ->
+- {{[], {[],Aft}, LA},Rs};
+-do_op(beginning_of_expression, {LB,{Bef, Aft},LA}, Rs) ->
+- [First|Rest] = lists:reverse(LB) ++ [lists:reverse(Bef, Aft)],
+- {{[], {[],First}, Rest ++ LA},[{move_combo, -cp_len(Bef), -length(LB), 0}|Rs]};
+-do_op(end_of_expression, {LB,{Bef, []},[]}, Rs) ->
+- {{LB, {Bef,[]}, []},Rs};
+-do_op(end_of_expression, {LB,{Bef, Aft},LA}, Rs) ->
+- [Last|Rest] = lists:reverse(LA) ++ [lists:reverse(Bef, Aft)],
+- {{LB ++ Rest, {lists:reverse(Last),[]}, []},[{move_combo, -cp_len(Bef), length(LA), cp_len(Last)}|Rs]};
+-do_op(beginning_of_line, {LB,{[_|_]=Bef, Aft},LA}, Rs) ->
+- {{LB, {[],reverse(Bef, Aft)}, LA},[{move_rel,-(cp_len(Bef))}|Rs]};
+-do_op(beginning_of_line, {LB,{[], Aft},LA}, Rs) ->
+- {{LB, {[],Aft}, LA},Rs};
+-do_op(end_of_line, {LB,{Bef, [_|_]=Aft},LA}, Rs) ->
+- {{LB, {reverse(Aft, Bef),[]}, LA},[{move_rel,cp_len(Aft)}|Rs]};
+-do_op(end_of_line, {LB,{Bef, []},LA}, Rs) ->
+- {{LB, {Bef,[]}, LA},Rs};
+-do_op(ctlu, {LB,{Bef, Aft},LA}, Rs) ->
++ {{Bef,Aft},[{move_rel,-N}|Rs]};
++do_op(beginning_of_line, [_|_]=Bef, Aft, Rs) ->
++ {{[],reverse(Bef, Aft)},[{move_rel,-(cp_len(Bef))}|Rs]};
++do_op(beginning_of_line, [], Aft, Rs) ->
++ {{[],Aft},Rs};
++do_op(end_of_line, Bef, [_|_]=Aft, Rs) ->
++ {{reverse(Aft, Bef),[]},[{move_rel,cp_len(Aft)}|Rs]};
++do_op(end_of_line, Bef, [], Rs) ->
++ {{Bef,[]},Rs};
++do_op(ctlu, Bef, Aft, Rs) ->
+ put(kill_buffer, reverse(Bef)),
+- {{LB, {[], Aft}, LA}, [{delete_chars, -cp_len(Bef)} | Rs]};
+-do_op(beep, {LB,{Bef, Aft},LA}, Rs) ->
+- {{LB,{Bef,Aft},LA},[beep|Rs]};
+-do_op(_, {LB,{Bef, Aft},LA}, Rs) ->
+- {{LB,{Bef,Aft},LA},[beep|Rs]}.
+-
+-blink(beep, C, {LB, {Bef, Aft}, LA}, Rs) ->
+- {{LB,{[C|Bef], Aft},LA}, [beep,{insert_chars, unicode, [C]}|Rs]};
+-blink({N, R}, C, MultiLine, Rs) ->
+- blink({N, R, C}, MultiLine, Rs).
+-%% same line
+-blink(beep, {LB,{Bef, Aft},LA}, Rs) ->
+- {{LB,{Bef, Aft},LA}, [beep|Rs]};
+-blink({N, 0, Paren}, {LB, {Bef, Aft}, LA}, Rs) ->
+- MoveBackToParen = {move_rel,-N-1},
+- MoveForwardToParen = {move_rel, N+1},
+- {blink,[MoveForwardToParen],{LB,{[Paren|Bef],Aft},LA},
+- [MoveBackToParen,{insert_chars, unicode,[Paren]}|Rs]};
+-%% multiline
+-blink({N, R, Paren}, {LB,{Bef, Aft},LA}, Rs) ->
+- LengthToClosingParen = cp_len([Paren|Bef]),
+- LengthOpeningParen = cp_len(lists:nth(R,LB)) - N - 1,
+- MoveToOpeningParen = {move_combo, -LengthToClosingParen, -R, LengthOpeningParen},
+- MoveToClosingParen = {move_combo, -LengthOpeningParen, R, LengthToClosingParen+1},
+- {blink,[MoveToClosingParen],{LB,{[Paren|Bef],Aft},LA},
+- [MoveToOpeningParen,{insert_chars, unicode,[Paren]}|Rs]}.
++ {{[], Aft}, [{delete_chars, -cp_len(Bef)} | Rs]};
++do_op(beep, Bef, Aft, Rs) ->
++ {{Bef,Aft},[beep|Rs]};
++do_op(_, Bef, Aft, Rs) ->
++ {{Bef,Aft},[beep|Rs]}.
+
+ %% over_word(Chars, InitialStack, InitialCount) ->
+ %% {RemainingChars,CharStack,Count}
+@@ -512,6 +423,8 @@ blink({N, R, Paren}, {LB,{Bef, Aft},LA}, Rs) ->
+ %% {RemainingChars,CharStack,Count}
+ %% Step over word/non-word characters pushing the stepped over ones on
+ %% the stack.
++
++
+ over_word(Cs, Stack, N) ->
+ L = length([1 || $\' <- Cs]),
+ case L rem 2 of
+@@ -572,84 +485,80 @@ word_char(_) -> false.
+ %% do proper parentheses matching check. Paren has NOT been added.
+
+ over_paren(Chars, Paren, Match) ->
+- over_paren(Chars, Paren, Match, 1, 1, 0, []).
+-
+-
+-over_paren([C,$$,$$|Cs], Paren, Match, D, N, R, L) ->
+- over_paren([C|Cs], Paren, Match, D, N+2, R, L);
+-over_paren([GC,$$|Cs], Paren, Match, D, N, R, L) ->
+- over_paren(Cs, Paren, Match, D, N+1+gc_len(GC), R, L);
+-over_paren([$\n|Cs], Paren, Match, D, _N, R, L) ->
+- over_paren(Cs, Paren, Match, D, 0, R+1, L);
+-over_paren([Match|_], _Paren, Match, 1, N, R, _) ->
+- {N, R};
+-over_paren([Match|Cs], Paren, Match, D, N, R, [Match|L]) ->
+- over_paren(Cs, Paren, Match, D-1, N+1, R, L);
+-over_paren([Paren|Cs], Paren, Match, D, N, R, L) ->
+- over_paren(Cs, Paren, Match, D+1, N+1, R, [Match|L]);
+-
+-over_paren([$)|Cs], Paren, Match, D, N, R, L) ->
+- over_paren(Cs, Paren, Match, D, N+1, R, [$(|L]);
+-over_paren([$]|Cs], Paren, Match, D, N, R, L) ->
+- over_paren(Cs, Paren, Match, D, N+1, R, [$[|L]);
+-over_paren([$}|Cs], Paren, Match, D, N, R, L) ->
+- over_paren(Cs, Paren, Match, D, N+1, R, [${|L]);
+-
+-over_paren([$(|Cs], Paren, Match, D, N, R, [$(|L]) ->
+- over_paren(Cs, Paren, Match, D, N+1, R, L);
+-over_paren([$[|Cs], Paren, Match, D, N, R, [$[|L]) ->
+- over_paren(Cs, Paren, Match, D, N+1, R, L);
+-over_paren([${|Cs], Paren, Match, D, N, R, [${|L]) ->
+- over_paren(Cs, Paren, Match, D, N+1, R, L);
+-
+-over_paren([$(|_], _, _, _, _, _, _) ->
++ over_paren(Chars, Paren, Match, 1, 1, []).
++
++
++over_paren([C,$$,$$|Cs], Paren, Match, D, N, L) ->
++ over_paren([C|Cs], Paren, Match, D, N+2, L);
++over_paren([GC,$$|Cs], Paren, Match, D, N, L) ->
++ over_paren(Cs, Paren, Match, D, N+1+gc_len(GC), L);
++over_paren([Match|_], _Paren, Match, 1, N, _) ->
++ N;
++over_paren([Match|Cs], Paren, Match, D, N, [Match|L]) ->
++ over_paren(Cs, Paren, Match, D-1, N+1, L);
++over_paren([Paren|Cs], Paren, Match, D, N, L) ->
++ over_paren(Cs, Paren, Match, D+1, N+1, [Match|L]);
++
++over_paren([$)|Cs], Paren, Match, D, N, L) ->
++ over_paren(Cs, Paren, Match, D, N+1, [$(|L]);
++over_paren([$]|Cs], Paren, Match, D, N, L) ->
++ over_paren(Cs, Paren, Match, D, N+1, [$[|L]);
++over_paren([$}|Cs], Paren, Match, D, N, L) ->
++ over_paren(Cs, Paren, Match, D, N+1, [${|L]);
++
++over_paren([$(|Cs], Paren, Match, D, N, [$(|L]) ->
++ over_paren(Cs, Paren, Match, D, N+1, L);
++over_paren([$[|Cs], Paren, Match, D, N, [$[|L]) ->
++ over_paren(Cs, Paren, Match, D, N+1, L);
++over_paren([${|Cs], Paren, Match, D, N, [${|L]) ->
++ over_paren(Cs, Paren, Match, D, N+1, L);
++
++over_paren([$(|_], _, _, _, _, _) ->
+ beep;
+-over_paren([$[|_], _, _, _, _, _, _) ->
++over_paren([$[|_], _, _, _, _, _) ->
+ beep;
+-over_paren([${|_], _, _, _, _, _, _) ->
++over_paren([${|_], _, _, _, _, _) ->
+ beep;
+
+-over_paren([GC|Cs], Paren, Match, D, N, R, L) ->
+- over_paren(Cs, Paren, Match, D, N+gc_len(GC), R, L);
+-over_paren([], _, _, _, _, _, _) ->
+- beep.
++over_paren([GC|Cs], Paren, Match, D, N, L) ->
++ over_paren(Cs, Paren, Match, D, N+gc_len(GC), L);
++over_paren([], _, _, _, _, _) ->
++ 0.
+
+ over_paren_auto(Chars) ->
+- over_paren_auto(Chars, 1, 1, 0, []).
+-
+-
+-over_paren_auto([C,$$,$$|Cs], D, N, R, L) ->
+- over_paren_auto([C|Cs], D, N+2, R, L);
+-over_paren_auto([GC,$$|Cs], D, N, R, L) ->
+- over_paren_auto(Cs, D, N+1+gc_len(GC), R, L);
+-over_paren_auto([$\n|Cs], D, _N, R, L) ->
+- over_paren_auto(Cs, D, 0, R+1, L);
+-
+-over_paren_auto([$(|_], _, N, R, []) ->
+- {N, R, $)};
+-over_paren_auto([$[|_], _, N, R, []) ->
+- {N, R, $]};
+-over_paren_auto([${|_], _, N, R, []) ->
+- {N, R, $}};
+-
+-over_paren_auto([$)|Cs], D, N, R, L) ->
+- over_paren_auto(Cs, D, N+1, R, [$(|L]);
+-over_paren_auto([$]|Cs], D, N, R, L) ->
+- over_paren_auto(Cs, D, N+1, R, [$[|L]);
+-over_paren_auto([$}|Cs], D, N, R, L) ->
+- over_paren_auto(Cs, D, N+1, R, [${|L]);
+-
+-over_paren_auto([$(|Cs], D, N, R, [$(|L]) ->
+- over_paren_auto(Cs, D, N+1, R, L);
+-over_paren_auto([$[|Cs], D, N, R, [$[|L]) ->
+- over_paren_auto(Cs, D, N+1, R, L);
+-over_paren_auto([${|Cs], D, N, R, [${|L]) ->
+- over_paren_auto(Cs, D, N+1, R, L);
+-
+-over_paren_auto([GC|Cs], D, N, R, L) ->
+- over_paren_auto(Cs, D, N+gc_len(GC), R, L);
+-over_paren_auto([], _, _, _, _) ->
+- beep.
++ over_paren_auto(Chars, 1, 1, []).
++
++
++over_paren_auto([C,$$,$$|Cs], D, N, L) ->
++ over_paren_auto([C|Cs], D, N+2, L);
++over_paren_auto([GC,$$|Cs], D, N, L) ->
++ over_paren_auto(Cs, D, N+1+gc_len(GC), L);
++
++over_paren_auto([$(|_], _, N, []) ->
++ {N, $)};
++over_paren_auto([$[|_], _, N, []) ->
++ {N, $]};
++over_paren_auto([${|_], _, N, []) ->
++ {N, $}};
++
++over_paren_auto([$)|Cs], D, N, L) ->
++ over_paren_auto(Cs, D, N+1, [$(|L]);
++over_paren_auto([$]|Cs], D, N, L) ->
++ over_paren_auto(Cs, D, N+1, [$[|L]);
++over_paren_auto([$}|Cs], D, N, L) ->
++ over_paren_auto(Cs, D, N+1, [${|L]);
++
++over_paren_auto([$(|Cs], D, N, [$(|L]) ->
++ over_paren_auto(Cs, D, N+1, L);
++over_paren_auto([$[|Cs], D, N, [$[|L]) ->
++ over_paren_auto(Cs, D, N+1, L);
++over_paren_auto([${|Cs], D, N, [${|L]) ->
++ over_paren_auto(Cs, D, N+1, L);
++
++over_paren_auto([GC|Cs], D, N, L) ->
++ over_paren_auto(Cs, D, N+gc_len(GC), L);
++over_paren_auto([], _, _, _) ->
++ 0.
+
+ %% erase_line(Line)
+ %% erase_inp(Line)
+@@ -658,57 +567,40 @@ over_paren_auto([], _, _, _, _) ->
+ %% length_after(Line)
+ %% prompt(Line)
+ %% current_line(Line)
+-%% current_chars(Line)
+ %% Various functions for accessing bits of a line.
+
+-erase_line() ->
+- [delete_line].
+-
+-erase_inp({line,_, L,_}) ->
+- reverse(erase([], L, [])).
++erase_line({line,Pbs,{Bef,Aft},_}) ->
++ reverse(erase(Pbs, Bef, Aft, [])).
+
+-erase_line(Rs) ->
+- [delete_line|Rs].
++erase_inp({line,_,{Bef,Aft},_}) ->
++ reverse(erase([], Bef, Aft, [])).
+
+-erase(Pbs, {_,{Bef, Aft},_}, Rs) ->
++erase(Pbs, Bef, Aft, Rs) ->
+ [{delete_chars,-cp_len(Pbs)-cp_len(Bef)},{delete_chars,cp_len(Aft)}|Rs].
+
+-redraw_line({line, Pbs, L,_}) ->
+- redraw(Pbs, L, []).
+-
+-multi_line_prompt(Pbs) ->
+- lists:duplicate(max(0,prim_tty:npwcwidthstring(Pbs)-3), $ )++".. ".
++redraw_line({line,Pbs,{Bef,Aft},_}) ->
++ reverse(redraw(Pbs, Bef, Aft, [])).
+
+-redraw(Pbs, {_,{_,_},_}=L, Rs) ->
+- [{redraw_prompt, Pbs, multi_line_prompt(Pbs), L} |Rs].
++redraw(Pbs, Bef, Aft, Rs) ->
++ [{move_rel,-cp_len(Aft)},{put_chars, unicode,reverse(Bef, Aft)},{put_chars, unicode,Pbs}|Rs].
+
+-chars_before({[],{Bef,_},_}) ->
+- Bef;
+-chars_before({LB,{Bef,_},_}) ->
+- lists:flatten(lists:join($\n, [Bef| [reverse(Line)|| Line <- LB]])).
+-
+-length_before({line,Pbs,{_,{Bef,_Aft},_},_}) ->
++length_before({line,Pbs,{Bef,_Aft},_}) ->
+ cp_len(Pbs) + cp_len(Bef).
+
+-length_after({line,_,{_,{_Bef,Aft},_},_}) ->
++length_after({line,_,{_Bef,Aft},_}) ->
+ cp_len(Aft).
+
+ prompt({line,Pbs,_,_}) ->
+ Pbs.
+
+-current_chars({line,_,MultiLine,_}) ->
+- current_line(MultiLine).
+-current_line({line,_,MultiLine,_}) ->
+- current_line(MultiLine) ++ "\n";
+-%% Convert a multiline tuple into a string with new lines
+-current_line({LinesBefore, {Before, After}, LinesAfter}) ->
+- CurrentLine = lists:reverse(Before, After),
+- unicode:characters_to_list(lists:flatten(
+- lists:filter(
+- fun (X) ->
+- X /= []
+- end,
+- lists:join($\n, lists:reverse(LinesBefore) ++ [CurrentLine] ++ LinesAfter)))).
++current_line({line,_,{Bef, Aft},_}) ->
++ get_line(Bef, Aft ++ "\n").
++
++current_chars({line,_,{Bef,Aft},_}) ->
++ get_line(Bef, Aft).
++
++get_line(Bef, Aft) ->
++ unicode:characters_to_list(reverse(Bef, Aft)).
+
+ %% Grapheme length in codepoints
+ gc_len(CP) when is_integer(CP) -> 1;
+--
+2.34.1
+
--
2.34.1