-
Notifications
You must be signed in to change notification settings - Fork 351
[WIP] Autolink revamp #893
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Autolink revamp #893
Conversation
|
||
def re(:f, :erlang) do | ||
~r{ | ||
# TODO: revise the erlang rules for function names |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO found
def re(:m, :erlang) do | ||
~r{ | ||
: # prefix | ||
# TODO: revise the erlang rules for module names |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO found
mfa = re_source(:mfa, :erlang) | ||
|
||
assert "[`example`](`:mod.example/1`)" == | ||
run(re({:custom_link, mfa}, :markdown), "[`example`](`:mod.example/1`)") |> hd() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a function call when a pipeline is only one function long
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ebert I find your suggestion specially for long lines like this not useful
mfa = re_source(:mfa) | ||
|
||
assert "[`example`](`Mod.example/1`)" == | ||
run(re({:custom_link, mfa}, :markdown), "[`example`](`Mod.example/1`)") |> hd() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a function call when a pipeline is only one function long
|
||
test "mfa" do | ||
assert ":my_erlang_module.is_fun!/123" == | ||
run(re(:mfa, :erlang), "`:my_erlang_module.is_fun!/123`") |> hd() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a function call when a pipeline is only one function long
|
||
test "functions" do | ||
assert "fun" == run(re(:f), "`fun/1`") |> hd() | ||
assert "is_FUN_99?" == run(re(:f), "`is_FUN_99?/1`") |> hd() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a function call when a pipeline is only one function long
end | ||
|
||
test "functions" do | ||
assert "fun" == run(re(:f), "`fun/1`") |> hd() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a function call when a pipeline is only one function long
test "modules" do | ||
refute run(re(:m), "module.fun/1") | ||
assert "Module" == run(re(:m), "Module.fun/1") |> hd() | ||
assert "Module.Some" == run(re(:m), "Module.Some.fun/1") |> hd() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a function call when a pipeline is only one function long
|
||
test "modules" do | ||
refute run(re(:m), "module.fun/1") | ||
assert "Module" == run(re(:m), "Module.fun/1") |> hd() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a function call when a pipeline is only one function long
@@ -1,10 +1,162 @@ | |||
defmodule ExDoc.SyntaxRule do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modules should have a @moduledoc tag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am afraid testing this is pretty much testing implementation detail. Can we please inline this module inside the current autolink one and erase those tests?
It also fixed this bug.
Eventhough it was announced in the docs, it didn't work. |
module_id: module_id, | ||
locals: locals | ||
}) | ||
|> Map.to_list() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a function call when a pipeline is only one function long
|
||
@spec re_source(atom, language) :: String.t() | ||
def re_source(name, language \\ :elixir) do | ||
re(name, language) |> Regex.source() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a function call when a pipeline is only one function long
`\s* # leading backtick | ||
(#{function_re_source}) # CAPTURE 1 | ||
\s*` # trailing backtick | ||
(?!`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be no trailing white-space at the end of a line.
"[#{text}](#{module}#{extension}##{prefix}#{enc_h(function)}/#{arity})" | ||
case language do | ||
:erlang -> | ||
cond do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cond statements should contain at least two conditions besides true
.
@doc false | ||
# The heart of the autolinking logic | ||
@spec replacement(String.t(), language, kind, String.t(), keyword) :: String.t() | ||
def replacement(string, language, kind, match, text \\ nil, options) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function is too complex (CC is 22, max is 9).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should I do here?
end | ||
|
||
:function -> | ||
cond do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function body is nested too deep (max depth is 2, was 3).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I can fix this, it will loose readability. What shall I do about it?
8c1c380
to
71205cb
Compare
71205cb
to
13c2c33
Compare
"[#{text}](#{elixir_docs}Kernel.SpecialForms" <> | ||
"#{extension}##{prefix}#{enc_h(function)}/#{arity})" | ||
@doc false | ||
def erlang_functions(string) when is_binary(string) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have made these set of functions no longer accesible in the documentation, as I consider they are not longer necessary. They have been kept because all the tests rely on them.
We should improve the docs of the newly introduce function that replaces them link/4
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those functions can be removed. We had them out to ease testing.
@type kind :: :function | :module | ||
@type link_type :: :normal | :custom | ||
|
||
random_string = 1..10_000_000 |> Enum.random() |> Integer.to_charlist(36) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's pick a random string. Having it being a different value every time we compile the code will make it hard to spot bugs.
Thanks @eksperimental for the great work. Unfortunately this PR is doing two things at once, refactoring the code and changing the user surface API. This makes it hard to review and exposes new contracts we are not sure we should expose. Can we please do the refactoring first and keep the user API the same? Then we can have a discussion on what becomes public and what becomes private. Thanks! |
- Rename ExDoc.SyntaxRule to ExDoc.Formatter.HTML.SyntaxRule - Fix syntax rules for normal links - Add internal option `:preprocess?` to Autolink.link/4, which is set to `true` by default - Fix Autolink.link_everything/2 to only preprocess and postprocess just once - Add corner-cases section in text, including a few ones
@josevalim what API changes are you refering to? I'm confused bc above you mention that we can remove the functions locals, erlang-functions, elixir-functions, etc |
|
||
defp re(:f, :erlang) do | ||
~r{ | ||
# TODO: revise the erlang rules for function names |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO found
defp re(:m, :erlang) do | ||
~r{ | ||
: # prefix | ||
# TODO: revise the erlang rules for module names |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO found
@kernel_function_strings for {f, a} <- kernel_exports, do: "#{f}/#{a}" | ||
@special_form_strings for {f, a} <- special_form_exports, do: "#{f}/#{a}" | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be no more than 1 consecutive blank lines.
defp re(:m, :erlang) do | ||
~r{ | ||
: # prefix | ||
# TODO: revise the erlang rules for module names |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be no trailing white-space at the end of a line.
@doc false | ||
# The heart of the autolinking logic | ||
@spec replacement(String.t(), language, kind, String.t(), keyword) :: String.t() | ||
def replacement(string, language, kind, match, text \\ nil, options) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function is too complex (CC is 21, max is 9).
ec5f50a
to
8c2d3d0
Compare
@eksperimental we can move them but in a separate commit. The public API should stay the same while refactoring. This means we shouldn't expose the link function and the existing API should remain documented. Thanks! |
The regex was too greedy
""" | ||
|
||
refute Autolink.preprocess(string) === string | ||
assert Autolink.preprocess(string) |> Autolink.postprocess() === string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a function call when a pipeline is only one function long
match in @kernel_function_strings -> | ||
"[#{text}](#{elixir_docs}Kernel#{extension}##{prefix}#{enc_h(function)}/#{arity})" | ||
:function -> | ||
cond do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function body is nested too deep (max depth is 2, was 3).
Ebert has finished reviewing this Pull Request and has found:
You can see more details about this review at https://ebertapp.io/github/elixir-lang/ex_doc/pulls/893. |
Closing in favor of #896 |
This started trying to fix a bug and I had to end up revamping a big part of the Autolink module.
I couldn't set a custom link to a built-in type,
wouldn't work, while
range()
would work.
So, long story short, custom link only work for Elixir functions and locals,
but nothing else. Now custom links can be used for anything like built-in types and erlang functions
It was extremely hard to grasp what was going on with the regexes, so I created a
SystaxRule
module.Please let me know what you guys think