Skip to content

Commit 5895609

Browse files
authored
Improve structure of generated docs (#1440)
* Improve structure of generated docs * Replace individual view pages with "View Index" * Remove debugging logic
1 parent d994787 commit 5895609

File tree

2 files changed

+95
-79
lines changed

2 files changed

+95
-79
lines changed

lib/mix/tasks/lvn.swiftui.gen.docs.ex

Lines changed: 93 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -4,88 +4,115 @@ defmodule Mix.Tasks.Lvn.Swiftui.Gen.Docs do
44
use Mix.Task
55
require Logger
66

7-
# Using a temporary folder outside of the project avoids ElixirLS file watching issues
8-
defp temp_doc_folder, do: Path.join(System.tmp_dir!(), "temp_swiftui_docs")
9-
defp generate_swift_lvn_docs_command, do: ~c"xcodebuild docbuild -scheme LiveViewNative -destination generic/platform=iOS -derivedDataPath #{temp_doc_folder()} -skipMacroValidation -skipPackagePluginValidation"
7+
defp generate_swift_lvn_docs_command(doc_path), do: ~c"xcrun xcodebuild docbuild -scheme LiveViewNative -destination generic/platform=iOS -derivedDataPath #{doc_path} -skipMacroValidation -skipPackagePluginValidation"
108
@swiftui_interface_path "Platforms/XROS.platform/Developer/SDKs/XROS.sdk/System/Library/Frameworks/SwiftUI.framework/Modules/SwiftUI.swiftmodule/arm64-apple-xros.swiftinterface"
119
defp generate_modifier_documentation_extensions(xcode_path), do: ~c(xcrun swift run ModifierGenerator documentation-extensions --interface "#{Path.join(xcode_path, @swiftui_interface_path)}" --output Sources/LiveViewNative/LiveViewNative.docc/DocumentationExtensions)
1210
@generate_documentation_extensions ~c(xcrun swift package plugin --allow-writing-to-package-directory generate-documentation-extensions)
1311
defp modifier_list(xcode_path), do: ~s(xcrun swift run ModifierGenerator list --interface "#{Path.join(xcode_path, @swiftui_interface_path)}" --modifier-search-path Sources/LiveViewNative/Stylesheets/Modifiers)
1412
@xcode_select_print_path ~c(xcode-select --print-path)
1513
@allow_writing_to_package_dir_command ~c"xcrun swift package plugin --allow-writing-to-package-directory generate-documentation-extensions"
1614
@doc_folder "generated_docs"
17-
@cheatsheet_path "#{@doc_folder}/view-index.cheatmd"
15+
@cheatsheet_path "#{@doc_folder}/view-index.md"
1816
@modifier_cheatsheet_path "#{@doc_folder}/modifier-index.md"
1917

2018
@shortdoc "Generates ex doc files for all SwiftUI views"
21-
def run(_) do
19+
def run(args) do
20+
{kwargs, [], []} = OptionParser.parse(args, strict: [doc_path: :string, no_generate_docc: :boolean])
21+
22+
# Using a temporary folder outside of the project avoids ElixirLS file watching issues
23+
doc_path = Keyword.get(kwargs, :doc_path, Path.join(System.tmp_dir!(), "temp_swiftui_docs"))
24+
|> Path.absname()
25+
2226
Logger.info("Locating Xcode installation")
2327
xcode_path = :os.cmd(@xcode_select_print_path) |> to_string() |> String.trim()
2428

25-
Logger.info("Enabling writing to package...")
26-
:os.cmd(@allow_writing_to_package_dir_command)
29+
if not Keyword.get(kwargs, :no_generate_docc, false) do
30+
Logger.info("Enabling writing to package...")
31+
:os.cmd(@allow_writing_to_package_dir_command)
32+
33+
Logger.info("Generating documentation extensions")
34+
:os.cmd(@generate_documentation_extensions)
2735

28-
Logger.info("Generating documentation extensions")
29-
:os.cmd(@generate_documentation_extensions)
30-
Logger.info("Generating modifier documentation extensions")
31-
:os.cmd(generate_modifier_documentation_extensions(xcode_path))
36+
Logger.info("Generating modifier documentation extensions")
37+
:os.cmd(generate_modifier_documentation_extensions(xcode_path))
3238

33-
Logger.info("Generating SwiftUI documentation files...")
34-
:os.cmd(generate_swift_lvn_docs_command())
39+
Logger.info("Generating SwiftUI documentation files...")
40+
:os.cmd(generate_swift_lvn_docs_command(doc_path))
41+
end
3542

3643
Logger.info("Generating LiveView Native documentation files...")
3744
# Ensure generated_docs folder exists
3845
File.mkdir("generated_docs")
3946

40-
# clear cheatsheet
41-
File.write!(@cheatsheet_path, "# View Index\n")
42-
4347
# generate documentation and cheatsheat
44-
for {category, views} <- categorized_views() do
45-
# build cheatsheet sections
46-
File.write!(@cheatsheet_path, "## #{category}\n{: .col-2}\n", [:append])
47-
48-
for view <- views do
49-
with {:ok, data} <-
50-
File.read(
51-
"#{temp_doc_folder()}/Build/Products/Debug-iphoneos/LiveViewNative.doccarchive/data/documentation/liveviewnative/#{view}.json"
52-
) do
53-
docs = Jason.decode!(data)
54-
path = "#{@doc_folder}/#{category}/#{view}.md"
55-
File.mkdir_p!(Path.dirname(path))
56-
File.write!(path, markdown(docs, docs))
57-
58-
# build cheatsheet entries
59-
File.write!(@cheatsheet_path, cheatsheet(docs, docs) <> "\n", [:append])
60-
end
61-
end
62-
end
48+
views = Path.wildcard("Sources/LiveViewNative/Views/**/*.swift")
49+
|> Enum.map(fn view -> Path.basename(view, ".swift") end)
50+
|> Enum.sort()
51+
|> Enum.map(fn view ->
52+
{
53+
"`<#{view}>`",
54+
"#{doc_path}/Build/Products/Debug-iphoneos/LiveViewNative.doccarchive/data/documentation/liveviewnative/#{view}.json"
55+
}
56+
end)
57+
write_cheatsheet(
58+
"View Index",
59+
doc_path,
60+
views,
61+
@cheatsheet_path
62+
)
6363

6464
Logger.info("Generating LiveView Native modifier documentation files...")
65+
modifiers = System.shell(modifier_list(xcode_path))
66+
|> elem(0)
67+
|> String.split("\n")
68+
|> Enum.map(fn modifier ->
69+
{
70+
modifier,
71+
"#{doc_path}/Build/Products/Debug-iphoneos/LiveViewNative.doccarchive/data/documentation/liveviewnative/_#{modifier}Modifier.json"
72+
}
73+
end)
74+
write_cheatsheet(
75+
"Modifier Index",
76+
doc_path,
77+
modifiers,
78+
@modifier_cheatsheet_path,
79+
true
80+
)
6581

66-
modifiers = System.shell(modifier_list(xcode_path)) |> elem(0) |> String.split("\n")
82+
if Keyword.get(kwargs, :doc_path) == nil do
83+
Logger.info("Cleaning up temporary files...")
84+
File.rm_rf(doc_path)
85+
end
86+
end
6787

88+
defp write_cheatsheet(title, doc_path, paths, output_path, use_tabs \\ false) do
6889
# clear cheatsheet
69-
File.write!(@modifier_cheatsheet_path, "# Modifier Index\n")
70-
71-
all_modifier_references = MapSet.new()
72-
for modifier <- modifiers do
73-
with {:ok, data} <-
74-
File.read(
75-
"#{temp_doc_folder()}/Build/Products/Debug-iphoneos/LiveViewNative.doccarchive/data/documentation/liveviewnative/_#{modifier}Modifier.json"
76-
) do
90+
File.write!(output_path, "# #{title}\n")
91+
92+
references = MapSet.new()
93+
for {name, path} <- paths do
94+
with {:ok, data} <- File.read(path) do
7795
docs = Jason.decode!(data)
78-
File.write!(@modifier_cheatsheet_path, "## #{modifier}\n", [:append])
79-
File.write!(@modifier_cheatsheet_path, "<!-- tabs-open -->\n", [:append])
80-
File.write!(@modifier_cheatsheet_path, modifier_cheatsheet(docs, docs) <> "\n", [:append])
81-
File.write!(@modifier_cheatsheet_path, "<!-- tabs-close -->\n", [:append])
82-
reduce_references(docs, all_modifier_references)
96+
File.write!(output_path, "## #{name}\n", [:append])
97+
if use_tabs do
98+
File.write!(output_path, "<!-- tabs-open -->\n", [:append])
99+
end
100+
ctx = if use_tabs do
101+
docs
102+
else
103+
docs |> Map.put("inlineHeadings", true)
104+
end
105+
File.write!(output_path, modifier_cheatsheet(docs, ctx) <> "\n", [:append])
106+
if use_tabs do
107+
File.write!(output_path, "<!-- tabs-close -->\n", [:append])
108+
end
109+
reduce_references(docs, references)
83110
end
84111
end
85112

86-
all_modifier_references = modifiers
87-
|> Enum.reduce(MapSet.new(), fn modifier, acc ->
88-
with {:ok, data} <- File.read("#{temp_doc_folder()}/Build/Products/Debug-iphoneos/LiveViewNative.doccarchive/data/documentation/liveviewnative/_#{modifier}Modifier.json")
113+
references = paths
114+
|> Enum.reduce(MapSet.new(), fn {_, path}, acc ->
115+
with {:ok, data} <- File.read(path)
89116
do
90117
docs = Jason.decode!(data)
91118
reduce_references(docs, acc)
@@ -95,9 +122,9 @@ defmodule Mix.Tasks.Lvn.Swiftui.Gen.Docs do
95122
end)
96123

97124
# collect references made in references
98-
all_modifier_references = Enum.reduce(all_modifier_references, all_modifier_references, fn reference, acc ->
125+
references = Enum.reduce(references, references, fn reference, acc ->
99126
path = String.trim_leading(reference, "doc://LiveViewNative/documentation/LiveViewNative/")
100-
with {:ok, data} <- File.read("#{temp_doc_folder()}/Build/Products/Debug-iphoneos/LiveViewNative.doccarchive/data/documentation/liveviewnative/#{path}.json")
127+
with {:ok, data} <- File.read("#{doc_path}/Build/Products/Debug-iphoneos/LiveViewNative.doccarchive/data/documentation/liveviewnative/#{path}.json")
101128
do
102129
docs = Jason.decode!(data)
103130
reduce_references(docs, acc)
@@ -107,13 +134,10 @@ defmodule Mix.Tasks.Lvn.Swiftui.Gen.Docs do
107134
end)
108135

109136
# write references to end of modifier index
110-
File.write!(@modifier_cheatsheet_path, "## Types\n", [:append])
111-
for reference <- Enum.sort(all_modifier_references) do
112-
File.write!(@modifier_cheatsheet_path, attribute_details(String.trim_leading(reference, "doc://LiveViewNative/documentation/LiveViewNative/")) <> "\n", [:append])
137+
File.write!(output_path, "## Types\n", [:append])
138+
for reference <- Enum.sort(references) do
139+
File.write!(output_path, attribute_details(String.trim_leading(reference, "doc://LiveViewNative/documentation/LiveViewNative/"), doc_path) <> "\n", [:append])
113140
end
114-
115-
Logger.info("Cleaning up temporary files...")
116-
File.rm_rf(temp_doc_folder())
117141
end
118142

119143
### Cheatsheet
@@ -184,7 +208,7 @@ defmodule Mix.Tasks.Lvn.Swiftui.Gen.Docs do
184208
<!-- attribute list -->
185209
# References
186210
187-
#{Enum.map(attributes, &attribute_details(Path.basename(url), &1))}
211+
#{Enum.map(attributes, &attribute_details(Path.basename(url), &1, ctx.doc_path))}
188212
189213
<!-- end attribute list -->
190214
"""
@@ -249,7 +273,7 @@ defmodule Mix.Tasks.Lvn.Swiftui.Gen.Docs do
249273
%{"references" => references, "includeAllReferences" => true}
250274
) do
251275
%{"title" => title} = Map.get(references, identifier)
252-
hash = "#{title |> String.replace("<", "") |> String.replace(">", "")}/1"
276+
hash = "#{identifier}/1"
253277
"[`#{title}`](##{hash})"
254278
end
255279

@@ -258,7 +282,7 @@ defmodule Mix.Tasks.Lvn.Swiftui.Gen.Docs do
258282
%{"references" => references, "identifier" => %{"url" => base_url}}
259283
) do
260284
%{"title" => title, "url" => url} = Map.get(references, identifier)
261-
hash = "#{title |> String.replace("<", "") |> String.replace(">", "")}/1"
285+
hash = "#{url}/1"
262286

263287
resolved_url =
264288
case url do
@@ -278,23 +302,24 @@ defmodule Mix.Tasks.Lvn.Swiftui.Gen.Docs do
278302

279303
def markdown(_data, _ctx), do: ""
280304

281-
defp attribute_details(view, identifier) do
282-
attribute_details("#{view}/#{Path.basename(identifier)}")
305+
defp attribute_details(view, identifier, doc_path) do
306+
attribute_details("#{view}/#{Path.basename(identifier)}", doc_path)
283307
end
284308

285-
defp attribute_details(path) do
286-
"#{temp_doc_folder()}/Build/Products/Debug-iphoneos/LiveViewNative.doccarchive/data/documentation/liveviewnative/#{path}.json"
309+
defp attribute_details(path, doc_path) do
310+
"#{doc_path}/Build/Products/Debug-iphoneos/LiveViewNative.doccarchive/data/documentation/liveviewnative/#{path}.json"
287311
|> File.read()
288312
|> case do
289313
{:ok, data} ->
290314
docs = Jason.decode!(data)
291315

292316
title = Map.get(docs, "metadata", %{}) |> Map.get("title", "")
317+
url = Map.get(docs, "identifier", %{}) |> Map.get("url", "")
293318
abstract = Map.get(docs, "abstract", [])
294319
content = Map.get(docs, "primaryContentSections", [])
295320

296321
docs = Map.put(docs, "inlineHeadings", true) |> Map.put("includeAllReferences", true)
297-
hash = "#{title}/1"
322+
hash = "#{url}/1"
298323

299324
"""
300325
<section id="#{hash}" class="detail">
@@ -349,13 +374,4 @@ defmodule Mix.Tasks.Lvn.Swiftui.Gen.Docs do
349374
), do: items |> Enum.reduce(acc, fn %{"content" => content}, acc -> reduce_references(content, acc) end)
350375

351376
defp reduce_references(_markdown, acc), do: acc
352-
353-
@spec categorized_views() :: %{String.t() => [String.t()]}
354-
defp categorized_views do
355-
Path.wildcard("Sources/LiveViewNative/Views/**/*.swift")
356-
|> Enum.group_by(
357-
&Path.basename(Path.dirname(&1)),
358-
&Path.basename(&1, ".swift")
359-
)
360-
end
361377
end

mix.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,10 @@ defmodule LiveViewNative.SwiftUI.MixProject do
153153
defp various_docs(args) do
154154
{opts, _, _} =
155155
OptionParser.parse(args,
156-
strict: [skip_gen_docs: :boolean, skip_livebooks: :boolean]
156+
strict: [skip_gen_docs: :boolean, skip_livebooks: :boolean, doc_path: :string, no_generate_docc: :boolean]
157157
)
158158

159-
unless opts[:skip_gen_docs], do: Mix.Task.run("lvn.swiftui.gen.docs")
159+
unless opts[:skip_gen_docs], do: Mix.Task.run("lvn.swiftui.gen.docs", args)
160160
unless opts[:skip_livebooks], do: Mix.Task.run("lvn.swiftui.gen.livemarkdown")
161161
Mix.Task.run("docs")
162162
end

0 commit comments

Comments
 (0)