diff --git a/CHANGELOG.md b/CHANGELOG.md index dc7b18f..e69de29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,103 +0,0 @@ -# 0.8.6 - -- Info level cops are displayed as info level -- (internal) eliminate deprecated API - -# 0.8.5 - -- work with rubocop over 0.90 - -# 0.8.4 - -- Ignore warnings and proceed if there is valid rubocop output -- fix test - -# 0.8.3 - -- Using relative config file path - -# 0.8.2 - -- update packages that have secirity issues - -# 0.8.1 - -- add `suppressRubocopWarnings` option that ignore warning - -# 0.8.0 - -- add useBundler config. We can set `true` to override to `bundle exec rubocop` - -# 0.7.1 - -- Fix autoCorrection find on windows - -# 0.7.0 - -- Fix autocorrect was not respecting the .rubocop.yml - -# 0.6.1 - -- Accepts a command is prefixed like 'bundle exec' - -# 0.6.0 - -- Set autocorrect as a formatter - -# 0.5.0 - -- Automatically detect and use bundled rubocop - -# 0.4.0 - -- Lint files opened before the extension is loaded. -- Don't clear diagnostics of other files. -- Kill running rubocop process on close a file. -- Run single process at a time (to avoid accidental process bomb) (using queue). - - This is caused by vscode's project-wide replace (opens all matched file at once). - -# 0.3.5 - -- force `force-exclude` option - -# 0.3.4 - -- don't execute when starting git diff mode - -# 0.3.3 - -- output error when stderr presented -- show specific error message for empty output (to identify problem) - -# 0.3.2 - -- execute on open - -# 0.3.1 - -- Rubocop saves the file before correcting it and runs checks again - -# 0.3.0 - -- Add auto correct command -- display message when config file is not exist - -# 0.2.2 - -- show message when occur errors on parsing JSON - -# 0.2.1 - -- find rubocop from PATH - -# 0.2.0 - -- enable specify config file (e.g. .rubocop.yml) - -# 0.1.11 - -- show warning when rubocop output is empty - -# 0.1.10 - -- handling JSON syntax error diff --git a/README.md b/README.md index 7124c01..a2b9d80 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,37 @@ -# Rubocop for Visual Studio Code +# Docker-Ruby-Rubocop +### Liniting as per your convenience -![travis status](https://travis-ci.org/misogi/vscode-ruby-rubocop.svg?branch=master) - -Visual Studio Code で rubocop を実行するエクステンションです。 - -This extension provides interfaces to rubocop for vscode. - -[rubocop](https://github.com/bbatsov/rubocop) is a code analyzer for ruby. - -[ruby rubocop in Code Market Place](https://marketplace.visualstudio.com/items/misogi.ruby-rubocop) - -![exec on save](./images/onsave.gif) - -## Problems - -This extension may have problems when using a rvm or chruby environment. -We recommend [vscode-ruby](https://marketplace.visualstudio.com/items?itemName=rebornix.Ruby). It can also lint ruby code. - -When autoCorrect is enabled, the history of changing file is broken. +Docker-Ruby-Rubocop is a VSCode etension that lets you lint ruby files using rubocop on system and rubocop running inside docker containers. ## Features - -- lint by executing the command "Ruby: lint by rubocop" (cmd+shift+p and type command) -- auto invoke when saving file -- auto correct command "Ruby: autocorrect by rubocop" - -### Exclude file - -The extension forces rubocop's `force-exclusion` option. - -If you do not want rubocop to be executed on some file, you can add AllCops/Exclude in rubocop.yml. The file can be saved without executing rubocop. - -# Installation - -Installation of ruby and rubocop is required. - -``` -gem install rubocop -``` - -- Type F1 (or Command + Shift + P) -- execute "Extensions: install extension" -- type rubocop and execute `ext install ruby-rubocop` - -If VSCode market place is not configured in your FLOSS distribution of code (you have Open VSX instead): - -1. Go on [VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=misogi.ruby-rubocop) and clic on the [Download Extension](https://marketplace.visualstudio.com/_apis/public/gallery/publishers/misogi/vsextensions/ruby-rubocop/0.8.5/vspackage) button. -2. Install the extension manually from the CLI: `code --install-extension misogi.ruby-rubocop-0.8.5.vsix` - -# ChangeLog - -[ChangeLog](CHANGELOG.md) - -## Configuration - -Specify configuration (via navigating to `File > Preferences > Workspace Settings` and editing file `settings.json):` - -```javascript -{ - // If not specified searches for 'rubocop' executable available on PATH (default and recommended) - "ruby.rubocop.executePath": "", - - // You can use specific path - // "ruby.rubocop.executePath": "/Users/you/.rbenv/shims/" - // "ruby.rubocop.executePath": "/Users/you/.rvm/gems/ruby-2.3.2/bin/" - // "ruby.rubocop.executePath": "D:/bin/Ruby22-x64/bin/" - - // If not specified, it assumes a null value by default. - "ruby.rubocop.configFilePath": "/path/to/config/.rubocop.yml", - - // default true - "ruby.rubocop.onSave": true -} -``` - -### Keybindings - -You can change the keybinding (via editing `keybindings.json`) - -```javascript -{ "key": "ctrl+alt+l", "command": "ruby.rubocopAutocorrect", - "when": "editorLangId == 'ruby'" } -``` - -# todo - -- more configurable command line options (like -R) -- integration with rbenv -- testing & CI support - -# Contribute with this extension - -Please install packages with yarn. - - yarn install - -You could install TSLint extension for .ts files. - -Please format code using prettier. - -``` -yarn prettier src/* test/* --write -``` - -# License - -このソフトウェアは MIT ライセンスの元で公開されています。[LICENSE.txt](LICENSE.txt) をご覧下さい。 - -This software is released under the MIT License, see [LICENSE.txt](LICENSE.txt). +___ +- Execute Rubocop inside docker or in the host machine +- Get Lint output for files with large number of lines + + +## A Note of Gratitude +___ +This extension is created by forking a good plugin name ruby-rubocop create by the man [misogi](https://github.com/misogi?tab=overview). Misogi have have provided the ruby commuity with a good linting plugin for years. His code heped me a lot while building this plugin. So, **a big thanks to the friend I have never met** 🤝🏽. + +## Installation and Setup +___ +1. Go to Vscode Extensions Tab +2. Search for Docker-Ruby-Rubocop +3. Click on the extension made by s035 and install. + +Open Extension's Setting and follow the below steps for respective modes. +#### Setup for using rubocop on docker (Docker Mode) +- check the "useDocker" checkbox. +- check the "disableEmptyFileCop" checkbox (if you enable this, you will receive an Lint/EmptyfileCop offence when linitng the files which are not present inside the docker container.) +- If you have an rubocop config file inside the docker, please specify the rubocop config file path inside the docker container. +#### Setup for using rubocop on system (Normal Mode) +- Uncheck the useDocker checkbox. +- Uncheck the disableEmptyFileCop. +- Provide the path where rubocop is available in the Execution Path Input Box. +- Provide the path for rubocop config file on system, if rubocop config file is available. + +If you want rubocop to be ran on every save, check the execute on save checkbox. + +### Issues +___ +If you face any issues or problemswhen using the extension, please feel free to create an issue. \ No newline at end of file diff --git a/images/logo.jpg b/images/logo.jpg new file mode 100644 index 0000000..6f6a92d Binary files /dev/null and b/images/logo.jpg differ diff --git a/images/onsave.gif b/images/onsave.gif deleted file mode 100644 index 141daa8..0000000 Binary files a/images/onsave.gif and /dev/null differ diff --git a/images/rubocop.png b/images/rubocop.png deleted file mode 100644 index 0b671ef..0000000 Binary files a/images/rubocop.png and /dev/null differ diff --git a/package.json b/package.json index 0c46b34..a12f6b3 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,16 @@ { - "name": "ruby-rubocop", - "version": "0.8.6", - "publisher": "misogi", - "displayName": "ruby-rubocop", - "description": "execute rubocop for current Ruby code.", + "name": "docker-ruby-rubocop", + "version": "0.3.0", + "publisher": "s035", + "displayName": "docker-ruby-rubocop", + "description": "Lint Rubycode use your local eubocop and rubocop on docker", "engines": { "vscode": "^1.62.0" }, - "icon": "images/rubocop.png", + "icon": "images/logo.jpg", "repository": { "type": "git", - "url": "https://github.com/misogi/vscode-ruby-rubocop.git" + "url": "https://github.com/suryanarayanan035/docker-ruby-rubocop.git" }, "categories": [ "Programming Languages", @@ -18,7 +18,7 @@ ], "activationEvents": [ "onLanguage:ruby", - "onCommand:ruby.rubocop" + "onCommand:docker-ruby.rubocop" ], "main": "./out/src/extension", "scripts": { @@ -47,7 +47,7 @@ ], "commands": [ { - "command": "ruby.rubocop", + "command": "docker-ruby.rubocop", "title": "Ruby: lint by rubocop" }, { @@ -64,32 +64,47 @@ ], "configuration": { "type": "object", - "title": "Ruby-Rubocop configuration", + "title": "Docker-Ruby-Rubocop configuration", "properties": { - "ruby.rubocop.executePath": { + "docker-ruby.rubocop.executePath": { "type": "string", "default": "", "description": "execution path of rubocop." }, - "ruby.rubocop.configFilePath": { + "docker-ruby.rubocop.configFilePath": { "type": "string", "default": "", "description": "Filepath to the configuration file for Rubocop" }, - "ruby.rubocop.onSave": { + "docker-ruby.rubocop.onSave": { "type": "boolean", "default": true, "description": "execute rubocop on save." }, - "ruby.rubocop.useBundler": { + "docker-ruby.rubocop.useBundler": { "type": "boolean", "default": false, "description": "execute rubocop using bundler (ie 'bundle exec rubocop')" }, - "ruby.rubocop.suppressRubocopWarnings": { + "docker-ruby.rubocop.suppressRubocopWarnings": { "type": "boolean", "default": false, "description": "Suppress warnings from rubocop and attempt to run regardless. (Useful if you share a rubocop.yml file and run into unrecognized cop errors you know are okay.)" + }, + "docker-ruby.rubocop.useDocker": { + "type": "boolean", + "default": false, + "description": "Whether to use docker or not. If this option is false, settig a value to dockerContainer will be useless." + }, + "docker-ruby.rubocop.dockerContainer": { + "type": "string", + "default": "", + "description": "Name of the docker container to use the interpretr from" + }, + "docker-ruby.rubocop.disableEmptyFileCop": { + "type": "boolean", + "default": false, + "description": "should Lint/EmptyFile Cop should run?" } } } diff --git a/src/configuration.ts b/src/configuration.ts index 90c130c..4245761 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -10,17 +10,11 @@ export interface RubocopConfig { configFilePath: string; useBundler: boolean; suppressRubocopWarnings: boolean; + useDocker?: boolean; + dockerContainer?: string; + disableEmptyFileCop: boolean; } -const detectBundledRubocop: () => boolean = () => { - try { - cp.execSync('bundle show rubocop', { cwd: vs.workspace.rootPath }); - return true; - } catch (e) { - return false; - } -}; - const autodetectExecutePath: (cmd: string) => string = (cmd) => { const key = 'PATH'; const paths = process.env[key]; @@ -45,35 +39,44 @@ const autodetectExecutePath: (cmd: string) => string = (cmd) => { */ export const getConfig: () => RubocopConfig = () => { const win32 = process.platform === 'win32'; - const cmd = win32 ? 'rubocop.bat' : 'rubocop'; - const conf = vs.workspace.getConfiguration('ruby.rubocop'); - let useBundler = conf.get('useBundler', false); - const configPath = conf.get('executePath', ''); + const conf = vs.workspace.getConfiguration('docker-ruby.rubocop'); + const useDocker = conf.get('useDocker', false); + const dockerContainer = conf.get('dockerContainer', ''); + let cmd; + if (useDocker && dockerContainer) { + cmd = `docker exec -i ${dockerContainer} rubocop`; + } else { + cmd = win32 ? 'rubocop.bat' : 'rubocop'; + } + const useBundler = conf.get('useBundler', false); + const executePath = conf.get('executePath', ''); const suppressRubocopWarnings = conf.get('suppressRubocopWarnings', false); let command: string; - - // if executePath is present in workspace config, use it. - if (configPath.length !== 0) { - command = configPath + cmd; - } else if (useBundler || detectBundledRubocop()) { - useBundler = true; + if (useDocker) { + command = cmd; + if (useBundler) command = cmd.replace(' rubocop', ' bundle exec rubocop'); // execute bundle rubocop inside docker + } else if (executePath.length !== 0) { + command = `${executePath}/${cmd}` + } else if (useBundler) { command = `bundle exec ${cmd}`; } else { const detectedPath = autodetectExecutePath(cmd); if (0 === detectedPath.length) { vs.window.showWarningMessage( - 'execute path is empty! please check ruby.rubocop.executePath' + 'execute path is empty! please check docker-ruby.rubocop.executePath' ); } command = detectedPath + cmd; } - return { command, configFilePath: conf.get('configFilePath', ''), onSave: conf.get('onSave', true), - useBundler, + useBundler: useBundler, suppressRubocopWarnings, + useDocker, + dockerContainer: conf.get('dockerContainer',''), + disableEmptyFileCop: conf.get('disableEmptyFileCop', false) }; }; diff --git a/src/extension.ts b/src/extension.ts index 1873d04..132d0ba 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -10,10 +10,13 @@ export function activate(context: vscode.ExtensionContext): void { context.subscriptions.push(diag); const rubocop = new Rubocop(diag); - const disposable = vscode.commands.registerCommand('ruby.rubocop', () => { - const document = vscode.window.activeTextEditor.document; - rubocop.execute(document); - }); + const disposable = vscode.commands.registerCommand( + 'docker-ruby.rubocop', + () => { + const document = vscode.window.activeTextEditor.document; + rubocop.execute(document); + } + ); context.subscriptions.push(disposable); diff --git a/src/rubocop.ts b/src/rubocop.ts index 0a1ecd1..d804713 100644 --- a/src/rubocop.ts +++ b/src/rubocop.ts @@ -85,32 +85,26 @@ function getCurrentPath(fileUri: vscode.Uri): string { // extract argument to an array function getCommandArguments(fileName: string): string[] { - let commandArguments = ['--stdin', fileName, '--force-exclusion']; + let commandArguments = ['--stdin',fileName,'--force-exclusion']; const extensionConfig = getConfig(); if (extensionConfig.configFilePath !== '') { - const found = [extensionConfig.configFilePath] - .concat( - (vscode.workspace.workspaceFolders || []).map((ws) => - path.join(ws.uri.path, extensionConfig.configFilePath) - ) - ) - .filter((p: string) => fs.existsSync(p)); - - if (found.length == 0) { + const prefix_command = extensionConfig.useDocker ? `exec ${extensionConfig.dockerContainer}` : ''; + let fileExistenceTest; + if(extensionConfig.useDocker) { + fileExistenceTest = cp.spawnSync('docker',`${prefix_command} test -f ${extensionConfig.configFilePath} && echo 'exists'`.split(' '), {shell: true}) + } + else { + fileExistenceTest = cp.spawnSync('test', `-f ${extensionConfig.configFilePath} && echo 'exists'`.split(' '), { shell:true }) + } + if(!fileExistenceTest.stdout?.toString().includes('exists')) { vscode.window.showWarningMessage( - `${extensionConfig.configFilePath} file does not exist. Ignoring...` + `${extensionConfig.configFilePath} file does not exist. Rubocop will run with default config` ); - } else { - if (found.length > 1) { - vscode.window.showWarningMessage( - `Found multiple files (${found}) will use ${found[0]}` - ); - } - const config = ['--config', found[0]]; - commandArguments = commandArguments.concat(config); } } - + if(extensionConfig.disableEmptyFileCop) { + commandArguments = commandArguments.concat('--except', 'Lint/EmptyFile') + } return commandArguments; } @@ -141,7 +135,6 @@ export class Rubocop { const fileName = document.fileName; const uri = document.uri; - const currentPath = getCurrentPath(uri); const onDidExec = (error: Error, stdout: string, stderr: string) => { this.reportError(error, stderr); @@ -180,10 +173,10 @@ export class Rubocop { .concat(jsonOutputFormat); const task = new Task(uri, (token) => { - const process = this.executeRubocop( + this.executeRubocop( args, + getCurrentPath(uri), document.getText(), - { cwd: currentPath }, (error, stdout, stderr) => { if (token.isCanceled) { return; @@ -195,7 +188,7 @@ export class Rubocop { } } ); - return () => process.kill(); + return () => console.log('call canceled'); }); this.taskQueue.enqueue(task); } @@ -215,19 +208,27 @@ export class Rubocop { // execute rubocop private executeRubocop( args: string[], + cwd: string, fileContents: string, - options: cp.ExecOptions, cb: (err: Error, stdout: string, stderr: string) => void - ): cp.ChildProcess { - let child; - if (this.config.useBundler) { - child = cp.exec(`${this.config.command} ${args.join(' ')}`, options, cb); - } else { - child = cp.execFile(this.config.command, args, options, cb); + ): void { + const options = {maxBuffer: 1073741824, input: fileContents} // 1073741824 bytes = 1 Megabyte + let command, spawnArgs; + if(this.config.useDocker) { + command = 'docker' + spawnArgs = this.config.command.replace('docker ', '').split(' ').concat(args) + } + else if(this.config.useBundler) { + command = 'bundle'; + spawnArgs = this.config.command.replace('bundle ', '').split(' ').concat(args) + options['cwd'] = cwd + } + else { + command = this.config.command; + spawnArgs = args; } - child.stdin.write(fileContents); - child.stdin.end(); - return child; + const child = cp.spawnSync(command, spawnArgs, options); + cb(child.error,child.stdout?.toString(),child.stderr?.toString()) } // parse rubocop(JSON) output