Skip to content

Commit

Permalink
Add the ability to auto-fix lint violations (#21)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
LuKks and sindresorhus committed Oct 11, 2020
1 parent cfbd9ca commit 30ebf8d
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 1 deletion.
6 changes: 6 additions & 0 deletions Default.sublime-commands
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"caption": "SublimeLinter XO: Fix current file",
"command": "xo_fix"
}
]
98 changes: 98 additions & 0 deletions Main.sublime-menu
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
[
{
"caption": "Tools",
"id": "tools",
"children": [
{
"id": "SublimeLinter-contrib-xo",
"caption": "SublimeLinter XO",
"children": [
{
"caption": "Fix current file",
"command": "xo_fix"
}
]
}
]
},
{
"caption": "Preferences",
"id": "preferences",
"children": [
{
"caption": "Package Settings",
"id": "package-settings",
"children": [
{
"caption": "SublimeLinter XO",
"children": [
{
"caption": "Settings - Default",
"command": "open_file",
"args": {
"file": "${packages}/SublimeLinter-contrib-xo/SublimeLinterContribXO.sublime-settings"
}
},
{
"caption": "Settings - User",
"command": "open_file",
"args": {
"file": "${packages}/User/SublimeLinterContribXO.sublime-settings"
}
},
{ "caption": "-" },
{
"command": "open_file", "args":
{
"file": "${packages}/SublimeLinter-contrib-xo/keymaps/Default (Linux).sublime-keymap",
"platform": "Linux"
},
"caption": "Key Bindings - Default"
},
{
"command": "open_file", "args":
{
"file": "${packages}/User/Default (Linux).sublime-keymap",
"platform": "Linux"
},
"caption": "Key Bindings - User"
},
{
"command": "open_file", "args":
{
"file": "${packages}/SublimeLinter-contrib-xo/keymaps/Default (OSX).sublime-keymap",
"platform": "OSX"
},
"caption": "Key Bindings - Default"
},
{
"command": "open_file", "args":
{
"file": "${packages}/User/Default (OSX).sublime-keymap",
"platform": "OSX"
},
"caption": "Key Bindings - User"
},
{
"command": "open_file", "args":
{
"file": "${packages}/SublimeLinter-contrib-xo/keymaps/Default (Windows).sublime-keymap",
"platform": "Windows"
},
"caption": "Key Bindings - Default"
},
{
"command": "open_file", "args":
{
"file": "${packages}/User/Default (Windows).sublime-keymap",
"platform": "Windows"
},
"caption": "Key Bindings - User"
}
]
}
]
}
]
}
]
3 changes: 3 additions & 0 deletions SublimeLinterContribXO.sublime-settings
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"fix_on_save": false
}
8 changes: 8 additions & 0 deletions keymaps/Default (Linux).sublime-keymap
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"command": "xo_fix",
"keys": [
"ctrl+alt+f"
]
}
]
8 changes: 8 additions & 0 deletions keymaps/Default (OSX).sublime-keymap
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"command": "xo_fix",
"keys": [
"ctrl+alt+f"
]
}
]
8 changes: 8 additions & 0 deletions keymaps/Default (Windows).sublime-keymap
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"command": "xo_fix",
"keys": [
"ctrl+alt+f"
]
}
]
100 changes: 99 additions & 1 deletion linter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import os
from SublimeLinter.lint import NodeLinter
import platform
import sublime
import sublime_plugin
import subprocess
from SublimeLinter.lint import (
NodeLinter,
linter as linter_module
)

# TODO: Properly export these in SL core: https://github.com/SublimeLinter/SublimeLinter/issues/1713
from SublimeLinter.lint.linter import PermanentError
Expand All @@ -17,6 +24,16 @@
}
OPTIMISTIC_SELECTOR = ', '.join({STANDARD_SELECTOR} | set(PLUGINS.values()))

startup_info = None
if platform.system() == 'Windows':
startup_info = subprocess.STARTUPINFO()
startup_info.dwFlags |= subprocess.STARTF_USESHOWWINDOW

settings = None

def plugin_loaded():
global settings
settings = sublime.load_settings('SublimeLinterContribXO.sublime-settings')

