From 93a8f91436f83c7246c51ac4557d1686abbbb7bc Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Tue, 29 Nov 2022 12:12:43 +0100 Subject: [PATCH 1/2] Update request and notification definitions to LSP 3.17 --- Sources/LanguageServerProtocol/Error.swift | 61 ++- Sources/LanguageServerProtocol/Messages.swift | 98 ++-- .../CancelWorkDoneProgressNotification.swift | 21 + .../DidChangeFileNotifications.swift | 45 ++ .../Notifications/LogTraceNotification.swift | 30 ++ .../Notifications/SetTraceNotification.swift | 23 + .../TextSynchronizationNotifications.swift | 91 +++- .../Notifications/WorkDoneProgress.swift | 199 ++++++++ .../Requests/CodeActionRequest.swift | 32 +- .../Requests/CodeActionResolveRequest.swift | 30 ++ .../Requests/CodeLensRefreshRequest.swift | 19 + .../Requests/CodeLensRequest.swift | 50 ++ .../Requests/CodeLensResolveRequest.swift | 31 ++ .../CompletionItemResolveRequest.swift | 30 ++ .../Requests/CompletionRequest.swift | 79 ++- .../CreateWorkDoneProgressRequest.swift | 23 + .../Requests/DiagnosticsRefreshRequest.swift | 18 + .../Requests/DocumentDiagnosticsRequest.swift | 171 +++++++ .../Requests/DocumentLinkRequest.swift | 54 +++ .../Requests/DocumentLinkResolveRequest.swift | 31 ++ .../Requests/DocumentSymbolRequest.swift | 5 + .../Requests/FoldingRangeRequest.swift | 10 +- .../Requests/FormattingRequests.swift | 9 + .../Requests/InitializeRequest.swift | 35 +- .../Requests/InlayHintRefreshRequest.swift | 18 + .../Requests/InlayHintRequest.swift | 11 +- .../Requests/InlayHintResolveRequest.swift | 30 ++ .../Requests/InlineValueRefreshRequest.swift | 18 + .../Requests/InlineValueRequest.swift | 148 ++++++ .../Requests/LinkedEditingRangeRequest.swift | 45 ++ .../Requests/MonikersRequest.swift | 95 ++++ .../Requests/SelectionRangeRequest.swift | 76 +++ .../Requests/SignatureHelpRequest.swift | 199 ++++++++ .../Requests/WillChangeFilesRequests.swift | 83 ++++ ...WillSaveWaitUntilTextDocumentRequest.swift | 27 ++ .../WorkspaceDiagnosticsRequest.swift | 178 +++++++ .../WorkspaceSymbolResolveRequest.swift | 30 ++ .../Requests/WorkspaceSymbolsRequest.swift | 102 +++- .../SupportTypes/CodeActionKind.swift | 7 + .../SupportTypes/CompletionItem.swift | 206 ++++++-- .../SupportTypes/Diagnostic.swift | 21 +- .../SupportTypes/InlayHint.swift | 18 +- .../SupportTypes/InsertReplaceEdit.swift | 31 ++ .../SupportTypes/LSPAny.swift | 3 + .../NotebookCellTextDocumentFilter.swift | 75 +++ .../SupportTypes/NotebookDocument.swift | 97 ++++ .../NotebookDocumentChangeEvent.swift | 90 ++++ .../NotebookDocumentIdentifier.swift | 22 + .../SupportTypes/PositionEncoding.swift | 36 ++ .../SupportTypes/ProgressToken.swift | 36 ++ .../SupportTypes/StringOrMarkupContent.swift | 36 ++ .../SupportTypes/TextDocumentEdit.swift | 30 +- .../SupportTypes/TextEdit.swift | 47 ++ .../SupportTypes/Tracing.swift | 17 + .../VersionedNotebookDocumentIdentifier.swift | 26 + .../VersionedTextDocumentIdentifier.swift | 30 +- .../SupportTypes/WorkspaceEdit.swift | 36 +- .../Clang/ClangLanguageServer.swift | 2 +- Sources/SourceKitLSP/SourceKitServer.swift | 6 +- .../SourceKitLSP/Swift/CodeCompletion.swift | 4 +- .../Swift/SwiftLanguageServer.swift | 7 +- .../Swift/SyntaxHighlightingToken.swift | 2 + .../CodingTests.swift | 458 +++++++++++++++++- Tests/SourceKitLSPTests/InlayHintTests.swift | 2 +- Tests/SourceKitLSPTests/SourceKitTests.swift | 10 +- .../SwiftCompletionTests.swift | 18 +- .../SwiftPMIntegration.swift | 30 +- Tests/SourceKitLSPTests/WorkspaceTests.swift | 38 +- 68 files changed, 3503 insertions(+), 193 deletions(-) create mode 100644 Sources/LanguageServerProtocol/Notifications/CancelWorkDoneProgressNotification.swift create mode 100644 Sources/LanguageServerProtocol/Notifications/DidChangeFileNotifications.swift create mode 100644 Sources/LanguageServerProtocol/Notifications/LogTraceNotification.swift create mode 100644 Sources/LanguageServerProtocol/Notifications/SetTraceNotification.swift create mode 100644 Sources/LanguageServerProtocol/Notifications/WorkDoneProgress.swift create mode 100644 Sources/LanguageServerProtocol/Requests/CodeActionResolveRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/CodeLensRefreshRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/CodeLensRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/CodeLensResolveRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/CompletionItemResolveRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/CreateWorkDoneProgressRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/DiagnosticsRefreshRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/DocumentDiagnosticsRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/DocumentLinkRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/DocumentLinkResolveRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/InlayHintRefreshRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/InlayHintResolveRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/InlineValueRefreshRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/InlineValueRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/LinkedEditingRangeRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/MonikersRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/SelectionRangeRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/SignatureHelpRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/WillChangeFilesRequests.swift create mode 100644 Sources/LanguageServerProtocol/Requests/WillSaveWaitUntilTextDocumentRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/WorkspaceDiagnosticsRequest.swift create mode 100644 Sources/LanguageServerProtocol/Requests/WorkspaceSymbolResolveRequest.swift create mode 100644 Sources/LanguageServerProtocol/SupportTypes/InsertReplaceEdit.swift create mode 100644 Sources/LanguageServerProtocol/SupportTypes/NotebookCellTextDocumentFilter.swift create mode 100644 Sources/LanguageServerProtocol/SupportTypes/NotebookDocument.swift create mode 100644 Sources/LanguageServerProtocol/SupportTypes/NotebookDocumentChangeEvent.swift create mode 100644 Sources/LanguageServerProtocol/SupportTypes/NotebookDocumentIdentifier.swift create mode 100644 Sources/LanguageServerProtocol/SupportTypes/PositionEncoding.swift create mode 100644 Sources/LanguageServerProtocol/SupportTypes/ProgressToken.swift create mode 100644 Sources/LanguageServerProtocol/SupportTypes/StringOrMarkupContent.swift create mode 100644 Sources/LanguageServerProtocol/SupportTypes/Tracing.swift create mode 100644 Sources/LanguageServerProtocol/SupportTypes/VersionedNotebookDocumentIdentifier.swift diff --git a/Sources/LanguageServerProtocol/Error.swift b/Sources/LanguageServerProtocol/Error.swift index 519c44c9f..4a0128d6d 100644 --- a/Sources/LanguageServerProtocol/Error.swift +++ b/Sources/LanguageServerProtocol/Error.swift @@ -22,19 +22,68 @@ public struct ErrorCode: RawRepresentable, Codable, Hashable { self.rawValue = rawValue } - // JSON RPC + // MARK: JSON RPC public static let parseError: ErrorCode = ErrorCode(rawValue: -32700) public static let invalidRequest: ErrorCode = ErrorCode(rawValue: -32600) public static let methodNotFound: ErrorCode = ErrorCode(rawValue: -32601) public static let invalidParams: ErrorCode = ErrorCode(rawValue: -32602) public static let internalError: ErrorCode = ErrorCode(rawValue: -32603) - public static let serverErrorStart: ErrorCode = ErrorCode(rawValue: -32099) - public static let serverErrorEnd: ErrorCode = ErrorCode(rawValue: -32000) - public static let workspaceNotOpen: ErrorCode = ErrorCode(rawValue: -32003) - public static let unknownErrorCode: ErrorCode = ErrorCode(rawValue: -32001) - // LSP + + /// This is the start range of JSON-RPC reserved error codes. + /// It doesn't denote a real error code. No LSP error codes should + /// be defined between the start and end range. For backwards + /// compatibility the `ServerNotInitialized` and the `UnknownErrorCode` + /// are left in the range. + public static let jsonrpcReservedErrorRangeStart = ErrorCode(rawValue: -32099) + public static let serverErrorStart: ErrorCode = jsonrpcReservedErrorRangeStart + + /// Error code indicating that a server received a notification or + /// request before the server has received the `initialize` request. + public static let serverNotInitialized = ErrorCode(rawValue: -32002) + public static let unknownErrorCode = ErrorCode(rawValue: -32001) + + /// This is the end range of JSON-RPC reserved error codes. + /// It doesn't denote a real error code. + public static let jsonrpcReservedErrorRangeEnd = ErrorCode(rawValue: -32000) + /// Deprecated, use jsonrpcReservedErrorRangeEnd + public static let serverErrorEnd = jsonrpcReservedErrorRangeEnd + + /// This is the start range of LSP reserved error codes. + /// It doesn't denote a real error code. + public static let lspReservedErrorRangeStart = ErrorCode(rawValue: -32899) + + /// A request failed but it was syntactically correct, e.g the + /// method name was known and the parameters were valid. The error + /// message should contain human readable information about why + /// the request failed. + public static let requestFailed = ErrorCode(rawValue: -32803) + + /// The server cancelled the request. This error code should + /// only be used for requests that explicitly support being + /// server cancellable. + public static let serverCancelled = ErrorCode(rawValue: -32802) + + /// The server detected that the content of a document got + /// modified outside normal conditions. A server should + /// NOT send this error code if it detects a content change + /// in it unprocessed messages. The result even computed + /// on an older state might still be useful for the client. + /// + /// If a client decides that a result is not of any use anymore + /// the client should cancel the request. + public static let contentModified = ErrorCode(rawValue: -32801) + + /// The client has canceled a request and a server as detected + /// the cancel. public static let cancelled: ErrorCode = ErrorCode(rawValue: -32800) + + /// This is the end range of LSP reserved error codes. + /// It doesn't denote a real error code. + public static let lspReservedErrorRangeEnd = ErrorCode(rawValue: -32800) + + // MARK: SourceKit-LSP specifiic eror codes + public static let workspaceNotOpen: ErrorCode = ErrorCode(rawValue: -32003) } /// An error response represented by a code and message. diff --git a/Sources/LanguageServerProtocol/Messages.swift b/Sources/LanguageServerProtocol/Messages.swift index 54883e04f..583784f4a 100644 --- a/Sources/LanguageServerProtocol/Messages.swift +++ b/Sources/LanguageServerProtocol/Messages.swift @@ -16,48 +16,70 @@ /// If you are adding a message for testing only, you can register it dynamically using /// `MessageRegistry._register()` which allows you to avoid bloating the real server implementation. public let builtinRequests: [_RequestType.Type] = [ - InitializeRequest.self, - ShutdownRequest.self, - WorkspaceFoldersRequest.self, - CompletionRequest.self, - HoverRequest.self, - WorkspaceSemanticTokensRefreshRequest.self, - WorkspaceSymbolsRequest.self, + ApplyEditRequest.self, CallHierarchyIncomingCallsRequest.self, CallHierarchyOutgoingCallsRequest.self, CallHierarchyPrepareRequest.self, - TypeHierarchyPrepareRequest.self, - TypeHierarchySupertypesRequest.self, - TypeHierarchySubtypesRequest.self, + CodeActionRequest.self, + CodeActionResolveRequest.self, + CodeLensRefreshRequest.self, + CodeLensRequest.self, + CodeLensResolveRequest.self, + ColorPresentationRequest.self, + CompletionItemResolveRequest.self, + CompletionRequest.self, + CreateWorkDoneProgressRequest.self, DeclarationRequest.self, DefinitionRequest.self, - ImplementationRequest.self, - ReferencesRequest.self, - DocumentHighlightRequest.self, + DiagnosticsRefreshRequest.self, + DocumentColorRequest.self, + DocumentDiagnosticsRequest.self, DocumentFormattingRequest.self, + DocumentHighlightRequest.self, + DocumentLinkRequest.self, + DocumentLinkResolveRequest.self, + DocumentOnTypeFormattingRequest.self, DocumentRangeFormattingRequest.self, DocumentSemanticTokensDeltaRequest.self, DocumentSemanticTokensRangeRequest.self, DocumentSemanticTokensRequest.self, - DocumentOnTypeFormattingRequest.self, - FoldingRangeRequest.self, DocumentSymbolRequest.self, - DocumentColorRequest.self, - ColorPresentationRequest.self, - CodeActionRequest.self, ExecuteCommandRequest.self, - ApplyEditRequest.self, + FoldingRangeRequest.self, + HoverRequest.self, + ImplementationRequest.self, + InitializeRequest.self, + InlayHintRefreshRequest.self, + InlayHintRequest.self, + InlayHintResolveRequest.self, + InlineValueRefreshRequest.self, + InlineValueRequest.self, + LinkedEditingRangeRequest.self, + MonikersRequest.self, + PollIndexRequest.self, PrepareRenameRequest.self, - RenameRequest.self, + ReferencesRequest.self, RegisterCapabilityRequest.self, + RenameRequest.self, + SelectionRangeRequest.self, + ShowMessageRequest.self, + ShutdownRequest.self, + SignatureHelpRequest.self, + SymbolInfoRequest.self, TypeDefinitionRequest.self, + TypeHierarchyPrepareRequest.self, + TypeHierarchySubtypesRequest.self, + TypeHierarchySupertypesRequest.self, UnregisterCapabilityRequest.self, - InlayHintRequest.self, - - // MARK: LSP Extension Requests - - SymbolInfoRequest.self, - PollIndexRequest.self, + WillCreateFilesRequest.self, + WillDeleteFilesRequest.self, + WillRenameFilesRequest.self, + WillSaveWaitUntilTextDocumentRequest.self, + WorkspaceDiagnosticsRequest.self, + WorkspaceFoldersRequest.self, + WorkspaceSemanticTokensRefreshRequest.self, + WorkspaceSymbolResolveRequest.self, + WorkspaceSymbolsRequest.self, ] /// The set of known notifications. @@ -66,19 +88,29 @@ public let builtinRequests: [_RequestType.Type] = [ /// here. If you are adding a message for testing only, you can register it dynamically using /// `MessageRegistry._register()` which allows you to avoid bloating the real server implementation. public let builtinNotifications: [NotificationType.Type] = [ - InitializedNotification.self, - ExitNotification.self, CancelRequestNotification.self, - LogMessageNotification.self, + CancelWorkDoneProgressNotification.self, DidChangeConfigurationNotification.self, + DidChangeNotebookDocumentNotification.self, + DidChangeTextDocumentNotification.self, DidChangeWatchedFilesNotification.self, DidChangeWorkspaceFoldersNotification.self, - DidOpenTextDocumentNotification.self, + DidCloseNotebookDocumentNotification.self, DidCloseTextDocumentNotification.self, - DidChangeTextDocumentNotification.self, - DidSaveTextDocumentNotification.self, - WillSaveTextDocumentNotification.self, + DidCreateFilesNotification.self, + DidDeleteFilesNotification.self, + DidOpenNotebookDocumentNotification.self, + DidOpenTextDocumentNotification.self, + DidRenameFilesNotification.self, + DidSaveNotebookDocumentNotification.self, + ExitNotification.self, + InitializedNotification.self, + LogMessageNotification.self, + LogTraceNotification.self, PublishDiagnosticsNotification.self, + SetTraceNotification.self, + ShowMessageNotification.self, + WorkDoneProgress.self, ] // MARK: Miscellaneous Message Types diff --git a/Sources/LanguageServerProtocol/Notifications/CancelWorkDoneProgressNotification.swift b/Sources/LanguageServerProtocol/Notifications/CancelWorkDoneProgressNotification.swift new file mode 100644 index 000000000..13b2586bf --- /dev/null +++ b/Sources/LanguageServerProtocol/Notifications/CancelWorkDoneProgressNotification.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct CancelWorkDoneProgressNotification: NotificationType { + public static var method: String = "window/workDoneProgress/cancel" + + public var token: ProgressToken + + public init(token: ProgressToken) { + self.token = token + } +} diff --git a/Sources/LanguageServerProtocol/Notifications/DidChangeFileNotifications.swift b/Sources/LanguageServerProtocol/Notifications/DidChangeFileNotifications.swift new file mode 100644 index 000000000..8e1e27da3 --- /dev/null +++ b/Sources/LanguageServerProtocol/Notifications/DidChangeFileNotifications.swift @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct DidCreateFilesNotification: NotificationType { + public static var method: String = "workspace/didCreateFiles" + + /// An array of all files/folders created in this operation. + public var files: [FileCreate] + + public init(files: [FileCreate]) { + self.files = files + } +} + +public struct DidRenameFilesNotification: NotificationType { + public static var method: String = "workspace/didRenameFiles" + + /// An array of all files/folders renamed in this operation. When a folder + /// is renamed, only the folder will be included, and not its children. + public var files: [FileRename] + + public init(files: [FileRename]) { + self.files = files + } +} + +public struct DidDeleteFilesNotification: NotificationType { + public static var method: String = "workspace/didDeleteFiles" + + /// An array of all files/folders created in this operation. + public var files: [FileDelete] + + public init(files: [FileDelete]) { + self.files = files + } +} diff --git a/Sources/LanguageServerProtocol/Notifications/LogTraceNotification.swift b/Sources/LanguageServerProtocol/Notifications/LogTraceNotification.swift new file mode 100644 index 000000000..2027703f5 --- /dev/null +++ b/Sources/LanguageServerProtocol/Notifications/LogTraceNotification.swift @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +/// A notification to log the trace of the server’s execution. The amount and content of these notifications depends on the current trace configuration. If trace is 'off', the server should not send any logTrace notification. If trace is 'messages', the server should not add the 'verbose' field in the LogTraceParams. +/// +/// $/logTrace should be used for systematic trace reporting. For single debugging messages, the server should send window/logMessage notifications. +public struct LogTraceNotification: NotificationType, Hashable, Codable { + public static let method: String = "$/logTrace" + + /// The message to be logged. + public var message: String + + /// Additional information that can be computed if the `trace` configuration + /// is set to `'verbose'` + public var verbose: String? + + public init(message: String, verbose: String?) { + self.message = message + self.verbose = verbose + } +} diff --git a/Sources/LanguageServerProtocol/Notifications/SetTraceNotification.swift b/Sources/LanguageServerProtocol/Notifications/SetTraceNotification.swift new file mode 100644 index 000000000..0f49bddd3 --- /dev/null +++ b/Sources/LanguageServerProtocol/Notifications/SetTraceNotification.swift @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +/// A notification that should be used by the client to modify the trace setting of the server. +public struct SetTraceNotification: NotificationType, Hashable, Codable { + public static let method: String = "$/setTrace" + + /// The new value that should be assigned to the trace setting. + public var value: Tracing + + public init(value: Tracing) { + self.value = value + } +} diff --git a/Sources/LanguageServerProtocol/Notifications/TextSynchronizationNotifications.swift b/Sources/LanguageServerProtocol/Notifications/TextSynchronizationNotifications.swift index e799319b1..4a0b12448 100644 --- a/Sources/LanguageServerProtocol/Notifications/TextSynchronizationNotifications.swift +++ b/Sources/LanguageServerProtocol/Notifications/TextSynchronizationNotifications.swift @@ -68,10 +68,24 @@ public struct DidCloseTextDocumentNotification: NotificationType, Hashable { public struct DidChangeTextDocumentNotification: NotificationType, Hashable { public static let method: String = "textDocument/didChange" - /// The document to change and its current version identifier. + /// The document that did change. The version number points + /// to the version after all provided content changes have + /// been applied. public var textDocument: VersionedTextDocumentIdentifier - /// Edits to the document. + /// The actual content changes. The content changes describe single state + /// changes to the document. So if there are two content changes c1 (at + /// array index 0) and c2 (at array index 1) for a document in state S then + /// c1 moves the document from S to S' and c2 from S' to S''. So c1 is + /// computed on the state S and c2 is computed on the state S'. + /// + /// To mirror the content of a document using change events use the following + /// approach: + /// - start with the same initial content + /// - apply the 'textDocument/didChange' notifications in the order you + /// receive them. + /// - apply the `TextDocumentContentChangeEvent`s in a single notification + /// in the order you receive them. public var contentChanges: [TextDocumentContentChangeEvent] /// Force the LSP to rebuild its AST for the given file. This is useful for clangd to workaround clangd's assumption that @@ -125,3 +139,76 @@ public struct DidSaveTextDocumentNotification: TextDocumentNotification, Hashabl /// Only provided if the server specified `includeText == true`. public var text: String? } + +/// The open notification is sent from the client to the server when a notebook document is opened. It is only sent by a client if the server requested the synchronization mode `notebook` in its `notebookDocumentSync` capability. +public struct DidOpenNotebookDocumentNotification: NotificationType, Hashable { + public static let method: String = "notebookDocument/didOpen" + + /// The notebook document that got opened. + public var notebookDocument: NotebookDocument + + /// The text documents that represent the content + /// of a notebook cell. + public var cellTextDocuments: [TextDocumentItem] + + public init(notebookDocument: NotebookDocument, cellTextDocuments: [TextDocumentItem]) { + self.notebookDocument = notebookDocument + self.cellTextDocuments = cellTextDocuments + } +} + +/// The change notification is sent from the client to the server when a notebook document changes. It is only sent by a client if the server requested the synchronization mode `notebook` in its `notebookDocumentSync` capability. +public struct DidChangeNotebookDocumentNotification: NotificationType, Hashable { + public static var method: String = "notebookDocument/didChange" + + /// The notebook document that did change. The version number points + /// to the version after all provided changes have been applied. + public var notebookDocument: VersionedNotebookDocumentIdentifier + + /// The actual changes to the notebook document. + /// + /// The change describes single state change to the notebook document. + /// So it moves a notebook document, its cells and its cell text document + /// contents from state S to S'. + /// + /// To mirror the content of a notebook using change events use the + /// following approach: + /// - start with the same initial content + /// - apply the 'notebookDocument/didChange' notifications in the order + /// you receive them. + public var change: NotebookDocumentChangeEvent + + public init(notebookDocument: VersionedNotebookDocumentIdentifier, change: NotebookDocumentChangeEvent) { + self.notebookDocument = notebookDocument + self.change = change + } +} + +/// The save notification is sent from the client to the server when a notebook document is saved. It is only sent by a client if the server requested the synchronization mode `notebook` in its `notebookDocumentSync` capability. +public struct DidSaveNotebookDocumentNotification: NotificationType { + public static var method: String = "notebookDocument/didSave" + + /// The notebook document that got saved. + public var notebookDocument: NotebookDocumentIdentifier + + public init(notebookDocument: NotebookDocumentIdentifier) { + self.notebookDocument = notebookDocument + } +} + +/// The close notification is sent from the client to the server when a notebook document is closed. It is only sent by a client if the server requested the synchronization mode `notebook` in its `notebookDocumentSync` capability. +public struct DidCloseNotebookDocumentNotification: NotificationType { + public static var method: String = "notebookDocument/didClose" + + /// The notebook document that got closed. + public var notebookDocument: NotebookDocumentIdentifier + + /// The text documents that represent the content + /// of a notebook cell that got closed. + public var cellTextDocuments: [TextDocumentIdentifier] + + public init(notebookDocument: NotebookDocumentIdentifier, cellTextDocuments: [TextDocumentIdentifier]) { + self.notebookDocument = notebookDocument + self.cellTextDocuments = cellTextDocuments + } +} diff --git a/Sources/LanguageServerProtocol/Notifications/WorkDoneProgress.swift b/Sources/LanguageServerProtocol/Notifications/WorkDoneProgress.swift new file mode 100644 index 000000000..b84cf4df6 --- /dev/null +++ b/Sources/LanguageServerProtocol/Notifications/WorkDoneProgress.swift @@ -0,0 +1,199 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public enum WorkDoneProgress: NotificationType, Hashable { + public static var method: String = "$/progress" + + case begin(WorkDoneProgressBegin) + case report(WorkDoneProgressReport) + case end(WorkDoneProgressEnd) + + public init(from decoder: Decoder) throws { + if let begin = try? WorkDoneProgressBegin(from: decoder) { + self = .begin(begin) + } else if let report = try? WorkDoneProgressReport(from: decoder) { + self = .report(report) + } else if let end = try? WorkDoneProgressEnd(from: decoder) { + self = .end(end) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected WorkDoneProgressBegin, WorkDoneProgressReport, or WorkDoneProgressEnd") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .begin(let begin): + try begin.encode(to: encoder) + case .report(let report): + try report.encode(to: encoder) + case .end(let end): + try end.encode(to: encoder) + } + } +} + +public struct WorkDoneProgressBegin: Codable, Hashable { + /// Mandatory title of the progress operation. Used to briefly inform about + /// the kind of operation being performed. + /// + /// Examples: "Indexing" or "Linking dependencies". + public var title: String + + /// Controls if a cancel button should show to allow the user to cancel the + /// long running operation. Clients that don't support cancellation are + /// allowed to ignore the setting. + public var cancellable: Bool? + + /// Optional, more detailed associated progress message. Contains + /// complementary information to the `title`. + /// + /// Examples: "3/25 files", "project/src/module2", "node_modules/some_dep". + /// If unset, the previous progress message (if any) is still valid. + public var message: String? + + /// Optional progress percentage to display (value 100 is considered 100%). + /// If not provided infinite progress is assumed and clients are allowed + /// to ignore the `percentage` value in subsequent in report notifications. + /// + /// The value should be steadily rising. Clients are free to ignore values + /// that are not following this rule. The value range is [0, 100] + public var percentage: Int? + + public init(title: String, cancellable: Bool? = nil, message: String? = nil, percentage: Int? = nil) { + self.title = title + self.cancellable = cancellable + self.message = message + self.percentage = percentage + } + + enum CodingKeys: CodingKey { + case kind + case title + case cancellable + case message + case percentage + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let kind = try container.decode(String.self, forKey: .kind) + guard kind == "begin" else { + throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of WorkDoneProgressBegin is not 'begin'") + } + + self.title = try container.decode(String.self, forKey: .title) + self.cancellable = try container.decodeIfPresent(Bool.self, forKey: .cancellable) + self.message = try container.decodeIfPresent(String.self, forKey: .message) + self.percentage = try container.decodeIfPresent(Int.self, forKey: .percentage) + } + + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode("begin", forKey: .kind) + try container.encode(self.title, forKey: .title) + try container.encodeIfPresent(self.cancellable, forKey: .cancellable) + try container.encodeIfPresent(self.message, forKey: .message) + try container.encodeIfPresent(self.percentage, forKey: .percentage) + } +} + +public struct WorkDoneProgressReport: Codable, Hashable { + /// Controls enablement state of a cancel button. This property is only valid + /// if a cancel button got requested in the `WorkDoneProgressBegin` payload. + /// + /// Clients that don't support cancellation or don't support control the + /// button's enablement state are allowed to ignore the setting. + public var cancellable: Bool? + + /// Optional, more detailed associated progress message. Contains + /// complementary information to the `title`. + /// + /// Examples: "3/25 files", "project/src/module2", "node_modules/some_dep". + /// If unset, the previous progress message (if any) is still valid. + public var message: String? + + /// Optional progress percentage to display (value 100 is considered 100%). + /// If not provided infinite progress is assumed and clients are allowed + /// to ignore the `percentage` value in subsequent in report notifications. + /// + /// The value should be steadily rising. Clients are free to ignore values + /// that are not following this rule. The value range is [0, 100] + public var percentage: Int? + + public init(cancellable: Bool? = nil, message: String? = nil, percentage: Int? = nil) { + self.cancellable = cancellable + self.message = message + self.percentage = percentage + } + + enum CodingKeys: CodingKey { + case kind + case cancellable + case message + case percentage + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let kind = try container.decode(String.self, forKey: .kind) + guard kind == "report" else { + throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of WorkDoneProgressReport is not 'report'") + } + + self.cancellable = try container.decodeIfPresent(Bool.self, forKey: .cancellable) + self.message = try container.decodeIfPresent(String.self, forKey: .message) + self.percentage = try container.decodeIfPresent(Int.self, forKey: .percentage) + } + + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode("report", forKey: .kind) + try container.encodeIfPresent(self.cancellable, forKey: .cancellable) + try container.encodeIfPresent(self.message, forKey: .message) + try container.encodeIfPresent(self.percentage, forKey: .percentage) + } +} + +public struct WorkDoneProgressEnd: Codable, Hashable { + /// Optional, a final message indicating to for example indicate the outcome + /// of the operation. + public var message: String? + + public init(message: String? = nil) { + self.message = message + } + + enum CodingKeys: CodingKey { + case kind + case message + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let kind = try container.decode(String.self, forKey: .kind) + guard kind == "end" else { + throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of WorkDoneProgressReport is not 'end'") + } + + self.message = try container.decodeIfPresent(String.self, forKey: .message) + } + + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode("end", forKey: .kind) + try container.encodeIfPresent(self.message, forKey: .message) + } +} diff --git a/Sources/LanguageServerProtocol/Requests/CodeActionRequest.swift b/Sources/LanguageServerProtocol/Requests/CodeActionRequest.swift index 9335f04f6..4a777f62c 100644 --- a/Sources/LanguageServerProtocol/Requests/CodeActionRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/CodeActionRequest.swift @@ -31,6 +31,9 @@ public struct CodeActionRequest: TextDocumentRequest, Hashable { public static let method: String = "textDocument/codeAction" public typealias Response = CodeActionRequestResponse? + /// The document in which the command was invoked. + public var textDocument: TextDocumentIdentifier + /// The range for which the command was invoked. @CustomCodable public var range: Range @@ -38,9 +41,6 @@ public struct CodeActionRequest: TextDocumentRequest, Hashable { /// Context carrying additional information. public var context: CodeActionContext - /// The document in which the command was invoked. - public var textDocument: TextDocumentIdentifier - public init(range: Range, context: CodeActionContext, textDocument: TextDocumentIdentifier) { self._range = CustomCodable(wrappedValue: range) self.context = context @@ -89,6 +89,24 @@ public enum CodeActionRequestResponse: ResponseType, Codable, Equatable { } } +/// The reason why code actions were requested. +public struct CodeActionTriggerKind: RawRepresentable, Codable, Hashable { + public var rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } + + /// Code actions were explicitly requested by the user or by an extension. + public static let invoked = CodeActionTriggerKind(rawValue: 1) + + /// Code actions were requested automatically. + /// + /// This typically happens when current selection in a file changes, but can + /// also be triggered when file content changes. + public static let automatic = CodeActionTriggerKind(rawValue: 2) +} + public struct CodeActionContext: Codable, Hashable { /// An array of diagnostics. @@ -99,13 +117,17 @@ public struct CodeActionContext: Codable, Hashable { /// so servers can omit computing them. public var only: [CodeActionKind]? - public init(diagnostics: [Diagnostic] = [], only: [CodeActionKind]? = nil) { + /// The reason why code actions were requested. + public var triggerKind: CodeActionTriggerKind? + + public init(diagnostics: [Diagnostic] = [], only: [CodeActionKind]? = nil, triggerKind: CodeActionTriggerKind? = nil) { self.diagnostics = diagnostics self.only = only + self.triggerKind = triggerKind } } -public struct CodeAction: Codable, Hashable { +public struct CodeAction: ResponseType, Hashable { /// A short, human-readable, title for this code action. public var title: String diff --git a/Sources/LanguageServerProtocol/Requests/CodeActionResolveRequest.swift b/Sources/LanguageServerProtocol/Requests/CodeActionResolveRequest.swift new file mode 100644 index 000000000..70b95ab5f --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/CodeActionResolveRequest.swift @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct CodeActionResolveRequest: RequestType { + public static var method: String = "codeAction/resolve" + public typealias Response = CodeAction + + public var codeAction: CodeAction + + public init(codeAction: CodeAction) { + self.codeAction = codeAction + } + + public init(from decoder: Decoder) throws { + self.codeAction = try CodeAction(from: decoder) + } + + public func encode(to encoder: Encoder) throws { + try self.codeAction.encode(to: encoder) + } +} diff --git a/Sources/LanguageServerProtocol/Requests/CodeLensRefreshRequest.swift b/Sources/LanguageServerProtocol/Requests/CodeLensRefreshRequest.swift new file mode 100644 index 000000000..fc6abac3e --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/CodeLensRefreshRequest.swift @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct CodeLensRefreshRequest: RequestType { + public static var method: String = "workspace/codeLens/refresh" + + public typealias Response = VoidResponse + + public init() { } +} diff --git a/Sources/LanguageServerProtocol/Requests/CodeLensRequest.swift b/Sources/LanguageServerProtocol/Requests/CodeLensRequest.swift new file mode 100644 index 000000000..2381e95a5 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/CodeLensRequest.swift @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +/// The code lens request is sent from the client to the server to compute code lenses for a given text document. +public struct CodeLensRequest: TextDocumentRequest { + public static var method: String = "textDocument/codeLens" + public typealias Response = [CodeLens]? + + /// The document to request code lens for. + public var textDocument: TextDocumentIdentifier + + public init(textDocument: TextDocumentIdentifier) { + self.textDocument = textDocument + } +} + +/// A code lens represents a command that should be shown along with +/// source text, like the number of references, a way to run tests, etc. +/// +/// A code lens is _unresolved_ when no command is associated to it. For +/// performance reasons the creation of a code lens and resolving should be done +/// in two stages. +public struct CodeLens: ResponseType, Hashable { + /// The range in which this code lens is valid. Should only span a single + /// line. + @CustomCodable + public var range: Range + + /// The command this code lens represents. + public var command: Command? + + /// A data entry field that is preserved on a code lens item between + /// a code lens and a code lens resolve request. + public var data: LSPAny? + + public init(range: Range, command: Command? = nil, data: LSPAny? = nil) { + self.range = range + self.command = command + self.data = data + } +} diff --git a/Sources/LanguageServerProtocol/Requests/CodeLensResolveRequest.swift b/Sources/LanguageServerProtocol/Requests/CodeLensResolveRequest.swift new file mode 100644 index 000000000..87a4d7941 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/CodeLensResolveRequest.swift @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +/// The code lens resolve request is sent from the client to the server to resolve the command for a given code lens item. +public struct CodeLensResolveRequest: RequestType { + public static var method: String = "codeLens/resolve" + public typealias Response = CodeLens + + public var codeLens: CodeLens + + public init(codeLens: CodeLens) { + self.codeLens = codeLens + } + + public init(from decoder: Decoder) throws { + self.codeLens = try CodeLens(from: decoder) + } + + public func encode(to encoder: Encoder) throws { + try codeLens.encode(to: encoder) + } +} diff --git a/Sources/LanguageServerProtocol/Requests/CompletionItemResolveRequest.swift b/Sources/LanguageServerProtocol/Requests/CompletionItemResolveRequest.swift new file mode 100644 index 000000000..08a0d0082 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/CompletionItemResolveRequest.swift @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct CompletionItemResolveRequest: RequestType { + public static var method: String = "completionItem/resolve" + public typealias Response = CompletionItem + + public var item: CompletionItem + + public init(item: CompletionItem) { + self.item = item + } + + public init(from decoder: Decoder) throws { + self.item = try CompletionItem(from: decoder) + } + + public func encode(to encoder: Encoder) throws { + try self.item.encode(to: encoder) + } +} diff --git a/Sources/LanguageServerProtocol/Requests/CompletionRequest.swift b/Sources/LanguageServerProtocol/Requests/CompletionRequest.swift index e6bb57db4..a50eeae4e 100644 --- a/Sources/LanguageServerProtocol/Requests/CompletionRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/CompletionRequest.swift @@ -85,17 +85,94 @@ public struct CompletionContext: Codable, Hashable { /// List of completion items. If this list has been filtered already, the `isIncomplete` flag /// indicates that the client should re-query code-completions if the filter text changes. public struct CompletionList: ResponseType, Hashable { + public struct InsertReplaceRanges: Codable, Hashable { + @CustomCodable + var insert: Range + + @CustomCodable + var replace: Range + + public init(insert: Range, replace: Range) { + self.insert = insert + self.replace = replace + } + } + + public enum ItemDefaultsEditRange: Codable, Hashable { + case range(Range) + case insertReplaceRanges(InsertReplaceRanges) + + public init(from decoder: Decoder) throws { + if let range = try? PositionRange(from: decoder).wrappedValue { + self = .range(range) + } else if let insertReplaceRange = try? InsertReplaceRanges(from: decoder) { + self = .insertReplaceRanges(insertReplaceRange) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected Range or InsertReplaceRanges") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .range(let range): + try PositionRange(wrappedValue: range).encode(to: encoder) + case .insertReplaceRanges(let insertReplaceRanges): + try insertReplaceRanges.encode(to: encoder) + } + } + } + + public struct ItemDefaults: Codable, Hashable { + /// A default commit character set. + public var commitCharacters: [String]? + + /// A default edit range + public var editRange: ItemDefaultsEditRange? + + /// A default insert text format + public var insertTextFormat: InsertTextFormat? + + /// A default insert text mode + public var insertTextMode: InsertTextMode? + + /// A default data value. + public var data: LSPAny? + + public init(commitCharacters: [String]? = nil, editRange: ItemDefaultsEditRange? = nil, insertTextFormat: InsertTextFormat? = nil, insertTextMode: InsertTextMode? = nil, data: LSPAny? = nil) { + self.commitCharacters = commitCharacters + self.editRange = editRange + self.insertTextFormat = insertTextFormat + self.insertTextMode = insertTextMode + self.data = data + } + } + /// Whether the list of completions is "complete" or not. /// /// When this value is `true`, the client should re-query the server when doing further filtering. public var isIncomplete: Bool + /// In many cases the items of an actual completion result share the same + /// value for properties like `commitCharacters` or the range of a text + /// edit. A completion list can therefore define item defaults which will + /// be used if a completion item itself doesn't specify the value. + /// + /// If a completion list specifies a default value and a completion item + /// also specifies a corresponding value the one from the item is used. + /// + /// Servers are only allowed to return default values if the client + /// signals support for this via the `completionList.itemDefaults` + /// capability. + public var itemDefaults: ItemDefaults? + /// The resulting completions. public var items: [CompletionItem] - public init(isIncomplete: Bool, items: [CompletionItem]) { + public init(isIncomplete: Bool, itemDefaults: ItemDefaults? = nil, items: [CompletionItem]) { self.isIncomplete = isIncomplete + self.itemDefaults = itemDefaults self.items = items } diff --git a/Sources/LanguageServerProtocol/Requests/CreateWorkDoneProgressRequest.swift b/Sources/LanguageServerProtocol/Requests/CreateWorkDoneProgressRequest.swift new file mode 100644 index 000000000..ce69d9ce9 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/CreateWorkDoneProgressRequest.swift @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct CreateWorkDoneProgressRequest: RequestType { + public static var method: String = "window/workDoneProgress/create" + public typealias Response = VoidResponse + + /// The token to be used to report progress. + public var token: ProgressToken + + public init(token: ProgressToken) { + self.token = token + } +} diff --git a/Sources/LanguageServerProtocol/Requests/DiagnosticsRefreshRequest.swift b/Sources/LanguageServerProtocol/Requests/DiagnosticsRefreshRequest.swift new file mode 100644 index 000000000..3a31873e7 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/DiagnosticsRefreshRequest.swift @@ -0,0 +1,18 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct DiagnosticsRefreshRequest: RequestType { + public static var method: String = "workspace/diagnostic/refresh" + public typealias Response = VoidResponse + + public init() { } +} diff --git a/Sources/LanguageServerProtocol/Requests/DocumentDiagnosticsRequest.swift b/Sources/LanguageServerProtocol/Requests/DocumentDiagnosticsRequest.swift new file mode 100644 index 000000000..479ba2a1b --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/DocumentDiagnosticsRequest.swift @@ -0,0 +1,171 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct DocumentDiagnosticsRequest: TextDocumentRequest { + public static var method: String = "textDocument/diagnostic" + public typealias Response = DocumentDiagnosticReport + + /// The text document. + public var textDocument: TextDocumentIdentifier + + /// The additional identifier provided during registration. + public var identifier: String? + + /// The result id of a previous response if provided. + public var previousResultId: String? + + public init(textDocument: TextDocumentIdentifier, identifier: String? = nil, previousResultId: String? = nil) { + self.textDocument = textDocument + self.identifier = identifier + self.previousResultId = previousResultId + } +} + +/// The result of a document diagnostic pull request. A report can +/// either be a full report containing all diagnostics for the +/// requested document or a unchanged report indicating that nothing +/// has changed in terms of diagnostics in comparison to the last +/// pull request. +public enum DocumentDiagnosticReport: ResponseType, Codable, Hashable { + case full(RelatedFullDocumentDiagnosticReport) + case unchanged(RelatedUnchangedDocumentDiagnosticReport) + + public init(from decoder: Decoder) throws { + if let full = try? RelatedFullDocumentDiagnosticReport(from: decoder) { + self = .full(full) + } else if let unchanged = try? RelatedUnchangedDocumentDiagnosticReport(from: decoder) { + self = .unchanged(unchanged) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected RelatedFullDocumentDiagnosticReport or RelatedUnchangedDocumentDiagnosticReport") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .full(let full): + try full.encode(to: encoder) + case .unchanged(let unchanged): + try unchanged.encode(to: encoder) + } + } +} + +/// The document diagnostic report kinds. +public struct DocumentDiagnosticReportKind: RawRepresentable, Codable, Hashable { + public var rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } + + /// A diagnostic report with a full + /// set of problems. + public static let full = DocumentDiagnosticReportKind(rawValue: "full") + + /// A report indicating that the last + /// returned report is still accurate. + public static let unchanged = DocumentDiagnosticReportKind(rawValue: "unchanged") +} + +/// A diagnostic report with a full set of problems. +public struct RelatedFullDocumentDiagnosticReport: Codable, Hashable { + /// An optional result id. If provided it will + /// be sent on the next diagnostic request for the + /// same document. + public var resultId: String? + + /// The actual items. + public var items: [Diagnostic] + + /// Diagnostics of related documents. This information is useful + /// in programming languages where code in a file A can generate + /// diagnostics in a file B which A depends on. An example of + /// such a language is C/C++ where marco definitions in a file + /// a.cpp and result in errors in a header file b.hpp. + public var relatedDocuments: [DocumentURI: DocumentDiagnosticReport]? + + public init(resultId: String? = nil, items: [Diagnostic], relatedDocuments: [DocumentURI : DocumentDiagnosticReport]? = nil) { + self.resultId = resultId + self.items = items + self.relatedDocuments = relatedDocuments + } + + enum CodingKeys: CodingKey { + case kind + case resultId + case items + case relatedDocuments + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let kind = try container.decode(DocumentDiagnosticReportKind.self, forKey: .kind) + guard kind == .full else { + throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of FullDocumentDiagnosticReport is not 'full'") + } + self.resultId = try container.decodeIfPresent(String.self, forKey: .resultId) + self.items = try container.decode([Diagnostic].self, forKey: .items) + self.relatedDocuments = try container.decodeIfPresent([DocumentURI: DocumentDiagnosticReport].self, forKey: .relatedDocuments) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(DocumentDiagnosticReportKind.full, forKey: .kind) + try container.encodeIfPresent(self.resultId, forKey: .resultId) + try container.encode(self.items, forKey: .items) + try container.encodeIfPresent(self.relatedDocuments, forKey: .relatedDocuments) + } +} + +/// A diagnostic report indicating that the last returned +/// report is still accurate. +public struct RelatedUnchangedDocumentDiagnosticReport: Codable, Hashable { + /// A result id which will be sent on the next + /// diagnostic request for the same document. + public var resultId: String + + /// Diagnostics of related documents. This information is useful + /// in programming languages where code in a file A can generate + /// diagnostics in a file B which A depends on. An example of + /// such a language is C/C++ where marco definitions in a file + /// a.cpp and result in errors in a header file b.hpp. + public var relatedDocuments: [DocumentURI: DocumentDiagnosticReport]? + + public init(resultId: String, relatedDocuments: [DocumentURI : DocumentDiagnosticReport]? = nil) { + self.resultId = resultId + self.relatedDocuments = relatedDocuments + } + + enum CodingKeys: CodingKey { + case kind + case resultId + case relatedDocuments + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let kind = try container.decode(DocumentDiagnosticReportKind.self, forKey: .kind) + guard kind == .unchanged else { + throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of FullDocumentDiagnosticReport is not 'unchanged'") + } + self.resultId = try container.decode(String.self, forKey: .resultId) + self.relatedDocuments = try container.decodeIfPresent([DocumentURI: DocumentDiagnosticReport].self, forKey: .relatedDocuments) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(DocumentDiagnosticReportKind.unchanged, forKey: .kind) + try container.encode(self.resultId, forKey: .resultId) + try container.encodeIfPresent(self.relatedDocuments, forKey: .relatedDocuments) + } +} diff --git a/Sources/LanguageServerProtocol/Requests/DocumentLinkRequest.swift b/Sources/LanguageServerProtocol/Requests/DocumentLinkRequest.swift new file mode 100644 index 000000000..6be8c51ce --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/DocumentLinkRequest.swift @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +/// The document links request is sent from the client to the server to request the location of links in a document. +public struct DocumentLinkRequest: RequestType { + public static let method: String = "textDocument/documentLink" + public typealias Response = [DocumentLink]? + + /// The document to provide document links for. + public var textDocument: TextDocumentIdentifier + + public init(textDocument: TextDocumentIdentifier) { + self.textDocument = textDocument + } +} + +/// A document link is a range in a text document that links to an internal or +/// external resource, like another text document or a web site. +public struct DocumentLink: ResponseType, Hashable { + /// The range this link applies to. + @CustomCodable + public var range: Range + + /// The uri this link points to. If missing a resolve request is sent later. + public var target: DocumentURI? + + /// The tooltip text when you hover over this link. + /// + /// If a tooltip is provided, is will be displayed in a string that includes + /// instructions on how to trigger the link, such as `{0} (ctrl + click)`. + /// The specific instructions vary depending on OS, user settings, and + /// localization. + public var tooltip: String? + + /// A data entry field that is preserved on a document link between a + /// DocumentLinkRequest and a DocumentLinkResolveRequest. + public var data: LSPAny? + + public init(range: Range, target: DocumentURI? = nil, tooltip: String? = nil, data: LSPAny? = nil) { + self.range = range + self.target = target + self.tooltip = tooltip + self.data = data + } +} diff --git a/Sources/LanguageServerProtocol/Requests/DocumentLinkResolveRequest.swift b/Sources/LanguageServerProtocol/Requests/DocumentLinkResolveRequest.swift new file mode 100644 index 000000000..fc88fb1d3 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/DocumentLinkResolveRequest.swift @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +/// The document links request is sent from the client to the server to request the location of links in a document. +public struct DocumentLinkResolveRequest: RequestType { + public static let method: String = "documentLink/resolve" + public typealias Response = DocumentLink + + public var documentLink: DocumentLink + + public init(documentLink: DocumentLink) { + self.documentLink = documentLink + } + + public init(from decoder: Decoder) throws { + self.documentLink = try DocumentLink(from: decoder) + } + + public func encode(to encoder: Encoder) throws { + try self.documentLink.encode(to: encoder) + } +} diff --git a/Sources/LanguageServerProtocol/Requests/DocumentSymbolRequest.swift b/Sources/LanguageServerProtocol/Requests/DocumentSymbolRequest.swift index bae891774..fa77bc69a 100644 --- a/Sources/LanguageServerProtocol/Requests/DocumentSymbolRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/DocumentSymbolRequest.swift @@ -74,6 +74,9 @@ public struct DocumentSymbol: Hashable, Codable { /// The kind of this symbol. public var kind: SymbolKind + /// Tags for this document symbol. + public var tags: [SymbolTag]? + /// Indicates if this symbol is deprecated. public var deprecated: Bool? @@ -97,6 +100,7 @@ public struct DocumentSymbol: Hashable, Codable { name: String, detail: String? = nil, kind: SymbolKind, + tags: [SymbolTag]? = nil, deprecated: Bool? = nil, range: Range, selectionRange: Range, @@ -105,6 +109,7 @@ public struct DocumentSymbol: Hashable, Codable { self.name = name self.detail = detail self.kind = kind + self.tags = tags self.deprecated = deprecated self._range = CustomCodable(wrappedValue: range) self._selectionRange = CustomCodable(wrappedValue: selectionRange) diff --git a/Sources/LanguageServerProtocol/Requests/FoldingRangeRequest.swift b/Sources/LanguageServerProtocol/Requests/FoldingRangeRequest.swift index 13f73913e..278e26793 100644 --- a/Sources/LanguageServerProtocol/Requests/FoldingRangeRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/FoldingRangeRequest.swift @@ -52,18 +52,26 @@ public struct FoldingRange: ResponseType, Hashable { /// is used to categorize folding ranges and used by commands like 'Fold all comments'. public var kind: FoldingRangeKind? + /// The text that the client should show when the specified range is + /// collapsed. If not defined or not supported by the client, a default + /// will be chosen by the client. + public var collapsedText: String? + public init( startLine: Int, startUTF16Index: Int? = nil, endLine: Int, endUTF16Index: Int? = nil, - kind: FoldingRangeKind? = nil) + kind: FoldingRangeKind? = nil, + collapsedText: String? = nil + ) { self.startLine = startLine self.startUTF16Index = startUTF16Index self.endLine = endLine self.endUTF16Index = endUTF16Index self.kind = kind + self.collapsedText = collapsedText } } diff --git a/Sources/LanguageServerProtocol/Requests/FormattingRequests.swift b/Sources/LanguageServerProtocol/Requests/FormattingRequests.swift index 67b880286..b2ac0a900 100644 --- a/Sources/LanguageServerProtocol/Requests/FormattingRequests.swift +++ b/Sources/LanguageServerProtocol/Requests/FormattingRequests.swift @@ -97,4 +97,13 @@ public struct FormattingOptions: Codable, Hashable { /// Whether to use spaces instead of tabs. public var insertSpaces: Bool + + /// Trim trailing whitespace on a line. + public var trimTrailingWhitespace: Bool? + + /// Insert a newline character at the end of the file if one does not exist. + public var insertFinalNewline: Bool? + + /// Trim all newlines after the final newline at the end of the file. + public var trimFinalNewlines: Bool? } diff --git a/Sources/LanguageServerProtocol/Requests/InitializeRequest.swift b/Sources/LanguageServerProtocol/Requests/InitializeRequest.swift index a1f41a823..c070b6549 100644 --- a/Sources/LanguageServerProtocol/Requests/InitializeRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/InitializeRequest.swift @@ -28,6 +28,20 @@ /// /// - Returns: public struct InitializeRequest: RequestType, Hashable { + /// Information about the client + public struct ClientInfo: Codable, Hashable { + // The name of the client as defined by the client. + public var name: String + + /// The client's version as defined by the client. + public var version: String? + + public init(name: String, version: String? = nil) { + self.name = name + self.version = version + } + } + public static let method: String = "initialize" public typealias Response = InitializeResult @@ -37,6 +51,17 @@ public struct InitializeRequest: RequestType, Hashable { /// If the client process dies, the server should exit. public var processId: Int? = nil + /// Information about the client + public var clientInfo: ClientInfo? = nil + + /// The locale the client is currently showing the user interface + /// in. This must not necessarily be the locale of the operating + /// system. + /// + /// Uses IETF language tags as the value's syntax + /// (See https://en.wikipedia.org/wiki/IETF_language_tag) + public var locale: String? = nil + /// The workspace path, or nil if no workspace is open. /// /// - Note: deprecated in favour of `rootURI`. @@ -53,12 +78,6 @@ public struct InitializeRequest: RequestType, Hashable { /// The capabilities provided by the client editor. public var capabilities: ClientCapabilities - public enum Tracing: String, Codable { - case off - case messages - case verbose - } - /// Whether to enable tracing. public var trace: Tracing? = .off @@ -67,6 +86,8 @@ public struct InitializeRequest: RequestType, Hashable { public init( processId: Int? = nil, + clientInfo: ClientInfo? = nil, + locale: String? = nil, rootPath: String? = nil, rootURI: DocumentURI?, initializationOptions: LSPAny? = nil, @@ -75,6 +96,8 @@ public struct InitializeRequest: RequestType, Hashable { workspaceFolders: [WorkspaceFolder]?) { self.processId = processId + self.clientInfo = clientInfo + self.locale = locale self.rootPath = rootPath self.rootURI = rootURI self.initializationOptions = initializationOptions diff --git a/Sources/LanguageServerProtocol/Requests/InlayHintRefreshRequest.swift b/Sources/LanguageServerProtocol/Requests/InlayHintRefreshRequest.swift new file mode 100644 index 000000000..e4a229afe --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/InlayHintRefreshRequest.swift @@ -0,0 +1,18 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct InlayHintRefreshRequest: RequestType { + public static var method: String = "workspace/inlayHint/refresh" + public typealias Response = VoidResponse + + public init() { } +} diff --git a/Sources/LanguageServerProtocol/Requests/InlayHintRequest.swift b/Sources/LanguageServerProtocol/Requests/InlayHintRequest.swift index a6c0ef1b3..8b6cdba3e 100644 --- a/Sources/LanguageServerProtocol/Requests/InlayHintRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/InlayHintRequest.swift @@ -12,9 +12,6 @@ /// Request for inline annotations to be displayed in the editor. /// -/// This implements the proposed `textDocument/inlayHint` API from -/// https://github.com/microsoft/language-server-protocol/pull/1249 (commit: `d55733d`) -/// /// - Parameters: /// - textDocument: The document for which to provide the inlay hints. /// @@ -31,17 +28,11 @@ public struct InlayHintRequest: TextDocumentRequest, Hashable { @CustomCodable public var range: Range? - /// The categories of hints that are interesting to the client - /// and should be filtered. - public var only: [InlayHintKind]? - public init( textDocument: TextDocumentIdentifier, - range: Range? = nil, - only: [InlayHintKind]? = nil + range: Range? = nil ) { self.textDocument = textDocument self._range = CustomCodable(wrappedValue: range) - self.only = only } } diff --git a/Sources/LanguageServerProtocol/Requests/InlayHintResolveRequest.swift b/Sources/LanguageServerProtocol/Requests/InlayHintResolveRequest.swift new file mode 100644 index 000000000..f2dea8fa0 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/InlayHintResolveRequest.swift @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct InlayHintResolveRequest: RequestType { + public static var method: String = "inlayHint/resolve" + public typealias Response = InlayHint + + public var inlayHint: InlayHint + + public init(inlayHint: InlayHint) { + self.inlayHint = inlayHint + } + + public init(from decoder: Decoder) throws { + self.inlayHint = try InlayHint(from: decoder) + } + + public func encode(to encoder: Encoder) throws { + try inlayHint.encode(to: encoder) + } +} diff --git a/Sources/LanguageServerProtocol/Requests/InlineValueRefreshRequest.swift b/Sources/LanguageServerProtocol/Requests/InlineValueRefreshRequest.swift new file mode 100644 index 000000000..dc2b10071 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/InlineValueRefreshRequest.swift @@ -0,0 +1,18 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +struct InlineValueRefreshRequest: RequestType { + static var method: String = "workspace/inlineValue/refresh" + typealias Response = VoidResponse + + public init() { } +} diff --git a/Sources/LanguageServerProtocol/Requests/InlineValueRequest.swift b/Sources/LanguageServerProtocol/Requests/InlineValueRequest.swift new file mode 100644 index 000000000..ed770da0b --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/InlineValueRequest.swift @@ -0,0 +1,148 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct InlineValueContext: Codable, Hashable { + /// The stack frame (as a DAP Id) where the execution has stopped. + public var frameId: Int + + /// The document range where execution has stopped. + /// Typically the end position of the range denotes the line where the + /// inline values are shown. + @CustomCodable + public var stoppedLocation: Range + + public init(frameId: Int, stoppedLocation: Range) { + self.frameId = frameId + self.stoppedLocation = stoppedLocation + } +} + +/// The inline value request is sent from the client to the server to compute inline values for a given text document that may be rendered in the editor at the end of lines. +public struct InlineValueRequest: TextDocumentRequest { + public static var method: String = "textDocument/inlineValue" + public typealias Response = [InlineValue]? + + /// The text document. + public var textDocument: TextDocumentIdentifier + + /// The document range for which inline values should be computed. + @CustomCodable + public var range: Range + + /// Additional information about the context in which inline values were + /// requested. + public var context: InlineValueContext + + public init(textDocument: TextDocumentIdentifier, range: Range, context: InlineValueContext) { + self.textDocument = textDocument + self.range = range + self.context = context + } +} + +/// Provide inline value as text. +public struct InlineValueText: Codable, Hashable { + /// The document range for which the inline value applies. + @CustomCodable + public var range: Range + + /// The text of the inline value. + public var text: String + + public init(range: Range, text: String) { + self.range = range + self.text = text + } +} + +/// Provide inline value through a variable lookup. +/// +/// If only a range is specified, the variable name will be extracted from +/// the underlying document. +/// +/// An optional variable name can be used to override the extracted name. +public struct InlineValueVariableLookup: Codable, Hashable { + /// The document range for which the inline value applies. + /// The range is used to extract the variable name from the underlying + /// document. + @CustomCodable + public var range: Range + + /// If specified the name of the variable to look up. + public var variableName: String? + + /// How to perform the lookup. + public var caseSensitiveLookup: Bool + + public init(range: Range, variableName: String? = nil, caseSensitiveLookup: Bool) { + self.range = range + self.variableName = variableName + self.caseSensitiveLookup = caseSensitiveLookup + } +} + +/// Provide an inline value through an expression evaluation. +/// +/// If only a range is specified, the expression will be extracted from the +/// underlying document. +/// +/// An optional expression can be used to override the extracted expression. +public struct InlineValueEvaluatableExpression: Codable, Hashable { + /// The document range for which the inline value applies. + /// The range is used to extract the evaluatable expression from the + /// underlying document. + @CustomCodable + public var range: Range + + /// If specified the expression overrides the extracted expression. + public var expression: String? + + public init(range: Range, expression: String? = nil) { + self.range = range + self.expression = expression + } +} + +/// Inline value information can be provided by different means: +/// - directly as a text value (class InlineValueText). +/// - as a name to use for a variable lookup (class InlineValueVariableLookup) +/// - as an evaluatable expression (class InlineValueEvaluatableExpression) +/// The InlineValue types combines all inline value types into one type. +public enum InlineValue: ResponseType, Hashable { + case text(InlineValueText) + case variableLookup(InlineValueVariableLookup) + case evaluatableExpression(InlineValueEvaluatableExpression) + + public init(from decoder: Decoder) throws { + if let text = try? InlineValueText(from: decoder) { + self = .text(text) + } else if let variableLookup = try? InlineValueVariableLookup(from: decoder) { + self = .variableLookup(variableLookup) + } else if let evaluatableExpression = try? InlineValueEvaluatableExpression(from: decoder) { + self = .evaluatableExpression(evaluatableExpression) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected InlineValueText, InlineValueEvaluatableExpression or InlineValueEvaluatableExpression") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .text(let text): + try text.encode(to: encoder) + case .variableLookup(let variableLookup): + try variableLookup.encode(to: encoder) + case .evaluatableExpression(let evaluatableExpression): + try evaluatableExpression.encode(to: encoder) + } + } +} diff --git a/Sources/LanguageServerProtocol/Requests/LinkedEditingRangeRequest.swift b/Sources/LanguageServerProtocol/Requests/LinkedEditingRangeRequest.swift new file mode 100644 index 000000000..d10370d9e --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/LinkedEditingRangeRequest.swift @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct LinkedEditingRangeRequest: TextDocumentRequest { + public static var method: String = "textDocument/linkedEditingRange" + public typealias Response = LinkedEditingRanges? + + /// The document in which the given symbol is located. + public var textDocument: TextDocumentIdentifier + + /// The document location of a given symbol. + public var position: Position + + public init(textDocument: TextDocumentIdentifier, position: Position) { + self.textDocument = textDocument + self.position = position + } +} + +public struct LinkedEditingRanges: ResponseType { + /// A list of ranges that can be renamed together. The ranges must have + /// identical length and contain identical text content. The ranges cannot + /// overlap. + @CustomCodable + public var ranges: [Range] + + /// An optional word pattern (regular expression) that describes valid + /// contents for the given ranges. If no pattern is provided, the client + /// configuration's word pattern will be used. + public var wordPattern: String? + + public init(ranges: [Range], wordPattern: String? = nil) { + self.ranges = ranges + self.wordPattern = wordPattern + } +} diff --git a/Sources/LanguageServerProtocol/Requests/MonikersRequest.swift b/Sources/LanguageServerProtocol/Requests/MonikersRequest.swift new file mode 100644 index 000000000..d41f1e11f --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/MonikersRequest.swift @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct MonikersRequest: TextDocumentRequest { + public static var method: String = "textDocument/moniker" + public typealias Response = [Moniker]? + + /// The document in which to lookup the symbol location. + public var textDocument: TextDocumentIdentifier + + /// The document location at which to lookup symbol information. + public var position: Position + + public init(textDocument: TextDocumentIdentifier, position: Position) { + self.textDocument = textDocument + self.position = position + } +} + +/// Moniker definition to match LSIF 0.5 moniker definition. +public struct Moniker: ResponseType, Hashable { + /// Moniker uniqueness level to define scope of the moniker. + public struct UniquenessLevel: RawRepresentable, Codable, Hashable { + public var rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } + + /// The moniker is only unique inside a document + public static let document = UniquenessLevel(rawValue: "document") + + /// The moniker is unique inside a project for which a dump got created + public static let project = UniquenessLevel(rawValue: "project") + + /// The moniker is unique inside the group to which a project belongs + public static let group = UniquenessLevel(rawValue: "group") + + /// The moniker is unique inside the moniker scheme. + public static let scheme = UniquenessLevel(rawValue: "scheme") + + /// The moniker is globally unique + public static let global = UniquenessLevel(rawValue: "global") + } + + /// The moniker kind. + public struct Kind: RawRepresentable, Codable, Hashable { + public var rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } + + /// The moniker represent a symbol that is imported into a project + public static let `import` = Kind(rawValue: "import") + + /// The moniker represents a symbol that is exported from a project + public static let export = Kind(rawValue: "export") + + /// The moniker represents a symbol that is local to a project (e.g. a local + /// variable of a function, a class not visible outside the project, ...) + public static let local = Kind(rawValue: "local") + } + + + + /// The scheme of the moniker. For example tsc or .Net + public var scheme: String + + /// The identifier of the moniker. The value is opaque in LSIF however + /// schema owners are allowed to define the structure if they want. + public var identifier: String + + /// The scope in which the moniker is unique + public var unique: UniquenessLevel + + /// The moniker kind if known. + public var kind: Kind? + + public init(scheme: String, identifier: String, unique: UniquenessLevel, kind: Kind? = nil) { + self.scheme = scheme + self.identifier = identifier + self.unique = unique + self.kind = kind + } +} diff --git a/Sources/LanguageServerProtocol/Requests/SelectionRangeRequest.swift b/Sources/LanguageServerProtocol/Requests/SelectionRangeRequest.swift new file mode 100644 index 000000000..ce72a8091 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/SelectionRangeRequest.swift @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct SelectionRangeRequest: TextDocumentRequest { + public static var method: String = "textDocument/selectionRange" + public typealias Response = [SelectionRange] + + /// The text document. + public var textDocument: TextDocumentIdentifier + + /// The positions inside the text document. + public var positions: [Position] + + public init(textDocument: TextDocumentIdentifier, positions: [Position]) { + self.textDocument = textDocument + self.positions = positions + } +} + +public struct SelectionRange: ResponseType, Codable, Hashable { + /// Indirect reference to a `SelectionRange`. + final class SelectionRangeBox: Codable, Hashable { + var selectionRange: SelectionRange + + init(selectionRange: SelectionRange) { + self.selectionRange = selectionRange + } + + init(from decoder: Decoder) throws { + self.selectionRange = try SelectionRange(from: decoder) + } + + func encode(to encoder: Encoder) throws { + try selectionRange.encode(to: encoder) + } + + static func == (lhs: SelectionRange.SelectionRangeBox, rhs: SelectionRange.SelectionRangeBox) -> Bool { + return lhs.selectionRange == rhs.selectionRange + } + + func hash(into hasher: inout Hasher) { + selectionRange.hash(into: &hasher) + } + } + + enum CodingKeys: String, CodingKey { + case range + case _parent = "parent" + } + + /// The range of this selection range. + @CustomCodable + public var range: Range + + /// The parent selection range containing this range. Therefore + /// `parent.range` must contain `this.range`. + private var _parent: SelectionRangeBox? + + public var parent: SelectionRange? { + return _parent?.selectionRange + } + + public init(range: Range, parent: SelectionRange? = nil) { + self.range = range + self._parent = parent.map(SelectionRangeBox.init) + } +} diff --git a/Sources/LanguageServerProtocol/Requests/SignatureHelpRequest.swift b/Sources/LanguageServerProtocol/Requests/SignatureHelpRequest.swift new file mode 100644 index 000000000..9a800a74d --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/SignatureHelpRequest.swift @@ -0,0 +1,199 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct SignatureHelpRequest: TextDocumentRequest { + public static var method: String = "textDocument/signatureHelp" + public typealias Response = SignatureHelp? + + /// The document in which the given symbol is located. + public var textDocument: TextDocumentIdentifier + + /// The document location of a given symbol. + public var position: Position + + /// The signature help context. This is only available if the client + /// specifies to send this using the client capability + /// `textDocument.signatureHelp.contextSupport === true` + public var context: SignatureHelpContext? + + public init(textDocument: TextDocumentIdentifier, position: Position, context: SignatureHelpContext? = nil) { + self.textDocument = textDocument + self.position = position + self.context = context + } +} + + +/// How a signature help was triggered. +public struct SignatureHelpTriggerKind: RawRepresentable, Codable, Hashable { + public var rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } + + /// Signature help was invoked manually by the user or by a command. + public static let invoked = SignatureHelpTriggerKind(rawValue: 1) + + /// Signature help was triggered by a trigger character. + public static let triggerCharacter = SignatureHelpTriggerKind(rawValue: 2) + + /// Signature help was triggered by the cursor moving or by the document + /// content changing. + public static let contentChange = SignatureHelpTriggerKind(rawValue: 3) +} + +/// Additional information about the context in which a signature help request +/// was triggered. +public struct SignatureHelpContext: Codable, Hashable { + /// Action that caused signature help to be triggered. + public var triggerKind: SignatureHelpTriggerKind + + /// Character that caused signature help to be triggered. + /// + /// This is undefined when triggerKind !== + /// SignatureHelpTriggerKind.TriggerCharacter + public var triggerCharacter: String? + + /// `true` if signature help was already showing when it was triggered. + /// + /// Retriggers occur when the signature help is already active and can be + /// caused by actions such as typing a trigger character, a cursor move, or + /// document content changes. + public var isRetrigger: Bool + + /// The currently active `SignatureHelp`. + /// + /// The `activeSignatureHelp` has its `SignatureHelp.activeSignature` field + /// updated based on the user navigating through available signatures. + public var activeSignatureHelp: SignatureHelp? + + public init(triggerKind: SignatureHelpTriggerKind, triggerCharacter: String? = nil, isRetrigger: Bool, activeSignatureHelp: SignatureHelp? = nil) { + self.triggerKind = triggerKind + self.triggerCharacter = triggerCharacter + self.isRetrigger = isRetrigger + self.activeSignatureHelp = activeSignatureHelp + } +} + +/// Signature help represents the signature of something +/// callable. There can be multiple signature but only one +/// active and only one active parameter. +public struct SignatureHelp: ResponseType, Hashable { + /// One or more signatures. If no signatures are available the signature help + /// request should return `null`. + public var signatures: [SignatureInformation] + + /// The active signature. If omitted or the value lies outside the + /// range of `signatures` the value defaults to zero or is ignore if + /// the `SignatureHelp` as no signatures. + /// + /// Whenever possible implementors should make an active decision about + /// the active signature and shouldn't rely on a default value. + /// + /// In future version of the protocol this property might become + /// mandatory to better express this. + public var activeSignature: Int? + + /// The active parameter of the active signature. If omitted or the value + /// lies outside the range of `signatures[activeSignature].parameters` + /// defaults to 0 if the active signature has parameters. If + /// the active signature has no parameters it is ignored. + /// In future version of the protocol this property might become + /// mandatory to better express the active parameter if the + /// active signature does have any. + public var activeParameter: Int? + + public init(signatures: [SignatureInformation], activeSignature: Int? = nil, activeParameter: Int? = nil) { + self.signatures = signatures + self.activeSignature = activeSignature + self.activeParameter = activeParameter + } +} + +/// Represents the signature of something callable. A signature +/// can have a label, like a function-name, a doc-comment, and +/// a set of parameters. +public struct SignatureInformation: Codable, Hashable { + /// The label of this signature. Will be shown in + /// the UI. + public var label: String + + /// The human-readable doc-comment of this signature. Will be shown + /// in the UI but can be omitted. + public var documentation: StringOrMarkupContent? + + /// The parameters of this signature. + public var parameters: [ParameterInformation]? + + /// The index of the active parameter. + /// + /// If provided, this is used in place of `SignatureHelp.activeParameter`. + public var activeParameter: Int? + + public init(label: String, documentation: StringOrMarkupContent? = nil, parameters: [ParameterInformation]? = nil, activeParameter: Int? = nil) { + self.label = label + self.documentation = documentation + self.parameters = parameters + self.activeParameter = activeParameter + } +} + +/// Represents a parameter of a callable-signature. A parameter can +/// have a label and a doc-comment. +public struct ParameterInformation: Codable, Hashable { + public enum Label: Codable, Hashable { + case string(String) + case offsets(start: Int, end: Int) + + public init(from decoder: Decoder) throws { + if let string = try? String(from: decoder) { + self = .string(string) + } else if let offsets = try? Array(from: decoder), offsets.count == 2 { + self = .offsets(start: offsets[0], end: offsets[1]) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected String or an array containing two integers") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .string(let string): + try string.encode(to: encoder) + case .offsets(start: let start, end: let end): + try [start, end].encode(to: encoder) + } + } + } + + /// The label of this parameter information. + /// + /// Either a string or an inclusive start and exclusive end offsets within + /// its containing signature label. (see SignatureInformation.label). The + /// offsets are based on a UTF-16 string representation as `Position` and + /// `Range` does. + /// + /// *Note*: a label of type string should be a substring of its containing + /// signature label. Its intended use case is to highlight the parameter + /// label part in the `SignatureInformation.label`. + public var label: Label + + /// The human-readable doc-comment of this parameter. Will be shown + /// in the UI but can be omitted. + public var documentation: StringOrMarkupContent? + + public init(label: Label, documentation: StringOrMarkupContent? = nil) { + self.label = label + self.documentation = documentation + } +} diff --git a/Sources/LanguageServerProtocol/Requests/WillChangeFilesRequests.swift b/Sources/LanguageServerProtocol/Requests/WillChangeFilesRequests.swift new file mode 100644 index 000000000..f8b272c69 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/WillChangeFilesRequests.swift @@ -0,0 +1,83 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +/// Represents information on a file/folder create. +public struct FileCreate: Codable, Hashable { + /// A file:// URI for the location of the file/folder being created. + public var uri: DocumentURI + + public init(uri: DocumentURI) { + self.uri = uri + } +} + +public struct WillCreateFilesRequest: RequestType { + public static var method: String = "workspace/willCreateFiles" + public typealias Response = WorkspaceEdit? + + /// An array of all files/folders created in this operation. + public var files: [FileCreate] + + public init(files: [FileCreate]) { + self.files = files + } +} + +/// Represents information on a file/folder rename. +public struct FileRename: Codable, Hashable { + + /// A file:// URI for the original location of the file/folder being renamed. + public var oldUri: DocumentURI + + /// A file:// URI for the new location of the file/folder being renamed. + public var newUri: DocumentURI + + public init(oldUri: DocumentURI, newUri: DocumentURI) { + self.oldUri = oldUri + self.newUri = newUri + } +} + +public struct WillRenameFilesRequest: RequestType { + public static var method: String = "workspace/willRenameFiles" + public typealias Response = WorkspaceEdit? + + /// An array of all files/folders renamed in this operation. When a folder + /// is renamed, only the folder will be included, and not its children. + public var files: [FileRename] + + public init(files: [FileRename]) { + self.files = files + } +} + +/// Represents information on a file/folder delete. +public struct FileDelete: Codable, Hashable { + /// A file:// URI for the location of the file/folder being deleted. + public var uri: DocumentURI + + public init(uri: DocumentURI) { + self.uri = uri + } +} + +public struct WillDeleteFilesRequest: RequestType { + public static var method: String = "workspace/willDeleteFiles" + public typealias Response = WorkspaceEdit? + + /// An array of all files/folders deleted in this operation. + public var files: [FileDelete] + + public init(files: [FileDelete]) { + self.files = files + } +} diff --git a/Sources/LanguageServerProtocol/Requests/WillSaveWaitUntilTextDocumentRequest.swift b/Sources/LanguageServerProtocol/Requests/WillSaveWaitUntilTextDocumentRequest.swift new file mode 100644 index 000000000..dd3c6bc53 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/WillSaveWaitUntilTextDocumentRequest.swift @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +/// The document will save request is sent from the client to the server before the document is actually saved. The request can return an array of TextEdits which will be applied to the text document before it is saved. Please note that clients might drop results if computing the text edits took too long or if a server constantly fails on this request. This is done to keep the save fast and reliable. If a server has registered for open / close events clients should ensure that the document is open before a willSaveWaitUntil notification is sent since clients can’t change the content of a file without ownership transferal. +public struct WillSaveWaitUntilTextDocumentRequest: RequestType { + public static let method: String = "textDocument/willSaveWaitUntil" + public typealias Response = [TextEdit]? + + /// The document that will be saved. + public var textDocument: TextDocumentIdentifier + + public var reason: TextDocumentSaveReason + + public init(textDocument: TextDocumentIdentifier, reason: TextDocumentSaveReason) { + self.textDocument = textDocument + self.reason = reason + } +} diff --git a/Sources/LanguageServerProtocol/Requests/WorkspaceDiagnosticsRequest.swift b/Sources/LanguageServerProtocol/Requests/WorkspaceDiagnosticsRequest.swift new file mode 100644 index 000000000..fed362bc0 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/WorkspaceDiagnosticsRequest.swift @@ -0,0 +1,178 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +/// A previous result id in a workspace pull request. +public struct PreviousResultId: Codable { + /// The URI for which the client knows a result id. + public var uri: DocumentURI + + /// The value of the previous result id. + public var value: String + + public init(uri: DocumentURI, value: String) { + self.uri = uri + self.value = value + } +} + +public struct WorkspaceDiagnosticsRequest: RequestType { + public static var method: String = "workspace/diagnostic" + public typealias Response = WorkspaceDiagnosticReport + + /// The additional identifier provided during registration. + public var identifier: String? + + /// The currently known diagnostic reports with their + /// previous result ids. + public var previousResultIds: [PreviousResultId] + + public init(identifier: String? = nil, previousResultIds: [PreviousResultId]) { + self.identifier = identifier + self.previousResultIds = previousResultIds + } +} + +/// A workspace diagnostic report. +public struct WorkspaceDiagnosticReport: ResponseType { + public var items: [WorkspaceDocumentDiagnosticReport] + + public init(items: [WorkspaceDocumentDiagnosticReport]) { + self.items = items + } +} + +/// A full document diagnostic report for a workspace diagnostic result. +public struct WorkspaceFullDocumentDiagnosticReport: Codable, Hashable { + /// An optional result id. If provided it will + /// be sent on the next diagnostic request for the + /// same document. + public var resultId: String? + + /// The actual items. + public var items: [Diagnostic] + + /// The URI for which diagnostic information is reported. + public var uri: DocumentURI + + /// The version number for which the diagnostics are reported. + /// If the document is not marked as open `null` can be provided. + public var version: Int? + + public init(resultId: String? = nil, items: [Diagnostic], uri: DocumentURI, version: Int? = nil) { + self.resultId = resultId + self.items = items + self.uri = uri + self.version = version + } + + enum CodingKeys: CodingKey { + case kind + case resultId + case items + case uri + case version + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let kind = try container.decode(DocumentDiagnosticReportKind.self, forKey: .kind) + guard kind == .full else { + throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of FullDocumentDiagnosticReport is not 'full'") + } + self.resultId = try container.decodeIfPresent(String.self, forKey: .resultId) + self.items = try container.decode([Diagnostic].self, forKey: .items) + self.uri = try container.decode(DocumentURI.self, forKey: .uri) + self.version = try container.decodeIfPresent(Int.self, forKey: .version) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(DocumentDiagnosticReportKind.full, forKey: .kind) + try container.encodeIfPresent(self.resultId, forKey: .resultId) + try container.encode(self.items, forKey: .items) + try container.encode(self.uri, forKey: .uri) + try container.encodeIfPresent(self.version, forKey: .version) + } +} + +/// An unchanged document diagnostic report for a workspace diagnostic result. +public struct WorkspaceUnchangedDocumentDiagnosticReport: Codable, Hashable { + /// A result id which will be sent on the next + /// diagnostic request for the same document. + public var resultId: String + + + /// The URI for which diagnostic information is reported. + public var uri: DocumentURI + + /// The version number for which the diagnostics are reported. + /// If the document is not marked as open `null` can be provided. + public var version: Int? + + public init(resultId: String, uri: DocumentURI, version: Int? = nil) { + self.resultId = resultId + self.uri = uri + self.version = version + } + + enum CodingKeys: CodingKey { + case kind + case resultId + case uri + case version + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let kind = try container.decode(DocumentDiagnosticReportKind.self, forKey: .kind) + guard kind == .unchanged else { + throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of FullDocumentDiagnosticReport is not 'unchanged'") + } + self.resultId = try container.decode(String.self, forKey: .resultId) + self.uri = try container.decode(DocumentURI.self, forKey: .uri) + self.version = try container.decodeIfPresent(Int.self, forKey: .version) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(DocumentDiagnosticReportKind.unchanged, forKey: .kind) + try container.encode(self.resultId, forKey: .resultId) + try container.encode(self.uri, forKey: .uri) + try container.encodeIfPresent(self.version, forKey: .version) + } +} + +/// A workspace diagnostic document report. +public enum WorkspaceDocumentDiagnosticReport: Codable, Hashable { + case full(WorkspaceFullDocumentDiagnosticReport) + case unchanged(WorkspaceUnchangedDocumentDiagnosticReport) + + public init(from decoder: Decoder) throws { + if let full = try? WorkspaceFullDocumentDiagnosticReport(from: decoder) { + self = .full(full) + } else if let unchanged = try? WorkspaceUnchangedDocumentDiagnosticReport(from: decoder) { + self = .unchanged(unchanged) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected WorkspaceFullDocumentDiagnosticReport or WorkspaceUnchangedDocumentDiagnosticReport") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .full(let full): + try full.encode(to: encoder) + case .unchanged(let unchanged): + try unchanged.encode(to: encoder) + } + } +} diff --git a/Sources/LanguageServerProtocol/Requests/WorkspaceSymbolResolveRequest.swift b/Sources/LanguageServerProtocol/Requests/WorkspaceSymbolResolveRequest.swift new file mode 100644 index 000000000..078372c46 --- /dev/null +++ b/Sources/LanguageServerProtocol/Requests/WorkspaceSymbolResolveRequest.swift @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct WorkspaceSymbolResolveRequest: RequestType { + public static var method: String = "workspaceSymbol/resolve" + public typealias Response = WorkspaceSymbol + + public var workspaceSymbol: WorkspaceSymbol + + public init(workspaceSymbol: WorkspaceSymbol) { + self.workspaceSymbol = workspaceSymbol + } + + public init(from decoder: Decoder) throws { + self.workspaceSymbol = try WorkspaceSymbol(from: decoder) + } + + public func encode(to encoder: Encoder) throws { + try self.workspaceSymbol.encode(to: encoder) + } +} diff --git a/Sources/LanguageServerProtocol/Requests/WorkspaceSymbolsRequest.swift b/Sources/LanguageServerProtocol/Requests/WorkspaceSymbolsRequest.swift index a342b2145..cecd71944 100644 --- a/Sources/LanguageServerProtocol/Requests/WorkspaceSymbolsRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/WorkspaceSymbolsRequest.swift @@ -24,7 +24,7 @@ public struct WorkspaceSymbolsRequest: RequestType, Hashable { public static let method: String = "workspace/symbol" - public typealias Response = [SymbolInformation]? + public typealias Response = [WorkspaceSymbolItem]? /// The document in which to lookup the symbol location. public var query: String @@ -34,11 +34,38 @@ public struct WorkspaceSymbolsRequest: RequestType, Hashable { } } +public enum WorkspaceSymbolItem: ResponseType, Hashable { + case symbolInformation(SymbolInformation) + case workspaceSymbol(WorkspaceSymbol) + + public init(from decoder: Decoder) throws { + if let symbolInformation = try? SymbolInformation(from: decoder) { + self = .symbolInformation(symbolInformation) + } else if let workspaceSymbol = try? WorkspaceSymbol(from: decoder) { + self = .workspaceSymbol(workspaceSymbol) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected SymbolInformation or WorkspaceSymbol") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .symbolInformation(let symbolInformation): + try symbolInformation.encode(to: encoder) + case .workspaceSymbol(let workspaceSymbol): + try workspaceSymbol.encode(to: encoder) + } + } +} + public struct SymbolInformation: Hashable, ResponseType { public var name: String public var kind: SymbolKind + public var tags: [SymbolTag]? + public var deprecated: Bool? public var location: Location @@ -47,13 +74,86 @@ public struct SymbolInformation: Hashable, ResponseType { public init(name: String, kind: SymbolKind, + tags: [SymbolTag]? = nil, deprecated: Bool? = nil, location: Location, containerName: String? = nil) { self.name = name self.kind = kind + self.tags = tags self.deprecated = deprecated self.location = location self.containerName = containerName } } + +/// A special workspace symbol that supports locations without a range +public struct WorkspaceSymbol: ResponseType, Hashable { + public enum WorkspaceSymbolLocation: Codable, Hashable { + public struct URI: Codable, Hashable { + public var uri: DocumentURI + + public init(uri: DocumentURI) { + self.uri = uri + } + } + + case location(Location) + case uri(URI) + + public init(from decoder: Decoder) throws { + if let location = try? Location(from: decoder) { + self = .location(location) + } else if let uri = try? WorkspaceSymbolLocation.URI(from: decoder) { + self = .uri(uri) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected Location or object containing a URI") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .location(let location): + try location.encode(to: encoder) + case .uri(let uri): + try uri.encode(to: encoder) + } + } + } + + /// The name of this symbol. + public var name: String + + /// The kind of this symbol. + public var kind: SymbolKind + + /// Tags for this completion item. + public var tags: [SymbolTag]? + + /// The name of the symbol containing this symbol. This information is for + /// user interface purposes (e.g. to render a qualifier in the user interface + /// if necessary). It can't be used to re-infer a hierarchy for the document + /// symbols. + public var containerName: String? + + /// The location of this symbol. Whether a server is allowed to + /// return a location without a range depends on the client + /// capability `workspace.symbol.resolveSupport`. + /// + /// See also `SymbolInformation.location`. + public var location: WorkspaceSymbolLocation + + /// A data entry field that is preserved on a workspace symbol between a + /// workspace symbol request and a workspace symbol resolve request. + public var data: LSPAny? + + public init(name: String, kind: SymbolKind, tags: [SymbolTag]? = nil, containerName: String? = nil, location: WorkspaceSymbolLocation, data: LSPAny? = nil) { + self.name = name + self.kind = kind + self.tags = tags + self.containerName = containerName + self.location = location + self.data = data + } +} diff --git a/Sources/LanguageServerProtocol/SupportTypes/CodeActionKind.swift b/Sources/LanguageServerProtocol/SupportTypes/CodeActionKind.swift index 57777b42b..924775913 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/CodeActionKind.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/CodeActionKind.swift @@ -43,4 +43,11 @@ public struct CodeActionKind: RawRepresentable, Codable, Hashable { /// Organize imports action. public static let sourceOrganizeImports: CodeActionKind = CodeActionKind(rawValue: "source.organizeImports") + + /// Base kind for a 'fix all' source action: `source.fixAll`. + /// + /// 'Fix all' actions automatically fix errors that have a clear fix that + /// do not require user input. They should not suppress errors or perform + /// unsafe fixes such as generating new types or classes. + public static let sourceFixAll: CodeActionKind = CodeActionKind(rawValue: "source.fixAll") } diff --git a/Sources/LanguageServerProtocol/SupportTypes/CompletionItem.swift b/Sources/LanguageServerProtocol/SupportTypes/CompletionItem.swift index ab61f94d8..26e9ea335 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/CompletionItem.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/CompletionItem.swift @@ -10,20 +10,93 @@ // //===----------------------------------------------------------------------===// +/// Additional details for a completion item label. +public struct CompletionItemLabelDetails: Codable, Hashable { + + /// An optional string which is rendered less prominently directly after + /// {@link CompletionItem.label label}, without any spacing. Should be + /// used for function signatures or type annotations. + public var detail: String? + + /// An optional string which is rendered less prominently after + /// `CompletionItemLabelDetails.detail`. Should be used for fully qualified + /// names or file path. + public var description: String? + + public init(detail: String? = nil, description: String? = nil) { + self.detail = detail + self.description = description + } +} + +/// Completion item tags are extra annotations that tweak the rendering of a +/// completion item. +public struct CompletionItemTag: RawRepresentable, Codable, Hashable { + public var rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } + + /// Render a completion as obsolete, usually using a strike-out. + public static let deprecated = CompletionItemTag(rawValue: 1) +} + +public enum CompletionItemEdit: Codable, Hashable { + case textEdit(TextEdit) + case insertReplaceEdit(InsertReplaceEdit) + + public init(from decoder: Decoder) throws { + if let textEdit = try? TextEdit(from: decoder) { + self = .textEdit(textEdit) + } else if let insertReplaceEdit = try? InsertReplaceEdit(from: decoder) { + self = .insertReplaceEdit(insertReplaceEdit) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected TextEdit or InsertReplaceEdit") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .textEdit(let textEdit): + try textEdit.encode(to: encoder) + case .insertReplaceEdit(let insertReplaceEdit): + try insertReplaceEdit.encode(to: encoder) + } + } +} + /// A single completion result. -public struct CompletionItem: Codable, Hashable { +public struct CompletionItem: ResponseType, Codable, Hashable { /// The display name of the completion. public var label: String + /// Additional details for the label + public var labelDetails: CompletionItemLabelDetails? + /// The kind of completion item - e.g. method, property. public var kind: CompletionItemKind + /// Tags for this completion item. + public var tags: [CompletionItemTag]? + /// An extended human-readable name (longer than `label`, but simpler than `documentation`). public var detail: String? /// A human-readable string that represents a doc-comment. - public var documentation: CompletionItemDocumentation? + public var documentation: StringOrMarkupContent? + + /// Whether the completion is for a deprecated symbol. + public var deprecated: Bool? + + /// Select this item when showing. + /// + /// *Note* that only one completion item can be selected and that the + /// tool / client decides which item that is. The rule is that the *first* + /// item of those that match best is selected. + public var preselect: Bool? /// The name to use for sorting the result. If `nil`, use `label. public var sortText: String? @@ -31,13 +104,6 @@ public struct CompletionItem: Codable, Hashable { /// The name to use for filtering the result. If `nil`, use `label. public var filterText: String? - /// The (primary) edit to apply to the document if this completion is accepted. - /// - /// This takes precedence over `insertText`. - /// - /// - Note: The range of the edit must contain the completion position. - public var textEdit: TextEdit? - /// **Deprecated**: use `textEdit` /// /// The string to insert into the document. If `nil`, use `label. @@ -46,31 +112,92 @@ public struct CompletionItem: Codable, Hashable { /// The format of the `textEdit.nextText` or `insertText` value. public var insertTextFormat: InsertTextFormat? - /// Whether the completion is for a deprecated symbol. - public var deprecated: Bool? + /// How whitespace and indentation is handled during completion + /// item insertion. If not provided the client's default value depends on + /// the `textDocument.completion.insertTextMode` client capability. + public var insertTextMode: InsertTextMode? + + /// The (primary) edit to apply to the document if this completion is accepted. + /// + /// This takes precedence over `insertText`. + /// + /// - Note: The range of the edit must contain the completion position. + public var textEdit: CompletionItemEdit? + + /// The edit text used if the completion item is part of a CompletionList and + /// CompletionList defines an item default for the text edit range. + /// + /// Clients will only honor this property if they opt into completion list + /// item defaults using the capability `completionList.itemDefaults`. + /// + /// If not provided and a list's default range is provided the label + /// property is used as a text. + public var textEditText: String? + + /// An optional array of additional text edits that are applied when + /// selecting this completion. Edits must not overlap (including the same + /// insert position) with the main edit nor with themselves. + /// + /// Additional text edits should be used to change text unrelated to the + /// current cursor position (for example adding an import statement at the + /// top of the file if the completion item will insert an unqualified type). + public var additionalTextEdits: [TextEdit]? + + /// An optional set of characters that when pressed while this completion is + /// active will accept it first and then type that character. *Note* that all + /// commit characters should have `length=1` and that superfluous characters + /// will be ignored. + public var commitCharacters: [String]? + + /// An optional command that is executed *after* inserting this completion. + /// *Note* that additional modifications to the current document should be + /// described with the additionalTextEdits-property. + public var command: Command? + + /// A data entry field that is preserved on a completion item between + /// a completion and a completion resolve request. + public var data: LSPAny? public init( label: String, + labelDetails: CompletionItemLabelDetails? = nil, kind: CompletionItemKind, + tags: [CompletionItemTag]? = nil, detail: String? = nil, - documentation: CompletionItemDocumentation? = nil, + documentation: StringOrMarkupContent? = nil, + deprecated: Bool? = nil, + preselect: Bool? = nil, sortText: String? = nil, filterText: String? = nil, - textEdit: TextEdit? = nil, insertText: String? = nil, insertTextFormat: InsertTextFormat? = nil, - deprecated: Bool? = nil) - { + insertTextMode: InsertTextMode? = nil, + textEdit: CompletionItemEdit? = nil, + textEditText: String? = nil, + additionalTextEdits: [TextEdit]? = nil, + commitCharacters: [String]? = nil, + command: Command? = nil, + data: LSPAny? = nil + ) { self.label = label + self.labelDetails = labelDetails + self.kind = kind + self.tags = tags self.detail = detail self.documentation = documentation + self.deprecated = deprecated + self.preselect = preselect self.sortText = sortText self.filterText = filterText - self.textEdit = textEdit self.insertText = insertText self.insertTextFormat = insertTextFormat - self.kind = kind - self.deprecated = deprecated + self.insertTextMode = insertTextMode + self.textEdit = textEdit + self.textEditText = textEditText + self.additionalTextEdits = additionalTextEdits + self.commitCharacters = commitCharacters + self.command = command + self.data = data } } @@ -84,29 +211,28 @@ public enum InsertTextFormat: Int, Codable, Hashable { case snippet = 2 } -public enum CompletionItemDocumentation: Hashable { - case string(String) - case markupContent(MarkupContent) -} +/// How whitespace and indentation is handled during completion +/// item insertion. +public struct InsertTextMode: RawRepresentable, Codable, Hashable { + public var rawValue: Int -extension CompletionItemDocumentation: Codable { - public init(from decoder: Decoder) throws { - if let string = try? String(from: decoder) { - self = .string(string) - } else if let markupContent = try? MarkupContent(from: decoder) { - self = .markupContent(markupContent) - } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected String or MarkupContent") - throw DecodingError.dataCorrupted(context) - } + public init(rawValue: Int) { + self.rawValue = rawValue } - public func encode(to encoder: Encoder) throws { - switch self { - case .string(let string): - try string.encode(to: encoder) - case .markupContent(let markupContent): - try markupContent.encode(to: encoder) - } - } + /// The insertion or replace strings is taken as it is. If the + /// value is multi line the lines below the cursor will be + /// inserted using the indentation defined in the string value. + /// The client will not apply any kind of adjustments to the + /// string. + public static let asIs = InsertTextMode(rawValue: 1) + + /// The editor adjusts leading whitespace of new lines so that + /// they match the indentation up to the cursor of the line for + /// which the item is accepted. + /// + /// Consider a line like this: <2tabs><3tabs>foo. Accepting a + /// multi line completion item is indented using 2 tabs and all + /// following lines inserted will be indented using 2 tabs as well. + public static let adjustIndentation = InsertTextMode(rawValue: 2) } diff --git a/Sources/LanguageServerProtocol/SupportTypes/Diagnostic.swift b/Sources/LanguageServerProtocol/SupportTypes/Diagnostic.swift index 684442e30..fa7d80df9 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/Diagnostic.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/Diagnostic.swift @@ -58,11 +58,16 @@ public struct Diagnostic: Codable, Hashable { /// The diagnostic message. public var message: String + /// Additional metadata about the diagnostic. + public var tags: [DiagnosticTag]? + /// Related diagnostic notes. public var relatedInformation: [DiagnosticRelatedInformation]? - /// Additional metadata about the diagnostic. - public var tags: [DiagnosticTag]? + /// A data entry field that is preserved between a + /// `textDocument/publishDiagnostics` notification and + /// `textDocument/codeAction` request. + public var data: LSPAny? /// All the code actions that address this diagnostic. /// **LSP Extension from clangd**. @@ -77,6 +82,7 @@ public struct Diagnostic: Codable, Hashable { message: String, tags: [DiagnosticTag]? = nil, relatedInformation: [DiagnosticRelatedInformation]? = nil, + data: LSPAny? = nil, codeActions: [CodeAction]? = nil) { self._range = CustomCodable(wrappedValue: range) @@ -87,6 +93,7 @@ public struct Diagnostic: Codable, Hashable { self.message = message self.tags = tags self.relatedInformation = relatedInformation + self.data = data self.codeActions = codeActions } } @@ -100,15 +107,25 @@ public struct DiagnosticTag: RawRepresentable, Codable, Hashable { self.rawValue = rawValue } + /// Unused or unnecessary code. + /// + /// Clients are allowed to render diagnostics with this tag faded out + /// instead of having an error squiggle. public static let unnecessary: DiagnosticTag = DiagnosticTag(rawValue: 1) + + /// Deprecated or obsolete code. + /// + /// Clients are allowed to rendered diagnostics with this tag strike through. public static let deprecated: DiagnosticTag = DiagnosticTag(rawValue: 2) } /// A 'note' diagnostic attached to a primary diagonstic that provides additional information. public struct DiagnosticRelatedInformation: Codable, Hashable { + /// The location of this related diagnostic information. public var location: Location + /// The message of this related diagnostic information. public var message: String /// All the code actions that address the parent diagnostic via this note. diff --git a/Sources/LanguageServerProtocol/SupportTypes/InlayHint.swift b/Sources/LanguageServerProtocol/SupportTypes/InlayHint.swift index 239cb7307..08f45a2c3 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/InlayHint.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/InlayHint.swift @@ -15,17 +15,17 @@ public struct InlayHint: ResponseType, Codable, Hashable { /// The position within the code that this hint is attached to. public var position: Position - /// The hint's kind, used for more flexible client-side styling. - public let kind: InlayHintKind? - /// The hint's text, e.g. a printed type public let label: InlayHintLabel + /// The hint's kind, used for more flexible client-side styling. + public let kind: InlayHintKind? + /// Optional text edits that are performed when accepting this inlay hint. public let textEdits: [TextEdit]? /// The tooltip text displayed when the inlay hint is hovered. - public let tooltip: MarkupContent? + public let tooltip: StringOrMarkupContent? /// Whether to render padding before the hint. public let paddingLeft: Bool? @@ -39,17 +39,17 @@ public struct InlayHint: ResponseType, Codable, Hashable { public init( position: Position, - kind: InlayHintKind? = nil, label: InlayHintLabel, + kind: InlayHintKind? = nil, textEdits: [TextEdit]? = nil, - tooltip: MarkupContent? = nil, + tooltip: StringOrMarkupContent? = nil, paddingLeft: Bool? = nil, paddingRight: Bool? = nil, data: LSPAny? = nil ) { self.position = position - self.kind = kind self.label = label + self.kind = kind self.textEdits = textEdits self.tooltip = tooltip self.paddingLeft = paddingLeft @@ -117,7 +117,7 @@ public struct InlayHintLabelPart: Codable, Hashable { public let value: String /// The tooltip to show when the part is hovered. - public let tooltip: MarkupContent? + public let tooltip: StringOrMarkupContent? /// An optional source code location representing this part. /// Used by the editor for hover and code navigation, e.g. @@ -129,7 +129,7 @@ public struct InlayHintLabelPart: Codable, Hashable { public init( value: String, - tooltip: MarkupContent? = nil, + tooltip: StringOrMarkupContent? = nil, location: Location? = nil, command: Command? = nil ) { diff --git a/Sources/LanguageServerProtocol/SupportTypes/InsertReplaceEdit.swift b/Sources/LanguageServerProtocol/SupportTypes/InsertReplaceEdit.swift new file mode 100644 index 000000000..5a3fd5a4d --- /dev/null +++ b/Sources/LanguageServerProtocol/SupportTypes/InsertReplaceEdit.swift @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +/// A special text edit to provide an insert and a replace operation. +public struct InsertReplaceEdit: Codable, Hashable { + /// The string to be inserted. + public var newText: String + + /// The range if the insert is requested + @CustomCodable + public var insert: Range + + /// The range if the replace is requested. + @CustomCodable + public var replace: Range + + public init(newText: String, insert: Range, replace: Range) { + self.newText = newText + self.insert = insert + self.replace = replace + } +} diff --git a/Sources/LanguageServerProtocol/SupportTypes/LSPAny.swift b/Sources/LanguageServerProtocol/SupportTypes/LSPAny.swift index 99fe385a2..913a07537 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/LSPAny.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/LSPAny.swift @@ -170,3 +170,6 @@ extension Array: LSPAnyCodable where Element: LSPAnyCodable { return .array(map { $0.encodeToLSPAny() }) } } + +public typealias LSPObject = [String: LSPAny] +public typealias LSPArray = [LSPAny] diff --git a/Sources/LanguageServerProtocol/SupportTypes/NotebookCellTextDocumentFilter.swift b/Sources/LanguageServerProtocol/SupportTypes/NotebookCellTextDocumentFilter.swift new file mode 100644 index 000000000..2aa9d1a0f --- /dev/null +++ b/Sources/LanguageServerProtocol/SupportTypes/NotebookCellTextDocumentFilter.swift @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +/// A notebook document filter denotes a notebook document by different properties. +public struct NotebookDocumentFilter: Codable, Hashable { + /// The type of the enclosing notebook. + public var notebookType: String? + + /// A Uri [scheme](#Uri.scheme), like `file` or `untitled`. + public var scheme: String? + + /// A glob pattern. + public var pattern: String? + + public init(notebookType: String? = nil, scheme: String? = nil, pattern: String? = nil) { + self.notebookType = notebookType + self.scheme = scheme + self.pattern = pattern + } +} + +/// A notebook cell text document filter denotes a cell text +/// document by different properties. +public struct NotebookCellTextDocumentFilter: Codable, Hashable { + public enum NotebookFilter: Codable, Hashable { + case string(String) + case notebookDocumentFilter(NotebookDocumentFilter) + + public init(from decoder: Decoder) throws { + if let string = try? String(from: decoder) { + self = .string(string) + } else if let filter = try? NotebookDocumentFilter(from: decoder) { + self = .notebookDocumentFilter(filter) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "NotebookFilter must be either a String or NotebookDocumentFilter") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .string(let string): + try string.encode(to: encoder) + case .notebookDocumentFilter(let filter): + try filter.encode(to: encoder) + } + } + } + + /// A filter that matches against the notebook + /// containing the notebook cell. If a string + /// value is provided it matches against the + /// notebook type. '*' matches every notebook. + public var notebook: NotebookFilter + + /// A language id like `python`. + /// + /// Will be matched against the language id of the + /// notebook cell document. '*' matches every language. + public var language: String? + + public init(notebook: NotebookFilter, language: String? = nil) { + self.notebook = notebook + self.language = language + } +} diff --git a/Sources/LanguageServerProtocol/SupportTypes/NotebookDocument.swift b/Sources/LanguageServerProtocol/SupportTypes/NotebookDocument.swift new file mode 100644 index 000000000..81cdc3466 --- /dev/null +++ b/Sources/LanguageServerProtocol/SupportTypes/NotebookDocument.swift @@ -0,0 +1,97 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public struct NotebookDocument: Codable, Hashable { + + /// The notebook document's URI. + public var uri: DocumentURI + + /// The type of the notebook. + public var notebookType: String + + /// The version number of this document (it will increase after each + /// change, including undo/redo). + public var version: Int + + /// Additional metadata stored with the notebook + /// document. + public var metadata: LSPObject? + + /// The cells of a notebook. + public var cells: [NotebookCell] + + public init(uri: DocumentURI, notebookType: String, version: Int, metadata: LSPObject? = nil, cells: [NotebookCell]) { + self.uri = uri + self.notebookType = notebookType + self.version = version + self.metadata = metadata + self.cells = cells + } +} + +/// A notebook cell kind. +public struct NotebookCellKind: RawRepresentable, Codable, Hashable { + public var rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } + + /// A markup-cell is formatted source that is used for display. + public static var markup = NotebookCellKind(rawValue: 1) + + /// A code-cell is source code. + public static var code = NotebookCellKind(rawValue: 2) +} + +public struct ExecutionSummary: Codable, Hashable { + /// A strict monotonically increasing value + /// indicating the execution order of a cell + /// inside a notebook. + public var executionOrder: Int + + /// Whether the execution was successful or + /// not if known by the client. + public var success: Bool? + + public init(executionOrder: Int, success: Bool?) { + self.executionOrder = executionOrder + self.success = success + } +} + +/// A notebook cell. +/// +/// A cell's document URI must be unique across ALL notebook +/// cells and can therefore be used to uniquely identify a +/// notebook cell or the cell's text document. +public struct NotebookCell: Codable, Hashable { + + /// The cell's kind + public var kind: NotebookCellKind + + /// The URI of the cell's text document content. + public var document: DocumentURI + + /// Additional metadata stored with the cell. + public var metadata: LSPObject? + + /// Additional execution summary information if supported by the client. + public var executionSummary: ExecutionSummary? + + public init(kind: NotebookCellKind, document: DocumentURI, metadata: LSPObject? = nil, executionSummary: ExecutionSummary? = nil) { + self.kind = kind + self.document = document + self.metadata = metadata + self.executionSummary = executionSummary + } +} diff --git a/Sources/LanguageServerProtocol/SupportTypes/NotebookDocumentChangeEvent.swift b/Sources/LanguageServerProtocol/SupportTypes/NotebookDocumentChangeEvent.swift new file mode 100644 index 000000000..220141f05 --- /dev/null +++ b/Sources/LanguageServerProtocol/SupportTypes/NotebookDocumentChangeEvent.swift @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +/// A change describing how to move a `NotebookCell` +/// array from state S to S'. +public struct NotebookCellArrayChange: Codable, Hashable { + /// The start offset of the cell that changed. + public var start: Int + + /// The deleted cells + public var deleteCount: Int + + /// The new cells, if any + public var cells: [NotebookCell]? + + public init(start: Int, deleteCount: Int, cells: [NotebookCell]? = nil) { + self.start = start + self.deleteCount = deleteCount + self.cells = cells + } +} + +/// A change event for a notebook document. +public struct NotebookDocumentChangeEvent: Codable, Hashable { + public struct CellsStructure: Codable, Hashable { + /// The change to the cell array. + public var array: NotebookCellArrayChange + + /// Additional opened cell text documents. + public var didOpen: [TextDocumentItem]? + + /// Additional closed cell text documents. + public var didClose: [TextDocumentIdentifier]? + + public init(array: NotebookCellArrayChange, didOpen: [TextDocumentItem]? = nil, didClose: [TextDocumentIdentifier]? = nil) { + self.array = array + self.didOpen = didOpen + self.didClose = didClose + } + } + + public struct CellsTextContent: Codable, Hashable { + public var document: VersionedTextDocumentIdentifier + public var changes: [TextDocumentContentChangeEvent] + + public init(document: VersionedTextDocumentIdentifier, changes: [TextDocumentContentChangeEvent]) { + self.document = document + self.changes = changes + } + } + + public struct Cells: Codable, Hashable { + /// Changes to the cell structure to add or + /// remove cells. + public var structure: CellsStructure? + + /// Changes to notebook cells properties like its + /// kind, execution summary or metadata. + public var data: [NotebookCell]? + + /// Changes to the text content of notebook cells. + public var textContent: [CellsTextContent]? + + public init(structure: CellsStructure? = nil, data: [NotebookCell]? = nil, textContent: [CellsTextContent]? = nil) { + self.structure = structure + self.data = data + self.textContent = textContent + } + } + + /// The changed meta data if any. + public var metadata: LSPObject? + + /// Changes to cells + public var cells: Cells? + + public init(metadata: LSPObject? = nil, cells: Cells? = nil) { + self.metadata = metadata + self.cells = cells + } +} diff --git a/Sources/LanguageServerProtocol/SupportTypes/NotebookDocumentIdentifier.swift b/Sources/LanguageServerProtocol/SupportTypes/NotebookDocumentIdentifier.swift new file mode 100644 index 000000000..72bfc5fe0 --- /dev/null +++ b/Sources/LanguageServerProtocol/SupportTypes/NotebookDocumentIdentifier.swift @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 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 +// +//===----------------------------------------------------------------------===// + +/// A literal to identify a notebook document in the client. +public struct NotebookDocumentIdentifier: Hashable, Codable { + + /// The notebook document's URI. + public var uri: DocumentURI + + public init(_ uri: DocumentURI) { + self.uri = uri + } +} diff --git a/Sources/LanguageServerProtocol/SupportTypes/PositionEncoding.swift b/Sources/LanguageServerProtocol/SupportTypes/PositionEncoding.swift new file mode 100644 index 000000000..6d25cb5f8 --- /dev/null +++ b/Sources/LanguageServerProtocol/SupportTypes/PositionEncoding.swift @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +/// A set of predefined position encoding kinds. +public struct PositionEncodingKind: RawRepresentable, Codable, Hashable { + public var rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } + + /// Character offsets count UTF-8 code units. + public static let utf8: PositionEncodingKind = PositionEncodingKind(rawValue: "utf-8") + + /// Character offsets count UTF-16 code units. + /// + /// This is the default and must always be supported + /// by servers + public static let utf16: PositionEncodingKind = PositionEncodingKind(rawValue: "utf-16") + + /// Character offsets count UTF-32 code units. + /// + /// Implementation note: these are the same as Unicode code points, + /// so this `PositionEncodingKind` may also be used for an + /// encoding-agnostic representation of character offsets. + public static let utf32: PositionEncodingKind = PositionEncodingKind(rawValue: "utf-32") +} diff --git a/Sources/LanguageServerProtocol/SupportTypes/ProgressToken.swift b/Sources/LanguageServerProtocol/SupportTypes/ProgressToken.swift new file mode 100644 index 000000000..41491e769 --- /dev/null +++ b/Sources/LanguageServerProtocol/SupportTypes/ProgressToken.swift @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public enum ProgressToken: Codable, Hashable { + case integer(Int) + case string(String) + + public init(from decoder: Decoder) throws { + if let integer = try? Int(from: decoder) { + self = .integer(integer) + } else if let string = try? String(from: decoder) { + self = .string(string) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected Int or String") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .integer(let integer): + try integer.encode(to: encoder) + case .string(let string): + try string.encode(to: encoder) + } + } +} diff --git a/Sources/LanguageServerProtocol/SupportTypes/StringOrMarkupContent.swift b/Sources/LanguageServerProtocol/SupportTypes/StringOrMarkupContent.swift new file mode 100644 index 000000000..c6be75b2e --- /dev/null +++ b/Sources/LanguageServerProtocol/SupportTypes/StringOrMarkupContent.swift @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public enum StringOrMarkupContent: Codable, Hashable { + case string(String) + case markupContent(MarkupContent) + + public init(from decoder: Decoder) throws { + if let string = try? String(from: decoder) { + self = .string(string) + } else if let markupContent = try? MarkupContent(from: decoder) { + self = .markupContent(markupContent) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected String or MarkupContent") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .string(let string): + try string.encode(to: encoder) + case .markupContent(let markupContent): + try markupContent.encode(to: encoder) + } + } +} diff --git a/Sources/LanguageServerProtocol/SupportTypes/TextDocumentEdit.swift b/Sources/LanguageServerProtocol/SupportTypes/TextDocumentEdit.swift index 76608a27a..65bac7ebd 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/TextDocumentEdit.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/TextDocumentEdit.swift @@ -14,14 +14,38 @@ /// /// For an edit where the document is implied, use `TextEdit`. public struct TextDocumentEdit: Hashable, Codable { + public enum Edit: Codable, Hashable { + case textEdit(TextEdit) + case annotatedTextEdit(AnnotatedTextEdit) + + public init(from decoder: Decoder) throws { + if let annotated = try? AnnotatedTextEdit(from: decoder) { + self = .annotatedTextEdit(annotated) + } else if let edit = try? TextEdit(from: decoder) { + self = .textEdit(edit) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected AnnotatedTextEdit or TextEdit") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .textEdit(let edit): + try edit.encode(to: encoder) + case .annotatedTextEdit(let annotated): + try annotated.encode(to: encoder) + } + } + } /// The potentially versioned document to which these edits apply. - public var textDocument: VersionedTextDocumentIdentifier + public var textDocument: OptionalVersionedTextDocumentIdentifier /// The edits to be applied, which must be non-overlapping. - public var edits: [TextEdit] + public var edits: [Edit] - public init(textDocument: VersionedTextDocumentIdentifier, edits: [TextEdit]) { + public init(textDocument: OptionalVersionedTextDocumentIdentifier, edits: [Edit]) { self.textDocument = textDocument self.edits = edits } diff --git a/Sources/LanguageServerProtocol/SupportTypes/TextEdit.swift b/Sources/LanguageServerProtocol/SupportTypes/TextEdit.swift index c353dfd89..b60b8e6bc 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/TextEdit.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/TextEdit.swift @@ -47,3 +47,50 @@ extension TextEdit: LSPAnyCodable { ]) } } + +/// Additional information that describes document changes. +public struct ChangeAnnotation: Codable, Hashable { + /// A human-readable string describing the actual change. The string + /// is rendered prominent in the user interface. + public var label: String + + /// A flag which indicates that user confirmation is needed + /// before applying the change. + public var needsConfirmation: Bool? = nil + + /// A human-readable string which is rendered less prominent in + /// the user interface. + public var description: String? = nil + + public init(label: String, needsConfirmation: Bool? = nil, description: String? = nil) { + self.label = label + self.needsConfirmation = needsConfirmation + self.description = description + } +} + + +/// An identifier referring to a change annotation managed by a workspace +/// edit. +public typealias ChangeAnnotationIdentifier = String + +/// A special text edit with an additional change annotation. +/// +/// Notionally a subtype of `TextEdit`. +public struct AnnotatedTextEdit: ResponseType, Hashable { + + /// The range of text to be replaced. + @CustomCodable + public var range: Range + + /// The new text. + public var newText: String + + public var annotationId: ChangeAnnotationIdentifier + + public init(range: Range, newText: String, annotationId: ChangeAnnotationIdentifier) { + self._range = CustomCodable(wrappedValue: range) + self.newText = newText + self.annotationId = annotationId + } +} diff --git a/Sources/LanguageServerProtocol/SupportTypes/Tracing.swift b/Sources/LanguageServerProtocol/SupportTypes/Tracing.swift new file mode 100644 index 000000000..745c2656a --- /dev/null +++ b/Sources/LanguageServerProtocol/SupportTypes/Tracing.swift @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +public enum Tracing: String, Codable { + case off + case messages + case verbose +} diff --git a/Sources/LanguageServerProtocol/SupportTypes/VersionedNotebookDocumentIdentifier.swift b/Sources/LanguageServerProtocol/SupportTypes/VersionedNotebookDocumentIdentifier.swift new file mode 100644 index 000000000..0adca2af8 --- /dev/null +++ b/Sources/LanguageServerProtocol/SupportTypes/VersionedNotebookDocumentIdentifier.swift @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 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 +// +//===----------------------------------------------------------------------===// + +/// Unique identifier for a document. +public struct VersionedNotebookDocumentIdentifier: Codable, Hashable { + + /// The version number of this notebook document. + public var version: Int + + /// The notebook document's URI. + public var uri: DocumentURI + + public init(version: Int, uri: DocumentURI) { + self.version = version + self.uri = uri + } +} diff --git a/Sources/LanguageServerProtocol/SupportTypes/VersionedTextDocumentIdentifier.swift b/Sources/LanguageServerProtocol/SupportTypes/VersionedTextDocumentIdentifier.swift index 7e6504394..5af3ad7dd 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/VersionedTextDocumentIdentifier.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/VersionedTextDocumentIdentifier.swift @@ -18,7 +18,35 @@ public struct VersionedTextDocumentIdentifier: Hashable, Codable { /// A URI that uniquely identifies the document. public var uri: DocumentURI - /// The version number of this document, or nil if unknown. + /// The version number of this document. + /// + /// The version number of a document will increase after each change, + /// including undo/redo. The number doesn't need to be consecutive. + public var version: Int + + public init(_ uri: DocumentURI, version: Int) { + self.uri = uri + self.version = version + } +} + +/// An identifier which optionally denotes a specific version of a text document. This information usually flows from the server to the client. +/// +/// Notionally a subtype of `TextDocumentIdentifier`. +public struct OptionalVersionedTextDocumentIdentifier: Hashable, Codable { + + /// A URI that uniquely identifies the document. + public var uri: DocumentURI + + /// The version number of this document. If an optional versioned text document + /// identifier is sent from the server to the client and the file is not + /// open in the editor (the server has not received an open notification + /// before) the server can send `null` to indicate that the version is + /// known and the content on disk is the master (as specified with document + /// content ownership). + /// + /// The version number of a document will increase after each change, + /// including undo/redo. The number doesn't need to be consecutive. public var version: Int? public init(_ uri: DocumentURI, version: Int?) { diff --git a/Sources/LanguageServerProtocol/SupportTypes/WorkspaceEdit.swift b/Sources/LanguageServerProtocol/SupportTypes/WorkspaceEdit.swift index 23de09e00..f345ab2b2 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/WorkspaceEdit.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/WorkspaceEdit.swift @@ -18,10 +18,20 @@ public struct WorkspaceEdit: Hashable, ResponseType { public var documentChanges: [WorkspaceEditDocumentChange]? + /// A map of change annotations that can be referenced in + /// `AnnotatedTextEdit`s or create, rename and delete file / folder + /// operations. + /// + /// Whether clients honor this property depends on the client capability + /// `workspace.changeAnnotationSupport`. + public var changeAnnotations: [ChangeAnnotationIdentifier: ChangeAnnotation]? + public init(changes: [DocumentURI: [TextEdit]]? = nil, - documentChanges: [WorkspaceEditDocumentChange]? = nil) { + documentChanges: [WorkspaceEditDocumentChange]? = nil, + changeAnnotation: [ChangeAnnotationIdentifier: ChangeAnnotation]? = nil) { self.changes = changes self.documentChanges = documentChanges + self.changeAnnotations = changeAnnotation } } @@ -114,10 +124,13 @@ public struct CreateFile: Codable, Hashable { public var uri: DocumentURI /// Additional options public var options: CreateFileOptions? + /// An optional annotation identifier describing the operation. + public var annotationId: ChangeAnnotationIdentifier? - public init(uri: DocumentURI, options: CreateFileOptions? = nil) { + public init(uri: DocumentURI, options: CreateFileOptions? = nil, annotationId: ChangeAnnotationIdentifier? = nil) { self.uri = uri self.options = options + self.annotationId = annotationId } // MARK: Codable conformance @@ -126,6 +139,7 @@ public struct CreateFile: Codable, Hashable { case kind case uri case options + case annotationId } public init(from decoder: Decoder) throws { @@ -136,6 +150,7 @@ public struct CreateFile: Codable, Hashable { } self.uri = try container.decode(DocumentURI.self, forKey: .uri) self.options = try container.decodeIfPresent(CreateFileOptions.self, forKey: .options) + self.annotationId = try container.decodeIfPresent(ChangeAnnotationIdentifier.self, forKey: .annotationId) } public func encode(to encoder: Encoder) throws { @@ -143,6 +158,7 @@ public struct CreateFile: Codable, Hashable { try container.encode("create", forKey: .kind) try container.encode(self.uri, forKey: .uri) try container.encodeIfPresent(self.options, forKey: .options) + try container.encodeIfPresent(self.annotationId, forKey: .annotationId) } } @@ -167,11 +183,14 @@ public struct RenameFile: Codable, Hashable { public var newUri: DocumentURI /// Rename options. public var options: RenameFileOptions? + /// An optional annotation identifier describing the operation. + public var annotationId: ChangeAnnotationIdentifier? - public init(oldUri: DocumentURI, newUri: DocumentURI, options: RenameFileOptions? = nil) { + public init(oldUri: DocumentURI, newUri: DocumentURI, options: RenameFileOptions? = nil, annotationId: ChangeAnnotationIdentifier? = nil) { self.oldUri = oldUri self.newUri = newUri self.options = options + self.annotationId = annotationId } // MARK: Codable conformance @@ -181,6 +200,7 @@ public struct RenameFile: Codable, Hashable { case oldUri case newUri case options + case annotationId } public init(from decoder: Decoder) throws { @@ -192,6 +212,7 @@ public struct RenameFile: Codable, Hashable { self.oldUri = try container.decode(DocumentURI.self, forKey: .oldUri) self.newUri = try container.decode(DocumentURI.self, forKey: .newUri) self.options = try container.decodeIfPresent(RenameFileOptions.self, forKey: .options) + self.annotationId = try container.decodeIfPresent(ChangeAnnotationIdentifier.self, forKey: .annotationId) } public func encode(to encoder: Encoder) throws { @@ -200,6 +221,7 @@ public struct RenameFile: Codable, Hashable { try container.encode(self.oldUri, forKey: .oldUri) try container.encode(self.newUri, forKey: .newUri) try container.encodeIfPresent(self.options, forKey: .options) + try container.encodeIfPresent(self.annotationId, forKey: .annotationId) } } @@ -222,10 +244,13 @@ public struct DeleteFile: Codable, Hashable { public var uri: DocumentURI /// Delete options. public var options: DeleteFileOptions? + /// An optional annotation identifier describing the operation. + public var annotationId: ChangeAnnotationIdentifier? - public init(uri: DocumentURI, options: DeleteFileOptions? = nil) { + public init(uri: DocumentURI, options: DeleteFileOptions? = nil, annotationId: ChangeAnnotationIdentifier? = nil) { self.uri = uri self.options = options + self.annotationId = annotationId } // MARK: Codable conformance @@ -234,6 +259,7 @@ public struct DeleteFile: Codable, Hashable { case kind case uri case options + case annotationId } public init(from decoder: Decoder) throws { @@ -244,6 +270,7 @@ public struct DeleteFile: Codable, Hashable { } self.uri = try container.decode(DocumentURI.self, forKey: .uri) self.options = try container.decodeIfPresent(DeleteFileOptions.self, forKey: .options) + self.annotationId = try container.decodeIfPresent(ChangeAnnotationIdentifier.self, forKey: .annotationId) } public func encode(to encoder: Encoder) throws { @@ -251,6 +278,7 @@ public struct DeleteFile: Codable, Hashable { try container.encode("delete", forKey: .kind) try container.encode(self.uri, forKey: .uri) try container.encodeIfPresent(self.options, forKey: .options) + try container.encodeIfPresent(self.annotationId, forKey: .annotationId) } } diff --git a/Sources/SourceKitLSP/Clang/ClangLanguageServer.swift b/Sources/SourceKitLSP/Clang/ClangLanguageServer.swift index 3de20bae4..40941de79 100644 --- a/Sources/SourceKitLSP/Clang/ClangLanguageServer.swift +++ b/Sources/SourceKitLSP/Clang/ClangLanguageServer.swift @@ -431,7 +431,7 @@ extension ClangLanguageServerShim { // with `forceRebuild` set in case any missing header files have been added. // This works well for us as the moment since clangd ignores the document version. let note = DidChangeTextDocumentNotification( - textDocument: VersionedTextDocumentIdentifier(uri, version: nil), + textDocument: VersionedTextDocumentIdentifier(uri, version: 0), contentChanges: [], forceRebuild: true) forwardNotificationToClangdOnQueue(note) diff --git a/Sources/SourceKitLSP/SourceKitServer.swift b/Sources/SourceKitLSP/SourceKitServer.swift index d99249286..6f25cfff4 100644 --- a/Sources/SourceKitLSP/SourceKitServer.swift +++ b/Sources/SourceKitLSP/SourceKitServer.swift @@ -1021,7 +1021,7 @@ extension SourceKitServer { func workspaceSymbols(_ req: Request) { let symbols = findWorkspaceSymbols( matching: req.params.query - ).map({symbolOccurrence -> SymbolInformation in + ).map({symbolOccurrence -> WorkspaceSymbolItem 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! @@ -1031,13 +1031,13 @@ extension SourceKitServer { uri: DocumentURI(URL(fileURLWithPath: symbolOccurrence.location.path)), range: Range(symbolPosition)) - return SymbolInformation( + return .symbolInformation(SymbolInformation( name: symbolOccurrence.symbol.name, kind: symbolOccurrence.symbol.kind.asLspSymbolKind(), deprecated: nil, location: symbolLocation, containerName: symbolOccurrence.getContainerName() - ) + )) }) req.reply(symbols) } diff --git a/Sources/SourceKitLSP/Swift/CodeCompletion.swift b/Sources/SourceKitLSP/Swift/CodeCompletion.swift index 1cc6c7389..55f5f693d 100644 --- a/Sources/SourceKitLSP/Swift/CodeCompletion.swift +++ b/Sources/SourceKitLSP/Swift/CodeCompletion.swift @@ -175,12 +175,12 @@ extension SwiftLanguageServer { kind: kind?.asCompletionItemKind(self.values) ?? .value, detail: typeName, documentation: docBrief != nil ? .markupContent(MarkupContent(kind: .markdown, value: docBrief!)) : nil, + deprecated: notRecommended ?? false, sortText: nil, filterText: filterName, - textEdit: textEdit, insertText: text, insertTextFormat: isInsertTextSnippet ? .snippet : .plain, - deprecated: notRecommended ?? false + textEdit: textEdit.map(CompletionItemEdit.textEdit) )) return true diff --git a/Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift b/Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift index 1e4186478..68e1d80e0 100644 --- a/Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift +++ b/Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift @@ -1295,11 +1295,6 @@ extension SwiftLanguageServer { } public func inlayHint(_ req: Request) { - guard req.params.only?.contains(.type) ?? true else { - req.reply([]) - return - } - let uri = req.params.textDocument.uri variableTypeInfos(uri, req.params.range) { infosResult in do { @@ -1312,8 +1307,8 @@ extension SwiftLanguageServer { let label = ": \(info.printedType)" return InlayHint( position: position, - kind: .type, label: .string(label), + kind: .type, textEdits: [ TextEdit(range: position..(_ value: T, mutate: (inout T) -> Void) -> T { diff --git a/Tests/SourceKitLSPTests/InlayHintTests.swift b/Tests/SourceKitLSPTests/InlayHintTests.swift index d3d31a5c6..05de97a53 100644 --- a/Tests/SourceKitLSPTests/InlayHintTests.swift +++ b/Tests/SourceKitLSPTests/InlayHintTests.swift @@ -64,8 +64,8 @@ final class InlayHintTests: XCTestCase { private func makeInlayHint(position: Position, kind: InlayHintKind, label: String) -> InlayHint { InlayHint( position: position, - kind: kind, label: .string(label), + kind: kind, textEdits: [ TextEdit(range: position.. Date: Fri, 2 Dec 2022 09:43:06 +0100 Subject: [PATCH 2/2] Add a test case checking that documentDependenciesUpdated works for clang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We weren’t sure if it works because it always sends 0 as the document’s version number, but everythig appears to be fine. --- ...itServer+WorkspaceForDocumentOnQueue.swift | 22 +++++++++++ .../SourceKitDTests/CrashRecoveryTests.swift | 8 ---- Tests/SourceKitLSPTests/LocalClangTests.swift | 39 +++++++++++++++++++ Tests/SourceKitLSPTests/WorkspaceTests.swift | 8 ---- 4 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 Sources/SKTestSupport/SourceKitServer+WorkspaceForDocumentOnQueue.swift diff --git a/Sources/SKTestSupport/SourceKitServer+WorkspaceForDocumentOnQueue.swift b/Sources/SKTestSupport/SourceKitServer+WorkspaceForDocumentOnQueue.swift new file mode 100644 index 000000000..92cfec865 --- /dev/null +++ b/Sources/SKTestSupport/SourceKitServer+WorkspaceForDocumentOnQueue.swift @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 +// +//===----------------------------------------------------------------------===// + +import LanguageServerProtocol +import SourceKitLSP + +public extension SourceKitServer { + func workspaceForDocumentOnQueue(uri: DocumentURI) -> Workspace? { + self.queue.sync { + return self.workspaceForDocument(uri: uri) + } + } +} diff --git a/Tests/SourceKitDTests/CrashRecoveryTests.swift b/Tests/SourceKitDTests/CrashRecoveryTests.swift index e513cfe65..9464ea136 100644 --- a/Tests/SourceKitDTests/CrashRecoveryTests.swift +++ b/Tests/SourceKitDTests/CrashRecoveryTests.swift @@ -40,14 +40,6 @@ fileprivate extension HoverResponse { } } -fileprivate extension SourceKitServer { - func workspaceForDocumentOnQueue(uri: DocumentURI) -> Workspace? { - self.queue.sync { - return self.workspaceForDocument(uri: uri) - } - } -} - final class CrashRecoveryTests: XCTestCase { func testSourcekitdCrashRecovery() throws { try XCTSkipUnless(isDarwinHost, "Linux and Windows use in-process sourcekitd") diff --git a/Tests/SourceKitLSPTests/LocalClangTests.swift b/Tests/SourceKitLSPTests/LocalClangTests.swift index 7cb8eb720..4f3096dd3 100644 --- a/Tests/SourceKitLSPTests/LocalClangTests.swift +++ b/Tests/SourceKitLSPTests/LocalClangTests.swift @@ -322,4 +322,43 @@ final class LocalClangTests: XCTestCase { throw e } } + + func testDocumentDependenciesUpdated() throws { + let ws = try! mutableSourceKitTibsTestWorkspace(name: "BasicCXX")! + + let cFileLoc = ws.testLoc("Object:ref:main") + + // Initially the workspace should build fine. + let documentOpened = self.expectation(description: "documentOpened") + ws.sk.handleNextNotification({ (note: LanguageServerProtocol.Notification) in + XCTAssert(note.params.diagnostics.isEmpty) + documentOpened.fulfill() + }) + + try! ws.openDocument(cFileLoc.url, language: .cpp) + + self.wait(for: [documentOpened], timeout: 5) + + // We rename Object to MyObject in the header. + _ = try ws.sources.edit { builder in + let headerFilePath = ws.sources.rootDirectory.appendingPathComponent("Object.h") + var headerFile = try! String(contentsOf: headerFilePath, encoding: .utf8) + let targetMarkerRange = headerFile.range(of: "/*Object*/")! + headerFile.replaceSubrange(targetMarkerRange, with: "My") + builder.write(headerFile, to: headerFilePath) + } + + // Now we should get a diagnostic in main.c file because `Object` is no longer defined. + let updatedNotificationsReceived = self.expectation(description: "updatedNotificationsReceived") + ws.sk.handleNextNotification({ (note: LanguageServerProtocol.Notification) in + XCTAssertFalse(note.params.diagnostics.isEmpty) + updatedNotificationsReceived.fulfill() + }) + + let clangdServer = ws.testServer.server!._languageService(for: cFileLoc.docUri, .cpp, in: ws.testServer.server!.workspaceForDocumentOnQueue(uri: cFileLoc.docUri)!)! + + clangdServer.documentDependenciesUpdated(cFileLoc.docUri) + + self.wait(for: [updatedNotificationsReceived], timeout: 5) + } } diff --git a/Tests/SourceKitLSPTests/WorkspaceTests.swift b/Tests/SourceKitLSPTests/WorkspaceTests.swift index fa4ee39af..f58ea3240 100644 --- a/Tests/SourceKitLSPTests/WorkspaceTests.swift +++ b/Tests/SourceKitLSPTests/WorkspaceTests.swift @@ -19,14 +19,6 @@ import SKTestSupport import TSCBasic import XCTest -fileprivate extension SourceKitServer { - func workspaceForDocumentOnQueue(uri: DocumentURI) -> Workspace? { - self.queue.sync { - return self.workspaceForDocument(uri: uri) - } - } -} - final class WorkspaceTests: XCTestCase { func testMultipleSwiftPMWorkspaces() throws {