Skip to content

Split SourceKitLSPServer.swift into multiple files #1359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Sources/SourceKitLSP/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ add_library(SourceKitLSP STATIC
DocumentSnapshot+FromFileContents.swift
IndexProgressManager.swift
IndexStoreDB+MainFilesProvider.swift
LanguageServerType.swift
LanguageService.swift
MessageHandlingDependencyTracker.swift
Rename.swift
ResponseError+Init.swift
SourceKitIndexDelegate.swift
Expand All @@ -16,6 +18,7 @@ add_library(SourceKitLSP STATIC
TestDiscovery.swift
TextEdit+IsNoop.swift
WorkDoneProgressManager.swift
WorkDoneProgressState.swift
Workspace.swift
)
target_sources(SourceKitLSP PRIVATE
Expand Down
48 changes: 48 additions & 0 deletions Sources/SourceKitLSP/LanguageServerType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 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 IndexStoreDB

/// Exhaustive enumeration of all toolchain language servers known to SourceKit-LSP.
enum LanguageServerType: Hashable {
case clangd
case swift

init?(language: Language) {
switch language {
case .c, .cpp, .objective_c, .objective_cpp:
self = .clangd
case .swift:
self = .swift
default:
return nil
}
}

init?(symbolProvider: SymbolProviderKind?) {
switch symbolProvider {
case .clang: self = .clangd
case .swift: self = .swift
case nil: return nil
}
}

/// The `LanguageService` class used to provide functionality for this language class.
var serverType: LanguageService.Type {
switch self {
case .clangd:
return ClangLanguageService.self
case .swift:
return SwiftLanguageService.self
}
}
}
233 changes: 233 additions & 0 deletions Sources/SourceKitLSP/MessageHandlingDependencyTracker.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 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 LSPLogging
import LanguageServerProtocol
import SKSupport

/// A lightweight way of describing tasks that are created from handling LSP
/// requests or notifications for the purpose of dependency tracking.
enum MessageHandlingDependencyTracker: DependencyTracker {
/// A task that changes the global configuration of sourcekit-lsp in any way.
///
/// No other tasks must execute simultaneously with this task since they
/// might be relying on this task to take effect.
case globalConfigurationChange

/// A request that depends on the state of all documents.
///
/// These requests wait for `documentUpdate` tasks for all documents to finish before being executed.
///
/// Requests that only read the semantic index and are not affected by changes to the in-memory file contents should
/// `freestanding` requests.
case workspaceRequest

/// Changes the contents of the document with the given URI.
///
/// Any other updates or requests to this document must wait for the
/// document update to finish before being executed
case documentUpdate(DocumentURI)

/// A request that concerns one document.
///
/// Any updates to this document must be processed before the document
/// request can be handled. Multiple requests to the same document can be
/// handled simultaneously.
case documentRequest(DocumentURI)

/// A request that doesn't have any dependencies other than global
/// configuration changes.
case freestanding

/// Whether this request needs to finish before `other` can start executing.
func isDependency(of other: MessageHandlingDependencyTracker) -> Bool {
switch (self, other) {
// globalConfigurationChange
case (.globalConfigurationChange, _): return true
case (_, .globalConfigurationChange): return true

// globalDocumentState
case (.workspaceRequest, .workspaceRequest): return false
case (.documentUpdate, .workspaceRequest): return true
case (.workspaceRequest, .documentUpdate): return true
case (.workspaceRequest, .documentRequest): return false
case (.documentRequest, .workspaceRequest): return false

// documentUpdate
case (.documentUpdate(let selfUri), .documentUpdate(let otherUri)):
return selfUri == otherUri
case (.documentUpdate(let selfUri), .documentRequest(let otherUri)):
return selfUri == otherUri
case (.documentRequest(let selfUri), .documentUpdate(let otherUri)):
return selfUri == otherUri

// documentRequest
case (.documentRequest, .documentRequest):
return false

// freestanding
case (.freestanding, _):
return false
case (_, .freestanding):
return false
}
}

init(_ notification: any NotificationType) {
switch notification {
case is CancelRequestNotification:
self = .freestanding
case is CancelWorkDoneProgressNotification:
self = .freestanding
case is DidChangeConfigurationNotification:
self = .globalConfigurationChange
case let notification as DidChangeNotebookDocumentNotification:
self = .documentUpdate(notification.notebookDocument.uri)
case let notification as DidChangeTextDocumentNotification:
self = .documentUpdate(notification.textDocument.uri)
case is DidChangeWatchedFilesNotification:
self = .globalConfigurationChange
case is DidChangeWorkspaceFoldersNotification:
self = .globalConfigurationChange
case let notification as DidCloseNotebookDocumentNotification:
self = .documentUpdate(notification.notebookDocument.uri)
case let notification as DidCloseTextDocumentNotification:
self = .documentUpdate(notification.textDocument.uri)
case is DidCreateFilesNotification:
self = .freestanding
case is DidDeleteFilesNotification:
self = .freestanding
case let notification as DidOpenNotebookDocumentNotification:
self = .documentUpdate(notification.notebookDocument.uri)
case let notification as DidOpenTextDocumentNotification:
self = .documentUpdate(notification.textDocument.uri)
case is DidRenameFilesNotification:
self = .freestanding
case let notification as DidSaveNotebookDocumentNotification:
self = .documentUpdate(notification.notebookDocument.uri)
case let notification as DidSaveTextDocumentNotification:
self = .documentUpdate(notification.textDocument.uri)
case is ExitNotification:
self = .globalConfigurationChange
case is InitializedNotification:
self = .globalConfigurationChange
case is LogMessageNotification:
self = .freestanding
case is LogTraceNotification:
self = .freestanding
case is PublishDiagnosticsNotification:
self = .freestanding
case is SetTraceNotification:
self = .globalConfigurationChange
case is ShowMessageNotification:
self = .freestanding
case let notification as WillSaveTextDocumentNotification:
self = .documentUpdate(notification.textDocument.uri)
case is WorkDoneProgress:
self = .freestanding
default:
logger.error(
"""
Unknown notification \(type(of: notification)). Treating as a freestanding notification. \
This might lead to out-of-order request handling
"""
)
self = .freestanding
}
}

init(_ request: any RequestType) {
switch request {
case let request as any TextDocumentRequest: self = .documentRequest(request.textDocument.uri)
case is ApplyEditRequest:
self = .freestanding
case is BarrierRequest:
self = .globalConfigurationChange
case is CallHierarchyIncomingCallsRequest:
self = .freestanding
case is CallHierarchyOutgoingCallsRequest:
self = .freestanding
case is CodeActionResolveRequest:
self = .freestanding
case is CodeLensRefreshRequest:
self = .freestanding
case is CodeLensResolveRequest:
self = .freestanding
case is CompletionItemResolveRequest:
self = .freestanding
case is CreateWorkDoneProgressRequest:
self = .freestanding
case is DiagnosticsRefreshRequest:
self = .freestanding
case is DocumentLinkResolveRequest:
self = .freestanding
case let request as ExecuteCommandRequest:
if let uri = request.textDocument?.uri {
self = .documentRequest(uri)
} else {
self = .freestanding
}
case is InitializeRequest:
self = .globalConfigurationChange
case is InlayHintRefreshRequest:
self = .freestanding
case is InlayHintResolveRequest:
self = .freestanding
case is InlineValueRefreshRequest:
self = .freestanding
case is PollIndexRequest:
self = .globalConfigurationChange
case is RenameRequest:
// Rename might touch multiple files. Make it a global configuration change so that edits to all files that might
// be affected have been processed.
self = .globalConfigurationChange
case is RegisterCapabilityRequest:
self = .globalConfigurationChange
case is ShowMessageRequest:
self = .freestanding
case is ShutdownRequest:
self = .globalConfigurationChange
case is TypeHierarchySubtypesRequest:
self = .freestanding
case is TypeHierarchySupertypesRequest:
self = .freestanding
case is UnregisterCapabilityRequest:
self = .globalConfigurationChange
case is WillCreateFilesRequest:
self = .freestanding
case is WillDeleteFilesRequest:
self = .freestanding
case is WillRenameFilesRequest:
self = .freestanding
case is WorkspaceDiagnosticsRequest:
self = .freestanding
case is WorkspaceFoldersRequest:
self = .freestanding
case is WorkspaceSemanticTokensRefreshRequest:
self = .freestanding
case is WorkspaceSymbolResolveRequest:
self = .freestanding
case is WorkspaceSymbolsRequest:
self = .freestanding
case is WorkspaceTestsRequest:
self = .workspaceRequest
default:
logger.error(
"""
Unknown request \(type(of: request)). Treating as a freestanding request. \
This might lead to out-of-order request handling
"""
)
self = .freestanding
}
}
}
Loading