Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions app/lib/linear_cli/cli/pager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ defmodule LinearCli.CLI.Pager do
- The content's line count does not exceed the terminal's row count

When paging is needed the content is written to a temp file and the
pager is invoked via `System.shell/1`, which — like
`Owl.IO.open_in_editor/2`'s own use of the same function — lets the
child process open `/dev/tty` directly for interactive keyboard
control.
The pager inherits the terminal's standard streams, so pagers that
render to standard output (such as `bat`) work as well as interactive
pagers such as `less`.
Comment on lines 13 to +16

## Testing

Expand All @@ -38,12 +37,12 @@ defmodule LinearCli.CLI.Pager do
- `:rows_fn` — 0-arity function returning terminal row count or `nil`
(defaults to `&Owl.IO.rows/0`)
- `:shell_fn` — 1-arity function receiving the full shell command
(defaults to `&System.shell/1`)
(defaults to a runner that inherits the terminal's standard streams)
"""
@spec maybe_page(String.t(), map()) :: :ok
def maybe_page(text, opts \\ %{}) do
rows_fn = Map.get(opts, :rows_fn, &Owl.IO.rows/0)
shell_fn = Map.get(opts, :shell_fn, &System.shell/1)
shell_fn = Map.get(opts, :shell_fn, &run_pager/1)
terminal_rows = rows_fn.()
pager = resolve_pager()

Expand Down Expand Up @@ -87,6 +86,23 @@ defmodule LinearCli.CLI.Pager do
:ok
end

# `System.shell/1` captures a child's stdout. That made pagers such as
# `bat` appear to succeed while their output was silently discarded.
defp run_pager(command) do
shell = System.find_executable("sh") || raise "could not find sh on PATH"

port =
Port.open({:spawn_executable, shell}, [
:nouse_stdio,
:exit_status,
args: ["-c", command]
])

receive do
{^port, {:exit_status, status}} -> {"", status}
end
end
Comment on lines +89 to +104

defp shell_quote(path) do
"'" <> String.replace(path, "'", "'\\''") <> "'"
end
Expand Down
12 changes: 12 additions & 0 deletions app/test/linear_cli/cli/pager_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ defmodule LinearCli.CLI.PagerTest do
@long_text String.duplicate("line\n", 50)
@short_text "just one line"

setup do
original = System.get_env("PAGER")
System.delete_env("PAGER")

on_exit(fn ->
case original do
nil -> System.delete_env("PAGER")
value -> System.put_env("PAGER", value)
end
end)
end

describe "maybe_page/2 - not a TTY" do
test "prints directly without invoking the pager" do
{invoked, shell_fn} = spy_shell()
Expand Down