Skip to content

Add progress reporting #236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Nov 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 18 additions & 14 deletions pylsp/plugins/autopep8_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,27 @@


@hookimpl(tryfirst=True) # Prefer autopep8 over YAPF
def pylsp_format_document(config, document, options): # pylint: disable=unused-argument
log.info("Formatting document %s with autopep8", document)
return _format(config, document)
def pylsp_format_document(config, workspace, document, options): # pylint: disable=unused-argument
with workspace.report_progress("format: autopep8"):
log.info("Formatting document %s with autopep8", document)
return _format(config, document)


@hookimpl(tryfirst=True) # Prefer autopep8 over YAPF
def pylsp_format_range(config, document, range, options): # pylint: disable=redefined-builtin,unused-argument
log.info("Formatting document %s in range %s with autopep8", document, range)

# First we 'round' the range up/down to full lines only
range['start']['character'] = 0
range['end']['line'] += 1
range['end']['character'] = 0

# Add 1 for 1-indexing vs LSP's 0-indexing
line_range = (range['start']['line'] + 1, range['end']['line'] + 1)
return _format(config, document, line_range=line_range)
def pylsp_format_range(
config, workspace, document, range, options
): # pylint: disable=redefined-builtin,unused-argument
with workspace.report_progress("format_range: autopep8"):
log.info("Formatting document %s in range %s with autopep8", document, range)

# First we 'round' the range up/down to full lines only
range['start']['character'] = 0
range['end']['line'] += 1
range['end']['character'] = 0

# Add 1 for 1-indexing vs LSP's 0-indexing
line_range = (range['start']['line'] + 1, range['end']['line'] + 1)
return _format(config, document, line_range=line_range)


def _format(config, document, line_range=None):
Expand Down
33 changes: 17 additions & 16 deletions pylsp/plugins/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,25 @@


@hookimpl
def pylsp_definitions(config, document, position):
settings = config.plugin_settings('jedi_definition')
code_position = _utils.position_to_jedi_linecolumn(document, position)
definitions = document.jedi_script(use_document_path=True).goto(
follow_imports=settings.get('follow_imports', True),
follow_builtin_imports=settings.get('follow_builtin_imports', True),
**code_position)
def pylsp_definitions(config, workspace, document, position):
with workspace.report_progress("go to definitions"):
settings = config.plugin_settings('jedi_definition')
code_position = _utils.position_to_jedi_linecolumn(document, position)
definitions = document.jedi_script(use_document_path=True).goto(
follow_imports=settings.get('follow_imports', True),
follow_builtin_imports=settings.get('follow_builtin_imports', True),
**code_position)

