defmodule Mix.Tasks.AddNavigation do @doc """ ## Usage Run the `mix add_navigation` task to automatically add navigation. Sections for navigation should be included in the notebook using the following comments: ```md ``` `livebook_utils` can update your navigation whenever your index file changes. You also have control over where navigation goes. ## Template Example Typically, we recommend putting a navigation section at the top and the bottom of the file. Here's an example template livebook file with navigation at the top and bottom of the file. ```md # Title ## Navigation ## SubTitle ## Navigation ``` You must have two or more notebooks in your index file with valid paths and titles to add navigation. ## Styling Currently, we don't provide any control over styling the navigation section. Here's how navigation looks out of the box:
""" use Mix.Task require Logger def run(_args) do Logger.info("Adding Navigation") index_path = Application.get_env(:livebook_utils, :index_path) notebooks_path = Application.get_env(:livebook_utils, :notebooks_path) index = File.read!(index_path) notebooks = get_notebooks(index, notebooks_path) if Enum.count(notebooks) <= 1 do raise "Cannot add navigation with one or fewer books" else write_navigation_sections!(notebooks, notebooks_path) end end defp get_notebooks(index, notebooks_path) do Regex.scan(~r/\[(.*)\]\((.*)\)/, index) |> Enum.map(fn [_, title, path] -> %{path: path, title: title} end) |> Enum.filter(fn %{path: path, title: title} -> String.contains?(path, ".livemd") and File.exists?(Path.join(notebooks_path, path)) and title != "" end) end defp write_navigation_sections!(notebooks, notebooks_path) do List.flatten([nil, notebooks]) |> Enum.chunk_every(3, 1, [nil]) |> Enum.map(fn [prev, current, next] -> file = File.read!(Path.join(notebooks_path, current.path)) file_with_nav = Regex.replace( ~r/(?:.|\n)*?/, file, nav_snippet(prev, current, next) ) File.write!("#{notebooks_path}/#{current.path}", file_with_nav) end) end defp nav_snippet(prev, _, next) do """