vscode-eslint: 'Fix all auto-fixable problems' doesn't fix as many issues as possible
I have ESLint extension 1.0.7 with VSCode 1.6.0 in win10 x64 and ESLint 3.7.1 globally installed.
First of all, please note that since ESLint 2.9.0:
when you use the --fix option, ESLint will make multiple passes over your code in an attempt to fix as many issues as possible.
Let’s say I have the following .eslintrc.js in C:\Users\Kostas
:
module.exports = {
"env": {
"browser": true,
"es6": true,
"greasemonkey": true,
"jquery": true
},
"extends": "eslint:recommended",
"rules": {
"indent": ["error","tab"],
}
};
Now, I have this Javascript file:
var localTimezone;
function convertDates(f) {
if (f === 'f1') {
format = 'DD.MM.YYYY HH:mm';
} else {
format = 'MMMM DD, YYYY';
}
}
If I do eslint --fix myfile.js
via CLI (a single time) it will be fixed into:
var localTimezone;
function convertDates(f) {
if (f === 'f1') {
format = 'DD.MM.YYYY HH:mm';
} else {
format = 'MMMM DD, YYYY';
}
}
But, in VSCode the ``Eslint: Fix all auto-fixable problems` command doesn’t fix all indentation at once: executing it the first time will only fix indentation on line 3, a second time fixes lines 4 + 9, and finally a 3rd time fixes lines 5-8, i.e. it requires multiple (3) executions.
For reference, in Atom with linter 1.11.18 and linter-eslint 8.0.0 using my global ESLint installation as well,
with the relevant command (Linter eslint: Fix File
) all indentation is fixed with a single execution.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 56
- Comments: 77 (18 by maintainers)
Wanted to chime in on this, first reported here (marked as duplicate): https://github.com/Microsoft/vscode-eslint/issues/575
Lots of online resources start to recommend to use eslint+prettier. Basically using eslint for its awesome code-quality rules, but prettier for its awesome formatting rules: https://prettier.io/docs/en/comparison.html
If someone uses VS Code and eslint, they would start by installing the extension vscode-eslint. The npm packages installed for the repository you work on would be eslint, prettier, eslint-config-prettier, eslint-plugin-prettier.
Prettier best practices integration with eslint tells you to use
"extends": ["plugin:prettier/recommended"]
. This will makeeslint --fix
also run the prettier formatter. If I run this on command line like sonode_modules/.bin/eslint --fix server FILE
it formats everything perfectly. This has already been established in this thread.In VS code I’ve set:
When I then save the same
FILE
in VS Code, I have to do three consecutive saves in order for the formatting to end up in its final state, equal to as when I ran it from command line. This is obviously not good and has also been established in this thread.I then turn to install Prettier in VS Code. This section about integrating VS Code Eslint+VS Code Prettier reads:
The recommended VS Code configuration to integrate these two is to set the configuration
"prettier.eslintIntegration" true
and remove"eslint.autoFixOnSave": true
and"javascript.format.enable": false
The purpose of prettier-eslint is to run
prettier
, followed byeslint --fix
. If one has set the ESLint configuration ("extends": ["plugin:prettier/recommended"]
), then prettier will already be run witheslint --fix
. In practice what will happen then is:prettier
is run.eslint --fix
is run. a.eslint --fix
runs the the prettier plugin and the rule called prettier, which again formats according in the same way as 1.This is a strong reason as to why possible the VS Code ESLint extension and the command
ESLint: Fix all auto-fixable Problems
should instead issue aeslint --fix FILE
instead of what it currently does. I would then not have to rely on the VS Code Prettier extension to format the document in a subpar way.I released 2.0.4 today which addresses this. Please note that for large file you need to increase the time budget to compute the fixes using
editor.codeActionsOnSaveTimeout
@alexeagle The following is just my opinion
var
withconst
. A formatter could be made to do this, but then the feature creep is going in the other direction and that defeats the purpose of separating formatting and linting anyway.One of the issues is with large files in VSCode. Basically the after-save hook only gives you a couple hundred millis to create patches, and ESLint will sometimes need longer than that, even with only one pass.
Today, this can happen in files > 2k SLOC
If we run multiple ESLint passes, who knows what the “maximum” filesize you can format will be, 800 SLOC? That’s why it’s important to set up determinant e2e tests that actually run in VSCode and will surface these issues/information.
Yes, I’m still working on it 👍
This issue can be solved by registering ESLint as a formatter rather than a command.
I tried playing around with
vscode-eslint
’s source code and modified it so that it takes the final output fromeslint --fix
and send the whole formatted code back to VS Code (rather than sending individual fixes as separate edit).As you can see here, when I run the autofix command, the cursor, which used to be on line 2 on the word
arguments
, immediately jumps down to the bottom, since the whole editor’s content is replaced.For comparison, here’s the original behavior — I had to run it several times, but here the cursor should stay at the word
arguments
and not jump down to the bottom of the file.However, by registering ESLint as a document formatter (activated via “Format document” command instead), we can simply send the final code to VS Code, and it will diff the editor’s contents with the formatted code to compute the minimal changes to apply. As far as I know, this function is not exposed via the extension API anywhere except through the “Format document” command. I mentioned this about a year ago in this issue.
Here, I updated the extension to register itself as a code formatter, and by calling the “Format document” command, the cursor stays at the correct position:
I’d be fine with it executing
eslint --fix
on the current file.I’ll take a stab at fixing this later this week, should be able to batch fixes from multiple eslint passes before resolving the handler for
textDocument.onWillSaveTextDocument
Doesn’t help that there are currently zero tests for this project, though
PR to register vscode-eslint as document formatter submitted — https://github.com/microsoft/vscode-eslint/pull/766
I was working on adding tests for existing code (Using same approach as the
vscode-prettier
project). As this extension has >20M installs and auto-updates, I would hate to introduce a regression. Still planning on fixing this bug after thatI can’t believe that after 3 years since reporting this issue is still not fixed. Any ideas will it be ever be resolved at all?
@tcg I think you’re still going to see weird behavior. With the suggested “ESLint with prettier config”, you’ll need to save multiple times to get all your fixes run. (That’s what this issue is about.)
Calling
eslint --fix File
is actually not an option due to the fact that the eslint extension validates the buffer and not the file on disk. Calling the command would change the file on disk and it might collide with edits the user does in the editor. In addition you would loose all markers (like breakpoints) in the document since for VS Code it will look like the whole buffer has changed.The underlying problem is that ESlint itself does not do one pass when fixing problems. It runs n times until not further changes are detected. This needs to be mirrored in the ESLint plugin.
We already have autofix on precommit, but there’s still a hefty amount of frustration caused by the lack of confidence in the editor display - is a line red because of how an import is ordered, or because the imported file doesn’t exist? Hence why they save multiple times in a row, even though they could fix it through the CLI.
VS Code’s source code has this function computeMoreMinimalEdits which basically takes a list of TextEdit operations and computes a minimal one.
This allows the whole document text to be replaced while keeping markers and stuff. However, I am not sure how this API could be invoked from the extension process.
Works so well! My life is completely changed! No more Cmd+S, Cmd+S, Cmd+S, Cmd+S 😂 ❤️
A new version with the fix is not released yet. You can run download and run this extension from source code in the meantime.
Are there any downsides to using this solution?
The problem is that the npm eslint module computes all the fixes by running in a loop until no more fixes are computed (see https://github.com/eslint/eslint/issues/5329 for the details). So for a given state S
To implement that in the extension we need to:
Trying to understand what’s the core issue here - can someone ELI5 this?
@PavelPolyakov thanks for your willingness to look into this.
The problem is that the result of the fixes need to be applied on the client side since the client owns the document content since it is open in the editor. After the first round is applied the eslint server receives a document change event with the change / new content and the parses the document again to compute any ESLint errors and their outstanding fixes.
The only way to build the recursive on the server side is doing the following:
We are also discussing if we can extend the API so that will save provider can ask for more time. But nothing concrete yet.
Is anyone still looking into this ? if not i might take a stab at it in the next few days
The issue still persists. 😦
Also, there are no other formatters installed, and my settings file looks like this:
“eslint.autoFixOnSave”: true, “editor.formatOnSave”: false
@raix running fix multiple times behind the scenes (until there are no more fixable problems left) which is what ESLint does via the cli.
trying to get a fix in PR this weekend
edit: closer to*
@dbaeumer do you know if this is an issue in the vs code extension or is it the CLIEngine?
Does anyone of you know why eslint needs n rounds to fix up these things. Doing n rounds in the extension needs quite some coding since it is a pre save hook and all the modifications need to be described on the version of the document. The way how I understand the round concept in eslint is that fixes are applied like this fix(fix(fix(fix(n) which moves the document to version n + 4. So I can’t simply take the fixes compute the rounds 2, 3, 4 and apply them to the document version n. Moreover I suspect that the edits might be overlapping and therefore a merge might not be possible. So the only save answer I see here is to run the fix rounds until I reach a fix point and then use a compare algorithm to compute a minimal edit set 😦