-
Notifications
You must be signed in to change notification settings - Fork 23
Adding command to fix TS files + other changes (see desc.) #149
Conversation
Xapphire13
commented
Mar 29, 2017
- Adding command to fix TS files.
- Added option to skip .d.ts files.
- Adding linter name so that it appears in the history log.
…ing linter name so that it appears in the history log.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a first pass review. I'm still not entirely sure about adding classes to the editors, but I can't see any harm to it and it does let the command only show up for files where it would make sense, which is a nice touch.
lib/main.js
Outdated
atom.config.observe('linter-tslint.useLocalTslint', (use) => { | ||
tslintCache.clear(); | ||
this.useLocalTslint = use; | ||
}), | ||
); | ||
|
||
atom.workspace.observeActivePaneItem((activeItem) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why observeActivePaneItem
instead of observeTextEditors
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lack of API knowledge =], will update, thanks
lib/main.js
Outdated
* @return {boolean} Whether or not the grammar is for TypeScript | ||
*/ | ||
function isTypescriptGrammar(grammar) { | ||
return grammar.fileTypes.find(fileType => /.*ts/i.test(fileType)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please put a note that this is not part of the public API if we can't find a way around using this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you elaborate what you mean by non-public API here? This is my first modification of an Atom package and am unaware of how else to do helper methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anything that isn't listed in the Atom Docs isn't part of the public API, and is subject to change at any time 😉.
In this case the Grammar object you are accessing is very bare bones, and doesn't expose a way to get to the fileTypes
property you are utilizing here. This comment shows a way around all this 😉.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it! Thanks for the detailed explanation
lib/main.js
Outdated
atom.commands.add(`atom-text-editor.${editorClass}`, { | ||
'linter-tslint:fix-file': () => { | ||
const textEditor = atom.workspace.getActiveTextEditor(); | ||
// const filePath = textEditor.getPath(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No reason to bring in commented out code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I missed that thanks!
lib/main.js
Outdated
|
||
atom.workspace.observeActivePaneItem((activeItem) => { | ||
if (activeItem && activeItem.getGrammar) { | ||
if (isTypescriptGrammar(activeItem.getGrammar())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using a non-public API can we validate it based on scopes like everything else with linter providers is done?
You can find an example of how to use the cursors to get the scopes and check it using the public API from linter-eslint
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went with a solution i found in the Atom api that seemed simpler.
lib/main.js
Outdated
if (!textEditor || textEditor.isModified()) { | ||
// Abort for invalid or unsaved text editors | ||
atom.notifications.addError('Linter-TSLint: Please save before fixing'); | ||
return Promise.resolve(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just mark the function as async
here and all return values will be Promises. You can also use await
on the lint()
call, as well as getting rid of the chained then()
calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I though async/await didn't make the ES6 spec and wasn't going to have wide support until ECMA 2017. I will change and test it out though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Atom has built in transpilation of all package files that start with 'use babel';
, as well as having a relatively new version of Node.js, so new features like that are usable in Atom.
lib/main.js
Outdated
rulesDirectory.push(this.rulesDirectory); | ||
} | ||
} | ||
if (atom.config.get('linter-tslint.ignoreTypings') && /.*\.d\.ts$/i.test(textEditor.getPath())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This setting should be observe()
'd like the others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, how about this for the second part? If the case-insensitivity is important add a .toLowerCase()
in there.
textEditor.getPath().endsWith('.d.ts')
Thanks for taking this on btw! I don't use TypeScript myself, and it seems the people that were maintaining this have abandoned it unfortunately so it's needed some attention for a while. |
I have updated with the changes requested. Is anything else needed from me now? (Still new to GitHub PRs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More thorough review now that I'm sure the command thing is something that should happen 😉.
Some of this might just be inherited issues exposed due to moving the lint()
code out into it's own function, but we might as well fix it now.
lib/main.js
Outdated
// we can enable commands only for TypeScript editors. | ||
this.subscriptions.add( | ||
atom.workspace.observeTextEditors((textEditor) => { | ||
if (textEditor.getRootScopeDescriptor().scopes.some(scope => scope === typescriptScope)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of accessing .scopes
directly, use getScopesArray()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the scopes allowed by the provider here should be used, so typescriptScope
should rather be typeScriptScopes
and be an array equal to those scopes (the definiton down there should just be a reference to the same array), then the check function should be:
textEditor.getRootScopeDescriptor().getScopesArray().some(scope => typeScriptScopes.includes(scope))
(This is pushing this line rather long, might want to split it up)
lib/main.js
Outdated
@@ -8,6 +8,8 @@ import fs from 'fs'; | |||
import requireResolve from 'resolve'; | |||
|
|||
const TSLINT_MODULE_NAME = 'tslint'; | |||
const typescriptScope = 'source.ts'; | |||
const editorClass = 'typescript-editor'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make this very specific, maybe something like linter-tslint-compatible-editor
?
lib/main.js
Outdated
this.subscriptions.add( | ||
atom.workspace.observeTextEditors((textEditor) => { | ||
if (textEditor.getRootScopeDescriptor().scopes.some(scope => scope === typescriptScope)) { | ||
textEditor.element.classList.add(editorClass); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This definitely isn't compatible with the public API, does this get you what you need?
atom.views.getView(textEditor);
That should at least get you the proper DOM element through the API.
lib/main.js
Outdated
|
||
// The fix replaces the file content and the cursor jumps automatically | ||
// to the beginning of the file, so save current cursor position | ||
const cursorPosition = textEditor.getCursorBufferPosition(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note: This only happens sometimes, and when it does happen, restoring the cursor like this for some reason doesn't seem to work from the limited testing from AtomLinter/linter-eslint#853 (it hasn't been pushed out to the public yet).
lib/main.js
Outdated
} catch (err) { | ||
atom.notifications.addWarning(err.message); | ||
} finally { | ||
// set cursor to the position before fix job |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"set" -> "Set" (or "Restore"?)
lib/main.js
Outdated
const linter = new Linter(Object.assign({ | ||
formatter: 'json', | ||
rulesDirectory, | ||
}, options)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it handle undefined
being passed in here? If not, you need a default specified up on L179.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undefined is handled yes
lib/main.js
Outdated
|
||
linter.lint(filePath, text, configuration); | ||
const lintResult = linter.getResult(); | ||
lint(textEditor, options) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Declare this as async lint(textEditor, options) {
...
lib/main.js
Outdated
// Text has been modified since the lint was triggered, tell linter not to update | ||
return null; | ||
} | ||
return this.getLinter(filePath).then((Linter) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And then await
on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Note that getLocalLinter
and getLinter
could be re-written in this manner as well if you wanted.)
lib/main.js
Outdated
return []; | ||
let { rulesDirectory } = configuration; | ||
if (rulesDirectory) { | ||
const configurationDir = path.dirname(configurationPath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dirname
will throw a deprecation if configurationPath
is null
or undefined
, is this a possibility? You probably need a check in here to handle that, as well as the usages further down.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code was already here so I'm not an expert in it's behavior.
lib/main.js
Outdated
let { rulesDirectory } = configuration; | ||
if (rulesDirectory) { | ||
const configurationDir = path.dirname(configurationPath); | ||
if (!Array.isArray(rulesDirectory)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this for backwards compatibility with older tslint
versions? Maybe put a comment in here for the reasoning behind this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no idea, this code was here before my change and is just moving with my refactoring of the method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I missed this till just now!
@Xapphire13 Would you be interested in helping to maintain this package? |
@Arcanemagus Yes, I'd love to help maintain this package =] |
@Xapphire13 Invite sent, if you have any questions feel free to bug me on Atom's Slack, there is also a #linter and #linter-plugin-dev channel there if I'm not around. Thanks for taking this up! |