Bug 1387555 - [mozlint] Make use of quickfix when using --edit with vim/nvim, r?dylan
MozReview-Commit-ID: BlJbWVv1CeO
--- a/python/mozlint/mozlint/cli.py
+++ b/python/mozlint/mozlint/cli.py
@@ -1,16 +1,15 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import print_function, unicode_literals
import os
-import subprocess
import sys
from argparse import REMAINDER, ArgumentParser
from mozlint.formatters import all_formatters
SEARCH_PATHS = []
@@ -137,34 +136,33 @@ def find_linters(linters=None):
continue
lints.append(os.path.join(search_path, f))
return lints
def run(paths, linters, fmt, outgoing, workdir, edit, list_linters=None, **lintargs):
from mozlint import LintRoller, formatters
+ from mozlint.editor import edit_results
if list_linters:
lint_paths = find_linters(linters)
print("Available linters: {}".format(
[os.path.splitext(os.path.basename(l))[0] for l in lint_paths]
))
return 0
lint = LintRoller(**lintargs)
lint.read(find_linters(linters))
# run all linters
results = lint.roll(paths, outgoing=outgoing, workdir=workdir)
- if edit:
- editor = os.environ['EDITOR']
- for path in results:
- subprocess.call([editor, path])
+ if edit and results:
+ edit_results(results)
results = lint.roll(results.keys())
formatter = formatters.get(fmt)
# Encode output with 'replace' to avoid UnicodeEncodeErrors on
# environments that aren't using utf-8.
out = formatter(results, failed=lint.failed).encode(
sys.stdout.encoding or 'ascii', 'replace')
new file mode 100644
--- /dev/null
+++ b/python/mozlint/mozlint/editor.py
@@ -0,0 +1,53 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from __future__ import unicode_literals, print_function
+
+import os
+import subprocess
+import tempfile
+
+from mozlint import formatters
+
+
+def get_editor():
+ return os.environ.get('EDITOR')
+
+
+def edit_results(results):
+ if not results:
+ return
+
+ editor = get_editor()
+ if not editor:
+ print('warning: could not find a default editor')
+ return
+
+ name = os.path.basename(editor)
+ if name in ('vim', 'nvim', 'gvim'):
+ cmd = [
+ editor,
+ # need errorformat to match both Error and Warning, with or without a column
+ '--cmd', 'set errorformat+=%f:\\ line\\ %l\\\\,\\ col\\ %c\\\\,\\ %trror\\ -\\ %m',
+ '--cmd', 'set errorformat+=%f:\\ line\\ %l\\\\,\\ col\\ %c\\\\,\\ %tarning\\ -\\ %m',
+ '--cmd', 'set errorformat+=%f:\\ line\\ %l\\\\,\\ %trror\\ -\\ %m',
+ '--cmd', 'set errorformat+=%f:\\ line\\ %l\\\\,\\ %tarning\\ -\\ %m',
+ # start with quickfix window opened
+ '-c', 'copen',
+ # running with -q seems to open an empty buffer in addition to the
+ # first file, this removes that empty buffer
+ '-c', '1bd',
+ ]
+
+ with tempfile.NamedTemporaryFile() as fh:
+ s = formatters.get('compact', summary=False)(results)
+ fh.write(s)
+ fh.flush()
+
+ cmd.extend(['-q', fh.name])
+ subprocess.call(cmd)
+
+ else:
+ for path, errors in results.iteritems():
+ subprocess.call([editor, path])
--- a/python/mozlint/mozlint/formatters/compact.py
+++ b/python/mozlint/mozlint/formatters/compact.py
@@ -8,16 +8,17 @@ from ..result import ResultContainer
class CompactFormatter(object):
"""Formatter for compact output.
This formatter prints one error per line, mimicking the
eslint 'compact' formatter.
"""
+ # If modifying this format, please also update the vim errorformats in editor.py
fmt = "{path}: line {lineno}{column}, {level} - {message} ({rule})"
def __init__(self, summary=True):
self.summary = summary
def __call__(self, result, **kwargs):
message = []
num_problems = 0
--- a/python/mozlint/test/test_cli.py
+++ b/python/mozlint/test/test_cli.py
@@ -42,18 +42,18 @@ def test_cli_run_with_edit(run, parser,
os.environ['EDITOR'] = 'echo'
ret = run(['-f', 'compact', '--edit', '--linter', 'external'])
out, err = capfd.readouterr()
out = out.splitlines()
assert ret == 1
assert len(out) == 5
assert out[0].endswith('foobar.js') # from the `echo` editor
- assert "files/foobar.js: line 1, col 1, Error" in out[1]
- assert "files/foobar.js: line 2, col 1, Error" in out[2]
+ assert "foobar.js: line 1, col 1, Error" in out[1]
+ assert "foobar.js: line 2, col 1, Error" in out[2]
del os.environ['EDITOR']
with pytest.raises(SystemExit):
parser.parse_args(['--edit'])
if __name__ == '__main__':
sys.exit(pytest.main(['--verbose', __file__]))
--- a/tools/lint/docs/usage.rst
+++ b/tools/lint/docs/usage.rst
@@ -70,8 +70,36 @@ To enable a pre-push git hook, run the f
$ ln -s /path/to/gecko/tools/lint/hooks.py .git/hooks/pre-push
To enable a pre-commit git hook, run the following command:
.. parsed-literal::
$ ln -s /path/to/gecko/tools/lint/hooks.py .git/hooks/pre-commit
+
+
+Fixing Lint Errors
+==================
+
+``Mozlint`` has a best-effort ability to fix lint errors:
+
+.. parsed-literal::
+
+ $ ./mach lint --fix
+
+Not all linters support fixing, and even the ones that do can not usually fix
+all types of errors. Any errors that cannot be automatically fixed, will be
+printed to stdout like normal. In that case, you can also fix errors manually:
+
+.. parsed-literal::
+
+ $ ./mach lint --edit
+
+This requires the $EDITOR environment variable be defined. For most editors,
+this will simply open each file containing errors one at a time. For vim (or
+neovim), this will populate the `quickfix list`_ with the errors.
+
+The ``--fix`` and ``--edit`` arguments can be combined, in which case any
+errors that can be fixed automatically will be, and the rest will be opened
+with your $EDITOR.
+
+.. _quickfix list: http://vimdoc.sourceforge.net/htmldoc/quickfix.html