Skip to content

Commit 181a04b

Browse files
authored
Merge pull request #1221 from elixir-lsp/codex/add-elixirls.dotformatter-server-setting
Add dotFormatter configuration option
2 parents 2e228b8 + 0f7e8ed commit 181a04b

File tree

6 files changed

+70
-18
lines changed

6 files changed

+70
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
### Unreleased
2+
- Added option `elixirLS.dotFormatter` to specify path to custom `.formatter.exs`
23

34
### v0.28.1: 24 May 2025
45

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@ Below is a list of configuration options supported by the ElixirLS language serv
486486
<dt>elixirLS.additionalWatchedExtensions</dt><dd>Additional file types capable of triggering a build on change</dd>
487487
<dt>elixirLS.languageServerOverridePath</dt><dd>Absolute path to an alternative ElixirLS release that will override the packaged release</dd>
488488
<dt>elixirLS.stdlibSrcDir</dt><dd>Path to Elixir's std lib source code. See [here](https://github.com/elixir-lsp/elixir_sense/pull/277) for more info</dd>
489+
<dt>elixirLS.dotFormatter</dt><dd>Path to a custom <code>.formatter.exs</code> file used when formatting documents</dd>
489490
</dl>
490491

491492
## Debug Adapter configuration options

apps/language_server/lib/language_server/providers/formatting.ex

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,26 @@ defmodule ElixirLS.LanguageServer.Providers.Formatting do
44
import ElixirLS.LanguageServer.RangeUtils
55
require Logger
66

7-
def format(%SourceFile{} = source_file, uri = "file:" <> _, project_dir, mix_project?)
7+
def format(source_file, uri, project_dir, mix_project?, opts \\ [])
8+
9+
def format(
10+
%SourceFile{} = source_file,
11+
uri = "file:" <> _,
12+
project_dir,
13+
mix_project?,
14+
opts
15+
)
816
when is_binary(project_dir) do
917
file_path = SourceFile.Path.absolute_from_uri(uri, project_dir)
1018
# file_path and project_dir are absolute paths with universal separators
1119
if SourceFile.Path.path_in_dir?(file_path, project_dir) do
1220
# file in project_dir we find formatter and options for file
13-
case SourceFile.formatter_for(uri, project_dir, mix_project?) do
14-
{:ok, {formatter, opts}} ->
15-
formatter_exs_dir = opts[:root]
21+
case SourceFile.formatter_for(uri, project_dir, mix_project?, opts) do
22+
{:ok, {formatter, formatter_opts}} ->
23+
formatter_exs_dir = formatter_opts[:root]
1624

17-
if should_format?(uri, formatter_exs_dir, opts[:inputs], project_dir) do
18-
do_format(source_file, formatter, opts)
25+
if should_format?(uri, formatter_exs_dir, formatter_opts[:inputs], project_dir) do
26+
do_format(source_file, formatter, formatter_opts)
1927
else
2028
JsonRpc.show_message(
2129
:info,
@@ -47,8 +55,8 @@ defmodule ElixirLS.LanguageServer.Providers.Formatting do
4755
end
4856
end
4957

50-
# if project_dir is not set or schema is not file we format with default options
51-
def format(%SourceFile{} = source_file, _uri, _project_dir, _mix_project?) do
58+
# if project_dir is not set or scheme is not file we format with default options
59+
def format(%SourceFile{} = source_file, _uri, _project_dir, _mix_project?, _opts) do
5260
do_format(source_file, nil, [])
5361
end
5462

apps/language_server/lib/language_server/server.ex

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1535,7 +1535,14 @@ defmodule ElixirLS.LanguageServer.Server do
15351535
state = %__MODULE__{}
15361536
) do
15371537
source_file = get_source_file(state, uri)
1538-
fun = fn -> Formatting.format(source_file, uri, state.project_dir, state.mix_project?) end
1538+
dot_formatter = Map.get(state.settings || %{}, "dotFormatter")
1539+
1540+
fun = fn ->
1541+
Formatting.format(source_file, uri, state.project_dir, state.mix_project?,
1542+
dot_formatter: dot_formatter
1543+
)
1544+
end
1545+
15391546
{:async, fun, state}
15401547
end
15411548

