├── images ├── Swift_logo.png ├── vscode-swift-completion.gif ├── vscode-swift-documentation.gif └── Swift_logo.svg ├── .vscodeignore ├── tsconfig.json ├── .vscode ├── settings.json ├── launch.json └── tasks.json ├── test ├── extension.test.ts └── index.ts ├── LICENSE ├── src └── extension.ts ├── .gitignore ├── package.json └── README.md /images/Swift_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RLovelett/vscode-swift/HEAD/images/Swift_logo.png -------------------------------------------------------------------------------- /images/vscode-swift-completion.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RLovelett/vscode-swift/HEAD/images/vscode-swift-completion.gif -------------------------------------------------------------------------------- /images/vscode-swift-documentation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RLovelett/vscode-swift/HEAD/images/vscode-swift-documentation.gif -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | typings/** 3 | out/test/** 4 | test/** 5 | src/** 6 | **/*.map 7 | .gitignore 8 | tsconfig.json 9 | vsc-extension-quickstart.md 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": ".", 11 | "moduleResolution": "node" 12 | }, 13 | "exclude": [ 14 | "node_modules", 15 | ".vscode-test" 16 | ] 17 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | "typescript.tsdk": "./node_modules/typescript/lib" // we want to use the TS server from our node_modules folder to control its version 10 | } -------------------------------------------------------------------------------- /test/extension.test.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Note: This example test is leveraging the Mocha test framework. 3 | // Please refer to their documentation on https://mochajs.org/ for help. 4 | // 5 | 6 | // The module 'assert' provides assertion methods from node 7 | import * as assert from 'assert'; 8 | 9 | // You can import and use all API from the 'vscode' module 10 | // as well as import your extension to test it 11 | import * as vscode from 'vscode'; 12 | import * as myExtension from '../src/extension'; 13 | 14 | // Defines a Mocha test suite to group tests of similar kind together 15 | suite("Extension Tests", () => { 16 | 17 | // Defines a Mocha unit test 18 | test("Something 1", () => { 19 | assert.equal(-1, [1, 2, 3].indexOf(5)); 20 | assert.equal(-1, [1, 2, 3].indexOf(0)); 21 | }); 22 | }); -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | var testRunner = require('vscode/lib/testrunner'); 14 | 15 | // You can directly control Mocha options by uncommenting the following lines 16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info 17 | testRunner.configure({ 18 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) 19 | useColors: true // colored output from test results 20 | }); 21 | 22 | module.exports = testRunner; -------------------------------------------------------------------------------- /images/Swift_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 11 | "stopOnEntry": false, 12 | "sourceMaps": true, 13 | "outDir": "${workspaceRoot}/out/src", 14 | "preLaunchTask": "npm" 15 | }, 16 | { 17 | "name": "Launch Tests", 18 | "type": "extensionHost", 19 | "request": "launch", 20 | "runtimeExecutable": "${execPath}", 21 | "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ], 22 | "stopOnEntry": false, 23 | "sourceMaps": true, 24 | "outDir": "${workspaceRoot}/out/test", 25 | "preLaunchTask": "npm" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${fileBasename}: the current opened file's basename 5 | // ${fileDirname}: the current opened file's dirname 6 | // ${fileExtname}: the current opened file's extension 7 | // ${cwd}: the current working directory of the spawned process 8 | 9 | // A task runner that calls a custom npm script that compiles the extension. 10 | { 11 | "version": "0.1.0", 12 | 13 | // we want to run npm 14 | "command": "npm", 15 | 16 | // the command is a shell script 17 | "isShellCommand": true, 18 | 19 | // show the output window only if unrecognized errors occur. 20 | "showOutput": "silent", 21 | 22 | // we run the custom script "compile" as defined in package.json 23 | "args": ["run", "compile", "--loglevel", "silent"], 24 | 25 | // The tsc compiler is started in watching mode 26 | "isWatching": true, 27 | 28 | // use the standard tsc in watch mode problem matcher to find compile problems in the output. 29 | "problemMatcher": "$tsc-watch" 30 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ryan Lovelett 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as net from 'net'; 4 | 5 | import { workspace, ExtensionContext, Uri } from 'vscode'; 6 | import { ServerOptions, Executable, LanguageClient, LanguageClientOptions, TransportKind } from 'vscode-languageclient'; 7 | 8 | // this method is called when your extension is activated 9 | // your extension is activated the very first time the command is executed 10 | export function activate(context: ExtensionContext) { 11 | // Load the path to the language server from settings 12 | let executableCommand = workspace.getConfiguration("swift") 13 | .get("languageServerPath", "/usr/local/bin/LanguageServer"); 14 | 15 | let run: Executable = { command: executableCommand }; 16 | let debug: Executable = run; 17 | let serverOptions: ServerOptions = { 18 | run: run, 19 | debug: debug 20 | }; 21 | 22 | // client extensions configure their server 23 | let clientOptions: LanguageClientOptions = { 24 | documentSelector: [ 25 | { language: 'swift', scheme: 'file' }, 26 | { language: 'swift', scheme: 'untitled' } 27 | ], 28 | synchronize: { 29 | configurationSection: 'swift', 30 | fileEvents: [ 31 | workspace.createFileSystemWatcher('**/*.swift', false, true, false), 32 | workspace.createFileSystemWatcher('**/.build/{debug,release}.yaml', false, false, false) 33 | ] 34 | } 35 | } 36 | 37 | let client = new LanguageClient('Swift', serverOptions, clientOptions); 38 | 39 | // Push the disposable to the context's subscriptions so that the 40 | // client can be deactivated on extension deactivation 41 | context.subscriptions.push(client.start()); 42 | } 43 | 44 | // this method is called when your extension is deactivated 45 | export function deactivate() { 46 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/visualstudiocode,osx,node,linux 3 | 4 | ### VisualStudioCode ### 5 | out/ 6 | server/ 7 | .vscode/* 8 | !.vscode/settings.json 9 | !.vscode/tasks.json 10 | !.vscode/launch.json 11 | 12 | 13 | ### OSX ### 14 | *.DS_Store 15 | .AppleDouble 16 | .LSOverride 17 | 18 | # Icon must end with two \r 19 | Icon 20 | # Thumbnails 21 | ._* 22 | # Files that might appear in the root of a volume 23 | .DocumentRevisions-V100 24 | .fseventsd 25 | .Spotlight-V100 26 | .TemporaryItems 27 | .Trashes 28 | .VolumeIcon.icns 29 | .com.apple.timemachine.donotpresent 30 | # Directories potentially created on remote AFP share 31 | .AppleDB 32 | .AppleDesktop 33 | Network Trash Folder 34 | Temporary Items 35 | .apdisk 36 | 37 | 38 | ### Node ### 39 | # Logs 40 | logs 41 | *.log 42 | npm-debug.log* 43 | 44 | # Runtime data 45 | pids 46 | *.pid 47 | *.seed 48 | *.pid.lock 49 | 50 | # Directory for instrumented libs generated by jscoverage/JSCover 51 | lib-cov 52 | 53 | # Coverage directory used by tools like istanbul 54 | coverage 55 | 56 | # nyc test coverage 57 | .nyc_output 58 | 59 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 60 | .grunt 61 | 62 | # node-waf configuration 63 | .lock-wscript 64 | 65 | # Compiled binary addons (http://nodejs.org/api/addons.html) 66 | build/Release 67 | 68 | # Dependency directories 69 | node_modules 70 | jspm_packages 71 | 72 | # Optional npm cache directory 73 | .npm 74 | 75 | # Optional eslint cache 76 | .eslintcache 77 | 78 | # Optional REPL history 79 | .node_repl_history 80 | 81 | # Output of 'npm pack' 82 | *.tgz 83 | 84 | 85 | ### Linux ### 86 | *~ 87 | 88 | # temporary files which can be created if a process still has a handle open of a deleted file 89 | .fuse_hidden* 90 | 91 | # KDE directory preferences 92 | .directory 93 | 94 | # Linux trash folder which might appear on any partition or disk 95 | .Trash-* 96 | 97 | # .nfs files are created when an open file is removed but is still being accessed 98 | .nfs* 99 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-swift-language", 3 | "displayName": "Swift", 4 | "description": "Swift language integration, through SourceKit, for VS Code.", 5 | "icon": "images/Swift_logo.png", 6 | "bugs": { 7 | "url": "https://github.com/RLovelett/vscode-swift/issues" 8 | }, 9 | "homepage": "https://github.com/RLovelett/vscode-swift/blob/master/README.md", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/RLovelett/vscode-swift.git" 13 | }, 14 | "license": "SEE LICENSE IN LICENSE", 15 | "version": "0.2.0", 16 | "publisher": "rlovelett", 17 | "author": { 18 | "name": "Ryan Lovelett", 19 | "email": "ryan@lovelett.me", 20 | "url": "https://github.com/RLovelett" 21 | }, 22 | "contributors": [ 23 | { 24 | "name": "Aaron Crespo", 25 | "email": "aaroncrespo@gmail.com", 26 | "url": "https://github.com/aaroncrespo" 27 | } 28 | ], 29 | "engines": { 30 | "vscode": "^1.12.0" 31 | }, 32 | "categories": [ 33 | "Languages" 34 | ], 35 | "keywords": [ 36 | "Swift", 37 | "SourceKit", 38 | "SourceKitten", 39 | "completion", 40 | "documentation" 41 | ], 42 | "preview": true, 43 | "activationEvents": [ 44 | "onLanguage:swift" 45 | ], 46 | "main": "./out/src/extension", 47 | "contributes": { 48 | "configuration": { 49 | "type": "object", 50 | "title": "Swift Extension Configuration", 51 | "properties": { 52 | "swift.languageServerPath": { 53 | "type": "string", 54 | "default": "/usr/local/bin/LanguageServer", 55 | "description": "The fully qualified path to the Swift Language Server executable." 56 | } 57 | } 58 | } 59 | }, 60 | "scripts": { 61 | "vscode:prepublish": "tsc -p ./", 62 | "compile": "tsc -watch -p ./", 63 | "postinstall": "node ./node_modules/vscode/bin/install" 64 | }, 65 | "devDependencies": { 66 | "@types/mocha": "^2.2.32", 67 | "@types/node": "^6.14.2", 68 | "mocha": "^5.2.0", 69 | "typescript": "^2.9.2", 70 | "vscode": "^1.1.26" 71 | }, 72 | "dependencies": { 73 | "vscode-languageclient": "^3.5.1" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift for Visual Studio Code 2 | 3 | This extension adds rich language support for the Swift language to VS Code. These features are provided by the Swift framework itself through [SourceKit](https://github.com/apple/swift/tree/master/tools/SourceKit) and [a Swift Language Server](https://github.com/RLovelett/langserver-swift) implementation. 4 | 5 | Currently the extension provides rudimentary support for: 6 | 7 | - Completion lists 8 | - Symbol resolution 9 | 10 | Future support for: 11 | 12 | - Document Highlights: highlights all 'equal' symbols in a Swift document. 13 | - Hover: provides hover information for a symbol selected in a Swift document. 14 | - Signature Help: provides signature help for a symbol selected in a Swift document. 15 | - Find References: find all project-wide references for a symbol selected in a Swift document. 16 | - List Workspace Symbols: list all project-wide symbols. 17 | - CodeLens: compute CodeLens statistics for a given Swift document. 18 | - Rename: project-wide rename of a symbol. 19 | - Debugger 20 | - [Swift Package Manger](https://swift.org/package-manager/) 21 | 22 | ## Features 23 | 24 | ### Completion Lists 25 | 26 | Use completion lists to find out about available standard library types and function signatures. 27 | 28 | ![Struct Def](https://i.giphy.com/26gJAJzxzZDsVEs24.gif) 29 | 30 | Do the same for your own custom types. Including documentation comments. 31 | 32 | ![Struct Docs](https://i.giphy.com/l0HlzNmlfLl7fyc0g.gif) 33 | 34 | ## Requirements 35 | 36 | This extension requires Swift to be installed on your system. [It also requires a Swift language server be installed as well.](https://github.com/RLovelett/langserver-swift) 37 | By default the extension looks for the language server to be installed at `/usr/local/bin/LanguageServer` (though this behavior is configurable). 38 | 39 | ### Swift 40 | 41 | * Swift Open Source `swift-DEVELOPMENT-SNAPSHOT-2016-12-01-a` toolchain (**Minimum REQUIRED for latest release**) 42 | 43 | ### macOS 44 | 45 | * macOS 10.11.6 (*El Capitan*) or higher 46 | * Xcode Version 8.2 beta (8C30a) or higher using one of the above toolchains (*Recommended*) 47 | 48 | ### Linux 49 | 50 | * [Coming Soon?](https://github.com/apple/swift/pull/5903) 51 | 52 | #### A few remarks about Linux support 53 | 54 | The language server that drives this extension depends on [SourceKit](https://github.com/apple/swift/tree/master/tools/SourceKit) to be available with the Swift toolchain. 55 | Unfortunately, at this time that means that Linux support is not really possible because [SourceKit is not built by default on Linux](https://github.com/apple/swift/pull/5903). 56 | 57 | [Of course it is _possible_ to build SourceKit for Linux](https://github.com/apple/swift/pull/5903). However, doing so is beyond the scope of this project. 58 | 59 | All of the dependencies of the language server, at least ostensibly, support Linux. So there should be little preventing Linux support beyond SourceKit being available on the platform. 60 | 61 | I want to stress: **I hope this will not be the long-term answer/solution**. In fact running on Linux was the whole reason why I started this extension. 62 | 63 | ## Extension Settings 64 | 65 | * `LanguageServer` path 66 | 67 | ## Known Issues 68 | 69 | - Does not run on Linux 70 | 71 | # Debug and Development 72 | 73 | The extension itself relies on a language server to interact with it. [This server has been developed to work with Visual Studio Code](https://github.com/RLovelett/langserver-swift). 74 | 75 | An example workflow for interactively debugging the language server while using it with the Visual Stuio Code client is provided in this section. The instructions are devided into two sections. The first section explains how to generate and configure an Xcode project for debugging. The second section explains how to configure the Visual Studio Code plugin to use the debug executable. 76 | 77 | ## Xcode (e.g., [langserver-swift](https://github.com/RLovelett/langserver-swift)) 78 | 79 | In the directory containing the clone of the language server use SwiftPM to generate an Xcode project. 80 | 81 | ``` 82 | % git clone https://github.com/RLovelett/langserver-swift.git 83 | % cd langserver-swift 84 | % swift package generate-xcodeproj --xcconfig-overrides settings.xcconfig 85 | ``` 86 | 87 | Since the language server client, e.g., VSCode, will actually launch the language server LLDB needs to be told to wait for the application to launch. This can be configured in Xcode after opening the generated project in Xcode. See the screenshot below. 88 | 89 | screen shot 2017-02-22 at 8 55 57 am 90 | 91 | The next step is to build the executable and launch LLDB. Both of these steps can be performed by going to "Product > Run" or the keyboard shortcut ⌘R. After building completes, Xcode should report something like "Waiting to attach to LanguageServer : LanguageServer". 92 | 93 | screen shot 2017-02-22 at 9 40 33 am 94 | 95 | One final step is to determine the `TARGET_BUILD_DIR`. This is used to tell the VSCode extension in the next section where the debug language server is located. 96 | 97 | From a terminal whose current working directory contains the Xcode project previously generated by SwiftPM you can get this information from `xcodebuild`. 98 | 99 | ``` 100 | % xcodebuild -project langserver-swift.xcodeproj -target "LanguageServer" -showBuildSettings | grep "TARGET_BUILD_DIR" 101 | TARGET_BUILD_DIR = /Users/ryan/Library/Developer/Xcode/DerivedData/langserver-swift-gellhgzzpradfqbgjnbtkvzjqymv/Build/Products/Debug 102 | ``` 103 | 104 | Take note of this value it will be used later. 105 | 106 | # VSCode (e.g., [vscode-swift](https://github.com/RLovelett/vscode-swift)) 107 | 108 | Open the directory containing the clone of the Visual Studio Code extension in Visual Studio Code. 109 | 110 | ``` 111 | % git clone https://github.com/RLovelett/vscode-swift.git 112 | % code . 113 | ``` 114 | 115 | Start the TypeScript compiler or the build task (e.g., ⇧⌘B or Tasks: Run Build Task). 116 | 117 | Now open `src/extension.ts` and provide the value of `TARGET_BUILD_DIR` for the debug executable. The change should be similar to the patch that follows. 118 | 119 | ``` 120 | diff --git a/src/extension.ts b/src/extension.ts 121 | index b5ad751..7970ae1 100644 122 | --- a/src/extension.ts 123 | +++ b/src/extension.ts 124 | @@ -13,7 +13,7 @@ export function activate(context: ExtensionContext) { 125 | .get("languageServerPath", "/usr/local/bin/LanguageServer"); 126 | 127 | let run: Executable = { command: executableCommand }; 128 | - let debug: Executable = run; 129 | + let debug: Executable = { command: "${TARGET_BUILD_DIR}/LanguageServer" }; 130 | let serverOptions: ServerOptions = { 131 | run: run, 132 | debug: debug 133 | ``` 134 | 135 | **NOTE:** Make sure the `${TARGET_BUILD_DIR}` is populated with the value you generated in the Xcode section. It is not an environment variable so that will not be evaluated. 136 | 137 | Once this is complete you should be able to open the VSCode debugger and and select `Launch Extension`. This should start both the language server (Xcode/Swift) and the extension (VScode/TypeScript) in debug mode. 138 | 139 | # Caveats 140 | 141 | 1. As noted above you might not be able to capture all the commands upon the language server initially starting up. The current hypothesis is that it takes a little bit of time for LLDB (the Swift debugger) to actually attach to the running process so a few instructions are missed. 142 | 143 | One recommendation is to put a break-point in [`handle.swift`](https://github.com/RLovelett/langserver-swift/blob/251641da96ac1e0ae90f0ead3aa2f210fcb2c599/Sources/LanguageServer/Functions/handle.swift#L17) as this is likely where the server is getting into to trouble. 144 | 145 | 2. Messages are logged to the `Console.app` using the `me.lovelett.langserver-swift` sub-system. One place to look the raw language server JSON-RPC messages is there. 146 | 147 | ## Release Notes 148 | 149 | For detailed release notes see: [Releases](https://github.com/RLovelett/vscode-swift/releases) 150 | 151 | ### 0.1.0 152 | 153 | * Swift 3.1 (via previews) 154 | * [Swift native language server implementation](https://github.com/RLovelett/langserver-swift) 155 | 156 | ### 0.0.5 157 | 158 | * Completions use snippet formatting for placeholders, allows the cursor to tab between placeholders 159 | * Completions index against all swift files in the workspace 160 | * Bug fixes 161 | 162 | ### 0.0.3 163 | 164 | * Allow configuring sourceKitten install location 165 | 166 | ### 0.0.2 167 | 168 | Preview release of the extension 169 | --------------------------------------------------------------------------------