From 9adcb8d05a5f1ae1a54520c84d1b2857f61199a6 Mon Sep 17 00:00:00 2001 From: Valentin Knabel Date: Sun, 8 Sep 2019 17:13:10 +0200 Subject: [PATCH] Fixed an issue preventing autocompletion to work reliably on Linux, fixes #54 --- CHANGELOG.md | 6 + README.md | 2 +- package.json | 2 +- src/clientMain.ts | 10 +- src/server/packages/available-packages.ts | 28 +--- src/server/packages/package-helpers.spec.ts | 136 ++++++++++++++++++++ src/server/packages/package-helpers.ts | 39 ++++++ src/server/sourcekites.ts | 9 +- 8 files changed, 198 insertions(+), 34 deletions(-) create mode 100644 src/server/packages/package-helpers.spec.ts create mode 100644 src/server/packages/package-helpers.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c8e5531..0f46f47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.8.1 + +- Fixed an issue preventing autocompletion to work reliably on Linux, fixes [#54](https://github.com/vknabel/vscode-swift-development-environment/issues/54) +- Installation instructions now correctly link `/usr/lib/libsourcekitdInProc.so`, noticed by [@kennethz3](https://github.com/kennethz3) +- Support quoted arguments in settings, fixed by [@haifengkao](https://github.com/haifengkao) + ## 2.8.0 - Now LSP-mode `sourcekite` supports `sourcekit-lsp.toolchainPath` after updating to [sourcekite@0.6.0](https://github.com/vknabel/sourcekite/releases/tag/0.6.0) diff --git a/README.md b/README.md index 3dc8b74..e76e7bc 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ sourcekit-lsp is easier to install and will be updated more frequently. On the o # Ensure you have libcurl4-openssl-dev installed (not pre-installed) # $ apt-get update && apt-get install libcurl4-openssl-dev # Ensure LD_LIBRARY_PATH contains /your/swift/usr/lib - # And have $ sudo ln -s /your/swift/usr/lib/libsourcekitdInProc.so /usr/lib/sourcekitdInProc + # And have $ sudo ln -s /your/swift/usr/lib/libsourcekitdInProc.so /usr/lib/libsourcekitdInProc.so $ make install PREFIX=/usr/local # For macOS diff --git a/package.json b/package.json index 2c80cda..4a38973 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "url": "https://github.com/vknabel" }, "license": "Apache-2.0", - "version": "2.8.0", + "version": "2.8.1", "publisher": "vknabel", "icon": "icons/icon.png", "galleryBanner": { diff --git a/src/clientMain.ts b/src/clientMain.ts index 538f42a..8a0f32a 100644 --- a/src/clientMain.ts +++ b/src/clientMain.ts @@ -51,7 +51,10 @@ function currentServerOptions(context: ExtensionContext) { path.join("out/src/server", "server.js") ); // The debug options for the server - const debugOptions = { execArgv: ["--nolazy", "--inspect=6004"] }; + const debugOptions = { + execArgv: ["--nolazy", "--inspect=6004"], + ...process.env + }; // If the extension is launched in debug mode then the debug server options are used // Otherwise the run options are used @@ -76,7 +79,10 @@ function currentServerOptions(context: ExtensionContext) { .getConfiguration("swift") .get("languageServerPath", "/usr/local/bin/LanguageServer"); - const run: Executable = { command: executableCommand }; + const run: Executable = { + command: executableCommand, + options: process.env + }; const debug: Executable = run; const serverOptions: ServerOptions = { run: run, diff --git a/src/server/packages/available-packages.ts b/src/server/packages/available-packages.ts index 1a93d89..b1fc773 100644 --- a/src/server/packages/available-packages.ts +++ b/src/server/packages/available-packages.ts @@ -1,9 +1,9 @@ -import * as path from "path"; import { descriptionPackage } from "./description-package"; -import { Package, Target } from "../package"; +import { Package } from "../package"; import { swiftFilePackage } from "./swift-file-package"; import { debugYamlPackage } from "./debug-yaml-package"; import { configPackage } from "./config-package"; +import { flatteningTargetsWithUniqueSources } from "./package-helpers"; export const availablePackages: Package = async fromPath => { const [ @@ -24,27 +24,3 @@ export const availablePackages: Package = async fromPath => { swiftFileTargets ); }; - -function flatteningTargetsWithUniqueSources(...targets: Target[][]) { - return targets.reduce( - (current, next) => [...current, ...removingDuplicateSources(next, current)], - [] - ); -} - -function removingDuplicateSources( - fromTargets: Target[], - uniqueTargets: Target[] -): Target[] { - return fromTargets.map(target => { - const swiftFilesWithoutTargets = Array.from(target.sources).filter( - source => - uniqueTargets.findIndex(desc => - desc.sources.has( - path.relative(desc.path, path.resolve(target.path, source)) - ) - ) === -1 - ); - return { ...target, sources: new Set(swiftFilesWithoutTargets) }; - }); -} diff --git a/src/server/packages/package-helpers.spec.ts b/src/server/packages/package-helpers.spec.ts new file mode 100644 index 0000000..fa81e8f --- /dev/null +++ b/src/server/packages/package-helpers.spec.ts @@ -0,0 +1,136 @@ +import { + removingDuplicateSources, + flatteningTargetsWithUniqueSources +} from "./package-helpers"; +import { Target } from "../package"; + +const uniqueTarget: Target = { + name: "Unique", + path: "Sources/Unique", + sources: new Set(["Hello.swift", "main.swift"]), + compilerArguments: [] +}; + +const unrelatedTarget: Target = { + name: "UnrelatedTarget", + path: "Sources/UnrelatedTarget", + sources: new Set(["Unrelated.swift"]), + compilerArguments: [] +}; + +describe("package helpers", () => { + describe("removingDuplicateSources", () => { + it("does not emit unique targets", () => { + const emittedTargets = removingDuplicateSources([], [uniqueTarget]); + expect(emittedTargets).toHaveLength(0); + }); + + it("unrelated source sets will be kept", () => { + const emittedTargets = removingDuplicateSources( + [unrelatedTarget], + [uniqueTarget] + ); + expect(emittedTargets).toEqual([unrelatedTarget]); + }); + + it("unrelated source sets with differing paths will be kept for same file names", () => { + const unrelatedTargetWithSameFileNames: Target = { + ...unrelatedTarget, + sources: uniqueTarget.sources + }; + + const emittedTargets = removingDuplicateSources( + [unrelatedTargetWithSameFileNames], + [uniqueTarget] + ); + expect(emittedTargets).toEqual([unrelatedTargetWithSameFileNames]); + }); + + it("source sets with same paths but different file names are kept", () => { + const samePathTargetWithDifferentSources = { + ...unrelatedTarget, + path: uniqueTarget.path + }; + const emittedTargets = removingDuplicateSources( + [samePathTargetWithDifferentSources], + [uniqueTarget] + ); + expect(emittedTargets).toEqual([samePathTargetWithDifferentSources]); + }); + + it("source sets with different paths but same files will be deuplicated", () => { + const differentPathTargetWithSameSources = { + ...unrelatedTarget, + path: "./", + sources: new Set( + Array(uniqueTarget.sources.values()).map( + sourceFile => `${uniqueTarget.path}/${sourceFile}` + ) + ) + }; + const emittedTargets = removingDuplicateSources( + [differentPathTargetWithSameSources], + [uniqueTarget] + ); + expect(emittedTargets).toEqual([differentPathTargetWithSameSources]); + }); + }); + + describe("flatteningTargetsWithUniqueSources", () => { + it("bug: configs did not override global paths", () => { + // see https://github.com/vknabel/vscode-swift-development-environment/issues/55 + const emittedTargets = flatteningTargetsWithUniqueSources( + [ + { + name: "HiModuleFromConfigs", + path: "/Users/vknabel/Desktop/AutocompleteIos/Sources/Hi", + sources: new Set(["Hi.swift"]), + compilerArguments: [] + } + ], + [ + { + name: "HiModuleFromDebugYaml", + path: "/Users/vknabel/Desktop/AutocompleteIos", + sources: new Set([ + "/Users/vknabel/Desktop/AutocompleteIos/Sources/Hi/Hi.swift" + ]), + compilerArguments: [] + } + ], + [ + { + name: "AutocompleteIos", + path: "/Users/vknabel/Desktop/AutocompleteIos", + sources: new Set([ + "/Users/vknabel/Desktop/AutocompleteIos/Package.swift" + ]), + compilerArguments: [] + } + ] + ); + expect(emittedTargets).toEqual([ + { + name: "HiModuleFromConfigs", + path: "/Users/vknabel/Desktop/AutocompleteIos/Sources/Hi", + sources: new Set(["Hi.swift"]), + compilerArguments: [] + }, + { + name: "HiModuleFromDebugYaml", + path: "/Users/vknabel/Desktop/AutocompleteIos", + sources: new Set([]), + compilerArguments: [] + }, + { + name: "AutocompleteIos", + path: "/Users/vknabel/Desktop/AutocompleteIos", + sources: new Set([ + "/Users/vknabel/Desktop/AutocompleteIos/Package.swift" + ]), + compilerArguments: [] + } + ]); + }); + }); +}); diff --git a/src/server/packages/package-helpers.ts b/src/server/packages/package-helpers.ts new file mode 100644 index 0000000..6aa2d73 --- /dev/null +++ b/src/server/packages/package-helpers.ts @@ -0,0 +1,39 @@ +import { Target } from "../package"; +import * as path from "path"; + +export function flatteningTargetsWithUniqueSources( + ...targets: Target[][] +): Target[] { + return targets.reduce( + (current, next) => [ + ...current, + ...removingDuplicateSources(next, current.map(normalizedTarget)) + ], + [] + ); +} + +export function removingDuplicateSources( + fromTargets: Target[], + uniqueTargets: Target[] +): Target[] { + return fromTargets.map(target => { + const swiftFilesWithoutTargets = Array.from(target.sources).filter( + source => + uniqueTargets.findIndex(desc => + desc.sources.has(path.resolve(target.path, source)) + ) === -1 + ); + return { ...target, sources: new Set(swiftFilesWithoutTargets) }; + }); +} + +function normalizedTarget(target: Target): Target { + return { + ...target, + sources: mapSet(target.sources, source => path.resolve(target.path, source)) + }; +} +function mapSet(set: Set, transform: (element: T) => R): Set { + return new Set(Array.from(set.values()).map(element => transform(element))); +} diff --git a/src/server/sourcekites.ts b/src/server/sourcekites.ts index 3d78516..8820858 100644 --- a/src/server/sourcekites.ts +++ b/src/server/sourcekites.ts @@ -26,7 +26,10 @@ function restartSourcekite() { } function createSkProtocolProcess() { - const env = { TOOLCHAIN_DIR: Current.config.toolchainPath }; + const env = { + ...process.env, + TOOLCHAIN_DIR: Current.config.toolchainPath || process.env["TOOLCHAIN_DIR"] + }; if (server.skProtocolProcessAsShellCmd) { const volumes = Current.config.workspacePaths.map( path => `-v '${path}:${path}'` @@ -44,9 +47,7 @@ function createSkProtocolProcess() { function initializeSKProtocolProcess() { Current.log( "sourcekite", - `***sourcekite initializing with skProtocolProcess at [${ - server.skProtocolPath - }]` + `***sourcekite initializing with skProtocolProcess at [${server.skProtocolPath}]` ); const pathSourcekite = Current.config.sourcekitePath;