Skip to main content

lib/pi/eval/output.ex

defmodule Pi.Eval.Output do
  @moduledoc false

  alias Pi.Protocol.Tool.OutputPart
  alias Pi.Protocol.UI.Block
  alias Pi.Protocol.UI.Display

  @inspect_opts [charlists: :as_lists, limit: 50, pretty: true]
  @preview_inspect_opts [
    charlists: :as_lists,
    limit: 20,
    pretty: false,
    printable_limit: 200,
    width: 1_000_000
  ]

  def inspect_value(value), do: inspect(value, @inspect_opts)
  def preview(value), do: inspect(value, @preview_inspect_opts)

  def error_text(%{text: text}) when is_binary(text), do: text
  def error_text(text) when is_binary(text), do: text

  def error_exception(%{exception: exception}) when is_map(exception), do: exception
  def error_exception(_), do: nil

  def maybe_io_part(parts, ""), do: parts
  def maybe_io_part(parts, io), do: parts ++ [OutputPart.text(io)]

  def display(parts), do: %Display{blocks: Enum.map(parts, &part_block/1)}

  def capture_io(fun) do
    {:ok, pid} = StringIO.open("")
    original = Application.get_env(:elixir, :ansi_enabled)
    original_gl = Process.group_leader()
    Application.put_env(:elixir, :ansi_enabled, false)
    Process.group_leader(self(), pid)

    try do
      result = fun.()
      {_, content} = StringIO.contents(pid)
      {result, content}
    after
      Process.group_leader(self(), original_gl)
      StringIO.close(pid)
      Application.put_env(:elixir, :ansi_enabled, original)
    end
  end

  defp part_block(%OutputPart{} = part) do
    struct(Block, type: block_type(part.kind), text: part.body, language: part.language)
  end

  defp block_type(:code), do: :source
  defp block_type(kind), do: kind
end