Skip to content

Commit

Permalink
Merge branch 'master' into fix-project-loading
Browse files Browse the repository at this point in the history
  • Loading branch information
axelson authored May 16, 2020
2 parents 3b77dc8 + ead80a4 commit ea23425
Show file tree
Hide file tree
Showing 19 changed files with 655 additions and 73 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,30 @@
Improvements:
- 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)
- For details see the [Code Completion section of the readme](https://github.com/elixir-lsp/elixir-ls/tree/a2a1f38bf0f47e074ec5d50636d669fae03a3d5e#code-completion)
- 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)
- 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)
- 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)

Bug Fixes:
- Dialyzer: Get beam file for preloaded modules. (thanks [Łukasz Samson](https://github.com/lukaszsamson)) [#218](https://github.com/elixir-lsp/elixir-ls/pull/218)
- 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)
- 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)

VSCode:
- 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)
- 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)

VSCode potentially breaking changes:
- 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)
- This changes the language id's `EEx`->`eex` and `HTML (EEx)`->`html-eex`
- If you have customized your emmet configuration configuration then you need to update it:
- Open VSCode and hit `Ctrl+Shift+P` or `Cmd+Shift+P` and type `"Preference: Open Settings (JSON)"`
- Add or edit your `emmet.includedLanguages` to include the new Language Id:
```json
"emmet.includeLanguages": {
"html-eex": "html"
}
```

### v0.3.3: 15 Apr 2020

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

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

Expand Down
2 changes: 1 addition & 1 deletion apps/elixir_ls_utils/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ defmodule ElixirLS.Utils.Mixfile do

defp deps do
[
{:jason, "~> 1.1"},
{:jason, "~> 1.2"},
{:mix_task_archive_deps, github: "JakeBecker/mix_task_archive_deps"}
]
end
Expand Down
6 changes: 3 additions & 3 deletions apps/language_server/lib/language_server/build.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
defmodule ElixirLS.LanguageServer.Build do
alias ElixirLS.LanguageServer.{Server, JsonRpc, SourceFile}
alias ElixirLS.LanguageServer.{Server, JsonRpc, SourceFile, Diagnostics}

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

prev_deps = cached_deps()
# FIXME: Private API
Mix.Dep.clear_cached()
:ok = Mix.Project.clear_deps_cache()

case reload_project() do
{:ok, mixfile_diagnostics} ->
Expand All @@ -29,6 +28,7 @@ defmodule ElixirLS.LanguageServer.Build do
load_all_modules()
end

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

{:error, mixfile_diagnostics} ->
Expand Down
151 changes: 151 additions & 0 deletions apps/language_server/lib/language_server/diagnostics.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
defmodule ElixirLS.LanguageServer.Diagnostics do
def normalize(diagnostics, root_path) do
for diagnostic <- diagnostics do
{type, file, line, description, stacktrace} =
extract_message_info(diagnostic.message, root_path)

diagnostic
|> update_message(type, description, stacktrace)
|> maybe_update_file(file)
|> maybe_update_position(line, stacktrace)
end
end

defp extract_message_info(list, root_path) when is_list(list) do
list
|> Enum.join()
|> extract_message_info(root_path)
end

defp extract_message_info(diagnostic_message, root_path) do
{reversed_stacktrace, reversed_description} =
diagnostic_message
|> String.trim_trailing()
|> String.split("\n")
|> Enum.reverse()
|> Enum.split_while(&is_stack?/1)

message = reversed_description |> Enum.reverse() |> Enum.join("\n") |> String.trim()
stacktrace = reversed_stacktrace |> Enum.map(&String.trim/1) |> Enum.reverse()

{type, message_without_type} = split_type_and_message(message)
{file, line, description} = split_file_and_description(message_without_type, root_path)

{type, file, line, description, stacktrace}
end

defp update_message(diagnostic, type, description, stacktrace) do
description =
if type do
"(#{type}) #{description}"
else
description
end

message =
if stacktrace != [] do
stacktrace =
stacktrace
|> Enum.map(&" │ #{&1}")
|> Enum.join("\n")
|> String.trim_trailing()

description <> "\n\n" <> "Stacktrace:\n" <> stacktrace
else
description
end

Map.put(diagnostic, :message, message)
end

defp maybe_update_file(diagnostic, path) do
if path do
Map.put(diagnostic, :file, path)
else
diagnostic
end
end

