Skip to content

Commit

Permalink
Workspace symbols (#674)
Browse files Browse the repository at this point in the history
* Workspace symbols

Wired up workspace symbols. Things work, but this needs some
_love_. The search is kind of slow, and results are not particularly
relevant. I also don't use this feature much, so I don't know if we're
supposed to find _everything_ or, say, definitions.

I'm putting this up here as a draft PR while I open another branch
that will work toward improving relevance and performance.

* Improved performance / utility of fuzzy matcher and scorer

Made somewhat significant changes to the fuzzy matcher to improve
performance and relevance of the results.

I found that the matcher was returning a ton of results when it was
returning references to things, so I pared it down to only consider
definitions, which seems to improve the relevance of results a lot.

I also only had it return definitions in one of the current project's
applications, as searching your dependencies for a definition didn't
seem to be a common use case.

The fuzzy matcher also now considers the type of the entry being
indexed when it builds the subject. This gives us much nicer and more
relevant search results, and gives us the ability to tune them more
specifically in the future.
  • Loading branch information
scohen authored Apr 17, 2024
1 parent dd5d5ce commit 084598f
Show file tree
Hide file tree
Showing 29 changed files with 664 additions and 169 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# This file's contents are auto-generated. Do not edit.
defmodule Lexical.Protocol.Types.Location.Link do
alias Lexical.Proto
alias Lexical.Protocol.Types
use Proto

deftype origin_selection_range: optional(Types.Range),
target_range: Types.Range,
target_selection_range: Types.Range,
target_uri: string()
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This file's contents are auto-generated. Do not edit.
defmodule Lexical.Protocol.Types.Workspace.Symbol do
alias Lexical.Proto
alias Lexical.Protocol.Types

defmodule Location do
use Proto
deftype uri: string()
end

use Proto

deftype container_name: optional(string()),
data: optional(any()),
kind: Types.Symbol.Kind,
location: one_of([Types.Location, Lexical.Protocol.Types.Workspace.Symbol.Location]),
name: string(),
tags: optional(list_of(Types.Symbol.Tag))
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file's contents are auto-generated. Do not edit.
defmodule Lexical.Protocol.Types.Workspace.Symbol.Params do
alias Lexical.Proto
alias Lexical.Protocol.Types
use Proto

deftype partial_result_token: optional(Types.Progress.Token),
query: string(),
work_done_token: optional(Types.Progress.Token)
end
6 changes: 6 additions & 0 deletions apps/protocol/lib/lexical/protocol/requests.ex
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ defmodule Lexical.Protocol.Requests do
defrequest "textDocument/documentSymbol", Types.Document.Symbol.Params
end

defmodule WorkspaceSymbol do
use Proto

defrequest "workspace/symbol", Types.Workspace.Symbol.Params
end

# Server -> Client requests

defmodule RegisterCapability do
Expand Down
6 changes: 6 additions & 0 deletions apps/protocol/lib/lexical/protocol/responses.ex
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ defmodule Lexical.Protocol.Responses do
defresponse optional(list_of(Types.Document.Symbol))
end

defmodule WorkspaceSymbol do
use Proto

defresponse optional(list_of(Types.Workspace.Symbol))
end

defmodule Shutdown do
use Proto
# yeah, this is odd... it has no params
Expand Down
4 changes: 4 additions & 0 deletions apps/remote_control/lib/lexical/remote_control/api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,8 @@ defmodule Lexical.RemoteControl.Api do
def document_symbols(%Project{} = project, %Document{} = document) do
RemoteControl.call(project, CodeIntelligence.Symbols, :for_document, [document])
end

def workspace_symbols(%Project{} = project, query) do
RemoteControl.call(project, CodeIntelligence.Symbols, :for_workspace, [query])
end
end
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule Lexical.RemoteControl.CodeIntelligence.Symbols do
alias Lexical.Document
alias Lexical.RemoteControl.CodeIntelligence.Symbols
alias Lexical.RemoteControl.Search
alias Lexical.RemoteControl.Search.Indexer
alias Lexical.RemoteControl.Search.Indexer.Entry
alias Lexical.RemoteControl.Search.Indexer.Extractors
Expand Down Expand Up @@ -30,6 +31,12 @@ defmodule Lexical.RemoteControl.CodeIntelligence.Symbols do
to_symbols(document, definitions)
end

def for_workspace(query) do
query
|> Search.Store.fuzzy([])
|> Enum.map(&Symbols.Workspace.from_entry/1)
end

defp to_symbols(%Document{} = document, entries) do
entries_by_block_id = Enum.group_by(entries, & &1.block_id)
rebuild_structure(entries_by_block_id, document, :root)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule Lexical.RemoteControl.CodeIntelligence.Symbols.Workspace do
defmodule Link do
defstruct [:uri, :range, :detail_range]

@type t :: %__MODULE__{
uri: Lexical.uri(),
range: Lexical.Document.Range.t(),
detail_range: Lexical.Document.Range.t()
}

def new(uri, range, detail_range \\ nil) do
%__MODULE__{uri: uri, range: range, detail_range: detail_range}
end
end

alias Lexical.Document
alias Lexical.Formats
alias Lexical.RemoteControl.Search.Indexer.Entry

defstruct [:name, :type, :link, container_name: nil]

@type t :: %__MODULE__{
container_name: String.t() | nil,
link: Link.t(),
name: String.t(),
type: atom()
}

def from_entry(%Entry{} = entry) do
link =
entry.path
|> Document.Path.to_uri()
|> Link.new(entry.block_range, entry.range)

name = symbol_name(entry.type, entry)

%__MODULE__{
name: name,
type: entry.type,
link: link
}
end

@module_types [
:struct,
:module,
:protocol,
:protocol_implementation
]

defp symbol_name(type, entry) when type in @module_types do
Formats.module(entry.subject)
end

defp symbol_name(_, entry),
do: entry.subject
end
Loading

0 comments on commit 084598f

Please sign in to comment.