Skip to content
This repository has been archived by the owner on Jun 1, 2023. It is now read-only.

Commit

Permalink
Add workspace symbols (#106)
Browse files Browse the repository at this point in the history
* Add support for workspace symbols

* Add spec for workspace symbols

* Remove try and add regex query spec for workspace symbols

* Add stdlib (memoized) to workspace symbols

* Use class variables and class methods

* Use full ROOT_PATH on spec to test invalid files
  • Loading branch information
faustinoaq authored May 6, 2018
1 parent 4243c3f commit 81a7171
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 21 deletions.
2 changes: 1 addition & 1 deletion spec/scry/response_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module Scry
io = IO::Memory.new
response = Response.new(results)
response.write(io)
io.to_s.should eq(%(Content-Length: 245\r\n\r\n{"jsonrpc":"2.0","id":32,"result":{"capabilities":{"textDocumentSync":1,"documentFormattingProvider":true,"definitionProvider":true,"documentSymbolProvider":true,"completionProvider":{"resolveProvider":true,"triggerCharacters":[".","\\\"","/"]}}}}))
io.to_s.should eq(%(Content-Length: 276\r\n\r\n{"jsonrpc":"2.0","id":32,"result":{"capabilities":{"textDocumentSync":1,"documentFormattingProvider":true,"definitionProvider":true,"documentSymbolProvider":true,"workspaceSymbolProvider":true,"completionProvider":{"resolveProvider":true,"triggerCharacters":[".","\\\"","/"]}}}}))
end
end
end
51 changes: 44 additions & 7 deletions spec/scry/symbol_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,39 +19,39 @@ module Scry
text_document = TextDocument.new("uri", ["class Test; end"])
processor = SymbolProcessor.new(text_document)
response = processor.run
result = response.result.as(Array(SymbolInformation)).try(&.first)
result = response.result.as(Array(SymbolInformation)).first
result.kind.is_a?(SymbolKind::Class).should be_true
end

it "returns Struct symbols as a Class" do
text_document = TextDocument.new("uri", ["struct Test; end"])
processor = SymbolProcessor.new(text_document)
response = processor.run
result = response.result.as(Array(SymbolInformation)).try(&.first)
result = response.result.as(Array(SymbolInformation)).first
result.kind.is_a?(SymbolKind::Class).should be_true
end

it "returns Module symbols" do
text_document = TextDocument.new("uri", ["module Test; end"])
processor = SymbolProcessor.new(text_document)
response = processor.run
result = response.result.as(Array(SymbolInformation)).try(&.first)
result = response.result.as(Array(SymbolInformation)).first
result.kind.is_a?(SymbolKind::Module).should be_true
end

it "returns Method symbols" do
text_document = TextDocument.new("uri", ["def test; end"])
processor = SymbolProcessor.new(text_document)
response = processor.run
result = response.result.as(Array(SymbolInformation)).try(&.first)
result = response.result.as(Array(SymbolInformation)).first
result.kind.is_a?(SymbolKind::Method).should be_true
end

it "returns instance vars as Variable symbols" do
text_document = TextDocument.new("uri", ["@bar = nil"])
processor = SymbolProcessor.new(text_document)
response = processor.run
result = response.result.as(Array(SymbolInformation)).try(&.first)
result = response.result.as(Array(SymbolInformation)).first
result.as(SymbolInformation).kind.is_a?(SymbolKind::Variable).should be_true
end

Expand Down Expand Up @@ -86,17 +86,54 @@ module Scry
text_document = TextDocument.new("uri", [%(HELLO = "world")])
processor = SymbolProcessor.new(text_document)
response = processor.run
result = response.result.as(Array(SymbolInformation)).try(&.first)
result = response.result.as(Array(SymbolInformation)).first
result.kind.is_a?(SymbolKind::Constant).should be_true
end

it "returns alias as Constant symbols" do
text_document = TextDocument.new("uri", [%(alias Hello = World)])
processor = SymbolProcessor.new(text_document)
response = processor.run
result = response.result.as(Array(SymbolInformation)).try(&.first)
result = response.result.as(Array(SymbolInformation)).first
result.kind.is_a?(SymbolKind::Constant).should be_true
end
end

describe "WorkspaceSymbols" do
it "return empty Symbols list if no query" do
processor = WorkspaceSymbolProcessor.new(0, ROOT_PATH, "")
response = processor.run
result = response.result.as(Array(SymbolInformation))
result.empty?.should be_true
end

it "return Symbols list with query match" do
processor = WorkspaceSymbolProcessor.new(0, ROOT_PATH, "salut")
response = processor.run
result = response.result.as(Array(SymbolInformation)).first
result.kind.is_a?(SymbolKind::Method).should be_true
end

it "return Symbols list with regex query match" do
processor = WorkspaceSymbolProcessor.new(0, ROOT_PATH, "sal*")
response = processor.run
result = response.result.as(Array(SymbolInformation)).first
result.kind.is_a?(SymbolKind::Method).should be_true
end

it "return stdlib Symbol with regex query match for File" do
processor = WorkspaceSymbolProcessor.new(0, ROOT_PATH, "Fil*")
response = processor.run
result = response.result.as(Array(SymbolInformation)).first
result.kind.is_a?(SymbolKind::Class).should be_true
end

it "return stdlib symbol with regex query match for initialize" do
processor = WorkspaceSymbolProcessor.new(0, ROOT_PATH, "initializ*")
response = processor.run
result = response.result.as(Array(SymbolInformation)).first
result.kind.is_a?(SymbolKind::Method).should be_true
end
end
end
end
12 changes: 12 additions & 0 deletions src/scry/context.cr
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ module Scry
end
end

private def dispatch_request(params : WorkspaceSymbolParams, msg)
case msg.method
when "workspace/symbol"
query = params.query
root_path = TextDocument.uri_to_filename(@workspace.root_uri)
workspace_symbol_processor = WorkspaceSymbolProcessor.new(msg.id, root_path, query)
response = workspace_symbol_processor.run
Log.logger.debug(response)
response
end
end

private def dispatch_request(params : CompletionItem, msg)
case msg.method
when "completionItem/resolve"
Expand Down
2 changes: 2 additions & 0 deletions src/scry/protocol/request_message.cr
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
require "./initialize_params"
require "./text_document_position_params"
require "./workspace_symbol_params"

module Scry
struct RequestMessage
alias RequestType = (TextDocumentPositionParams |
InitializeParams |
DocumentFormattingParams |
TextDocumentParams |
WorkspaceSymbolParams |
CompletionItem)?

JSON.mapping({
Expand Down
22 changes: 9 additions & 13 deletions src/scry/protocol/server_capabilities.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,27 @@ module Scry
end
end

# Specify Sever capabilities supported,
# Currently Crystal supports:
# Specify supported sever capabilities:
#
# - Go to definition or Implementation (WIP)
# - Go to Implementation (WIP)
# - Diagnostics (WIP)
# - Formatting (WIP)
# - Symbols https://github.com/crystal-lang/crystal/blob/1f3e8b0e742b55c1feb5584dc932e87034365f48/samples/compiler/visitor_example.cr
# - Rename https://github.com/crystal-lang/crystal/blob/1f3e8b0e742b55c1feb5584dc932e87034365f48/samples/compiler/transformer_example.cr
# - Completion https://github.com/TechMagister/cracker
# - Hover
# - Code Actions
# - Signature Help
# - Range Formatting
# - Symbols (WIP)
# - Completion (WIP)
# - Hover (WIP)
# - Code Actions (WIP)
#
# Features not supported by Crystal yet:
#
# - CodeLens
# - Rename
# - Find References
#
# To enable more capabilities, See: https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md
struct ServerCapabilities
JSON.mapping(
textDocumentSync: TextDocumentSyncKind,
documentFormattingProvider: Bool,
definitionProvider: Bool,
documentSymbolProvider: Bool,
workspaceSymbolProvider: Bool,
completionProvider: CompletionOptions
)

Expand All @@ -53,6 +48,7 @@ module Scry
@documentFormattingProvider = true
@definitionProvider = true
@documentSymbolProvider = true
@workspaceSymbolProvider = true
@completionProvider = CompletionOptions.new
end
end
Expand Down
9 changes: 9 additions & 0 deletions src/scry/protocol/workspace_symbol_params.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require "json"

module Scry
struct WorkspaceSymbolParams
JSON.mapping({
query: String,
}, true)
end
end
44 changes: 44 additions & 0 deletions src/scry/symbol.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "compiler/crystal/syntax"
require "./protocol/workspace_symbol_params"

module Scry
class SymbolVisitor < Crystal::Visitor
Expand Down Expand Up @@ -113,6 +114,49 @@ module Scry
node.accept(visitor)

ResponseMessage.new(@text_document.id, visitor.symbols)
rescue
ResponseMessage.new(@text_document.id, [] of SymbolInformation)
end
end

class WorkspaceSymbolProcessor
@@crystal_path_symbols : Array(SymbolInformation)?
@query_regex : Regex

def initialize(@msg_id : Int32 | String, @root_path : String, @query : String)
@workspace_files = Dir.glob(File.join(root_path, "**", "*.cr"))
@query_regex = Regex.new(@query)
end

def run
symbols = [] of SymbolInformation
unless @query.empty?
symbols.concat self.class.search_symbols(@workspace_files, @query_regex)
symbols.concat self.class.crystal_path_symbols.select(&.name.match(@query_regex))
end
ResponseMessage.new(@msg_id, symbols)
end

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

def self.search_symbols(files, query_regex)
symbols = [] of SymbolInformation
files.each do |file|
visitor = SymbolVisitor.new("file://#{file}")
parser = Crystal::Parser.new(File.read(file))
parser.filename = file
node = parser.parse
node.accept(visitor)
symbols.concat visitor.symbols.select(&.name.match(query_regex))
rescue
next
end
symbols
end
end
end

0 comments on commit 81a7171

Please sign in to comment.