vknabel / vscode-swift-development-environment

New home of Swift Development Environment for VS Code
https://marketplace.visualstudio.com/items?itemName=vknabel.vscode-swift-development-environment
Apache License 2.0
175 stars 14 forks source link

Can't get autocomplete for dependencies #27

Closed alfadhela closed 6 years ago

alfadhela commented 6 years ago

I am using swift package (SPM) with dependencies such as PerfectHTTPServer. is there any configuration I need to do to make autocomplete work? Swift 4.1.2 and 4.0.3

vknabel commented 6 years ago

Hi. Sorry for the late response. Actually just opening the project should be sufficient. I will have a look on this.

joscdk commented 6 years ago

Hi, @vknabel just fyi, i am just playing around with this plugin as an alternative to Xcode for development on Vapor, and facing the samme issue with no autocomplete from dependencies (In this case various Vapor plugins)

vknabel commented 6 years ago

I have experienced the same, too. Although this seems to depend on the external module itself. Some dependencies worked fine for me while others did not. This seems to reach back to SourceKit itself. But as we know of Xcode, this should definitely be possible.

Probably this is why langserver-swift has the same problem https://github.com/RLovelett/langserver-swift/issues/56 as @joscdk knows.

yeswolf commented 6 years ago

I think I can shed some light here. To have autocompletion for dependencies (and any SourceKit request working correctly for them), it's critical to have almost the same compiler arguments as compiler uses for building the project. Plus we need some additional args for taking dependencies into account. How can we achieve it:

  1. Get the information from .yaml inside the build folder (same approach as nuclide-swift uses). Currently, it's the only way to get more or less complete list of compiler arguments for each file. Take the other-args element as a first part of arguments.

  2. Add "-module-name", "<module-name>", "-Onone",. Use the module name from the .yaml for each file - for example, this way you can get a completion for Package.swift, cause here you need to submit a PackageDescription module name.

  3. Next, add the debug / release folder to this arguments array in the following way:

"-Xcc",
"-I",
"-Xcc",
"<project path>/.build/<debug or release>",
"-I",
"<project path>/.build/<debug or release>",
"-Xcc",
"-F",
"-Xcc",
"<project path>/.build/<debug or release>",
"-F",
"<project path>/.build/<debug or release>"

By "debug" or "release" here I mean the symlink, it's ok to submit it instead of the folder it points to.

That's basically how Xcode forms compiler arguments that tell the SourceKit the information about the project model (if we launch it with SOURCEKIT_LOGGING=3, we can see almost the same set of arguments for the completion request, the only difference will be using the DerivedData instead of .build directory). Not sure I took everything into account, but such approach works.

I was testing ide-swift and this extension by submitting such combination of compiler arguments directly (by hardcoding them) with several projects using Vapor/Kitura both on macOS/Linux. The completion works much better.

Vapor/Kitura have a large number of dependencies, so I think it's safe to assume that such approach should work for other projects too.

Also, for some reason it works better with ide-swift - for some reason some of the correct responses are not handled by SDE. As example - completion after import. After typing "t" in the "import" on the top-level, I immediately have a response like

{
  key.results: [
    {
      key.kind: source.lang.swift.decl.module,
      key.name: "Accelerate",
      key.sourcetext: "Accelerate",
      key.description: "Accelerate",
      key.typename: "Module",
      key.context: source.codecompletion.context.othermodule,
      key.num_bytes_to_erase: 0,
      key.modulename: "Accelerate"
    },
    {
      key.kind: source.lang.swift.decl.module,
      key.name: "Accounts",
      key.sourcetext: "Accounts",
      key.description: "Accounts",
      key.typename: "Module",
      key.context: source.codecompletion.context.othermodule,
      key.num_bytes_to_erase: 0,
      key.modulename: "Accounts"
    },
    {
      key.kind: source.lang.swift.decl.module,
      key.name: "AddressBook",
      key.sourcetext: "AddressBook",
      key.description: "AddressBook",
      key.typename: "Module",
      key.context: source.codecompletion.context.othermodule,
      key.num_bytes_to_erase: 0,
      key.modulename: "AddressBook"
    },    

and none of module names are shown by the SDE in the completion. ide-swift shows them correctly for the same project with the same compiler arguments. Same situation with some class methods and especially global vars.

Here is a proof-of-concept for this project on macOS. Hope it helps.

vknabel commented 6 years ago

@yeswolf thank you a lot for your help! Sorry for the late response, but didm't enough time to actually work through your comment.

Once I have enough time, I will definitely dive deeper into this!

vknabel commented 6 years ago

In order to apply the changes @yeswolf proposed, I needed to refactor how SDE handles modules/targets (otherwise it would've get too messy).

Additionally I added a new swift.targets setting as described in 9510d22/CHANGELOG.md#2.5.0, which allows to declare and override completion targets (might be helpful for debugging and when using Xcode projects).

The nightly build including these changes: vscode-swift-development-environment-2.5.0.vsix.zip


Instead of adding <project path>/.build/<debug or release> directly, I used import-paths of the (debug|release).yaml command.


Also, for some reason it works better with ide-swift - for some reason some of the correct responses are not handled by SDE. As example - completion after import. After typing "t" in the "import" on the top-level, I immediately have a response like and none of module names are shown by the SDE in the completion. ide-swift shows them correctly for the same project with the same compiler arguments. Same situation with some class methods and especially global vars.

I will have another look on this. If it takes some more time, I will create a dedicated issue.


  1. Add "-module-name", "", "-Onone",. Use the module name from the .yaml for each file - for example, this way you can get a completion for Package.swift, cause here you need to submit a PackageDescription module name.

Just passing -module-name PackageDescription -Onone /path/to/Package.swift did not solve the autocompletion problem. Probably an import path is still missing, which should be somewhere in the SPM project as it actually compiles the manifest. May deserve a separate issue, too.