# SPDX-FileCopyrightText: 2024 igniter contributors # # SPDX-License-Identifier: MIT defmodule Igniter.Scribe do @moduledoc """ Contains functions for use with the `--scribe` option in Igniter. See [the guide](/documentation/documenting-tasks.md) for more. """ @doc """ Sets the path and title of the document being generated. Only the first call to this is honored. """ def start_document(igniter, title, contents, opts \\ []) do if igniter.assigns[:scribe?] do igniter |> append_content("# #{title}") |> append_content(contents) |> Igniter.assign( :test_files, Map.merge(igniter.assigns[:test_files] || %{}, opts[:files] || %{}) ) else igniter end end @doc """ Adds a new section to the documentation. """ def section(igniter, header, explanation, callback) do if igniter.assigns[:scribe?] do current_header = igniter.assigns[:scribe][:header] nesting_level = igniter.assigns[:scribe][:nesting_level] || 1 header = String.duplicate("#", nesting_level + 1) <> " " <> header igniter |> assign(:header, header) |> assign(:nesting_level, nesting_level + 1) |> append_content(header) |> append_content(explanation) |> callback.() |> assign(:header, current_header) |> assign(:nesting_level, nesting_level) else callback.(igniter) end end def patch(original_igniter, callback) do if original_igniter.assigns[:scribe?] do new_igniter = callback.(original_igniter) new_igniter.rewrite |> Enum.reduce(new_igniter, fn source, igniter -> existing_source = Rewrite.source(original_igniter.rewrite, source.path) lang = if Path.extname(source.path) in [".ex", ".eex"], do: "elixir", else: nil case existing_source do {:ok, existing_source} -> if Rewrite.Source.version(existing_source) == Rewrite.Source.version(source) do igniter else TextDiff.format( existing_source |> Rewrite.Source.get(:content) |> eof_newline(), source |> Rewrite.Source.get(:content) |> eof_newline(), color: false, line_numbers: false, format: [ separator: "" ] ) |> IO.iodata_to_binary() |> String.trim_trailing() |> String.split("\n") |> Enum.map_join("\n", fn " " <> str -> str other -> other end) |> case do "" -> igniter diff -> igniter |> append_content("Update `#{source.path}`:") |> append_content(""" ```diff #{diff} ``` """) end end _ -> igniter |> append_content("Create `#{source.path}`:") |> append_content(""" ```#{lang} #{Rewrite.Source.get(source, :content)} ``` """) end end) else callback.(original_igniter) end end @doc false def write(igniter, path) do File.write!(path, igniter.assigns[:scribe][:content]) igniter end defp append_content(igniter, nil) do igniter end defp append_content(igniter, content) do assign(igniter, :content, (igniter.assigns[:scribe][:content] || "") <> "\n" <> content) end defp assign(igniter, key, value) do Igniter.assign(igniter, :scribe, Map.put(igniter.assigns[:scribe] || %{}, key, value)) end defp eof_newline(string), do: String.trim_trailing(string) <> "\n" end