defp maybe_update_position(diagnostic, line, stacktrace) do
cond do
line ->
%{diagnostic | position: line}

diagnostic.position ->
diagnostic

true ->
line = extract_line_from_stacktrace(diagnostic.file, stacktrace)
%{diagnostic | position: line}
end
end

defp split_type_and_message(message) do
case Regex.run(~r/^\*\* \(([\w\.]+?)?\) (.*)/s, message) do
[_, type, rest] ->
{type, rest}

_ ->
{nil, message}
end
end

defp split_file_and_description(message, root_path) do
with [_, file, line, description] <- Regex.run(~r/^(.*?):(\d+): (.*)/s, message),
{:ok, path} <- file_path(file, root_path) do
{path, String.to_integer(line), description}
else
_ ->
{nil, nil, message}
end
end

defp file_path(nil, _root_path) do
{:error, :file_not_found}
end

defp file_path(file, root_path) do
path = Path.join([root_path, file])

if File.exists?(path) do
{:ok, path}
else
file_path_in_umbrella(file, root_path)
end
end

defp file_path_in_umbrella(file, root_path) do
case [root_path, "apps", "*", file] |> Path.join() |> Path.wildcard() do
[] ->
{:error, :file_not_found}

[path] ->
{:ok, path}

_ ->
{:error, :more_than_one_file_found}
end
end

defp is_stack?(" " <> str) do
Regex.match?(~r/.*\.(ex|erl):\d+: /, str) ||
Regex.match?(~r/.*expanding macro: /, str)
end

defp is_stack?(_) do
false
end

defp extract_line_from_stacktrace(file, stacktrace) do
Enum.find_value(stacktrace, fn stack_item ->
with [_, _, file_relative, line] <-
Regex.run(~r/(\(.+?\)\s+)?(.*\.ex):(\d+): /, stack_item),
true <- String.ends_with?(file, file_relative) do
String.to_integer(line)
else
_ ->
nil
end
end)
end
end
15 changes: 14 additions & 1 deletion apps/language_server/lib/language_server/dialyzer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -374,14 +374,16 @@ defmodule ElixirLS.LanguageServer.Dialyzer do

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

mod_deps = Map.merge(mod_deps, new_mod_deps)
mod_deps = update_mod_deps(mod_deps, new_mod_deps, removed_modules)
warnings = add_warnings(warnings, raw_warnings)

md5 =
for {file, {_, hash}} <- file_changes, into: md5 do
{file, hash}
end

md5 = remove_files(md5, removed_files)

{active_plt, mod_deps, md5, warnings}
end)

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

defp update_mod_deps(mod_deps, new_mod_deps, removed_modules) do
mod_deps
|> Map.merge(new_mod_deps)
|> Map.drop(removed_modules)
|> Map.new(fn {mod, deps} -> {mod, deps -- removed_modules} end)
end

defp remove_files(md5, removed_files) do
Map.drop(md5, removed_files)
end

defp add_warnings(warnings, raw_warnings) do
new_warnings =
for {_, {file, line, m_or_mfa}, _} = warning <- raw_warnings,
Expand Down
16 changes: 10 additions & 6 deletions apps/language_server/lib/language_server/dialyzer/manifest.ex
Original file line number Diff line number Diff line change
Expand Up @@ -114,19 +114,23 @@ defmodule ElixirLS.LanguageServer.Dialyzer.Manifest do
Path.join([Mix.Utils.mix_home(), "elixir-ls-#{otp_vsn()}_elixir-#{System.version()}"])
end

@elixir_apps [:elixir, :eex, :ex_unit, :iex, :logger, :mix]

defp build_elixir_plt() do
JsonRpc.show_message(
:info,
"Building core Dialyzer Elixir PLT. This will take a few minutes (often 15+) and can be disabled in the settings."
)

files =
Path.join([Application.app_dir(:elixir), "**/*.beam"])
|> Path.wildcard()
|> Enum.map(&pathname_to_module/1)
|> expand_references()
|> Enum.map(&Utils.get_beam_file/1)
|> Enum.filter(&is_list/1)
Enum.flat_map(@elixir_apps, fn app ->
Path.join([Application.app_dir(app), "**/*.beam"])
|> Path.wildcard()
|> Enum.map(&pathname_to_module/1)
|> expand_references()
|> Enum.map(&Utils.get_beam_file/1)
|> Enum.filter(&is_list/1)
end)

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

Expand Down
Loading

0 comments on commit ea23425

Please sign in to comment.