-
Notifications
You must be signed in to change notification settings - Fork 32
Conversation
src/scry/symbol.cr
Outdated
|
||
class WorkspaceSymbolProcessor | ||
def initialize(@msg_id : Int32 | String, @root_path : String, @query : String) | ||
@all_files = Dir.glob("#{root_path}/**/*.cr") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why doesn't this use the workspace, and find files based on prelude and requires?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and find files based on prelude and requires?
What do you mean? 😅 This will get all "workspace" files to analyze all "workspace" symbols
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh ok, so this symbol feature is just to search in your project (your own code), not everywhere (stdlib, other shards, etc) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, just your code 😅
Although this should work on shards as well since lib
is analyzed too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok it's fine then. I think I'd even restrict to src
, to not show implementation detail symbols from shards
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bew Well, I guess I like your idea of listing stdlib symbols, so, this feature will support listing symbols from:
- Your code
- Your shards
- Crystal stdlib
And works very fast!!! 🎉 and stdlib is memoized ✨
spec/scry/symbol_spec.cr
Outdated
it "return Symbols list with query match" do | ||
processor = WorkspaceSymbolProcessor.new(0, "#{ROOT_PATH}/src", "salut") | ||
response = processor.run | ||
result = response.result.as(Array(SymbolInformation)).try(&.first) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try
is uselesw here as you can the object to a non-nilable type juste before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! 👍
spec/scry/symbol_spec.cr
Outdated
end | ||
|
||
it "return Symbols list with query match" do | ||
processor = WorkspaceSymbolProcessor.new(0, "#{ROOT_PATH}/src", "salut") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use regex here? Or add another spec for a regex query?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A little Use File.join
commit ? 😃
src/scry/symbol.cr
Outdated
visitor = SymbolVisitor.new("file://#{file}") | ||
parser = Crystal::Parser.new(File.read(file)) | ||
parser.filename = file | ||
node = parser.parse |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what if there is a syntax error in one of the file? I think the exception needs to be rescued and the file skipped.
Is it possible to send errors in addition to valid results? (say, here are the results for files X & Y, but there is an error with file Z)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I can handle the error, but the response should still be managed by diagnostic provider
I think I can return an empty array if syntax error is found
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at https://microsoft.github.io/language-server-protocol/specification#workspace-symbols-request-leftwards_arrow_with_hook you should be able to return null and an error in case an exception happens during the workspace symbol request.
I think it could be possible to return a result & an error? but how the valid_result/error is handled would vastly depend on the language client implementation..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, Actually we're managing that via response_error.cr
, although I think for better user experience we should ignore parsing errors here since the diagnostics already do this work.
I don't want to see a popup saying "LSP server has an error" every time I try to list symbols on files with syntax errors 😅
src/scry/symbol.cr
Outdated
@workspace_files = Dir.glob(File.join(root_path, "**", "*.cr")) | ||
if @@crystal_path_files.empty? | ||
# Memoize crystal stdlib | ||
@@crystal_path_files = search_symbols(Dir.glob(File.join(Scry.default_crystal_path, "**", "*.cr")), ".*") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to crystal_path_symbols
as it stores symbols
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 👍
src/scry/symbol.cr
Outdated
symbols.concat visitor.symbols.select(&.name.match(Regex.new(@query))) | ||
end | ||
symbols.concat search_symbols(@workspace_files, @query) | ||
symbols.concat @@crystal_path_files.select(&.name.match(Regex.new(@query))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could store the Regex
object in @query
in the initialize
, instead of storing the String
and re-create/re-compile the regex on each usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
Also, why in you last screenshot some entries have non-absolute path even though it's not in the current workspace (e.g: |
@bew Interesting I didn't realize of that 👍 I think Although, If you test it, still works |
src/scry/symbol.cr
Outdated
end | ||
ResponseMessage.new(@msg_id, symbols) | ||
end | ||
|
||
def search_symbols(files, query) | ||
def search_symbols(files, query_regex) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if it's a good idea, but you could have search_symbols
as a class method (as it doesn't depend on any ivars), and use special getter for @@crystal_path_symbols
:
@@crystal_path_symbols : Array(SymbolInformation)?
def self.crystal_path_symbols
@@crystal_path_symbols ||= begin
crystal_path_files = Dir.glob(File.join(Scry.default_crystal_path, "**", "*.cr"))
search_symbols(crystal_path_files, Regex.new(".*"))
end
end
# and when you need crystal_path_symbols, you just call the getter, which will return
# `@@crystal_path_symbols` if it's not nil, and will search the symbols if it's nil.
def self.search_symbols(x, y)
end
This way you can remove the cvar init from the initiailizer and keep it clean 😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
src/scry/symbol.cr
Outdated
@query_regex = Regex.new(@query) | ||
end | ||
|
||
def run | ||
symbols = [] of SymbolInformation | ||
unless @query.empty? | ||
symbols.concat search_symbols(@workspace_files, @query_regex) | ||
symbols.concat @@crystal_path_symbols.select(&.name.match(@query_regex)) | ||
symbols.concat self.class.search_symbols(@workspace_files, @query_regex) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird I thought it was simpler to call..
Another way is WorkspaceSymbolProcessor.search_symbols
or {{@type}}.search_symbols
(as you like, self.class
is fine too)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I prefer self.class
😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no prob'!
def self.search_symbols(files, query_regex) | ||
symbols = [] of SymbolInformation | ||
files.each do |file| | ||
visitor = SymbolVisitor.new("file://#{file}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if there is anyway we can reuse the SymbolProcessor here since the code is so similar? Although it would probably need to be decoupled from the ResponseMessage, maybe we can refactor that later.
Fixes #102 🎉