class XO(NodeLinter):
npm_name = 'xo'
Expand Down Expand Up @@ -73,3 +90,84 @@ def ensure_plugin_installed(self) -> bool:
# the environment. Silently, do not notify and disturb the user!
self.notify_unassign()
raise PermanentError()

def make_fake_linter(view):
settings = linter_module.get_linter_settings(XO, view)
return XO(view, settings)

def xo_fix(self, view, content):
if isinstance(content, str):
content = content.encode()

encoding = view.encoding()
if encoding == 'Undefined':
encoding = 'utf-8'

print('xo_fix -> content length:', len(content))
print('xo_fix -> encoding:', encoding)
print('xo_fix -> xo_env.PWD:', self.xo_env['PWD'])
print('xo_fix -> xo_project_root:', self.xo_project_root)

# TODO: Change to use `subprocess.run()` when Sublime updates Python to 3.5 or later.
proc = subprocess.Popen(
[self.xo_path, '--stdin', '--fix'],
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
env=self.xo_env,
cwd=self.xo_project_root,
startupinfo=startup_info,
)
stdout, stderr = proc.communicate(content)
print('xo_fix -> stdout len:', len(stdout))
print('xo_fix -> stderr len:', len(stderr))
print('xo_fix -> stderr content:', stderr.decode(encoding))
print('xo_fix -> returncode:', proc.returncode)

if proc.returncode != 0:
sublime.message_dialog("[xo_fix " + str(proc.returncode) + "] " + stderr.decode(encoding))
return None

return stdout.decode(encoding)

class XoFixCommand(sublime_plugin.TextCommand):
def is_enabled(self):
print('XoFixCommand -> is_enabled?')
linter = make_fake_linter(self.view)
print('XoFixCommand -> project_root ->', linter.context.get('project_root'))

self.xo_start_dir = linter.get_start_dir()
print('XoFixCommand -> xo_start_dir', self.xo_start_dir)
if not self.xo_start_dir:
print('XoFixCommand -> xo_start_dir -> False')
return False

self.xo_path = linter.find_local_executable(self.xo_start_dir, 'xo')
print('XoFixCommand -> xo_path', self.xo_path)
if not self.xo_path:
print('XoFixCommand -> xo_path -> False')
return False

self.xo_project_root = linter.context.get('project_root')
self.xo_env = os.environ.copy()
self.xo_env['PWD'] = self.xo_project_root
self.xo_env['PATH'] += os.pathsep + '/usr/local/bin' # Ensure correct PATH for Node.js on macOS

print('XoFixCommand -> environ.path ->', self.xo_env['PATH'])
print('XoFixCommand -> project_root ->', self.xo_project_root)
print('XoFixCommand -> return -> True')
return True

def run(self, edit):
region = sublime.Region(0, self.view.size())
content = self.view.substr(region)

replacement = xo_fix(self, self.view, content)
if replacement != None and content != replacement:
self.view.replace(edit, region, replacement)

class XoFixListener(sublime_plugin.EventListener):
def on_pre_save(self, view):
if not settings.get('fix_on_save', False):
return
view.run_command('xo_fix')
25 changes: 25 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,31 @@ In order for `xo` to be executed by SublimeLinter, you must ensure that its path
- [SublimeLinter settings](https://sublimelinter.readthedocs.org/en/latest/settings.html)
- [Linter settings](https://sublimelinter.readthedocs.org/en/latest/linter_settings.html)

Also, you can change general plugin setting from: “Preferences › Package Settings › SublimeLinter XO”.

## Auto-fix

To run the auto-fixer, press the `ctrl+alt+f` shortcut or use the menu entry “Tools › SublimeLinter XO › Fix current file”.

The shortcut can be changed in “Preferences › Key Bindings” by adding the following to the array:

```json
{
"keys": [
"ctrl+alt+f"
],
"command": "xo_fix"
}
```

If you want to run the auto-fixer when saving a file, you can enable the `fix_on_save` setting:

```json
{
"fix_on_save": true
}
```

## Tips

### Using non-JS syntax
Expand Down

0 comments on commit 30ebf8d

Please sign in to comment.