Skip to content

Commit

Permalink
adds specs for functions
Browse files Browse the repository at this point in the history
  • Loading branch information
ityonemo committed Jun 27, 2023
1 parent 7ce1ae2 commit 3fe6d26
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 20 deletions.
18 changes: 12 additions & 6 deletions lib/zig.doc/generator.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule Zig.Doc.Generator do
alias ExDoc.DocAST
alias Zig.Doc.Spec

def modulenode_from_config({id, options}, sema_module) do
# options must include 'file' key
Expand All @@ -16,7 +17,7 @@ defmodule Zig.Doc.Generator do
# TODO: needs source_path and source_url
node = %ExDoc.ModuleNode{id: "#{id}", doc_line: 1, doc: doc_ast}

Enum.reduce(parsed_document.code, node, &obtain_content(&1, &2, file_path))
Enum.reduce(parsed_document.code, node, &obtain_content(&1, &2, file_path, sema))

else
:error -> Mix.raise("zig doc config error: configuration for module #{id} requires a `:file` option")
Expand All @@ -25,9 +26,7 @@ defmodule Zig.Doc.Generator do
end
end

defp obtain_content({:fn, fun = %{pub: true}, fn_parts}, acc, file_path) do
name = Keyword.fetch!(fn_parts, :name)

defp obtain_content({:fn, fun = %{pub: true}, fn_parts}, acc, file_path, sema) do
doc_ast = if fndoc = fun.doc_comment do
DocAST.parse!(fndoc, "text/markdown", [file: file_path, line: fun.position.line])
end
Expand All @@ -36,6 +35,12 @@ defmodule Zig.Doc.Generator do
type = Keyword.fetch!(fn_parts, :type)
params = Keyword.fetch!(fn_parts, :params)

# find the function in the sema
specs = sema.functions
|> Enum.find(&(&1.name == name))
|> Spec.function_from_sema
|> List.wrap

param_string = params
|> Enum.map(fn {var, _, type} -> "#{var}: #{type}" end)
|> Enum.join(", ")
Expand All @@ -48,11 +53,12 @@ defmodule Zig.Doc.Generator do
name: name,
arity: length(params),
doc: doc_ast,
signature: signature
signature: signature,
specs: specs
}

%{acc | docs: [node | acc.docs]}
end

defp obtain_content(_, acc, _), do: acc
defp obtain_content(_, acc, _, _), do: acc
end
24 changes: 24 additions & 0 deletions lib/zig.doc/sema.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule Zig.Doc.Sema do
@type type :: atom

@type fun :: %{
name: atom,
return: type,
args: [type],
}

@type const :: %{
name: atom,
type: type
}

@type file :: %{
functions: [function],
consts: [const],
types: [type]
}

def new(addin \\ []) do
Enum.into(addin, %{functions: [], consts: [], types: []})
end
end
15 changes: 7 additions & 8 deletions lib/zig.doc/spec.ex
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
defmodule Zig.Doc.Spec do
@spec from_sema(term) :: Macro.t
def from_sema(sema) do
alias Zig.Doc.Sema

name = :function_name_placeholder
return_type = {:placeholder, [], Elixir}
args = []
@spec function_from_sema(Sema.fun) :: Macro.t

args = Enum.map(args, fn type -> {type, [], Elixir} end)
def function_from_sema(fun) do
name = fun.name
return_type = {fun.return, [], Elixir}
args = Enum.map(fun.args, fn type -> {type, [], Elixir} end)

spec = {:"::", [], [{name, [], args}, return_type]}
{:ok, spec}
{:"::", [], [{name, [], args}, return_type]}
end
end
2 changes: 1 addition & 1 deletion test/_support/sema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ defmodule Zig.SemaAPI do
@callback run_sema(Path.t) :: {:ok, json} | {:error, String.t}
end

Mox.defmock(Zig.Doc.Sema, for: Zig.SemaAPI)
Mox.defmock(Zig.SemaMock, for: Zig.SemaAPI)
18 changes: 15 additions & 3 deletions test/_support/zig_doc_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Zig.Doc.Case do
using do
quote do
import Mox
import Zig.Doc.Case, only: [get_module: 1, expect_sema: 1]
import Zig.Doc.Case, only: [get_module: 1, expect_sema: 1, assert_code: 2]

setup :set_mox_from_context
setup :verify_on_exit!
Expand All @@ -13,11 +13,23 @@ defmodule Zig.Doc.Case do

def get_module(file) do
[module] =
Zig.Doc.add_zig_doc_config([], [module: [file: file]], Zig.Doc.Sema)
Zig.Doc.add_zig_doc_config([], [module: [file: file]], Zig.SemaMock)
module
end

def expect_sema(sema) do
Mox.expect(Zig.Doc.Sema, :run_sema, fn _ -> sema end)
Mox.expect(Zig.SemaMock, :run_sema, fn _ -> sema end)
end

defmacro assert_code(string, data) do
quote do
tgt = unquote(string)
|> Code.format_string!
|> IO.iodata_to_binary

assert tgt == unquote(data)
|> List.first
|> Macro.to_string()
end
end
end
11 changes: 9 additions & 2 deletions test/documentation/function_test.exs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
defmodule ZigDocTest.Documentation.ModuleTest do
defmodule ZigDocTest.Documentation.FunctionTest do
use Zig.Doc.Case, async: true

alias Zig.Doc.Sema

test "function-level documentation is generated" do
expect_sema({:ok, %{}})
expect_sema({:ok, Sema.new(functions: [%{name: :foo, return: :i32, args: [:i32]}])})

assert %{docs: [function]} =
get_module("test/_sources/function.zig")

assert [{:p, [], [" this is the function foo"], %{}}] = function.doc
assert "foo(value: i32) i32" = function.signature

assert_code("""
foo(i32) :: i32
""",
function.specs)
end
end
5 changes: 5 additions & 0 deletions test/documentation/module_test.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
defmodule ZigDocTest.Documentation.ModuleTest do
use Zig.Doc.Case, async: true

alias Zig.Doc.Sema

test "module-level documentation is generated" do

expect_sema({:ok, Sema.new()})

assert %{doc: [{:p, [], [" tests module-level comment content"], %{}}]} =
get_module("test/_sources/module.zig")
end
Expand Down

0 comments on commit 3fe6d26

Please sign in to comment.