Skip to content

Commit

Permalink
[erlang-ls#108] Add codeLens, codeAction and executeCommand plumbing.
Browse files Browse the repository at this point in the history
  • Loading branch information
alanz committed Dec 13, 2019
1 parent e60946e commit e55fcb3
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 4 deletions.
26 changes: 23 additions & 3 deletions include/erlang_ls.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,6 @@
| ?COMPLETION_ITEM_KIND_OPERATOR
| ?COMPLETION_ITEM_KIND_TYPE_PARAM.

-define(CODE_ACTION_KIND_QUICKFIX, 1).
-type code_action_kind() :: ?CODE_ACTION_KIND_QUICKFIX.

-type initialize_params() :: #{ processId := number() | null
, rootPath => binary() | null
, rootUri := uri() | null
Expand Down Expand Up @@ -560,6 +557,29 @@
, more_trigger_character => string()
}.

%%------------------------------------------------------------------------------
%% Code Actions
%%------------------------------------------------------------------------------

-define(CODE_ACTION_KIND_QUICKFIX, 1).
-type code_action_kind() :: ?CODE_ACTION_KIND_QUICKFIX.

-type code_action_context() :: #{ diagnostics := [diagnostic()]
, only => [code_action_kind()]
}.

-type code_action_params() :: #{ textDocument := text_document_id()
, range := range()
, context := code_action_context()
}.

-type code_action() :: #{ title := string()
, kind => code_action_kind()
, diagnostics => [diagnostic()]
, edit => workspace_edit()
, command => command()
}.

%%------------------------------------------------------------------------------
%% Internals
%%------------------------------------------------------------------------------
Expand Down
73 changes: 73 additions & 0 deletions src/els_code_action_provider.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
-module(els_code_action_provider).

-behaviour(els_provider).

-export([ handle_request/2
, is_enabled/0
, options/0
]).

-include("erlang_ls.hrl").

%%==============================================================================
%% els_provider functions
%%==============================================================================

-spec is_enabled() -> boolean().
is_enabled() -> true.

%% /**
%% * The server provides code actions. The `CodeActionOptions` return type is
%% * only valid if the client signals code action literal support via the
%% * property `textDocument.codeAction.codeActionLiteralSupport`.
%% */
%% codeActionProvider?: boolean | CodeActionOptions;

-spec options() -> boolean() | map().
options() ->
Capabilities = els_config:get(capabilities),
lager:info("code_actions: [Capabilities=~p]", [Capabilities]),
true.

-spec handle_request(any(), els_provider:state()) ->
{any(), els_provider:state()}.
handle_request({document_codeaction, Params}, State) ->
#{ <<"textDocument">> := #{ <<"uri">> := Uri}
, <<"range">> := RangeLSP
, <<"context">> := Context } = Params,
Result = code_actions(Uri, RangeLSP, Context),
{Result, State}.

%%==============================================================================
%% Internal Functions
%%==============================================================================


%% result: (Command | CodeAction)[] | null where CodeAction is defined
%% as follows:
-spec code_actions(uri(), range(), code_action_context()) -> [map()].
code_actions(_Uri, Range, _Context) ->
#{ <<"start">> := #{ <<"character">> := _StartCol
, <<"line">> := StartLine }
, <<"end">> := _End
} = Range,
%% {ok, _Document} = els_utils:lookup_document(Uri),
Actions = [add_lines_action( <<"Add TODO comment">>
, <<"%% TODO: something\n">>
, StartLine)],
lager:info("actions: [Actions=~p]", [Actions]),
Actions.


%% TODO: either provide a literal workspaceedit, or a command, based
%% on the `textDocument.codeAction.codeActionLiteralSupport` client
%% capability.
-spec add_lines_action(binary(), binary(), number()) -> code_action().
add_lines_action(Title, Lines, Before) ->
#{ title => Title
, command =>
els_utils:make_command( Title
, <<"add-lines">>
, [#{ lines => Lines
, before => Before }])
}.
46 changes: 46 additions & 0 deletions src/els_code_lens_provider.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
-module(els_code_lens_provider).

-behaviour(els_provider).

-export([ handle_request/2
, is_enabled/0
, options/0
]).

-include("erlang_ls.hrl").

%%==============================================================================
%% els_provider functions
%%==============================================================================

-spec is_enabled() -> boolean().
is_enabled() -> true.

-spec options() -> map().
options() ->
#{ resolveProvider => false }.

-spec handle_request(any(), els_provider:state()) ->
{any(), els_provider:state()}.
handle_request({document_codelens, Params}, State) ->
#{ <<"textDocument">> := #{ <<"uri">> := Uri}} = Params,
Lenses = lenses(Uri),
{Lenses, State}.

%%==============================================================================
%% Internal Functions
%%==============================================================================
-spec lenses(uri()) -> [map()].
lenses(Uri) ->
{ok, _Document} = els_utils:lookup_document(Uri),
Lenses = [#{ range => one_line_range(3)
, command =>
els_utils:make_command(<<"Foo Title">>
, <<"foo command">>, [])}],
lager:info("lenses: [Lenses=~p]", [Lenses]),
Lenses.

-spec one_line_range(non_neg_integer()) -> range().
one_line_range(Line) ->
Range = #{from => {Line, 1}, to => {Line + 1, 1}},
els_protocol:range(Range).
47 changes: 47 additions & 0 deletions src/els_execute_command_provider.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
-module(els_execute_command_provider).

-behaviour(els_provider).

-export([ handle_request/2
, is_enabled/0
, options/0
]).