apps/language_server/lib/language_server/source_file.ex

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,12 @@ defmodule ElixirLS.LanguageServer.SourceFile do
235235
"""
236236
end
237237

238-
@spec formatter_for(String.t(), String.t() | nil, boolean) ::
238+
@spec formatter_for(String.t(), String.t() | nil, boolean, keyword()) ::
239239
{:ok, {function | nil, keyword()}} | {:error, any}
240-
def formatter_for(uri = "file:" <> _, project_dir, mix_project?) when is_binary(project_dir) do
240+
def formatter_for(uri, project_dir, mix_project?, opts \\ [])
241+
242+
def formatter_for(uri = "file:" <> _, project_dir, mix_project?, opts)
243+
when is_binary(project_dir) do
241244
path = __MODULE__.Path.from_uri(uri)
242245

243246
try do
@@ -250,7 +253,7 @@ defmodule ElixirLS.LanguageServer.SourceFile do
250253
{:ok, config_mtime} = MixProjectCache.config_mtime()
251254
{:ok, mix_project} = MixProjectCache.get()
252255

253-
opts = [
256+
formatter_opts = [
254257
deps_paths: deps_paths,
255258
manifest_path: manifest_path,
256259
config_mtime: config_mtime,
@@ -286,16 +289,20 @@ defmodule ElixirLS.LanguageServer.SourceFile do
286289
end
287290
]
288291

289-
{:ok, Mix.Tasks.ElixirLSFormat.formatter_for_file(path, opts)}
292+
formatter_opts =
293+
formatter_opts
294+
|> maybe_put_dot_formatter(opts)
295+
296+
{:ok, Mix.Tasks.ElixirLSFormat.formatter_for_file(path, formatter_opts)}
290297
else
291298
{:error, :project_not_loaded}
292299
end
293300
else
294-
opts = [
295-
root: project_dir
296-
]
301+
formatter_opts =
302+
[root: project_dir]
303+
|> maybe_put_dot_formatter(opts)
297304

298-
{:ok, Mix.Tasks.ElixirLSFormat.formatter_for_file(path, opts)}
305+
{:ok, Mix.Tasks.ElixirLSFormat.formatter_for_file(path, formatter_opts)}
299306
end
300307
catch
301308
kind, payload ->
@@ -322,7 +329,15 @@ defmodule ElixirLS.LanguageServer.SourceFile do
322329
end
323330
end
324331

325-
def formatter_for(_, _, _), do: {:error, :project_dir_not_set}
332+
def formatter_for(_, _, _, _), do: {:error, :project_dir_not_set}
333+
334+
defp maybe_put_dot_formatter(opts_list, opts) do
335+
if dot = Keyword.get(opts, :dot_formatter) do
336+
Keyword.put(opts_list, :dot_formatter, dot)
337+
else
338+
opts_list
339+
end
340+
end
326341

327342
defp format_code(code, opts) do
328343
try do

apps/language_server/test/providers/formatting_test.exs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,4 +503,24 @@ defmodule ElixirLS.LanguageServer.Providers.FormattingTest do
503503

504504
MixProjectCache.store(state)
505505
end
506+
507+
@tag :fixture
508+
test "custom dot formatter path is used" do
509+
in_fixture(Path.join(__DIR__, ".."), "formatter", fn ->
510+
store_mix_cache()
511+
project_dir = Path.expand(".")
512+
path = Path.join(project_dir, "lib/custom.ex")
513+
File.write!(path, "foo 1")
514+
source_file = %SourceFile{text: "foo 1", version: 1, dirty?: true}
515+
uri = SourceFile.Path.to_uri(path)
516+
517+
assert {:ok, [%TextEdit{}, %TextEdit{}]} =
518+
Formatting.format(source_file, uri, project_dir, true)
519+
520+
assert {:ok, []} =
521+
Formatting.format(source_file, uri, project_dir, true,
522+
dot_formatter: Path.join(project_dir, "lib/.formatter.exs")
523+
)
524+
end)
525+
end
506526
end

0 commit comments

Comments
 (0)