return [
{
'uri': uris.uri_with(document.uri, path=str(d.module_path)),
'range': {
'start': {'line': d.line - 1, 'character': d.column},
'end': {'line': d.line - 1, 'character': d.column + len(d.name)},
return [
{
'uri': uris.uri_with(document.uri, path=str(d.module_path)),
'range': {
'start': {'line': d.line - 1, 'character': d.column},
'end': {'line': d.line - 1, 'character': d.column + len(d.name)},
}
}
}
for d in definitions if d.is_definition() and _not_internal_definition(d)
]
for d in definitions if d.is_definition() and _not_internal_definition(d)
]


def _not_internal_definition(definition):
Expand Down
103 changes: 52 additions & 51 deletions pylsp/plugins/flake8_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,57 +30,58 @@ def pylsp_settings():

@hookimpl
def pylsp_lint(workspace, document):
config = workspace._config
settings = config.plugin_settings('flake8', document_path=document.path)
log.debug("Got flake8 settings: %s", settings)

ignores = settings.get("ignore", [])
per_file_ignores = settings.get("perFileIgnores")

if per_file_ignores:
prev_file_pat = None
for path in per_file_ignores:
try:
file_pat, errors = path.split(":")
prev_file_pat = file_pat
except ValueError:
# It's legal to just specify another error type for the same
# file pattern:
if prev_file_pat is None:
log.warning(
"skipping a Per-file-ignore with no file pattern")
continue
file_pat = prev_file_pat
errors = path
if PurePath(document.path).match(file_pat):
ignores.extend(errors.split(","))

opts = {
'config': settings.get('config'),
'exclude': settings.get('exclude'),
'filename': settings.get('filename'),
'hang-closing': settings.get('hangClosing'),
'ignore': ignores or None,
'max-complexity': settings.get('maxComplexity'),
'max-line-length': settings.get('maxLineLength'),
'indent-size': settings.get('indentSize'),
'select': settings.get('select'),
}

# flake takes only absolute path to the config. So we should check and
# convert if necessary
if opts.get('config') and not os.path.isabs(opts.get('config')):
opts['config'] = os.path.abspath(os.path.expanduser(os.path.expandvars(
opts.get('config')
)))
log.debug("using flake8 with config: %s", opts['config'])

# Call the flake8 utility then parse diagnostics from stdout
flake8_executable = settings.get('executable', 'flake8')

args = build_args(opts)
output = run_flake8(flake8_executable, args, document)
return parse_stdout(document, output)
with workspace.report_progress("lint: flake8"):
config = workspace._config
settings = config.plugin_settings('flake8', document_path=document.path)
log.debug("Got flake8 settings: %s", settings)

ignores = settings.get("ignore", [])
per_file_ignores = settings.get("perFileIgnores")

if per_file_ignores:
prev_file_pat = None
for path in per_file_ignores:
try:
file_pat, errors = path.split(":")
prev_file_pat = file_pat
except ValueError:
# It's legal to just specify another error type for the same
# file pattern:
if prev_file_pat is None:
log.warning(
"skipping a Per-file-ignore with no file pattern")
continue
file_pat = prev_file_pat
errors = path
if PurePath(document.path).match(file_pat):
ignores.extend(errors.split(","))

opts = {
'config': settings.get('config'),
'exclude': settings.get('exclude'),
'filename': settings.get('filename'),
'hang-closing': settings.get('hangClosing'),
'ignore': ignores or None,
'max-complexity': settings.get('maxComplexity'),
'max-line-length': settings.get('maxLineLength'),
'indent-size': settings.get('indentSize'),
'select': settings.get('select'),
}

# flake takes only absolute path to the config. So we should check and
# convert if necessary
if opts.get('config') and not os.path.isabs(opts.get('config')):
opts['config'] = os.path.abspath(os.path.expanduser(os.path.expandvars(
opts.get('config')
)))
log.debug("using flake8 with config: %s", opts['config'])

# Call the flake8 utility then parse diagnostics from stdout
flake8_executable = settings.get('executable', 'flake8')

args = build_args(opts)
output = run_flake8(flake8_executable, args, document)
return parse_stdout(document, output)


def run_flake8(flake8_executable, args, document):
Expand Down
69 changes: 37 additions & 32 deletions pylsp/plugins/jedi_rename.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,44 @@


@hookimpl
def pylsp_rename(config, workspace, document, position, new_name): # pylint: disable=unused-argument
log.debug('Executing rename of %s to %s', document.word_at_position(position), new_name)
kwargs = _utils.position_to_jedi_linecolumn(document, position)
kwargs['new_name'] = new_name
try:
refactoring = document.jedi_script().rename(**kwargs)
except NotImplementedError as exc:
raise Exception('No support for renaming in Python 2/3.5 with Jedi. '
'Consider using the rope_rename plugin instead') from exc
log.debug('Finished rename: %s', refactoring.get_diff())
changes = []
for file_path, changed_file in refactoring.get_changed_files().items():
uri = uris.from_fs_path(str(file_path))
doc = workspace.get_maybe_document(uri)
changes.append({
'textDocument': {
'uri': uri,
'version': doc.version if doc else None
},
'edits': [
{
'range': {
'start': {'line': 0, 'character': 0},
'end': {
'line': _num_lines(changed_file.get_new_code()),
'character': 0,
def pylsp_rename(config, workspace, document, position, new_name): # pylint: disable=unused-argument,too-many-locals
with workspace.report_progress("rename", percentage=0) as report_progress:
log.debug('Executing rename of %s to %s', document.word_at_position(position), new_name)
kwargs = _utils.position_to_jedi_linecolumn(document, position)
kwargs['new_name'] = new_name
report_progress("refactoring")
try:
refactoring = document.jedi_script().rename(**kwargs)
except NotImplementedError as exc:
raise Exception('No support for renaming in Python 2/3.5 with Jedi. '
'Consider using the rope_rename plugin instead') from exc
log.debug('Finished rename: %s', refactoring.get_diff())
changes = []

changed_files = refactoring.get_changed_files()
for n, (file_path, changed_file) in enumerate(changed_files.items()):
report_progress(changed_file, percentage=n/len(changed_files)*100)
uri = uris.from_fs_path(str(file_path))
doc = workspace.get_maybe_document(uri)
changes.append({
'textDocument': {
'uri': uri,
'version': doc.version if doc else None
},
'edits': [
{
'range': {
'start': {'line': 0, 'character': 0},
'end': {
'line': _num_lines(changed_file.get_new_code()),
'character': 0,
},
},
},
'newText': changed_file.get_new_code(),
}
],
})
return {'documentChanges': changes}
'newText': changed_file.get_new_code(),
}
],
})
return {'documentChanges': changes}


def _num_lines(file_contents):
Expand Down
55 changes: 28 additions & 27 deletions pylsp/plugins/mccabe_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,31 @@


@hookimpl
def pylsp_lint(config, document):
threshold = config.plugin_settings('mccabe', document_path=document.path).get(THRESHOLD, DEFAULT_THRESHOLD)
log.debug("Running mccabe lint with threshold: %s", threshold)

try:
tree = compile(document.source, document.path, "exec", ast.PyCF_ONLY_AST)
except SyntaxError:
# We'll let the other linters point this one out
return None

visitor = mccabe.PathGraphingAstVisitor()
visitor.preorder(tree, visitor)

diags = []
for graph in visitor.graphs.values():
if graph.complexity() >= threshold:
diags.append({
'source': 'mccabe',
'range': {
'start': {'line': graph.lineno - 1, 'character': graph.column},
'end': {'line': graph.lineno - 1, 'character': len(document.lines[graph.lineno])},
},
'message': 'Cyclomatic complexity too high: %s (threshold %s)' % (graph.complexity(), threshold),
'severity': lsp.DiagnosticSeverity.Warning
})

return diags
def pylsp_lint(config, workspace, document):
with workspace.report_progress("lint: mccabe"):
threshold = config.plugin_settings('mccabe', document_path=document.path).get(THRESHOLD, DEFAULT_THRESHOLD)
log.debug("Running mccabe lint with threshold: %s", threshold)

try:
tree = compile(document.source, document.path, "exec", ast.PyCF_ONLY_AST)
except SyntaxError:
# We'll let the other linters point this one out
return None

visitor = mccabe.PathGraphingAstVisitor()
visitor.preorder(tree, visitor)

diags = []
for graph in visitor.graphs.values():
if graph.complexity() >= threshold:
diags.append({
'source': 'mccabe',
'range': {
'start': {'line': graph.lineno - 1, 'character': graph.column},
'end': {'line': graph.lineno - 1, 'character': len(document.lines[graph.lineno])},
},
'message': 'Cyclomatic complexity too high: %s (threshold %s)' % (graph.complexity(), threshold),
'severity': lsp.DiagnosticSeverity.Warning
})

return diags
49 changes: 25 additions & 24 deletions pylsp/plugins/pycodestyle_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,31 @@

@hookimpl
def pylsp_lint(workspace, document):
config = workspace._config
settings = config.plugin_settings('pycodestyle', document_path=document.path)
log.debug("Got pycodestyle settings: %s", settings)

opts = {
'exclude': settings.get('exclude'),
'filename': settings.get('filename'),
'hang_closing': settings.get('hangClosing'),
'ignore': settings.get('ignore'),
'max_line_length': settings.get('maxLineLength'),
'indent_size': settings.get('indentSize'),
'select': settings.get('select'),
}
kwargs = {k: v for k, v in opts.items() if v}
styleguide = pycodestyle.StyleGuide(kwargs)

c = pycodestyle.Checker(
filename=document.uri, lines=document.lines, options=styleguide.options,
report=PyCodeStyleDiagnosticReport(styleguide.options)
)
c.check_all()
diagnostics = c.report.diagnostics

return diagnostics
with workspace.report_progress("lint: pycodestyle"):
config = workspace._config
settings = config.plugin_settings('pycodestyle', document_path=document.path)
log.debug("Got pycodestyle settings: %s", settings)

opts = {
'exclude': settings.get('exclude'),
'filename': settings.get('filename'),
'hang_closing': settings.get('hangClosing'),
'ignore': settings.get('ignore'),
'max_line_length': settings.get('maxLineLength'),
'indent_size': settings.get('indentSize'),
'select': settings.get('select'),
}
kwargs = {k: v for k, v in opts.items() if v}
styleguide = pycodestyle.StyleGuide(kwargs)

c = pycodestyle.Checker(
filename=document.uri, lines=document.lines, options=styleguide.options,
report=PyCodeStyleDiagnosticReport(styleguide.options)
)
c.check_all()
diagnostics = c.report.diagnostics

return diagnostics


class PyCodeStyleDiagnosticReport(pycodestyle.BaseReport):
Expand Down
Loading