From 47ff9ccefde37f7d4f94fda0e5ac4993930f6d6d Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Mon, 15 Dec 2025 09:09:44 -0500 Subject: [PATCH] Revert "Add dynamic local schema detection for SourceKit-LSP (#1979)" This reverts commit a59a38497a29115f2fc93a92bf0bea5070869bf4. --- CHANGELOG.md | 4 - src/WorkspaceContext.ts | 5 - .../generateSourcekitConfiguration.ts | 32 +-- src/sourcekit-lsp/SourcekitSchemaRegistry.ts | 91 ------- .../generateSourcekitConfiguration.test.ts | 235 ------------------ 5 files changed, 2 insertions(+), 365 deletions(-) delete mode 100644 src/sourcekit-lsp/SourcekitSchemaRegistry.ts delete mode 100644 test/unit-tests/commands/generateSourcekitConfiguration.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 63f379f97..911b974c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,6 @@ ## {{releaseVersion}} - {{releaseDate}} -### Added - -- When editing .sourcekit-lsp/config.json use the JSON schema from the toolchain ([#1979](https://github.com/swiftlang/vscode-swift/pull/1979)) - ### Fixed - Fix extension failing to find `lldb-dap` when `swift.path` points at an Xcode toolchain ([#1998](https://github.com/swiftlang/vscode-swift/pull/1998)) diff --git a/src/WorkspaceContext.ts b/src/WorkspaceContext.ts index c679b6c00..d448e31bc 100644 --- a/src/WorkspaceContext.ts +++ b/src/WorkspaceContext.ts @@ -28,7 +28,6 @@ import { CommentCompletionProviders } from "./editor/CommentCompletion"; import { SwiftLogger } from "./logging/SwiftLogger"; import { SwiftLoggerFactory } from "./logging/SwiftLoggerFactory"; import { LanguageClientToolchainCoordinator } from "./sourcekit-lsp/LanguageClientToolchainCoordinator"; -import { SourcekitSchemaRegistry } from "./sourcekit-lsp/SourcekitSchemaRegistry"; import { DocCDocumentationRequest, ReIndexProjectRequest } from "./sourcekit-lsp/extensions"; import { SwiftPluginTaskProvider } from "./tasks/SwiftPluginTaskProvider"; import { SwiftTaskProvider } from "./tasks/SwiftTaskProvider"; @@ -63,7 +62,6 @@ export class WorkspaceContext implements vscode.Disposable { public documentation: DocumentationManager; public testRunManager: TestRunManager; public projectPanel: ProjectPanelProvider; - private sourcekitSchemaRegistry: SourcekitSchemaRegistry; private lastFocusUri: vscode.Uri | undefined; private initialisationFinished = false; @@ -110,7 +108,6 @@ export class WorkspaceContext implements vscode.Disposable { this.currentDocument = null; this.commentCompletionProvider = new CommentCompletionProviders(); this.projectPanel = new ProjectPanelProvider(this); - this.sourcekitSchemaRegistry = new SourcekitSchemaRegistry(this); const onChangeConfig = vscode.workspace.onDidChangeConfiguration(async event => { // Clear build path cache when build-related configurations change @@ -235,7 +232,6 @@ export class WorkspaceContext implements vscode.Disposable { this.statusItem, this.buildStatus, this.projectPanel, - this.sourcekitSchemaRegistry.register(), ]; this.lastFocusUri = vscode.window.activeTextEditor?.document.uri; @@ -251,7 +247,6 @@ export class WorkspaceContext implements vscode.Disposable { } dispose() { - this.sourcekitSchemaRegistry.dispose(); this.folders.forEach(f => f.dispose()); this.folders.length = 0; this.subscriptions.forEach(item => item.dispose()); diff --git a/src/commands/generateSourcekitConfiguration.ts b/src/commands/generateSourcekitConfiguration.ts index 1d38aae33..8ea269d29 100644 --- a/src/commands/generateSourcekitConfiguration.ts +++ b/src/commands/generateSourcekitConfiguration.ts @@ -18,11 +18,10 @@ import { FolderContext } from "../FolderContext"; import { WorkspaceContext } from "../WorkspaceContext"; import configuration from "../configuration"; import { selectFolder } from "../ui/SelectFolderQuickPick"; -import { fileExists } from "../utilities/filesystem"; import restartLSPServer from "./restartLSPServer"; -const sourcekitDotFolder: string = ".sourcekit-lsp"; -const sourcekitConfigFileName: string = "config.json"; +export const sourcekitDotFolder: string = ".sourcekit-lsp"; +export const sourcekitConfigFileName: string = "config.json"; export async function generateSourcekitConfiguration(ctx: WorkspaceContext): Promise { if (ctx.folders.length === 0) { @@ -129,13 +128,6 @@ async function checkURLExists(url: string): Promise { } export async function determineSchemaURL(folderContext: FolderContext): Promise { - // Check if local schema exists first - if (await hasLocalSchema(folderContext)) { - const localPath = localSchemaPath(folderContext); - return vscode.Uri.file(localPath).toString(); - } - - // Fall back to remote URL for older toolchains const version = folderContext.toolchain.swiftVersion; const versionString = `${version.major}.${version.minor}`; let branch = @@ -146,26 +138,6 @@ export async function determineSchemaURL(folderContext: FolderContext): Promise< return schemaURL(branch); } -/** - * Returns the path to the local sourcekit-lsp schema file in the toolchain. - * This file only exists in newer toolchains (approximately Swift 6.3+). - */ -export function localSchemaPath(folderContext: FolderContext): string { - return join( - folderContext.toolchain.toolchainPath, - "share", - "sourcekit-lsp", - "config.schema.json" - ); -} - -/** - * Checks if the local schema file exists in the toolchain. - */ -export async function hasLocalSchema(folderContext: FolderContext): Promise { - return await fileExists(localSchemaPath(folderContext)); -} - async function getValidatedFolderContext( uri: vscode.Uri, workspaceContext: WorkspaceContext diff --git a/src/sourcekit-lsp/SourcekitSchemaRegistry.ts b/src/sourcekit-lsp/SourcekitSchemaRegistry.ts deleted file mode 100644 index 640617989..000000000 --- a/src/sourcekit-lsp/SourcekitSchemaRegistry.ts +++ /dev/null @@ -1,91 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the VS Code Swift open source project -// -// Copyright (c) 2025 the VS Code Swift project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of VS Code Swift project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// -import * as vscode from "vscode"; - -import { FolderContext } from "../FolderContext"; -import { WorkspaceContext } from "../WorkspaceContext"; -import { - determineSchemaURL, - sourcekitConfigFilePath, -} from "../commands/generateSourcekitConfiguration"; - -/** - * Manages dynamic JSON schema associations for sourcekit-lsp config files. - * This allows VS Code to provide validation and IntelliSense using the - * appropriate schema (local or remote) based on the toolchain. - */ -export class SourcekitSchemaRegistry { - private disposables: vscode.Disposable[] = []; - - constructor(private workspaceContext: WorkspaceContext) {} - - /** - * Registers event handlers to dynamically configure JSON schemas - * for sourcekit-lsp config documents. - */ - register(): vscode.Disposable { - // Handle documents that are already open - vscode.workspace.textDocuments.forEach(doc => { - void this.configureSchemaForDocument(doc); - }); - - // Handle newly opened documents - const onDidOpenDisposable = vscode.workspace.onDidOpenTextDocument(doc => { - void this.configureSchemaForDocument(doc); - }); - - this.disposables.push(onDidOpenDisposable); - - return vscode.Disposable.from(...this.disposables); - } - - /** - * Configures the JSON schema for a document if it's a sourcekit-lsp config file. - */ - private async configureSchemaForDocument(document: vscode.TextDocument): Promise { - if (document.languageId !== "json") { - return; - } - - const folderContext = await this.getFolderContextForDocument(document); - if (!folderContext) { - return; - } - - const schemaUrl = await determineSchemaURL(folderContext); - - // Use VS Code's JSON language configuration API to associate the schema - await vscode.commands.executeCommand("json.setSchema", document.uri.toString(), schemaUrl); - } - - /** - * Gets the FolderContext for a document if it's a sourcekit-lsp config file. - */ - private async getFolderContextForDocument( - document: vscode.TextDocument - ): Promise { - for (const folderContext of this.workspaceContext.folders) { - const configPath = sourcekitConfigFilePath(folderContext); - if (document.uri.fsPath === configPath) { - return folderContext; - } - } - return null; - } - - dispose() { - this.disposables.forEach(d => d.dispose()); - this.disposables = []; - } -} diff --git a/test/unit-tests/commands/generateSourcekitConfiguration.test.ts b/test/unit-tests/commands/generateSourcekitConfiguration.test.ts deleted file mode 100644 index e60ed775a..000000000 --- a/test/unit-tests/commands/generateSourcekitConfiguration.test.ts +++ /dev/null @@ -1,235 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the VS Code Swift open source project -// -// Copyright (c) 2025 the VS Code Swift project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of VS Code Swift project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// -import { expect } from "chai"; -import * as path from "path"; -import * as sinon from "sinon"; -import * as vscode from "vscode"; - -import { FolderContext } from "@src/FolderContext"; -import { - determineSchemaURL, - hasLocalSchema, - localSchemaPath, -} from "@src/commands/generateSourcekitConfiguration"; -import configuration from "@src/configuration"; -import { SwiftToolchain } from "@src/toolchain/toolchain"; -import * as filesystemModule from "@src/utilities/filesystem"; -import { Version } from "@src/utilities/version"; - -import { mockGlobalModule } from "../../MockUtils"; - -suite("generateSourcekitConfiguration - Schema Detection", () => { - let sandbox: sinon.SinonSandbox; - let mockFolderContext: FolderContext; - let fileExistsStub: sinon.SinonStub; - const mockedConfiguration = mockGlobalModule(configuration); - - function createMockFolderContext(toolchainPath: string, version: Version): FolderContext { - const mockToolchain = { - toolchainPath, - swiftVersion: version, - } as SwiftToolchain; - - return { - folder: vscode.Uri.file("/test/workspace"), - swiftVersion: version, - toolchain: mockToolchain, - name: "TestFolder", - } as FolderContext; - } - - setup(() => { - sandbox = sinon.createSandbox(); - fileExistsStub = sandbox.stub(filesystemModule, "fileExists"); - mockedConfiguration.lspConfigurationBranch = ""; - }); - - teardown(() => { - sandbox.restore(); - }); - - suite("localSchemaPath()", () => { - test("returns correct path for toolchain", () => { - mockFolderContext = createMockFolderContext("/path/to/toolchain", new Version(6, 3, 0)); - - const result = localSchemaPath(mockFolderContext); - - expect(result).to.equal( - path.normalize("/path/to/toolchain/share/sourcekit-lsp/config.schema.json") - ); - }); - - test("returns correct path for toolchain with trailing slash", () => { - mockFolderContext = createMockFolderContext( - path.normalize("/path/to/toolchain/"), - new Version(6, 3, 0) - ); - - const result = localSchemaPath(mockFolderContext); - - expect(result).to.equal( - path.normalize("/path/to/toolchain/share/sourcekit-lsp/config.schema.json") - ); - }); - - test("returns correct path for nested toolchain directory", () => { - mockFolderContext = createMockFolderContext( - path.normalize("/usr/local/swift-6.3.0"), - new Version(6, 3, 0) - ); - - const result = localSchemaPath(mockFolderContext); - - expect(result).to.equal( - path.normalize("/usr/local/swift-6.3.0/share/sourcekit-lsp/config.schema.json") - ); - }); - }); - - suite("hasLocalSchema()", () => { - test("returns true when schema file exists", async () => { - mockFolderContext = createMockFolderContext( - path.normalize("/path/to/toolchain"), - new Version(6, 3, 0) - ); - fileExistsStub.resolves(true); - - const result = await hasLocalSchema(mockFolderContext); - - expect(result).to.be.true; - expect(fileExistsStub).to.have.been.calledWith( - path.normalize("/path/to/toolchain/share/sourcekit-lsp/config.schema.json") - ); - }); - - test("returns false when schema file doesn't exist", async () => { - mockFolderContext = createMockFolderContext( - path.normalize("/path/to/toolchain"), - new Version(6, 1, 0) - ); - fileExistsStub.resolves(false); - - const result = await hasLocalSchema(mockFolderContext); - - expect(result).to.be.false; - }); - - test("returns false on filesystem errors", async () => { - mockFolderContext = createMockFolderContext("/path/to/toolchain", new Version(6, 3, 0)); - fileExistsStub.resolves(false); - - const result = await hasLocalSchema(mockFolderContext); - - expect(result).to.be.false; - }); - }); - - suite("determineSchemaURL()", () => { - test("returns file:// URL when local schema exists", async () => { - mockFolderContext = createMockFolderContext("/path/to/toolchain", new Version(6, 3, 0)); - fileExistsStub.resolves(true); - - const result = await determineSchemaURL(mockFolderContext); - - expect(result).to.match(/^file:\/\//); - expect(result).to.include("config.schema.json"); - expect(result).to.include("sourcekit-lsp"); - }); - - test("returns https:// URL when local schema doesn't exist", async () => { - mockFolderContext = createMockFolderContext( - path.normalize("/path/to/toolchain"), - new Version(6, 1, 0) - ); - fileExistsStub.resolves(false); - - const result = await determineSchemaURL(mockFolderContext); - - expect(result).to.match(/^https:\/\//); - expect(result).to.include("githubusercontent.com"); - expect(result).to.include("sourcekit-lsp"); - }); - - test("local schema path includes share/sourcekit-lsp/config.schema.json", async () => { - mockFolderContext = createMockFolderContext( - path.normalize("/usr/local/swift-6.3"), - new Version(6, 3, 0) - ); - fileExistsStub.resolves(true); - - const result = await determineSchemaURL(mockFolderContext); - - expect(result).to.include("/usr/local/swift-6.3"); - expect(result).to.include("share/sourcekit-lsp/config.schema.json"); - }); - - test("remote URL uses correct branch for release version", async () => { - mockFolderContext = createMockFolderContext( - path.normalize("/path/to/toolchain"), - new Version(6, 2, 0) - ); - fileExistsStub.resolves(false); - - const fetchStub = sandbox.stub(globalThis, "fetch"); - fetchStub.resolves({ - ok: true, - status: 200, - } as Response); - - const result = await determineSchemaURL(mockFolderContext); - - expect(result).to.include("release/6.2"); - }); - - test("remote URL uses main for dev version", async () => { - mockFolderContext = createMockFolderContext( - path.normalize("/path/to/toolchain"), - new Version(6, 3, 0, true) - ); - fileExistsStub.resolves(false); - - const fetchStub = sandbox.stub(globalThis, "fetch"); - fetchStub.resolves({ - ok: true, - status: 200, - } as Response); - - const result = await determineSchemaURL(mockFolderContext); - - expect(result).to.include("main"); - }); - - test("falls back to main when branch doesn't exist", async () => { - mockFolderContext = createMockFolderContext( - path.normalize("/path/to/toolchain"), - new Version(5, 9, 0) - ); - fileExistsStub.resolves(false); - - const fetchStub = sandbox.stub(globalThis, "fetch"); - fetchStub.onFirstCall().resolves({ - ok: false, - status: 404, - } as Response); - fetchStub.onSecondCall().resolves({ - ok: true, - status: 200, - } as Response); - - const result = await determineSchemaURL(mockFolderContext); - - expect(result).to.include("main"); - }); - }); -});