├── .gitignore ├── .travis.yml ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── feature.png ├── package.json ├── src ├── configuration.ts ├── extension.ts ├── lint.ts └── runner.ts ├── test ├── extension.test.ts └── index.ts ├── tsconfig.json └── vsc-extension-quickstart.md /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: 5 | - "6" 6 | 7 | os: 8 | - osx 9 | - linux 10 | 11 | before_install: 12 | - if [ $TRAVIS_OS_NAME == "linux" ]; then 13 | export CXX="g++-4.9" CC="gcc-4.9" DISPLAY=:99.0; 14 | sh -e /etc/init.d/xvfb start; 15 | sleep 3; 16 | sudo apt-get install python -y; 17 | sudo pip install cpplint; 18 | which cpplint; 19 | fi 20 | - if [ $TRAVIS_OS_NAME == "macos" ]; then 21 | brew install python virtualenv; 22 | pip install cpplint; 23 | which cpplint; 24 | fi 25 | 26 | install: 27 | - node --version 28 | - npm --version 29 | - npm install 30 | - npm run vscode:prepublish 31 | 32 | script: 33 | - npm test --silent -------------------------------------------------------------------------------- /.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 | "outFiles": [ "${workspaceRoot}/out/src/**/*.js" ], 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 | "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ], 25 | "preLaunchTask": "npm" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /.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 | "isBackground": true, 27 | 28 | // use the standard tsc in watch mode problem matcher to find compile problems in the output. 29 | "problemMatcher": "$tsc-watch" 30 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | test/** 5 | src/** 6 | **/*.map 7 | .gitignore 8 | tsconfig.json 9 | vsc-extension-quickstart.md 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to the "cpplint" extension will be documented in this file. 3 | 4 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 5 | 6 | ## [0.1.3] 7 | ### Change 8 | 9 | - for earlier versions of cpplint -- g-arjones 10 | 11 | ## [0.1.2] 12 | ### Change 13 | 14 | - fix cpplint in 500ms when setting and save happened to avoid large file freeze 15 | 16 | ## [0.1.0] 17 | ### Change 18 | 19 | - update vscode baseline to 1.21 20 | - when exclude is a relative path, multiroot will scaned 21 | 22 | ## [0.1.0] 23 | ### Change 24 | 25 | - configure to ConfigManager singletone 26 | - lint when file open 27 | 28 | ## [0.0.10] 29 | ### Add 30 | 31 | - add lint language settings 32 | 33 | ### fix 34 | 35 | - when no active editer exists, extension will crash 36 | 37 | ## [0.0.9] 38 | ### Add 39 | 40 | - add extensions and header setting arguments 41 | 42 | ## [0.0.8] 43 | ### Add 44 | 45 | - add root and repository setting arguments 46 | 47 | ## [0.0.6 - 0.0.7] 48 | ### Add 49 | 50 | - add multi root support 51 | 52 | ## [0.0.5] 53 | 54 | - add line length configuration 55 | - add travis ci status 56 | - add exclude configuration 57 | - add filters configuration 58 | - add verbose configuration 59 | 60 | ## [0.0.4] 61 | 62 | - fix readme image 63 | - add source link 64 | 65 | ## [0.0.3] 66 | 67 | - add lintMode, able to choose scan single file or the whole workspace 68 | 69 | ## [0.0.2] 70 | 71 | - add missing dependence of lodash 72 | 73 | ## [0.0.1] 74 | 75 | - Initial release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 secularbird 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpplint README 2 | 3 | This extension utilizes the cpplint checker to provide C and C++ code style checker within Visual Studio Code. 4 | 5 | [![Build Status](https://travis-ci.org/secularbird/cpplint-extension.svg?branch=master)](https://travis-ci.org/secularbird/cpplint-extension) 6 | 7 | ## Features 8 | 9 | * check coding style of cpp and c, when open and save source file 10 | 11 | ![feature](https://github.com/secularbird/cpplint-extension/raw/master/feature.png) 12 | 13 | ## Requirements 14 | 15 | ### Install cpplint 16 | 17 | #### Install from source 18 | 19 | https://github.com/cpplint/cpplint 20 | 21 | #### Mac & Linux 22 | 23 | ```bash 24 | sudo pip install cpplint 25 | ``` 26 | 27 | #### Windows 28 | 29 | * install anaconda 30 | download link: https://repo.continuum.io/archive/Anaconda2-5.0.0-Windows-x86_64.exe 31 | 32 | * install cpplint 33 | open the anaconda Prompt, run the following command 34 | ```batch 35 | pip install cpplint 36 | ``` 37 | 38 | #### Check the install result 39 | 40 | ##### Linux 41 | 42 | ```text 43 | ls -l /usr/local/bin/cpplint 44 | -rwxr-xr-x 1 root root 381 May 3 08:19 /usr/local/bin/cpplint 45 | ``` 46 | 47 | ##### Mac 48 | 49 | ```bash 50 | ls -l /opt/local/Library/Frameworks/Python.framework/Versions/2.7/bin/cpplint 51 | -rwxr-xr-x 1 root wheel 468 Apr 30 22:57 /opt/local/Library/Frameworks/Python.framework/Versions/2.7/bin/cpplint 52 | ``` 53 | 54 | or 55 | 56 | ```bash 57 | ls -l /opt/local/Library/Frameworks/Python.framework/Versions/3.5/bin/cpplint 58 | -rwxr-xr-x 1 root wheel 267 Apr 30 22:03 /opt/local/Library/Frameworks/Python.framework/Versions/3.5/bin/cpplint 59 | ``` 60 | 61 | ##### Windows 62 | 63 | ``` bath 64 | dir c:\ProgramData\Anaconda2\Scripts\cpplint.exe 65 | ``` 66 | 67 | ## Extension Settings 68 | 69 | * `cpplint.cpplintPath`: set cpplint executable path, path on windows should like `c:\\ProgramData\\Anaconda2\\Scripts\\cpplint.exe` 70 | * `cpplint.lintMode`: set cpplint mode, avialable value are single and workspace 71 | * `cpplint.lineLength`: set line length strict, default is 80 characters 72 | * `cpplint.excludes`: set exclude rules, which is related path and shell globbing is preforming, abosluted path is supported right now, 73 |
Examples:
74 |         ["one.cc"]
75 |         ["src/\*.cc"]
76 |         ["src/\*.cc", "test/\*.cc"]
77 | * `cpplint.filters`: set filters, only error messages whose category names pass the filters will be printed 78 | * `cpplint.root`: set the root directory used for deriving header guard CPP variables 79 | * `cpplint.repository`: set top level directory of the repository, used to derive the header guard CPP variable 80 | * `cpplint.extensions`: set the allowed file extensions that cpplint will check 81 | * `cpplint.languages`: set the allowed vscode language identifiers that cpplint will check *(Currently only on single file mode)* 82 | * `cpplint.headers`: set the allowed header extensions that cpplint will consider to be header files 83 | * `cpplint.verbose`: verbose level, errors with lower verbosity levels have lower confidence and are more likely to be false positives 84 | 85 | ## Known Issues 86 | 87 | Any issues please contact: [cpplint](https://github.com/secularbird/cpplint-extension/issues) 88 | -------------------------------------------------------------------------------- /feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secularbird/cpplint-extension/e9b3696b6ea780f5ba4d474d0c08c518b179abe6/feature.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cpplint", 3 | "displayName": "cpplint", 4 | "description": "code style check tool extension for cpplint", 5 | "version": "0.1.3", 6 | "publisher": "mine", 7 | "repository": { 8 | "type": "Git", 9 | "url": "https://github.com/secularbird/cpplint-extension" 10 | }, 11 | "engines": { 12 | "vscode": "^1.21.0" 13 | }, 14 | "categories": [ 15 | "Linters" 16 | ], 17 | "keywords": [ 18 | "multi-root ready" 19 | ], 20 | "activationEvents": [ 21 | "onLanguage:cpp", 22 | "onLanguage:c", 23 | "onCommand:cpplint.runAnalysis", 24 | "onCommand:cpplint.runWholeAnalysis" 25 | ], 26 | "main": "./out/src/extension", 27 | "contributes": { 28 | "commands": [ 29 | { 30 | "command": "cpplint.runAnalysis", 31 | "title": "Analyze current file", 32 | "category": "cpplinter" 33 | }, 34 | { 35 | "command": "cpplint.runWholeAnalysis", 36 | "title": "Analyze current workspace", 37 | "category": "cpplinter" 38 | } 39 | ], 40 | "configuration": { 41 | "type": "object", 42 | "title": "cpplint", 43 | "properties": { 44 | "cpplint.cpplintPath": { 45 | "type": "string", 46 | "default": "/usr/local/bin/cpplint", 47 | "description": "The path to the cpplint executable. If not set, the default location will be used." 48 | }, 49 | "cpplint.lintMode": { 50 | "type": "string", 51 | "default": "single", 52 | "enum": [ 53 | "single", 54 | "workspace" 55 | ], 56 | "description": "single is fast, only provides information of current active file, workspace is slow, provides informations of the whole workspace" 57 | }, 58 | "cpplint.lineLength": { 59 | "type": "number", 60 | "default": 80, 61 | "description": "This is the allowed line length for the project." 62 | }, 63 | "cpplint.excludes": { 64 | "type": "array", 65 | "default": [], 66 | "description": "Exclude the given path from the list of files to be linted.Relative paths are evaluated relative to the current directory and shell globbing is performed" 67 | }, 68 | "cpplint.filters": { 69 | "type": "array", 70 | "default": [], 71 | "description": "Specify a comma-separated list of category-filters to apply: only error messages whose category names pass the filters will be printed." 72 | }, 73 | "cpplint.verbose": { 74 | "type": "number", 75 | "default": 0, 76 | "enum": [ 77 | 0, 78 | 1, 79 | 2, 80 | 4, 81 | 5 82 | ], 83 | "description": "Specify a number 0-5 to restrict errors to certain verbosity levels. Errors with lower verbosity levels have lower confidence and are more likely to be false positives." 84 | }, 85 | "cpplint.repository": { 86 | "type": "string", 87 | "default": "${workspaceFolder}", 88 | "description": "The top level directory of the repository, used to derive the header guard CPP variable. By default, this is determined by searching for a path that contains .git, .hg, or .svn. When this flag is specified, the given path is used instead. This option allows the header guard CPP variable to remain consistent even if members of a team have different repository root directories (such as when checking out a subdirectory with SVN). In addition, users of non-mainstream version control systems can use this flag to ensure readable header guard CPP variables." 89 | }, 90 | "cpplint.root": { 91 | "type": "string", 92 | "default": "${workspaceFolder}/include", 93 | "description": "The root directory used for deriving header guard CPP variables. This directory is relative to the top level directory of the repository which by default is determined by searching for a directory that contains .git, .hg, or .svn but can also be controlled with the --repository flag. If the specified directory does not exist, this flag is ignored." 94 | }, 95 | "cpplint.extensions": { 96 | "type": "array", 97 | "default": [ 98 | "cpp", 99 | "h++", 100 | "cuh", 101 | "c", 102 | "c++", 103 | "cu", 104 | "hxx", 105 | "hpp", 106 | "cc", 107 | "cxx", 108 | "h" 109 | ], 110 | "description": "The allowed file extensions that cpplint will check." 111 | }, 112 | "cpplint.languages": { 113 | "type": "array", 114 | "default": [ 115 | "cpp", 116 | "c" 117 | ], 118 | "description": "The allowed vscode language identifiers that cpplint will check." 119 | }, 120 | "cpplint.headers": { 121 | "type": "array", 122 | "default": [ 123 | "h++", 124 | "cuh", 125 | "hxx", 126 | "hpp", 127 | "h" 128 | ], 129 | "description": "The allowed header extensions that cpplint will consider to be header files." 130 | } 131 | } 132 | } 133 | }, 134 | "scripts": { 135 | "vscode:prepublish": "tsc -p ./", 136 | "compile": "tsc -watch -p ./", 137 | "postinstall": "node ./node_modules/vscode/bin/install", 138 | "test": "node ./node_modules/vscode/bin/test" 139 | }, 140 | "devDependencies": { 141 | "typescript": "^2.0.3", 142 | "vscode": "^1.0.0", 143 | "mocha": "^2.3.3", 144 | "@types/node": "^6.0.40", 145 | "@types/mocha": "^2.2.32" 146 | }, 147 | "dependencies": { 148 | "lodash": "^4.17.4" 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/configuration.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as vscode from 'vscode'; 4 | import { platform } from 'os'; 5 | import { join } from 'path'; 6 | import { each, isNull } from 'lodash'; 7 | import { existsSync } from 'fs'; 8 | 9 | export class ConfigManager { 10 | 11 | private static _instance: ConfigManager = new ConfigManager(); 12 | 13 | private config: { [key: string]: any } = {}; 14 | 15 | constructor() { 16 | if (ConfigManager._instance) { 17 | throw new Error("Error: Instantiation failed: Use ConfigManager.getInstance() instead of new."); 18 | } 19 | ConfigManager._instance = this; 20 | } 21 | 22 | public static getInstance(): ConfigManager { 23 | return ConfigManager._instance; 24 | } 25 | 26 | public getConfig(): { [key: string]: any } { 27 | return this.config; 28 | } 29 | 30 | private findCpplintPath(settings: vscode.WorkspaceConfiguration): string { 31 | let cpplintPath = settings.get('cpplintPath', null); 32 | 33 | if (isNull(cpplintPath)) { 34 | let p = platform(); 35 | if (p === 'win32') { 36 | // TODO: add win32 and win64 cpplint path 37 | } 38 | else if (p === 'linux' || p === 'darwin') { 39 | let attempts = ['/usr/local/bin/cpplint']; 40 | for (let index = 0; index < attempts.length; index++) { 41 | if (existsSync(attempts[index])) { 42 | cpplintPath = attempts[index]; 43 | break; 44 | } 45 | } 46 | } 47 | } 48 | 49 | return cpplintPath; 50 | } 51 | 52 | public isSingleMode(): boolean { 53 | if (this.config['lintMode'] == 'single') { 54 | return true; 55 | } else { 56 | return false; 57 | } 58 | } 59 | 60 | public isSupportLanguage(language: string): boolean { 61 | if (this.config["languages"].indexOf(language) >= 0) { 62 | return true; 63 | } else { 64 | return false; 65 | } 66 | } 67 | 68 | public initialize() { 69 | this.config = {}; 70 | let settings = vscode.workspace.getConfiguration('cpplint'); 71 | 72 | if (settings) { 73 | var cpplintPath = this.findCpplintPath(settings); 74 | 75 | if (!existsSync(cpplintPath)) { 76 | vscode.window.showErrorMessage('Cpplint: Could not find cpplint executable'); 77 | } 78 | 79 | this.config['cpplintPath'] = cpplintPath; 80 | 81 | var linelength = settings.get("lineLength", 80); 82 | this.config['lineLength'] = linelength; 83 | 84 | var lintmode = settings.get('lintMode', 'single'); 85 | this.config['lintMode'] = lintmode; 86 | 87 | var excludes = settings.get('excludes', []) 88 | this.config['excludes'] = excludes; 89 | 90 | var filters = settings.get("filters", []) 91 | this.config["filters"] = filters; 92 | 93 | var root = settings.get("root", "") 94 | this.config["root"] = root; 95 | 96 | var languages = settings.get("languages", []) 97 | this.config["languages"] = languages; 98 | 99 | var extensions = settings.get("extensions", "") 100 | this.config["extensions"] = extensions; 101 | 102 | var headers = settings.get("headers", "") 103 | this.config["headers"] = headers; 104 | 105 | var repository = settings.get("repository", "") 106 | this.config["repository"] = repository; 107 | 108 | this.config["filters"].forEach(element => { 109 | if (element[0] != '-' && element[0] != '+') { 110 | vscode.window.showErrorMessage("filter [" + element + '] must start with + or -, please check your settings'); 111 | return false; 112 | } 113 | }); 114 | 115 | var verbose = settings.get("verbose", 0) 116 | this.config['verbose'] = verbose; 117 | } 118 | return this.config; 119 | } 120 | } -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // The module 'vscode' contains the VS Code extensibility API 3 | // Import the module and reference it with the alias vscode in your code below 4 | import * as vscode from 'vscode'; 5 | import * as an from './runner'; 6 | import { Lint } from './lint'; 7 | import { analysisResult } from './lint' 8 | import * as configuration from './configuration' 9 | import { ConfigManager } from './configuration'; 10 | 11 | let outputChannel: vscode.OutputChannel; 12 | let statusItem: vscode.StatusBarItem; 13 | let timer: NodeJS.Timer; 14 | 15 | let diagnosticCollection: vscode.DiagnosticCollection = vscode.languages.createDiagnosticCollection('cpplint'); 16 | 17 | // this method is called when your extension is activated 18 | // your extension is activated the very first time the command is executed 19 | export function activate(context: vscode.ExtensionContext) { 20 | outputChannel = vscode.window.createOutputChannel('CppLint'); 21 | // outputChannel.appendLine('CppLint is running.'); 22 | // Use the console to output diagnostic information (console.log) and errors (console.error) 23 | // This line of code will only be executed once when your extension is activated 24 | console.log('Congratulations, your extension "cpplint" is now active!'); 25 | 26 | loadConfigure(); 27 | 28 | // The command has been defined in the package.json file 29 | // Now provide the implementation of the command with registerCommand 30 | // The commandId parameter must match the command field in package.json 31 | 32 | let single = vscode.commands.registerCommand('cpplint.runAnalysis', runAnalysis); 33 | context.subscriptions.push(single); 34 | 35 | let whole = vscode.commands.registerCommand('cpplint.runWholeAnalysis', runWholeAnalysis); 36 | context.subscriptions.push(whole); 37 | 38 | vscode.workspace.onDidChangeConfiguration((()=>loadConfigure()).bind(this)); 39 | } 40 | 41 | function runAnalysis(): Promise { 42 | var edit = vscode.window.activeTextEditor; 43 | if (edit == undefined) { 44 | return Promise.reject("no edit opened"); 45 | } 46 | 47 | outputChannel.show(); 48 | outputChannel.clear(); 49 | 50 | let start = 'CppLint started: ' + new Date().toString(); 51 | outputChannel.appendLine(start); 52 | 53 | let result = an.runOnFile(); 54 | outputChannel.appendLine(result); 55 | 56 | let end = 'CppLint ended: ' + new Date().toString(); 57 | outputChannel.appendLine(end); 58 | 59 | // vscode.window.showInformationMessage(edit.document.uri.fsPath) 60 | return Promise.resolve() 61 | } 62 | 63 | function runWholeAnalysis(): Promise { 64 | outputChannel.show(); 65 | outputChannel.clear(); 66 | 67 | let start = 'CppLint started: ' + new Date().toString(); 68 | outputChannel.appendLine(start); 69 | 70 | let result = an.runOnWorkspace(); 71 | outputChannel.appendLine(result); 72 | 73 | let end = 'CppLint ended: ' + new Date().toString(); 74 | outputChannel.appendLine(end); 75 | 76 | // vscode.window.showInformationMessage(edit.document.uri.fsPath) 77 | return Promise.resolve() 78 | } 79 | 80 | // this method is called when your extension is deactivated 81 | export function deactivate() { 82 | clearTimeout(timer) 83 | vscode.window.showInformationMessage("Cpplint deactivated") 84 | } 85 | 86 | function doLint() { 87 | if (vscode.window.activeTextEditor) { 88 | let language = vscode.window.activeTextEditor.document.languageId 89 | if (ConfigManager.getInstance().isSupportLanguage(language)) { 90 | if (ConfigManager.getInstance().isSingleMode()) { 91 | Lint(diagnosticCollection, false); 92 | } else { 93 | Lint(diagnosticCollection, true); 94 | } 95 | } 96 | } 97 | clearTimeout(timer) 98 | } 99 | 100 | function startLint() { 101 | timer = global.setTimeout(doLint, 1.5 * 1000); 102 | } 103 | 104 | function startLint2() { 105 | timer = global.setTimeout(doLint, 500); 106 | } 107 | 108 | function loadConfigure() { 109 | ConfigManager.getInstance().initialize(); 110 | if (ConfigManager.getInstance().isSingleMode()) { 111 | startLint2(); 112 | vscode.window.onDidChangeActiveTextEditor((() => startLint2()).bind(this)); 113 | vscode.workspace.onDidSaveTextDocument((() => startLint2()).bind(this)); 114 | } else { 115 | // start timer to do workspace lint 116 | startLint(); 117 | vscode.workspace.onDidSaveTextDocument((() => startLint()).bind(this)); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/lint.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * LINTER.TS 3 | * --------- 4 | * Parses cpplint output and adds linting hints to files. 5 | */ 6 | 7 | import * as vscode from 'vscode'; 8 | import * as fs from 'fs'; 9 | import * as path from 'path'; 10 | import { runOnFile } from './runner'; 11 | import { runOnWorkspace } from './runner'; 12 | import { ConfigManager } from './configuration'; 13 | 14 | function getCorrectFileName(p: string): string { 15 | if (!fs.existsSync(p)) { 16 | p = path.join(vscode.workspace.rootPath, p); 17 | if (!fs.existsSync(p)) { 18 | return null; 19 | } 20 | } 21 | return p; 22 | } 23 | 24 | function cpplintSeverityToDiagnosticSeverity(severity: string): vscode.DiagnosticSeverity { 25 | switch (severity) { 26 | case 'error': 27 | return vscode.DiagnosticSeverity.Error; 28 | case 'warning': 29 | return vscode.DiagnosticSeverity.Warning; 30 | default: 31 | return vscode.DiagnosticSeverity.Information; 32 | } 33 | } 34 | 35 | export function analysisResult(diagnosticCollection: vscode.DiagnosticCollection, result: string) { 36 | diagnosticCollection.clear(); 37 | 38 | // 1 = path, 2 = line, 3 = severity, 4 = message 39 | let regex = /^(.*):([0-9]+):\s*(\w+):(.*\s+\[.*\])\s+\[([0-9]+)\]/gm; 40 | let regexArray: RegExpExecArray; 41 | let fileData: { [key: string]: RegExpExecArray[] } = {}; 42 | while (regexArray = regex.exec(result)) { 43 | if (regexArray[1] === undefined || regexArray[2] === undefined 44 | || regexArray[3] === undefined || regexArray[4] === undefined 45 | || regexArray[5] === undefined) { 46 | continue; 47 | } 48 | 49 | let fileName = getCorrectFileName(regexArray[1]); 50 | if (!(fileName in fileData)) { 51 | fileData[fileName] = []; 52 | } 53 | fileData[fileName].push(regexArray); 54 | } 55 | 56 | for (let fileName in fileData) { 57 | vscode.workspace.openTextDocument(fileName).then((doc: vscode.TextDocument) => { 58 | let diagnostics: vscode.Diagnostic[] = []; 59 | for (let index = 0; index < fileData[fileName].length; index++) { 60 | let array = fileData[fileName][index]; 61 | let line = Number(array[2]); 62 | let severity = array[3]; 63 | let message = array[4]; 64 | 65 | if (line > 0) { 66 | line--; 67 | } 68 | 69 | let l = doc.lineAt(line); 70 | let r = new vscode.Range(line, 0, line, l.text.length); 71 | let d = new vscode.Diagnostic(r, `${message}`, cpplintSeverityToDiagnosticSeverity(severity)); 72 | d.source = 'cpplint'; 73 | diagnostics.push(d); 74 | } 75 | diagnosticCollection.set(doc.uri, diagnostics); 76 | }); 77 | } 78 | } 79 | 80 | export function Lint(diagnosticCollection: vscode.DiagnosticCollection, enableworkspace: boolean) { 81 | let cpplintOutput; 82 | if (enableworkspace) { 83 | cpplintOutput = runOnWorkspace(); 84 | } else { 85 | cpplintOutput = runOnFile(); 86 | } 87 | analysisResult(diagnosticCollection, cpplintOutput) 88 | } 89 | -------------------------------------------------------------------------------- /src/runner.ts: -------------------------------------------------------------------------------- 1 | import { spawnSync } from "child_process"; 2 | import * as vscode from 'vscode'; 3 | import { ConfigManager } from "./configuration"; 4 | import * as path from 'path'; 5 | 6 | export function runOnFile() { 7 | if (vscode.window.activeTextEditor == undefined) { 8 | return "" 9 | } 10 | let activedoc = vscode.window.activeTextEditor.document; 11 | let filename = activedoc.fileName; 12 | let workspacefolder = vscode.workspace.getWorkspaceFolder(activedoc.uri) 13 | let workspaces = null; 14 | if (workspacefolder != undefined) { 15 | workspaces = [workspacefolder.uri.fsPath] 16 | } 17 | 18 | if (ConfigManager.getInstance().isSupportLanguage(activedoc.languageId)) { 19 | let result = runCppLint(filename, workspaces, false); 20 | return result; 21 | } else { 22 | return ""; 23 | } 24 | } 25 | 26 | export function runOnWorkspace() { 27 | let workspaces: string[] = []; 28 | for (let folder of vscode.workspace.workspaceFolders) { 29 | workspaces = workspaces.concat(folder.uri.fsPath) 30 | } 31 | let result = runCppLint(null, workspaces, true); 32 | return result; 33 | } 34 | 35 | export function runCppLint(filename: string, workspaces: string[], enableworkspace: boolean) { 36 | let config = ConfigManager.getInstance().getConfig(); 37 | let cpplint = config["cpplintPath"]; 38 | let linelength = "--linelength=" + config['lineLength']; 39 | let param: string[] = ['--output=eclipse', linelength]; 40 | 41 | if (config['excludes'].length != 0) { 42 | config['excludes'].forEach(element => { 43 | if (element[0] == "/") { 44 | param.push("--exclude=" + element); 45 | } else { 46 | workspaces.forEach(workspace => { 47 | param.push("--exclude=" + workspace + "/" + element) 48 | }); 49 | } 50 | }); 51 | } 52 | 53 | if (config['filters'].length != 0) { 54 | param.push("--filter=" + config["filters"].join(',')) 55 | } 56 | 57 | if (config["extensions"].length != 0) { 58 | param.push("--extensions=" + config["extensions"].join(',')) 59 | } 60 | 61 | if (config["headers"].length != 0) { 62 | param.push("--headers=" + config["headers"].join(',')) 63 | } 64 | 65 | param.push("--verbose=" + config['verbose']); 66 | 67 | if (enableworkspace) { 68 | let out = []; 69 | for (let workspace of workspaces) { 70 | out.push("Scan workspace: " + workspace); 71 | let workspaceparam = param; 72 | if (config['repository'].length != 0) { 73 | let repo: string = "--repository=" + config["repository"].replace("${workspaceFolder}", workspace); 74 | repo = repo.replace("${workspaceFolderBasename}", path.basename(workspace)); 75 | 76 | workspaceparam.push(repo); 77 | } 78 | 79 | if (config['root'].length != 0) { 80 | let root: string = "--root=" + config["root"].replace("${workspaceFolder}", workspace); 81 | root = root.replace("${workspaceFolderBasename}", path.basename(workspace)); 82 | 83 | workspaceparam.push(root); 84 | } 85 | workspaceparam = workspaceparam.concat(["--recursive", workspace]); 86 | 87 | let output = lint(cpplint, workspaceparam); 88 | out = output; 89 | } 90 | return out.join('\n'); 91 | 92 | } else { 93 | let workspace = "" 94 | if (workspaces != null) { 95 | workspace = workspaces[0]; 96 | } 97 | 98 | if (config['repository'].length != 0) { 99 | let repo: string = "--repository=" + config["repository"].replace("${workspaceFolder}", workspace); 100 | repo = repo.replace("${workspaceFolderBasename}", path.basename(workspace)); 101 | 102 | param.push(repo); 103 | } 104 | 105 | if (config['root'].length != 0) { 106 | let root: string = "--root=" + config["root"].replace("${workspaceFolder}", workspace); 107 | root = root.replace("${workspaceFolderBasename}", path.basename(workspace)); 108 | 109 | param.push(root); 110 | } 111 | 112 | param.push(filename); 113 | let output = lint(cpplint, param); 114 | let end = 'CppLint ended: ' + new Date().toString(); 115 | let out = output; 116 | return out.join('\n'); 117 | } 118 | } 119 | 120 | function lint(exec: string, params: string[]) { 121 | let result = spawnSync(exec, params) 122 | let stdout = result.stdout; 123 | let stderr = result.stderr; 124 | let out = [result.stdout, result.stderr] 125 | return out; 126 | } 127 | -------------------------------------------------------------------------------- /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; -------------------------------------------------------------------------------- /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 | }, 12 | "exclude": [ 13 | "node_modules", 14 | ".vscode-test" 15 | ] 16 | } -------------------------------------------------------------------------------- /vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your first VS Code Extension 2 | 3 | ## What's in the folder 4 | * This folder contains all of the files necessary for your extension 5 | * `package.json` - this is the manifest file in which you declare your extension and command. 6 | The sample plugin registers a command and defines its title and command name. With this information 7 | VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. 8 | * `src/extension.ts` - this is the main file where you will provide the implementation of your command. 9 | The file exports one function, `activate`, which is called the very first time your extension is 10 | activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 11 | We pass the function containing the implementation of the command as the second parameter to 12 | `registerCommand`. 13 | 14 | ## Get up and running straight away 15 | * press `F5` to open a new window with your extension loaded 16 | * run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World` 17 | * set breakpoints in your code inside `src/extension.ts` to debug your extension 18 | * find output from your extension in the debug console 19 | 20 | ## Make changes 21 | * you can relaunch the extension from the debug toolbar after changing code in `src/extension.ts` 22 | * you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes 23 | 24 | ## Explore the API 25 | * you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts` 26 | 27 | ## Run tests 28 | * open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests` 29 | * press `F5` to run the tests in a new window with your extension loaded 30 | * see the output of the test result in the debug console 31 | * make changes to `test/extension.test.ts` or create new test files inside the `test` folder 32 | * by convention, the test runner will only consider files matching the name pattern `**.test.ts` 33 | * you can create folders inside the `test` folder to structure your tests any way you want --------------------------------------------------------------------------------