Skip to content

Add workspace/symbols support #123

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

Merged
merged 3 commits into from
Aug 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ SourceKit-LSP is still in early development, so you may run into rough edges wit
| Jump to Definition | ✅ | |
| Find References | ✅ | |
| Background Indexing | ❌ | Build project to update the index using [Indexing While Building](#indexing-while-building) |
| Workspace Symbols | | |
| Workspace Symbols | | |
| Refactoring | ❌ | |
| Formatting | ❌ | |
| Folding | ✅ | |
Expand Down
1 change: 1 addition & 0 deletions Sources/LanguageServerProtocol/Messages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public let builtinRequests: [_RequestType.Type] = [
WorkspaceFoldersRequest.self,
CompletionRequest.self,
HoverRequest.self,
WorkspaceSymbolsRequest.self,
DefinitionRequest.self,
ReferencesRequest.self,
DocumentHighlightRequest.self,
Expand Down
8 changes: 7 additions & 1 deletion Sources/LanguageServerProtocol/ServerCapabilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public struct ServerCapabilities: Codable, Hashable {
/// Whether the server provides "textDocument/codeAction".
public var codeActionProvider: CodeActionServerCapabilities?

/// The server provides workspace symbol support.
public var workspaceSymbolProvider: Bool?

// TODO: fill-in the rest.

public init(
Expand All @@ -66,7 +69,8 @@ public struct ServerCapabilities: Codable, Hashable {
foldingRangeProvider: Bool? = nil,
documentSymbolProvider: Bool? = nil,
colorProvider: Bool? = nil,
codeActionProvider: CodeActionServerCapabilities? = nil
codeActionProvider: CodeActionServerCapabilities? = nil,
workspaceSymbolProvider: Bool? = nil
)
{
self.textDocumentSync = textDocumentSync
Expand All @@ -82,6 +86,7 @@ public struct ServerCapabilities: Codable, Hashable {
self.documentSymbolProvider = documentSymbolProvider
self.colorProvider = colorProvider
self.codeActionProvider = codeActionProvider
self.workspaceSymbolProvider = workspaceSymbolProvider
}

public init(from decoder: Decoder) throws {
Expand All @@ -93,6 +98,7 @@ public struct ServerCapabilities: Codable, Hashable {
self.documentSymbolProvider = try container.decodeIfPresent(Bool.self, forKey: .documentSymbolProvider)
self.colorProvider = try container.decodeIfPresent(Bool.self, forKey: .colorProvider)
self.codeActionProvider = try container.decodeIfPresent(CodeActionServerCapabilities.self, forKey: .codeActionProvider)
self.workspaceSymbolProvider = try container.decodeIfPresent(Bool.self, forKey: .workspaceSymbolProvider)

if let textDocumentSync = try? container.decode(TextDocumentSyncOptions.self, forKey: .textDocumentSync) {
self.textDocumentSync = textDocumentSync
Expand Down
55 changes: 55 additions & 0 deletions Sources/LanguageServerProtocol/WorkspaceSymbols.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

/// Request for all symbols that match a certain query string.
///
/// This request looks up the canonical occurence of each symbol which has a name that contains the query string.
/// The list of symbol information is returned
///
/// Servers that provide workspace symbol queries should set the `workspaceSymbolProvider` server capability.
///
/// - Parameters:
/// - query: The string that should be looked for in symbols of the workspace.
///
/// - Returns: Information about each symbol with a name that contains the query string
public struct WorkspaceSymbolsRequest: RequestType, Hashable {

public static let method: String = "workspace/symbol"
public typealias Response = [SymbolInformation]?

/// The document in which to lookup the symbol location.
public var query: String

public init(query: String) {
self.query = query
}
}

public struct SymbolInformation: Hashable, ResponseType {
public var name: String

public var kind: SymbolKind

public var deprecated: Bool?

public var location: Location

public var containerName: String?

public init(name: String, kind: SymbolKind, deprecated: Bool?, location: Location, containerName: String?) {
self.name = name
self.kind = kind
self.deprecated = deprecated
self.location = location
self.containerName = containerName
}
}
86 changes: 85 additions & 1 deletion Sources/SourceKit/SourceKitServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public final class SourceKitServer: LanguageServer {
registerWorkspaceNotfication(SourceKitServer.didSaveDocument)
registerWorkspaceRequest(SourceKitServer.completion)
registerWorkspaceRequest(SourceKitServer.hover)
registerWorkspaceRequest(SourceKitServer.workspaceSymbols)
registerWorkspaceRequest(SourceKitServer.definition)
registerWorkspaceRequest(SourceKitServer.references)
registerWorkspaceRequest(SourceKitServer.documentSymbolHighlight)
Expand Down Expand Up @@ -269,7 +270,8 @@ extension SourceKitServer {
clientCapabilities: req.params.capabilities.textDocument?.codeAction,
codeActionOptions: CodeActionOptions(codeActionKinds: nil),
supportsCodeActions: false // TODO: Turn it on after a provider is implemented.
)
),
workspaceSymbolProvider: true
)))
}

Expand Down Expand Up @@ -338,6 +340,51 @@ extension SourceKitServer {
toolchainTextDocumentRequest(req, workspace: workspace, fallback: nil)
}

/// Find all symbols in the workspace that include a string in their name.
/// - returns: An array of SymbolOccurrences that match the string.
func findWorkspaceSymbols(matching: String) -> [SymbolOccurrence] {
var symbolOccurenceResults: [SymbolOccurrence] = []
workspace?.index?.forEachCanonicalSymbolOccurrence(
containing: matching,
anchorStart: false,
anchorEnd: false,
subsequence: true,
ignoreCase: true
) {symbol in
if !symbol.location.isSystem && !symbol.roles.contains(.accessorOf) {
symbolOccurenceResults.append(symbol)
}
return true
}
return symbolOccurenceResults
}

/// Handle a workspace/symbols request, returning the SymbolInformation.
/// - returns: An array with SymbolInformation for each matching symbol in the workspace.
func workspaceSymbols(_ req: Request<WorkspaceSymbolsRequest>, workspace: Workspace) {
let symbols = findWorkspaceSymbols(
matching: req.params.query
).map({symbolOccurrence -> SymbolInformation in
let symbolPosition = Position(
line: symbolOccurrence.location.line - 1, // 1-based -> 0-based
// FIXME: we need to convert the utf8/utf16 column, which may require reading the file!
utf16index: symbolOccurrence.location.utf8Column - 1)

let symbolLocation = Location(
url: URL(fileURLWithPath: symbolOccurrence.location.path),
range: Range(symbolPosition))

return SymbolInformation(
name: symbolOccurrence.symbol.name,
kind: symbolOccurrence.symbol.kind.asLspSymbolKind(),
deprecated: nil,
location: symbolLocation,
containerName: symbolOccurrence.getContainerName()
)
})
req.reply(symbols)
}

/// Forwards a SymbolInfoRequest to the appropriate toolchain service for this document.
func symbolInfo(_ req: Request<SymbolInfoRequest>, workspace: Workspace) {
toolchainTextDocumentRequest(req, workspace: workspace, fallback: [])
Expand Down Expand Up @@ -523,3 +570,40 @@ public func languageService(

public typealias Notification = LanguageServerProtocol.Notification
public typealias Diagnostic = LanguageServerProtocol.Diagnostic

extension IndexSymbolKind {
func asLspSymbolKind() -> SymbolKind {
switch self {
case .class:
return .class
case .classMethod, .instanceMethod, .staticMethod:
return .method
case .instanceProperty, .staticProperty, .classProperty:
return .property
case .enum:
return .enum
case .enumConstant:
return .enumMember
case .protocol:
return .interface
case .function, .conversionFunction:
return .function
case .variable:
return .variable
case .struct:
return .struct
case .parameter:
return .typeParameter

default:
return .null
}
}
}

extension SymbolOccurrence {
/// Get the name of the symbol that is a parent of this symbol, if one exists
func getContainerName() -> String? {
return relations.first(where: { $0.roles.contains(.childOf) })?.symbol.name
}
}
3 changes: 2 additions & 1 deletion Tests/LanguageServerProtocolJSONRPCTests/CodingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ final class CodingTests: XCTestCase {
referencesProvider: nil,
documentHighlightProvider: nil,
foldingRangeProvider: nil,
codeActionProvider: nil)), id: .number(2), json: """
codeActionProvider: nil,
workspaceSymbolProvider: nil)), id: .number(2), json: """
{
"id" : 2,
"jsonrpc" : "2.0",
Expand Down