在命令行中获取光标位置(Elixir)

时间:2017-10-09 20:39:43

标签: elixir

我想知道是否有一种捕捉绝对光标位置的方法 来自Elixir的命令行。

我知道我必须使用以下ansi转义序列\ 033 [6n, 并在执行之后:

echo -en "\033[6n" 

打印出我正在寻找的内容,但我不确定如何从Elixir获取命令响应。

谢谢!

4 个答案:

答案 0 :(得分:4)

这个让我疯狂,我不得不挖掘那么多我不能说的话题。我将添加与解决方案相关的所有线程,它们都值得一读。

首先,我们不能使用System.cmd,System.cmd在没有tty的情况下运行

iex(1)> System.cmd("tty", [])
{"not a tty\n", 1}

我们要做的事情需要TTY。因此,同样的

有几个有趣的库

https://github.com/alco/porcelain

但这也不适用于TTY

iex(1)> Porcelain.shell("tty")
%Porcelain.Result{err: nil, out: "not a tty\n", status: 1}

然后来到另一个图书馆

https://github.com/aleandros/shell_stream

这个似乎分享了TTY

iex(3)> ShellStream.shell("tty") |> Enum.to_list
["/dev/pts/6"]

此TTY与当前终端的TTY相同,这意味着TTY正在传播到子进程

接下来是检查我们是否可以获得坐标

iex(8)> ShellStream.shell("echo -en '033[6n'") |> Enum.to_list
[]

因此经过大量的打击和试验后,我提出了一种方法

defmodule CursorPos do

  def get_pos do
      settings = ShellStream.shell("stty -g") |> Enum.to_list

      #ShellStream.shell("stty -echo -echoctl -imaxbel -isig -icanon min 1 time 0")
      ShellStream.shell("stty raw -echo")
      #settings |> IO.inspect
      spawn(fn ->
            IO.write "\e[6n"
            #ShellStream.shell "echo -en \"\033[6n\" > `tty`"
            :timer.sleep(50)
            IO.write "\n"
           end)
      io = IO.stream(:stdio,1)
      data = io |> Stream.take_while(&(&1 != "R"))
      data|> Enum.join  |> IO.inspect
      ShellStream.shell("stty #{settings}")
  end

  def main(args) do
      get_pos
  end
end

这种作品但仍然需要你按回车来读取stdio

$ ./cursorpos
^[[24;1R

"\e[24;1"

它还会改变屏幕坐标以获取它们,这不是人们想要的。但问题是坐标控制字符需要由shell处理,而不是子shell。我尝试使用

ShellStream.shell("stty -echo -echoctl -imaxbel -isig -icanon min 1 time 0")

不起作用,stty不会影响我们需要获取坐标的父shell。所以下一个可能的解决方案是在下面做

$ EXISTING=$(stty -g);stty -echo -echonl -imaxbel -isig -icanon min 1 time 0; ./cursorpos ; stty $EXISTING
"\e[24;1"

这是有效的,因为我们能够改变当前tty的属性。现在你可能想深入挖掘并找到如何从代码中做到这一点。

我已将所有代码放在Github项目下面

https://github.com/tarunlalwani/elixir-get-current-cursor-position

另外你应该看看下面的项目

https://github.com/henrik/progress_bar

如果你的光标位置不重要,那么你可以自己将光标固定在某个位置。

<强>参考

https://unix.stackexchange.com/questions/88296/get-vertical-cursor-position

How to get the cursor position in bash?

https://groups.google.com/forum/#!topic/elixir-lang-talk/9B1oe3KgjnE

https://hexdocs.pm/elixir/IO.html#getn/3

https://unix.stackexchange.com/questions/264920/why-doesnt-the-enter-key-send-eol

http://man7.org/linux/man-pages/man1/stty.1.html

https://github.com/jfreeze/ex_ncurses

How can I get the cursor's position in an ANSI terminal?

Get console user input as typed, char by char

答案 1 :(得分:1)

我设法达到的最接近的是这条线:

~C(bash -c "echo -en '\033[6n'") |> :os.cmd

但是,它返回'\e[6n'而不是光标位置。必须是:os.cmd/1函数中包含转义符号的内容,不能确定。

虽然它不是一个完整的答案,但希望它无论如何都有帮助。

答案 2 :(得分:1)

我知道这是奇怪的宏观内容,但它确实有效! ;)当我研究IO.ANSI.Sequence.home()函数here的实现时,我发现了它。

特别感谢Thijs

defmodule Foo do                          
  import IO.ANSI.Sequence                   
  IO.ANSI.Sequence.defsequence :bar, 6, "n" 
end

然后简单地打电话: IO.puts Foo.bar

答案 3 :(得分:-1)

如果您知道系统命令,请使用System module从Elixir

执行它