-
Notifications
You must be signed in to change notification settings - Fork 423
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
Add a signature provider #477
Comments
So the bulk of the work necessary to implement this feature is available via the new I drop a proof of concept on how to implement this feature. For Livebook, we want to find the closest node to a cursor that is not: an operator nor a container. We also want to reject any call where the cursor is directly under a do-end block. The code is below. (Edit: the code below does not expand the pipe operator, you need to do so while traversing). Also note that we can use this functionality to implement struct field autocompletion, as done in IEx: elixir-lang/elixir@25614e0 with {:ok, ast} <- Code.Fragment.container_cursor_to_quoted(cursor, token_metadata: true),
{left, meta, right} <- find_cursor_call(ast, :root),
false <- cursor_in_do_end?(meta, right) do
# We are almost there. left may be a local/import or a
# remote call. In case of a remote call, it may be a
# dynamic call (such as a var), an alias that does not
# exist, etc. So those cases have to be validated too.
{left, meta, right}
else
_ -> nil
end
defp find_cursor_call({:__cursor__, _, []}, acc) do
acc
end
defp find_cursor_call({left, meta, right} = node, acc) when is_list(right) do
if is_atom(left) and (Macro.operator?(left, length(right))) or
left in [:__block__, :__aliases__, :., :{}, :<<>>, :%, :%{}]) do
find_cursor_call(left, acc) || find_cursor_call(right, acc)
else
find_cursor_call(left, node) || find_cursor_call(right, node)
end
end
defp find_cursor_call({left, meta, right}, acc) do
find_cursor_call(left, acc) || find_cursor_call(right, acc)
end
defp find_cursor_call({left, right}, acc) do
find_cursor_call(left, acc) || find_cursor_call(right, acc)
end
defp find_cursor_call(list, acc) when is_list(list) do
Enum.find_value(list, &find_cursor_call(&1, acc))
end
defp find_cursor_call(_other, _acc) do
nil
end
defp cursor_in_do_end?(meta, args) do
is_list(meta[:do]) and
Enum.any?(Macro.prewalker(List.last(args)), &match?({:__cursor__, _, []}, &1))
end |
This requires some advanced features in Elixir's Code.Fragment, so we are keeping it here for now to track it.
The text was updated successfully, but these errors were encountered: