Skip to content
This repository was archived by the owner on Aug 15, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions apps/els_core/include/els_core.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -548,8 +548,8 @@
}.

-type document_ontypeformatting_options() :: false |
#{ first_trigger_character := string()
, more_trigger_character => string()
#{ firstTriggerCharacter := binary()
, moreTriggerCharacter => [binary()]
}.

%%------------------------------------------------------------------------------
Expand Down Expand Up @@ -612,7 +612,8 @@
| {atom(), atom()} %% record_def_field, record_field
| string() %% include, include_lib
| {atom(), arity()}
| {module(), atom(), arity()}.
| {module(), atom(), arity()}
| pos().
-type poi() :: #{ kind := poi_kind()
, id := poi_id()
, data := any()
Expand Down
4 changes: 4 additions & 0 deletions apps/els_core/src/els_client.erl
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,10 @@ request_params({initialize, {RootUri, InitOptions}}) ->
}
, <<"hover">> =>
#{ <<"contentFormat">> => ContentFormat }
, <<"documentOnTypeFormattingProvider">> =>
#{ firstTriggerCharacter => <<",">>
, moreTriggercharacter => []
}
},
#{ <<"rootUri">> => RootUri
, <<"initializationOptions">> => InitOptions
Expand Down
2 changes: 2 additions & 0 deletions apps/els_core/src/els_config.erl
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ do_initialize(RootUri, Capabilities, InitOptions, {ConfigPath, Config}) ->
= maps:get("compiler_telemetry_enabled", Config, false),

IndexingEnabled = maps:get(<<"indexingEnabled">>, InitOptions, true),
FormatOnTypeEnabled = maps:get("format_on_type", Config, false),

%% Passed by the LSP client
ok = set(root_uri , RootUri),
Expand All @@ -147,6 +148,7 @@ do_initialize(RootUri, Capabilities, InitOptions, {ConfigPath, Config}) ->
ok = set(macros , Macros),
ok = set(plt_path , DialyzerPltPath),
ok = set(code_reload , CodeReload),
ok = set(format_on_type , FormatOnTypeEnabled),
?LOG_INFO("Config=~p", [Config]),
ok = set(runtime, maps:merge( els_config_runtime:default_config()
, Runtime)),
Expand Down
10 changes: 8 additions & 2 deletions apps/els_lsp/src/els_dt_document.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@
, lookup/1
]).

-export([ new/2
-export([ get_element_at_pos/3
, new/2
, pois/1
, pois/2
, get_element_at_pos/3
, uri/1
, functions_at_pos/3
, applications_at_pos/3
, wrapping_functions/2
, wrapping_functions/3
, text/1
]).

%%==============================================================================
Expand Down Expand Up @@ -63,6 +64,7 @@
, md5 => binary()
, pois => [poi()]
}.

-export_type([ id/0
, item/0
, kind/0
Expand Down Expand Up @@ -155,6 +157,10 @@ new(Uri, Text, Id, Kind) ->
pois(#{ pois := POIs }) ->
POIs.

-spec text(item()) -> binary().
text(#{text := Text}) ->
Text.

%% @doc Returns the list of POIs of the given types for the current
%% document
-spec pois(item(), [poi_kind()]) -> [poi()].
Expand Down
97 changes: 90 additions & 7 deletions apps/els_lsp/src/els_formatting_provider.erl
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,17 @@ is_enabled_document() -> true.

-spec is_enabled_range() -> boolean().
is_enabled_range() ->
false.
true.

%% NOTE: because erlang_ls does not send incremental document changes
%% via `textDocument/didChange`, this kind of formatting does not
%% make sense.
-spec is_enabled_on_type() -> document_ontypeformatting_options().
is_enabled_on_type() -> false.
is_enabled_on_type() ->
case els_config:get(format_on_type) of
true -> #{firstTriggerCharacter => <<".">>, moreTriggerCharacter => []};
false -> false
end.

-spec handle_request(any(), state()) -> {any(), state()}.
handle_request({document_formatting, Params}, State) ->
Expand Down Expand Up @@ -154,8 +158,87 @@ format_document_local(Dir, RelativePath,
rangeformat_document(_Uri, _Document, _Range, _Options) ->
{ok, []}.

-spec ontypeformat_document(binary(), map()
, number(), number(), string(), formatting_options())
-> {ok, [text_edit()]}.
ontypeformat_document(_Uri, _Document, _Line, _Col, _Char, _Options) ->
{ok, []}.
-spec ontypeformat_document(binary(),
map(),
number(),
number(),
string(),
formatting_options()) ->
{ok, [text_edit()]}.
ontypeformat_document(_Uri, Document, Line, Col, <<".">>, _Options) ->
case find_matching_range(Document, Line) of
[] ->
{ok, []};
[MatchingRange] ->
{StartLine, _} = Id = els_poi:id(MatchingRange),
Text = els_dt_document:text(Document),
RangeText = els_text:range(Text, Id, {Line, Col - 1}),
% Skip formatting if the . is on a commented line.
case string:trim(
els_text:line(Text, Line - 1), both)
of
<<"%", _/binary>> ->
{ok, []};
_ ->
ParseF =
fun(Dir) ->
TmpFile = tmp_file(Dir),
ok = file:write_file(TmpFile, <<RangeText/binary, ".">>),
Opts =
#{formatter => default_formatter,
paper => 100,
parse_macro_definitions => false,
truncate_strings => true,
parenthesize_infix_operations => true,
break_indent => 2,
output_dir => current},
RebarState = #{},
T = rebar3_formatter:new(default_formatter, Opts, RebarState),
rebar3_formatter:format_file(TmpFile, T),
{ok, Bin} = file:read_file(TmpFile),
Bin
end,
%% rebar3_formatter adds a newline, since we terminate on .
%% We want to leave the cursor at the current char rather
%% than jumping to a newline
NewText =
string:trim(
tempdir:mktmp(ParseF), trailing, "\n"),
{ok,
[#{range =>
#{start => #{line => StartLine - 1, character => 0},
'end' => #{line => Line - 1, character => Col}},
newText => NewText}]}
end
end;
ontypeformat_document(_Uri, _Document, _Line, _Col, Char, _Options) ->
?LOG_INFO("Got unhandled character in ontypeformat_document. No formatter "
"configured for char: ~p",
[Char]),
{ok, []}.

-spec find_foldable_ranges(els_dt_document:item()) -> [poi()].
find_foldable_ranges(Document) ->
Pois = els_dt_document:pois(Document),
lists:filter(fun (#{kind := folding_range}) ->
true;
(_) ->
false
end,
Pois).

-spec find_matching_range(els_dt_document:item(), number()) -> [poi()].
find_matching_range(Document, Line) ->
lists:filter(fun(#{range := #{from := {FromLine, _}, to := {ToLine, _}}}) ->
Line >= FromLine andalso Line =< ToLine
end,
find_foldable_ranges(Document)).

-spec tmp_file(string()) -> any().
tmp_file(Dir) ->
Unique = erlang:unique_integer([positive]),
{A, B, C} = os:timestamp(),
N = node(),
filename:join(Dir,
lists:flatten(
io_lib:format("~p-~p.~p.~p.~p", [N, A, B, C, Unique]))).
2 changes: 2 additions & 0 deletions apps/els_lsp/src/els_general_provider.erl
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ server_capabilities() ->
els_formatting_provider:is_enabled_document()
, documentRangeFormattingProvider =>
els_formatting_provider:is_enabled_range()
, documentOnTypeFormattingProvider =>
els_formatting_provider:is_enabled_on_type()
, foldingRangeProvider =>
els_folding_range_provider:is_enabled()
, implementationProvider =>
Expand Down
5 changes: 5 additions & 0 deletions apps/els_lsp/src/els_poi.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

-export([ match_pos/2
, sort/1
, id/1
]).

%%==============================================================================
Expand Down Expand Up @@ -42,6 +43,10 @@ match_pos(POIs, Pos) ->
, to := To
}} = POI <- POIs, (From =< Pos) andalso (Pos =< To)].

-spec id(poi()) -> poi_id().
id(#{id := Id}) ->
Id.

%% @doc Sorts pois based on their range
%%
%% Order is defined using els_range:compare/2.
Expand Down
2 changes: 1 addition & 1 deletion rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
, {yamerl, "0.8.1"}
, {docsh, "0.7.2"}
, {elvis_core, "~> 1.3"}
, {rebar3_format, "0.8.2"}
, {rebar3_format, "1.0.1"}
%%, {erlfmt, "1.0.0"}
, {erlfmt, {git, "https://github.com/gomoripeti/erlfmt.git", {tag, "erlang_ls_parser_error_loc"}}} %% Temp until erlfmt PR 325 is merged (commit d4422d1)
, {ephemeral, "2.0.4"}
Expand Down
12 changes: 6 additions & 6 deletions rebar.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
{ref,"e93db1c6725760def005c69d72f53b1a889b4c2f"}},
0},
{<<"jsx">>,{pkg,<<"jsx">>,<<"3.0.0">>},0},
{<<"katana_code">>,{pkg,<<"katana_code">>,<<"0.2.1">>},1},
{<<"katana_code">>,{pkg,<<"katana_code">>,<<"1.1.2">>},1},
{<<"providers">>,{pkg,<<"providers">>,<<"1.8.1">>},1},
{<<"quickrand">>,{pkg,<<"quickrand">>,<<"2.0.1">>},1},
{<<"rebar3_format">>,{pkg,<<"rebar3_format">>,<<"0.8.2">>},0},
{<<"rebar3_format">>,{pkg,<<"rebar3_format">>,<<"1.0.1">>},0},
{<<"redbug">>,{pkg,<<"redbug">>,<<"2.0.6">>},0},
{<<"tdiff">>,{pkg,<<"tdiff">>,<<"0.1.2">>},0},
{<<"uuid">>,{pkg,<<"uuid_erl">>,<<"2.0.1">>},0},
Expand All @@ -30,10 +30,10 @@
{<<"ephemeral">>, <<"B3E57886ADD5D90C82FE3880F5954978222A122CB8BAA123667401BBAAEC51D6">>},
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>},
{<<"jsx">>, <<"20A170ABD4335FC6DB24D5FAD1E5D677C55DADF83D1B20A8A33B5FE159892A39">>},
{<<"katana_code">>, <<"B2195859DF57D8BEBF619A9FD3327CD7D01563A98417156D0F4C5FAB435F2630">>},
{<<"katana_code">>, <<"4336743263236C3213FF1B979ECAE263940B9084ACE90E9434838F3F98CCA578">>},
{<<"providers">>, <<"70B4197869514344A8A60E2B2A4EF41CA03DEF43CFB1712ECF076A0F3C62F083">>},
{<<"quickrand">>, <<"6D861FA11E6EB51BB2343A2616EFF704C2681A9997F41ABC78E58FA76DA33981">>},
{<<"rebar3_format">>, <<"2D64DA61E0B87FCA6C4512ADA6D9CBC2B27ADC9AE6844178561147E7121761BD">>},
{<<"rebar3_format">>, <<"F8D275C9BD9A2541880D03071AF75FD6B78FC80D74158DC0A2C48A913B03D9F4">>},
{<<"redbug">>, <<"A764690B012B67C404562F9C6E1BA47A73892EE17DF5C15F670B1A5BF9D2F25A">>},
{<<"tdiff">>, <<"4E1B30321F1B3D600DF65CD60858EDE1235FE4E5EE042110AB5AD90CD6464AC5">>},
{<<"uuid">>, <<"1FD9079C544D521063897887A1C5B3302DCA98F9BB06AADCDC6FB0663F256797">>},
Expand All @@ -46,10 +46,10 @@
{<<"ephemeral">>, <<"4B293D80F75F9C4575FF4B9C8E889A56802F40B018BF57E74F19644EFEE6C850">>},
{<<"getopt">>, <<"53E1AB83B9CEB65C9672D3E7A35B8092E9BDC9B3EE80721471A161C10C59959C">>},
{<<"jsx">>, <<"37BECA0435F5CA8A2F45F76A46211E76418FBEF80C36F0361C249FC75059DC6D">>},
{<<"katana_code">>, <<"8448AD3F56D9814F98A28BE650F7191BDD506575E345CC16D586660B10F6E992">>},
{<<"katana_code">>, <<"E7E6162A44E826A03F68B503B7D92981B894BF834C1EF0E647783F7D6688021C">>},
{<<"providers">>, <<"E45745ADE9C476A9A469EA0840E418AB19360DC44F01A233304E118A44486BA0">>},
{<<"quickrand">>, <<"14DB67D4AEF6B8815810EC9F3CCEF5E324B73B56CAE3687F99D752B85BDD4C96">>},
{<<"rebar3_format">>, <<"CA8FF27638C2169593D1449DACBE8895634193ED3334E906B54FC97F081F5213">>},
{<<"rebar3_format">>, <<"3896FA8BCA201C8A6B725D5DE67E0031F00A087C4BE69B4202CA7EA7AA0DA9E3">>},
{<<"redbug">>, <<"AAD9498671F4AB91EACA5099FE85A61618158A636E6286892C4F7CF4AF171D04">>},
{<<"tdiff">>, <<"E0C2E168F99252A5889768D5C8F1E6510A184592D4CFA06B22778A18D33D7875">>},
{<<"uuid">>, <<"AB57CACCD51F170011E5F444CE865F84B41605E483A9EFCC468C1AFAEC87553B">>},
Expand Down