Skip to content

Commit ea23425

Browse files
authored
Merge branch 'master' into fix-project-loading
2 parents 3b77dc8 + ead80a4 commit ea23425

File tree

19 files changed

+655
-73
lines changed

19 files changed

+655
-73
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,30 @@
33
Improvements:
44
- Add autocompletion of struct fields on a binding when we know for sure what type of struct it is. (thanks [Łukasz Samson](https://github.com/lukaszsamson)) [#202](https://github.com/elixir-lsp/elixir-ls/pull/202)
55
- For details see the [Code Completion section of the readme](https://github.com/elixir-lsp/elixir-ls/tree/a2a1f38bf0f47e074ec5d50636d669fae03a3d5e#code-completion)
6+
- Add all core elixir apps to the Dialyzer PLT. (thanks [Eric Entin](https://github.com/ericentin)) [#225](https://github.com/elixir-lsp/elixir-ls/pull/225)
7+
- Change "did not receive workspace/didChangeConfiguration" log level from warning to info (thanks [Jason Axelson](https://github.com/axelson)) [#222](https://github.com/elixir-lsp/elixir-ls/pull/222)
8+
- Automatically create a `.gitignore` file inside the `.elixir-ls` dir so that users do not need to manually add it to their gitignore (thanks [Thanabodee Charoenpiriyakij](https://github.com/wingyplus)) [#232](https://github.com/elixir-lsp/elixir-ls/pull/232)
69

710
Bug Fixes:
811
- Dialyzer: Get beam file for preloaded modules. (thanks [Łukasz Samson](https://github.com/lukaszsamson)) [#218](https://github.com/elixir-lsp/elixir-ls/pull/218)
912
- Warn when using the debugger on Elixir 1.10.0-1.10.2. (thanks [Jason Axelson](https://github.com/axelson)) [#221](https://github.com/elixir-lsp/elixir-ls/pull/221)
13+
- Don't return snippets to clients that don't declare `snippetSupport` for function completions (thanks [Jeffrey Xiao](https://github.com/jeffrey-xiao)) [#223](https://github.com/elixir-lsp/elixir-ls/pull/223)
1014

1115
VSCode:
1216
- Add basic support for `.html.leex` files for Phoenix LiveView (thanks [oskarkook](https://github.com/oskarkook)) [#82](https://github.com/elixir-lsp/vscode-elixir-ls/pull/82)
17+
- Add filetype and watcher for `.html.leex` files for Phoenix LiveView (thanks [Byron Hambly](https://github.com/delta1)) [#83](https://github.com/elixir-lsp/vscode-elixir-ls/pull/83)
18+
19+
VSCode potentially breaking changes:
20+
- Change language id to be lowercase kebab-case in accordance with [VSCode guidelines](https://code.visualstudio.com/docs/languages/identifiers#_new-identifier-guidelines). This also fixes an issue displaying the elixir logo for html.eex files. (thanks [Matt Furden](https://github.com/zolrath)) [#87](https://github.com/elixir-lsp/vscode-elixir-ls/pull/87)
21+
- This changes the language id's `EEx`->`eex` and `HTML (EEx)`->`html-eex`
22+
- If you have customized your emmet configuration configuration then you need to update it:
23+
- Open VSCode and hit `Ctrl+Shift+P` or `Cmd+Shift+P` and type `"Preference: Open Settings (JSON)"`
24+
- Add or edit your `emmet.includedLanguages` to include the new Language Id:
25+
```json
26+
"emmet.includeLanguages": {
27+
"html-eex": "html"
28+
}
29+
```
1330

1431
### v0.3.3: 15 Apr 2020
1532

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ You may want to install Elixir and Erlang from source, using the [kiex](https://
4747
| Neovim | [coc.nvim](https://github.com/neoclide/coc.nvim) | Does not support debugger |
4848
| Emacs | [lsp-mode](https://github.com/emacs-lsp/lsp-mode) | Supports debugger via [dap-mode](https://github.com/yyoncho/dap-mode) |
4949
| Emacs | [eglot](https://github.com/joaotavora/eglot) | |
50+
| Kate | [built-in LSP Client plugin](https://kate-editor.org/post/2020/2020-01-01-kate-lsp-client-status/) | Does not support debugger |
5051

5152
Feel free to create and publish your own client packages and add them to this list!
5253

apps/elixir_ls_utils/mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ defmodule ElixirLS.Utils.Mixfile do
2626

2727
defp deps do
2828
[
29-
{:jason, "~> 1.1"},
29+
{:jason, "~> 1.2"},
3030
{:mix_task_archive_deps, github: "JakeBecker/mix_task_archive_deps"}
3131
]
3232
end

apps/language_server/lib/language_server/build.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
defmodule ElixirLS.LanguageServer.Build do
2-
alias ElixirLS.LanguageServer.{Server, JsonRpc, SourceFile}
2+
alias ElixirLS.LanguageServer.{Server, JsonRpc, SourceFile, Diagnostics}
33

44
def build(parent, root_path, opts) do
55
if Path.absname(File.cwd!()) != Path.absname(root_path) do
@@ -13,8 +13,7 @@ defmodule ElixirLS.LanguageServer.Build do
1313
IO.puts("Compiling with Mix env #{Mix.env()}")
1414

1515
prev_deps = cached_deps()
16-
# FIXME: Private API
17-
Mix.Dep.clear_cached()
16+
:ok = Mix.Project.clear_deps_cache()
1817

1918
case reload_project() do
2019
{:ok, mixfile_diagnostics} ->
@@ -29,6 +28,7 @@ defmodule ElixirLS.LanguageServer.Build do
2928
load_all_modules()
3029
end
3130

31+
diagnostics = Diagnostics.normalize(diagnostics, root_path)
3232
Server.build_finished(parent, {status, mixfile_diagnostics ++ diagnostics})
3333

3434
{:error, mixfile_diagnostics} ->
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
defmodule ElixirLS.LanguageServer.Diagnostics do
2+
def normalize(diagnostics, root_path) do
3+
for diagnostic <- diagnostics do
4+
{type, file, line, description, stacktrace} =
5+
extract_message_info(diagnostic.message, root_path)
6+
7+
diagnostic
8+
|> update_message(type, description, stacktrace)
9+
|> maybe_update_file(file)
10+
|> maybe_update_position(line, stacktrace)
11+
end
12+
end
13+
14+
defp extract_message_info(list, root_path) when is_list(list) do
15+
list
16+
|> Enum.join()
17+
|> extract_message_info(root_path)
18+
end
19+
20+
defp extract_message_info(diagnostic_message, root_path) do
21+
{reversed_stacktrace, reversed_description} =
22+
diagnostic_message
23+
|> String.trim_trailing()
24+
|> String.split("\n")
25+
|> Enum.reverse()
26+
|> Enum.split_while(&is_stack?/1)
27+
28+
message = reversed_description |> Enum.reverse() |> Enum.join("\n") |> String.trim()
29+
stacktrace = reversed_stacktrace |> Enum.map(&String.trim/1) |> Enum.reverse()
30+
31+
{type, message_without_type} = split_type_and_message(message)
32+
{file, line, description} = split_file_and_description(message_without_type, root_path)
33+
34+
{type, file, line, description, stacktrace}
35+
end
36+
37+
defp update_message(diagnostic, type, description, stacktrace) do
38+
description =
39+
if type do
40+
"(#{type}) #{description}"
41+
else
42+
description
43+
end
44+
45+
message =
46+
if stacktrace != [] do
47+
stacktrace =
48+
stacktrace
49+
|> Enum.map(&" │ #{&1}")
50+
|> Enum.join("\n")
51+
|> String.trim_trailing()
52+
53+
description <> "\n\n" <> "Stacktrace:\n" <> stacktrace
54+
else
55+
description
56+
end
57+
58+
Map.put(diagnostic, :message, message)
59+
end
60+
61+
defp maybe_update_file(diagnostic, path) do
62+
if path do
63+
Map.put(diagnostic, :file, path)
64+
else
65+
diagnostic
66+
end
67+
end
68+
69+
defp maybe_update_position(diagnostic, line, stacktrace) do
70+
cond do
71+
line ->
72+
%{diagnostic | position: line}
73+
74+
diagnostic.position ->
75+
diagnostic
76+
77+
true ->
78+
line = extract_line_from_stacktrace(diagnostic.file, stacktrace)
79+
%{diagnostic | position: line}
80+
end
81+
end
82+
83+
defp split_type_and_message(message) do
84+
case Regex.run(~r/^\*\* \(([\w\.]+?)?\) (.*)/s, message) do
85+
[_, type, rest] ->
86+
{type, rest}
87+
88+
_ ->
89+
{nil, message}
90+
end
91+
end
92+
93+
defp split_file_and_description(message, root_path) do
94+
with [_, file, line, description] <- Regex.run(~r/^(.*?):(\d+): (.*)/s, message),
95+
{:ok, path} <- file_path(file, root_path) do
96+
{path, String.to_integer(line), description}
97+
else
98+
_ ->
99+
{nil, nil, message}
100+
end
101+
end
102+
103+
defp file_path(nil, _root_path) do
104+
{:error, :file_not_found}
105+
end
106+
107+
defp file_path(file, root_path) do
108+
path = Path.join([root_path, file])
109+
110+
if File.exists?(path) do
111+
{:ok, path}
112+
else
113+
file_path_in_umbrella(file, root_path)
114+
end
115+
end
116+
117+
defp file_path_in_umbrella(file, root_path) do
118+
case [root_path, "apps", "*", file] |> Path.join() |> Path.wildcard() do
119+
[] ->
120+
{:error, :file_not_found}
121+
122+
[path] ->
123+
{:ok, path}
124+
125+
_ ->
126+
{:error, :more_than_one_file_found}
127+
end
128+
end
129+
130+
defp is_stack?(" " <> str) do
131+
Regex.match?(~r/.*\.(ex|erl):\d+: /, str) ||
132+
Regex.match?(~r/.*expanding macro: /, str)
133+
end
134+
135+
defp is_stack?(_) do
136+
false
137+
end
138+
139+
defp extract_line_from_stacktrace(file, stacktrace) do
140+
Enum.find_value(stacktrace, fn stack_item ->
141+
with [_, _, file_relative, line] <-
142+
Regex.run(~r/(\(.+?\)\s+)?(.*\.ex):(\d+): /, stack_item),
143+
true <- String.ends_with?(file, file_relative) do
144+
String.to_integer(line)
145+
else
146+
_ ->
147+
nil
148+
end
149+
end)
150+
end
151+
end

apps/language_server/lib/language_server/dialyzer.ex

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,14 +374,16 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
374374

375375
{active_plt, new_mod_deps, raw_warnings} = Analyzer.analyze(active_plt, files_to_analyze)
376376

377-
mod_deps = Map.merge(mod_deps, new_mod_deps)
377+
mod_deps = update_mod_deps(mod_deps, new_mod_deps, removed_modules)
378378
warnings = add_warnings(warnings, raw_warnings)
379379

380380
md5 =
381381
for {file, {_, hash}} <- file_changes, into: md5 do
382382
{file, hash}
383383
end
384384

385+
md5 = remove_files(md5, removed_files)
386+
385387
{active_plt, mod_deps, md5, warnings}
386388
end)
387389

@@ -393,6 +395,17 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
393395
analysis_finished(parent, :ok, active_plt, mod_deps, md5, warnings, timestamp, build_ref)
394396
end
395397

398+
defp update_mod_deps(mod_deps, new_mod_deps, removed_modules) do
399+
mod_deps
400+
|> Map.merge(new_mod_deps)
401+
|> Map.drop(removed_modules)
402+
|> Map.new(fn {mod, deps} -> {mod, deps -- removed_modules} end)
403+
end
404+
405+
defp remove_files(md5, removed_files) do
406+
Map.drop(md5, removed_files)
407+
end
408+
396409
defp add_warnings(warnings, raw_warnings) do
397410
new_warnings =
398411
for {_, {file, line, m_or_mfa}, _} = warning <- raw_warnings,

apps/language_server/lib/language_server/dialyzer/manifest.ex

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,19 +114,23 @@ defmodule ElixirLS.LanguageServer.Dialyzer.Manifest do
114114
Path.join([Mix.Utils.mix_home(), "elixir-ls-#{otp_vsn()}_elixir-#{System.version()}"])
115115
end
116116

117+
@elixir_apps [:elixir, :eex, :ex_unit, :iex, :logger, :mix]
118+
117119
defp build_elixir_plt() do
118120
JsonRpc.show_message(
119121
:info,
120122
"Building core Dialyzer Elixir PLT. This will take a few minutes (often 15+) and can be disabled in the settings."
121123
)
122124

123125
files =
124-
Path.join([Application.app_dir(:elixir), "**/*.beam"])
125-
|> Path.wildcard()
126-
|> Enum.map(&pathname_to_module/1)
127-
|> expand_references()
128-
|> Enum.map(&Utils.get_beam_file/1)
129-
|> Enum.filter(&is_list/1)
126+
Enum.flat_map(@elixir_apps, fn app ->
127+
Path.join([Application.app_dir(app), "**/*.beam"])
128+
|> Path.wildcard()
129+
|> Enum.map(&pathname_to_module/1)
130+
|> expand_references()
131+
|> Enum.map(&Utils.get_beam_file/1)
132+
|> Enum.filter(&is_list/1)
133+
end)
130134

131135
File.mkdir_p!(Path.dirname(elixir_plt_path()))
132136

0 commit comments

Comments
 (0)