Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use languageId field provided by LSP clients. #662

Merged
merged 10 commits into from
Mar 28, 2024
7 changes: 5 additions & 2 deletions apps/remote_control/lib/lexical/remote_control/build.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule Lexical.RemoteControl.Build do
alias Lexical.Document
alias Lexical.Project
alias Lexical.RemoteControl
alias Lexical.RemoteControl.Build.Document.Compilers.HEEx
alias Lexical.RemoteControl.Build.State
alias Lexical.VM.Versions

Expand All @@ -26,7 +27,8 @@ defmodule Lexical.RemoteControl.Build do
end

def compile_document(%Project{} = project, %Document{} = document) do
unless Path.absname(document.path) == "mix.exs" do
with false <- Path.absname(document.path) == "mix.exs",
false <- HEEx.recognizes?(document.language_id) do
RemoteControl.call(project, GenServer, :cast, [__MODULE__, {:compile_file, document}])
end

Expand All @@ -35,7 +37,8 @@ defmodule Lexical.RemoteControl.Build do

# this is for testing
def force_compile_document(%Project{} = project, %Document{} = document) do
unless Path.absname(document.path) == "mix.exs" do
with false <- Path.absname(document.path) == "mix.exs",
false <- HEEx.recognizes?(document.language_id) do
RemoteControl.call(project, GenServer, :call, [
__MODULE__,
{:force_compile_file, document}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ defmodule Lexical.RemoteControl.Build.Document.Compilers.EEx do

@behaviour Compiler

def recognizes?(%Document{} = document) do
Path.extname(document.path) == ".eex"
end
@impl true
def recognizes?(%Document{language_id: "eex"}), do: true
def recognizes?(_), do: false

def enabled? do
true
end
@impl true
def enabled?, do: true

@impl true
def compile(%Document{} = document) do
with {:ok, quoted} <- eex_to_quoted(document),
:ok <- eval_quoted(document, quoted) do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@ defmodule Lexical.RemoteControl.Build.Document.Compilers.Elixir do
alias Lexical.RemoteControl.Build.Document.Compilers

@behaviour Build.Document.Compiler
@valid_extensions ~w(.ex .exs)

def recognizes?(%Document{} = doc) do
Path.extname(doc.path) in @valid_extensions
end
@impl true
def recognizes?(%Document{language_id: "elixir"}), do: true
def recognizes?(_), do: false

def enabled? do
true
end
@impl true
def enabled?, do: true

@impl true
def compile(%Document{} = document) do
case to_quoted(document) do
{:ok, quoted} ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ defmodule Lexical.RemoteControl.Build.Document.Compilers.HEEx do

@behaviour Compiler

def recognizes?(%Document{} = document) do
Path.extname(document.path) == ".heex"
end
def recognizes?(%Document{language_id: "phoenix-heex"}), do: true
def recognizes?(%Document{language_id: "heex"}), do: true
def recognizes?(_), do: false

def enabled? do
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ defmodule Lexical.RemoteControl.CodeAction.Handlers.ReplaceRemoteFunction do
end
end

@function_re ~r/(warning: )?([^\/]+)\/(.*) is undefined or private. Did you mean:.*/
@function_re ~r/(warning: |function )?([^\/]+)\/(.*) is undefined or private. Did you mean:.*/
defp extract_function(message) do
result =
with [[_, _, module_and_function, arity]] <- Regex.scan(@function_re, message),
Expand Down
16 changes: 10 additions & 6 deletions apps/server/lib/lexical/server/state.ex
Original file line number Diff line number Diff line change
Expand Up @@ -170,17 +170,21 @@ defmodule Lexical.Server.State do
end
end

def apply(%__MODULE__{} = state, %DidOpen{lsp: event}) do
%TextDocument.Item{text: text, uri: uri, version: version} =
text_document = event.text_document

case Document.Store.open(uri, text, version) do
def apply(%__MODULE__{} = state, %DidOpen{} = did_open) do
%TextDocument.Item{
text: text,
uri: uri,
version: version,
language_id: language_id
} = did_open.lsp.text_document
scohen marked this conversation as resolved.
Show resolved Hide resolved

case Document.Store.open(uri, text, version, language_id) do
:ok ->
Logger.info("opened #{uri}")
{:ok, state}

error ->
Logger.error("Could not open #{text_document.uri} #{inspect(error)}")
Logger.error("Could not open #{uri} #{inspect(error)}")
error
end
end
Expand Down
35 changes: 32 additions & 3 deletions projects/lexical_shared/lib/lexical/document.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@ defmodule Lexical.Document do

import Lexical.Document.Line

require Logger

alias __MODULE__.Path, as: DocumentPath

@derive {Inspect, only: [:path, :version, :dirty?, :lines]}

defstruct [:uri, :path, :version, dirty?: false, lines: nil]
defstruct [:uri, :language_id, :path, :version, dirty?: false, lines: nil]

@type version :: non_neg_integer()
@type fragment_position :: Position.t() | Convertible.t()
@type t :: %__MODULE__{
uri: String.t(),
language_id: String.t(),
version: version(),
dirty?: boolean,
lines: Lines.t(),
Expand All @@ -41,14 +44,18 @@ defmodule Lexical.Document do
as a binary and the vewrsion.
"""
@spec new(Lexical.path() | Lexical.uri(), String.t(), version()) :: t
def new(maybe_uri, text, version) do
def new(maybe_uri, text, version, language_id \\ nil) do
uri = DocumentPath.ensure_uri(maybe_uri)
path = DocumentPath.from_uri(uri)

language_id = language_id || language_id_from_path(path)

%__MODULE__{
uri: uri,
version: version,
lines: Lines.new(text),
path: DocumentPath.from_uri(uri)
path: path,
language_id: language_id
}
end

Expand Down Expand Up @@ -219,6 +226,28 @@ defmodule Lexical.Document do
|> IO.iodata_to_binary()
end

@spec language_id_from_path(Lexical.path()) :: String.t()
defp language_id_from_path(path) do
case Path.extname(path) do
".ex" ->
"elixir"

".exs" ->
"elixir"

".eex" ->
"eex"

".heex" ->
"phoenix-heex"

extension ->
Logger.warning("can't infer lang ID for #{path}, ext: #{extension}.")

"unsupported (#{extension})"
end
end

# private

defp line_count(%__MODULE__{} = document) do
Expand Down
22 changes: 12 additions & 10 deletions projects/lexical_shared/lib/lexical/document/store.ex
Original file line number Diff line number Diff line change
Expand Up @@ -91,24 +91,25 @@ defmodule Lexical.Document.Store do
end
end

@spec open(t, Lexical.uri(), String.t(), pos_integer()) :: {:ok, t} | {:error, :already_open}
def open(%__MODULE__{temporary_open_refs: refs} = store, uri, text, version)
@spec open(t, Lexical.uri(), String.t(), pos_integer(), String.t()) ::
{:ok, t} | {:error, :already_open}
def open(%__MODULE__{temporary_open_refs: refs} = store, uri, text, version, language_id)
when is_map_key(refs, uri) do
{_, store} =
store
|> maybe_cancel_ref(uri)
|> pop_open_doc(uri)

open(store, uri, text, version)
open(store, uri, text, version, language_id)
end

def open(%__MODULE__{} = store, uri, text, version) do
def open(%__MODULE__{} = store, uri, text, version, language_id) do
case store.open do
%{^uri => _} ->
{:error, :already_open}

_ ->
document = Document.new(uri, text, version)
document = Document.new(uri, text, version, language_id)
store = put_open_doc(store, document)
{:ok, store}
end
Expand Down Expand Up @@ -256,9 +257,10 @@ defmodule Lexical.Document.Store do
GenServer.call(name(), {:open?, uri})
end

@spec open(Lexical.uri(), String.t(), pos_integer()) :: :ok | {:error, :already_open}
def open(uri, text, version) do
GenServer.call(name(), {:open, uri, text, version})
@spec open(Lexical.uri(), String.t(), String.t(), pos_integer() | nil) ::
:ok | {:error, :already_open}
def open(uri, text, version, language_id \\ nil) do
GenServer.call(name(), {:open, uri, text, version, language_id})
end

@spec open_temporary(Lexical.uri() | Path.t()) ::
Expand Down Expand Up @@ -308,9 +310,9 @@ defmodule Lexical.Document.Store do
{:reply, reply, new_state}
end

def handle_call({:open, uri, text, version}, _from, %State{} = state) do
def handle_call({:open, uri, text, version, language_id}, _from, %State{} = state) do
{reply, new_state} =
case State.open(state, uri, text, version) do
case State.open(state, uri, text, version, language_id) do
{:ok, _} = success -> success
error -> {error, state}
end
Expand Down
Loading