├── .editorconfig ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── .vscodeignore ├── LICENSE ├── README.md ├── extension.js ├── fixture.swift ├── icon.svg ├── package.json ├── parser.js ├── screenshot.png └── server.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "runtimeExecutable": "${execPath}", 9 | "args": [ 10 | "--extensionDevelopmentPath=${workspaceRoot}", 11 | "${workspaceRoot}/fixture.swift" 12 | ], 13 | "stopOnEntry": false 14 | }, 15 | { 16 | "name": "Launch Tests", 17 | "type": "extensionHost", 18 | "request": "launch", 19 | "runtimeExecutable": "${execPath}", 20 | "args": [ 21 | "--extensionDevelopmentPath=${workspaceRoot}", 22 | "--extensionTestsPath=${workspaceRoot}/test" 23 | ], 24 | "stopOnEntry": false 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "swiftlint.enable": true, 3 | "javascript.validate.syntaxValidation": false 4 | } 5 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .editorconfig 3 | .gitignore 4 | *.png 5 | *.swift 6 | test 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015 Shinnosuke Watanabe 4 | 5 | All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vscode-swiftlint 2 | 3 | [![Dependency Status](https://david-dm.org/shinnn/vscode-swiftlint.svg)](https://david-dm.org/shinnn/vscode-swiftlint) 4 | [![devDependency Status](https://david-dm.org/shinnn/vscode-swiftlint/dev-status.svg)](https://david-dm.org/shinnn/vscode-swiftlint#info=devDependencies) 5 | 6 | A [Visual Studio Code](https://code.visualstudio.com/) extension to lint [Swift](https://developer.apple.com/swift/) with [SwiftLint](https://github.com/realm/SwiftLint): 7 | 8 | > enforce Swift style and conventions, loosely based on 9 | [GitHub's Swift Style Guide](https://github.com/github/swift-style-guide) 10 | 11 | ![screenshot](screenshot.png) 12 | 13 | ## Installation 14 | 15 | 1. Ensure `swiftlint` binary is [installed](https://github.com/realm/SwiftLint#installation). 16 | 2. Run [`Install Extension`](https://code.visualstudio.com/docs/editor/extension-gallery#_install-an-extension) command from [Command Palette](https://code.visualstudio.com/Docs/editor/codebasics#_command-palette). 17 | 3. Search and choose `swiftlint`. 18 | 19 | See the [extension installation guide](https://code.visualstudio.com/docs/editor/extension-gallery) for details. 20 | 21 | ## Usage 22 | 23 | Enable the linter in the VS Code [settings](https://code.visualstudio.com/docs/customization/userandworkspace). 24 | 25 | ```json 26 | { 27 | "swiftlint.enable": true 28 | } 29 | ``` 30 | 31 | You can also configure SwiftLint by adding a [`.swiftlint.yml`](https://github.com/realm/SwiftLint#configuration) file to the workspace directory. 32 | 33 | ## License 34 | 35 | Copyright (c) 2015 [Shinnosuke Watanabe](https://github.com/shinnn) 36 | 37 | Licensed under [the MIT License](./LICENSE). 38 | -------------------------------------------------------------------------------- /extension.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | const langClient = require('vscode-languageclient'); 6 | const LanguageClient = langClient.LanguageClient; 7 | const SettingMonitor = langClient.SettingMonitor; 8 | const vscode = require('vscode'); 9 | 10 | exports.activate = function activateSwiftLint(context) { 11 | const serverModule = path.join(__dirname, 'server.js'); 12 | 13 | const client = new LanguageClient('Standard Linter', { 14 | run: { 15 | module: serverModule 16 | }, 17 | debug: { 18 | module: serverModule, 19 | options: { 20 | execArgv: ['--nolazy', '--debug=6004'] 21 | } 22 | } 23 | }, { 24 | documentSelector: ['swift'], 25 | synchronize: { 26 | fileEvents: vscode.workspace.createFileSystemWatcher('.swiftlint.yml') 27 | } 28 | }); 29 | 30 | context.subscriptions.push(new SettingMonitor(client, 'swiftlint.enable').start()); 31 | }; 32 | -------------------------------------------------------------------------------- /fixture.swift: -------------------------------------------------------------------------------- 1 | class pug : Dog {} 2 | let img = try! getImage(); 3 | -------------------------------------------------------------------------------- /icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Swift 13 | 14 | Lint 15 | 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swiftlint", 3 | "version": "0.1.1", 4 | "displayName": "SwiftLint", 5 | "description": "Use SwiftLint to lint Swift files", 6 | "publisher": "shinnn", 7 | "repository": "https://github.com/shinnn/vscode-standard", 8 | "homepage": "https://github.com/shinnn/vscode-standard#readme", 9 | "bugs": "https://github.com/shinnn/vscode-standard/issues", 10 | "license": "MIT", 11 | "icon": "icon.svg", 12 | "galleryBanner": { 13 | "color": "#F2F2F2", 14 | "theme": "light" 15 | }, 16 | "engines": { 17 | "vscode": "^0.10.1" 18 | }, 19 | "categories": [ 20 | "Linters" 21 | ], 22 | "activationEvents": [ 23 | "onLanguage:swift" 24 | ], 25 | "main": "extension.js", 26 | "contributes": { 27 | "configuration": { 28 | "type": "object", 29 | "title": "SwiftLint configuration options", 30 | "properties": { 31 | "swiftlint.enable": { 32 | "type": "boolean", 33 | "default": false, 34 | "description": "Control whether SwiftLint is enabled for Swift files or not." 35 | } 36 | } 37 | } 38 | }, 39 | "scripts": { 40 | "test": "eslint --config @shinnn/node --fix extension.js parser.js server.js" 41 | }, 42 | "dependencies": { 43 | "concat-stream": "1.5.1", 44 | "vscode-languageclient": "0.10.7", 45 | "vscode-languageserver": "0.10.6", 46 | "which-promise": "1.0.0" 47 | }, 48 | "devDependencies": { 49 | "@shinnn/eslint-config-node": "^1.0.1", 50 | "eslint": "^1.10.0", 51 | "vscode": "^0.10.4" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /parser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const langServer = require('vscode-languageserver'); 4 | 5 | function parseSeverity(severity) { 6 | if (severity === 'error') { 7 | return langServer.DiagnosticSeverity.Error; 8 | } 9 | 10 | return langServer.DiagnosticSeverity.Warning; 11 | } 12 | 13 | module.exports = function parseSwiftLintStdout(str) { 14 | if (typeof str !== 'string') { 15 | throw new TypeError( 16 | String(str) + 17 | ' is not a string. Expected a standard output of SwiftLint.' 18 | ); 19 | } 20 | 21 | const regex = /^([^:]+):(\d+):(?:(\d+):)? (error|warning): (.+): (.+) \((.+)\)$/gm; 22 | const results = []; 23 | let matched; 24 | 25 | while ((matched = regex.exec(str)) !== null) { 26 | results.push({ 27 | message: `SwiftLint: ${matched[5]}: ${matched[6]} (${matched[7]})`, 28 | severity: parseSeverity(matched[4]), 29 | range: { 30 | start: { 31 | line: Number(matched[2]) - 1, 32 | character: matched[3] === undefined ? 0 : Number(matched[3]) - 1 33 | }, 34 | end: { 35 | line: Number(matched[2]) - 1, 36 | character: matched[3] === undefined ? Number.MAX_SAFE_INTEGER : Number(matched[3]) - 1 37 | } 38 | } 39 | }); 40 | } 41 | 42 | return results; 43 | }; 44 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shinnn/vscode-swiftlint/1b4837e51475fc8c59f2a2b58831dbdd393fb460/screenshot.png -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const concatStream = require('concat-stream'); 4 | const langServer = require('vscode-languageserver'); 5 | const spawn = require('child_process').spawn; 6 | const whichPromise = require('which-promise'); 7 | const parser = require('./parser.js'); 8 | 9 | let cwd; 10 | let swiftLintPath; 11 | 12 | const connection = langServer.createConnection(process.stdin, process.stdout); 13 | const documents = new langServer.TextDocuments(); 14 | 15 | function getMessage(err, document) { 16 | if (typeof err.message === 'string') { 17 | return err.message; 18 | } 19 | 20 | return 'An unknown error occurred while validating file:' + 21 | langServer.Files.uriToFilePath(document.uri); 22 | } 23 | 24 | function validate(document) { 25 | const uri = document.uri; 26 | 27 | const cp = spawn(swiftLintPath, ['lint', '--use-stdin'], {cwd}); 28 | cp.stdout.setEncoding('utf8'); 29 | cp.stdout.pipe(concatStream({encoding: 'string'}, out => { 30 | connection.sendDiagnostics({uri, diagnostics: parser(out)}); 31 | })); 32 | 33 | cp.on('error', err => connection.window.showErrorMessage(getMessage(err, document))); 34 | 35 | cp.stdin.end(new Buffer(document.getText())); 36 | } 37 | 38 | function validateAll() { 39 | documents.all().forEach(document => validate(document)); 40 | } 41 | 42 | connection.onInitialize(params => { 43 | cwd = params.rootPath; 44 | 45 | return whichPromise('swiftlint').then(binPath => { 46 | swiftLintPath = binPath; 47 | }, () => Promise.reject(new langServer.ResponseError( 48 | 99, 49 | '`swiftlint` command is not installed. Install it and then press Retry. ' + 50 | 'https://github.com/realm/SwiftLint#installation', 51 | {retry: true} 52 | ))) 53 | .catch(err => Promise.reject(new langServer.ResponseError(99, err.message, {retry: false}))); 54 | }); 55 | 56 | connection.onDidChangeConfiguration(() => validateAll()); 57 | connection.onDidChangeWatchedFiles(() => validateAll()); 58 | 59 | documents.onDidChangeContent(event => validate(event.document)); 60 | documents.listen(connection); 61 | 62 | connection.listen(); 63 | --------------------------------------------------------------------------------