summaryrefslogtreecommitdiff
path: root/lib/kernel/src/prim_tty.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/src/prim_tty.erl')
-rw-r--r--lib/kernel/src/prim_tty.erl62
1 files changed, 56 insertions, 6 deletions
diff --git a/lib/kernel/src/prim_tty.erl b/lib/kernel/src/prim_tty.erl
index 6f984feba9..2b144d4b9e 100644
--- a/lib/kernel/src/prim_tty.erl
+++ b/lib/kernel/src/prim_tty.erl
@@ -138,6 +138,7 @@
unicode,
buffer_before = [], %% Current line before cursor in reverse
buffer_after = [], %% Current line after cursor not in reverse
+ buffer_expand, %% Characters in expand buffer
cols = 80,
rows = 24,
xn = false,
@@ -147,6 +148,7 @@
right = <<"\e[C">>,
%% Tab to next 8 column windows is "\e[1I", for unix "ta" termcap
tab = <<"\e[1I">>,
+ delete_after_cursor = <<"\e[J">>,
insert = false,
delete = false,
position = <<"\e[6n">>, %% "u7" on my Linux
@@ -164,6 +166,7 @@
}.
-type request() ::
{putc, unicode:unicode_binary()} |
+ {expand, unicode:unicode_binary()} |
{insert, unicode:unicode_binary()} |
{delete, integer()} |
{move, integer()} |
@@ -319,6 +322,16 @@ init(State, {unix,_}) ->
false -> (#state{})#state.position
end,
+ %% According to the manual this should only be issued when the cursor
+ %% is at position 0, but until we encounter such a console we keep things
+ %% simple and issue this with the cursor anywhere
+ DeleteAfter = case tgetstr("cd") of
+ {ok, DA} ->
+ DA;
+ false ->
+ (#state{})#state.delete_after_cursor
+ end,
+
State#state{
cols = Cols,
xn = tgetflag("xn"),
@@ -329,8 +342,9 @@ init(State, {unix,_}) ->
insert = Insert,
delete = Delete,
tab = Tab,
- position = Position
- };
+ position = Position,
+ delete_after_cursor = DeleteAfter
+ };
init(State, {win32, _}) ->
State#state{
%% position = false,
@@ -513,6 +527,26 @@ handle_request(State = #state{ options = #{ tty := false } }, Request) ->
_Ignore ->
{<<>>, State}
end;
+%% Clear the expand buffer after the cursor when we handle any request.
+handle_request(State = #state{ buffer_expand = Expand, unicode = U }, Request)
+ when Expand =/= undefined ->
+ 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 = #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?
@@ -650,6 +684,13 @@ cols([SkipSeq | T], Unicode) when is_binary(SkipSeq) ->
%% so we skip that
cols(T, Unicode).
+cols(_ColsPerLine, CurrCols, [], _Unicode) ->
+ CurrCols;
+cols(ColsPerLine, CurrCols, ["\r\n" | T], Unicode) ->
+ CurrCols + (ColsPerLine - CurrCols) + cols(ColsPerLine, 0, T, Unicode);
+cols(ColsPerLine, CurrCols, [H | T], Unicode) ->
+ cols(ColsPerLine, CurrCols + cols([H], Unicode), T, Unicode).
+
update_geometry(State) ->
case tty_window_size(State#state.tty) of
{ok, {Cols, Rows}} when Cols > 0 ->
@@ -738,6 +779,10 @@ insert_buf(State, Binary) when is_binary(Binary) ->
insert_buf(State, Binary, [], []).
insert_buf(State, Bin, LineAcc, Acc) ->
case string:next_grapheme(Bin) of
+ [] when State#state.buffer_expand =/= undefined ->
+ Expand = lists:reverse(LineAcc),
+ {[Acc, characters_to_output(Expand)],
+ State#state{ buffer_expand = characters_to_buffer(Expand) }};
[] ->
NewBB = characters_to_buffer(LineAcc) ++ State#state.buffer_before,
NewState = State#state{ buffer_before = NewBB },
@@ -769,12 +814,17 @@ insert_buf(State, Bin, LineAcc, Acc) ->
true ->
<<$\r>>
end,
- insert_buf(State#state{ buffer_before = [], buffer_after = [] }, Rest, [],
- [Acc, [characters_to_output(lists:reverse(LineAcc)), Tail]]);
+ if State#state.buffer_expand =:= undefined ->
+ 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);
%% We have gotten a code point that may be part of the previous grapheme cluster.
- [Char | Rest] when Char >= 128, LineAcc =:= [], State#state.buffer_before =/= [] ->
+ [Char | Rest] when Char >= 128, LineAcc =:= [], State#state.buffer_before =/= [],
+ State#state.buffer_expand =:= undefined ->
[PrevChar | BB] = State#state.buffer_before,
case string:next_grapheme([PrevChar | Bin]) of
[PrevChar | _] ->
@@ -863,7 +913,7 @@ encode(UnicodeChars, false) ->
-ifdef(debug).
dbg(_) ->
ok.
--endif
+-endif.
%% Nif functions
-spec isatty(stdin | stdout | stderr) -> boolean() | ebadf.