-include("erlang_ls.hrl").

%%==============================================================================
%% els_provider functions
%%==============================================================================

-spec is_enabled() -> boolean().
is_enabled() -> true.

-spec options() -> map().
options() ->
#{ commands => ["foo"] }.

-spec handle_request(any(), els_provider:state()) ->
{any(), els_provider:state()}.
handle_request({workspace_executecommand, Params}, State) ->
#{ <<"command">> := Command } = Params,
Arguments = maps:get(<<"arguments">>, Params, []),
Result = execute_command(Command, Arguments),
{Result, State}.

%%==============================================================================
%% Internal Functions
%%==============================================================================

%% TODO: the actual commands should be in some sort of registry,
%% together with their handlers, so that we guarantee that any command
%% sent to the client is actually valid.

-spec execute_command(string(), [any()]) -> [map()].
execute_command(<<"add-lines">>, [#{ <<"lines">> := _Lines
, <<"before">> := _Before }] = Arguments) ->
lager:info("execute_command:add-lines [Arguments=~p]", [Arguments]),
[];
execute_command(Command, Arguments) ->
lager:info("execute_command: [Command=~p] [Arguments=~p]"
, [Command, Arguments]),
[].
42 changes: 42 additions & 0 deletions src/els_methods.erl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
, textdocument_formatting/2
, textdocument_rangeformatting/2
, textdocument_ontypeformatting/2
, textdocument_codeaction/2
, textdocument_codelens/2
, workspace_executecommand/2
, workspace_didchangewatchedfiles/2
, workspace_symbol/2
]).
Expand Down Expand Up @@ -125,12 +128,18 @@ initialize(Params, State) ->
els_document_symbol_provider:is_enabled()
, workspaceSymbolProvider =>
els_workspace_symbol_provider:is_enabled()
, codeActionProvider =>
els_code_action_provider:options()
, codeLensProvider =>
els_code_lens_provider:options()
, documentFormattingProvider =>
els_formatting_provider:is_enabled_document()
, documentRangeFormattingProvider =>
els_formatting_provider:is_enabled_range()
, documentOnTypeFormattingProvider =>
els_formatting_provider:is_enabled_on_type()
, executeCommandProvider =>
els_execute_command_provider:options()
%% AZ: didchangewatchedfiles is not listed in
%% ServerCapabilities in the LSP spec.
, didChangeWatchedFiles =>
Expand Down Expand Up @@ -316,6 +325,39 @@ textdocument_ontypeformatting(Params, State) ->
{document_ontypeformatting, Params}),
{response, Response, State}.

%%==============================================================================
%% textDocument/codeAction
%%==============================================================================

-spec textdocument_codeaction(params(), state()) -> result().
textdocument_codeaction(Params, State) ->
Provider = els_code_action_provider,
Response = els_provider:handle_request(Provider,
{document_codeaction, Params}),
{response, Response, State}.

%%==============================================================================
%% textDocument/codeLens
%%==============================================================================

-spec textdocument_codelens(params(), state()) -> result().
textdocument_codelens(Params, State) ->
Provider = els_code_lens_provider,
Response = els_provider:handle_request(Provider,
{document_codelens, Params}),
{response, Response, State}.

%%==============================================================================
%% workspace/executeCommand
%%==============================================================================

-spec workspace_executecommand(params(), state()) -> result().
workspace_executecommand(Params, State) ->
Provider = els_execute_command_provider,
Response = els_provider:handle_request(Provider,
{workspace_executecommand, Params}),
{response, Response, State}.

%%==============================================================================
%% workspace/didChangeWatchedFiles
%%==============================================================================
Expand Down
8 changes: 7 additions & 1 deletion src/els_provider.erl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
| els_references_provider
| els_formatting_provider
| els_document_highlight_provider
| els_workspace_symbol_provider.
| els_workspace_symbol_provider
| els_code_lens_provider
| els_code_action_provider
| els_execute_command_provider.
-type request() :: {atom(), map()}.
-type state() :: any().

Expand Down Expand Up @@ -89,6 +92,9 @@ providers() ->
, els_formatting_provider
, els_document_highlight_provider
, els_workspace_symbol_provider
, els_code_lens_provider
, els_code_action_provider
, els_execute_command_provider
].

-spec enabled_providers() -> [provider()].
Expand Down
8 changes: 8 additions & 0 deletions src/els_utils.erl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
, lookup_document/1
, project_relative/1
, halt/1
, make_command/3
]).

-include("erlang_ls.hrl").
Expand Down Expand Up @@ -75,6 +76,13 @@ project_relative(Uri) ->
<<RootUri:RootUriSize/binary, RelativePath/binary>> = Uri,
binary_to_list(string:trim(RelativePath, leading, [$/, $\\ ])).

-spec make_command(binary(), binary(), [any()]) -> command().
make_command(Title, Command, Args) ->
#{ title => Title
, command => Command
, arguments => Args
}.

%%==============================================================================
%% Internal functions
%%==============================================================================
Expand Down
3 changes: 3 additions & 0 deletions test/prop_statem.erl
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ initialize_post(_S, _Args, Res) ->
, referencesProvider => true
, documentHighlightProvider => true
, documentSymbolProvider => true
, executeCommandProvider => #{ commands => ["foo"] }
, codeActionProvider => true
, codeLensProvider => #{ resolveProvider => false }
, workspaceSymbolProvider => true
, documentFormattingProvider => true
, documentRangeFormattingProvider => false
Expand Down

0 comments on commit e55fcb3

Please sign in to comment.