Skip to content

Commit d94a9cc

Browse files
Max Veytsmanwhatyouhide
Max Veytsman
authored andcommitted
Add cursor movement to IO.ANSI (#7396)
1 parent 4f03ec6 commit d94a9cc

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

lib/elixir/lib/io/ansi.ex

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,33 @@ defmodule IO.ANSI do
170170
@doc "Sends cursor home."
171171
defsequence(:home, "", "H")
172172

173+
@doc """
174+
Sends cursor to the absolute position specified by `line` and `column`.
175+
176+
Line `0` and column `0` would mean the top left corner.
177+
"""
178+
@spec cursor(non_neg_integer, non_neg_integer) :: String.t()
179+
def cursor(line, column)
180+
when is_integer(line) and line >= 0 and is_integer(column) and column >= 0 do
181+
"\e[#{line};#{column}H"
182+
end
183+
184+
@doc "Sends cursor `lines` up."
185+
@spec cursor_up(pos_integer) :: String.t()
186+
def cursor_up(lines \\ 1) when is_integer(lines) and lines >= 1, do: "\e[#{lines}A"
187+
188+
@doc "Sends cursor `lines` down."
189+
@spec cursor_down(pos_integer) :: String.t()
190+
def cursor_down(lines \\ 1) when is_integer(lines) and lines >= 1, do: "\e[#{lines}B"
191+
192+
@doc "Sends cursor `columns` to the left."
193+
@spec cursor_left(pos_integer) :: String.t()
194+
def cursor_left(columns \\ 1) when is_integer(columns) and columns >= 1, do: "\e[#{columns}C"
195+
196+
@doc "Sends cursor `columns` to the right."
197+
@spec cursor_right(pos_integer) :: String.t()
198+
def cursor_right(columns \\ 1) when is_integer(columns) and columns >= 1, do: "\e[#{columns}D"
199+
173200
@doc "Clears screen."
174201
defsequence(:clear, "2", "J")
175202

lib/elixir/test/elixir/io/ansi_test.exs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,69 @@ defmodule IO.ANSITest do
155155
IO.ANSI.color_background(5, -1, 1)
156156
end
157157
end
158+
159+
test "cursor/2" do
160+
assert IO.ANSI.cursor(0, 0) == "\e[0;0H"
161+
assert IO.ANSI.cursor(11, 12) == "\e[11;12H"
162+
163+
assert_raise FunctionClauseError, fn ->
164+
IO.ANSI.cursor(-1, 5)
165+
end
166+
167+
assert_raise FunctionClauseError, fn ->
168+
IO.ANSI.cursor(5, -1)
169+
end
170+
end
171+
172+
test "cursor_up/1" do
173+
assert IO.ANSI.cursor_up() == "\e[1A"
174+
assert IO.ANSI.cursor_up(12) == "\e[12A"
175+
176+
assert_raise FunctionClauseError, fn ->
177+
IO.ANSI.cursor_up(0)
178+
end
179+
180+
assert_raise FunctionClauseError, fn ->
181+
IO.ANSI.cursor_up(-1)
182+
end
183+
end
184+
185+
test "cursor_down/1" do
186+
assert IO.ANSI.cursor_down() == "\e[1B"
187+
assert IO.ANSI.cursor_down(2) == "\e[2B"
188+
189+
assert_raise FunctionClauseError, fn ->
190+
IO.ANSI.cursor_right(0)
191+
end
192+
193+
assert_raise FunctionClauseError, fn ->
194+
IO.ANSI.cursor_down(-1)
195+
end
196+
end
197+
198+
test "cursor_left/1" do
199+
assert IO.ANSI.cursor_left() == "\e[1C"
200+
assert IO.ANSI.cursor_left(3) == "\e[3C"
201+
202+
assert_raise FunctionClauseError, fn ->
203+
IO.ANSI.cursor_left(0)
204+
end
205+
206+
assert_raise FunctionClauseError, fn ->
207+
IO.ANSI.cursor_left(-1)
208+
end
209+
end
210+
211+
test "cursor_right/1" do
212+
assert IO.ANSI.cursor_right() == "\e[1D"
213+
assert IO.ANSI.cursor_right(4) == "\e[4D"
214+
215+
assert_raise FunctionClauseError, fn ->
216+
IO.ANSI.cursor_right(0)
217+
end
218+
219+
assert_raise FunctionClauseError, fn ->
220+
IO.ANSI.cursor_right(-1)
221+
end
222+
end
158223
end

0 commit comments

Comments
 (0)