Skip to content

Commit ce0adbf

Browse files
committed
Add error end functionality
1 parent 6fd72c3 commit ce0adbf

File tree

4 files changed

+28
-18
lines changed

4 files changed

+28
-18
lines changed

pylsp_mypy/plugin.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from pylsp.config.config import Config
3131
from pylsp.workspace import Document, Workspace
3232

33-
line_pattern: str = r"((?:^[a-z]:)?[^:]+):(?:(\d+):)?(?:(\d+):)? (\w+): (.*)"
33+
line_pattern: str = r"((?:^[a-z]:)?[^:]+):(?:(\d+):)?(?:(\d+):)?(?:(\d+):)?(?:(\d+):)? (\w+): (.*)"
3434

3535
log = logging.getLogger(__name__)
3636

@@ -79,7 +79,7 @@ def parse_line(line: str, document: Optional[Document] = None) -> Optional[Dict[
7979
"""
8080
result = re.match(line_pattern, line)
8181
if result:
82-
file_path, linenoStr, offsetStr, severity, msg = result.groups()
82+
file_path, linenoStr, offsetStr, endlinenoStr, endoffsetStr, severity, msg = result.groups()
8383

8484
if file_path != "<string>": # live mode
8585
# results from other files can be included, but we cannot return
@@ -90,25 +90,26 @@ def parse_line(line: str, document: Optional[Document] = None) -> Optional[Dict[
9090

9191
lineno = int(linenoStr or 1) - 1 # 0-based line number
9292
offset = int(offsetStr or 1) - 1 # 0-based offset
93+
end_lineno = (int(endlinenoStr) - 1) if endlinenoStr else None
94+
end_offset = (int(endoffsetStr) - 1) if endoffsetStr else None
9395
errno = 2
9496
if severity == "error":
9597
errno = 1
9698
diag: Dict[str, Any] = {
9799
"source": "mypy",
98100
"range": {
99101
"start": {"line": lineno, "character": offset},
100-
# There may be a better solution, but mypy does not provide end
101-
"end": {"line": lineno, "character": offset + 1},
102+
"end": {"line": end_lineno or lineno, "character": end_offset},
102103
},
103104
"message": msg,
104105
"severity": errno,
105106
}
106-
if document:
107-
# although mypy does not provide the end of the affected range, we
108-
# can make a good guess by highlighting the word that Mypy flagged
109-
word = document.word_at_position(diag["range"]["start"])
110-
if word:
111-
diag["range"]["end"]["character"] = diag["range"]["start"]["character"] + len(word)
107+
if diag["range"]["end"]["character"] is None:
108+
diag["range"]["end"]["character"] = (
109+
offset + len(word)
110+
if (document and (word := document.word_at_position(diag["range"]["start"])))
111+
else offset + 1
112+
)
112113

113114
return diag
114115
return None
@@ -229,7 +230,7 @@ def get_diagnostics(
229230
if dmypy:
230231
dmypy_status_file = settings.get("dmypy_status_file", ".dmypy.json")
231232

232-
args = ["--show-column-numbers"]
233+
args = ["--show-error-end"]
233234

234235
global tmpFile
235236
if live_mode and not is_saved:

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
python-lsp-server
2-
mypy
2+
mypy >= 0.981
33
tomli >= 1.1.0 ; python_version < "3.11"
44
black
55
pre-commit

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ python_requires = >= 3.7
2121
packages = find:
2222
install_requires =
2323
python-lsp-server >=1.7.0
24-
mypy
24+
mypy >= 0.981
2525
tomli >= 1.1.0 ; python_version < "3.11"
2626

2727
[flake8]

test/test_plugin.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"""
1919
TYPE_ERR_MSG = '"Dict[<nothing>, <nothing>]" has no attribute "append" [attr-defined]'
2020

21-
TEST_LINE = 'test_plugin.py:279:8: error: "Request" has no attribute "id"'
21+
TEST_LINE = 'test_plugin.py:279:8:279:19: error: "Request" has no attribute "id"'
22+
TEST_LINE_WITHOUT_END = 'test_plugin.py:279:8: error: "Request" has no attribute "id"'
2223
TEST_LINE_WITHOUT_COL = "test_plugin.py:279: " 'error: "Request" has no attribute "id"'
2324
TEST_LINE_WITHOUT_LINE = "test_plugin.py: " 'error: "Request" has no attribute "id"'
2425

@@ -65,14 +66,22 @@ def test_plugin(workspace, last_diagnostics_monkeypatch):
6566
diag = diags[0]
6667
assert diag["message"] == TYPE_ERR_MSG
6768
assert diag["range"]["start"] == {"line": 0, "character": 0}
68-
assert diag["range"]["end"] == {"line": 0, "character": 1}
69+
assert diag["range"]["end"] == {"line": 0, "character": 8}
6970

7071

7172
def test_parse_full_line(workspace):
7273
diag = plugin.parse_line(TEST_LINE) # TODO parse a document here
7374
assert diag["message"] == '"Request" has no attribute "id"'
7475
assert diag["range"]["start"] == {"line": 278, "character": 7}
75-
assert diag["range"]["end"] == {"line": 278, "character": 8}
76+
assert diag["range"]["end"] == {"line": 278, "character": 18}
77+
78+
79+
def test_parse_line_without_end(workspace):
80+
doc = Document(DOC_URI, workspace)
81+
diag = plugin.parse_line(TEST_LINE_WITHOUT_END, doc)
82+
assert diag["message"] == '"Request" has no attribute "id"'
83+
assert diag["range"]["start"] == {"line": 278, "character": 7}
84+
assert diag["range"]["end"] == {"line": 278, "character": 13}
7685

7786

7887
def test_parse_line_without_col(workspace):
@@ -95,7 +104,7 @@ def test_parse_line_without_line(workspace):
95104
def test_parse_line_with_context(monkeypatch, word, bounds, workspace):
96105
doc = Document(DOC_URI, workspace)
97106
monkeypatch.setattr(Document, "word_at_position", lambda *args: word)
98-
diag = plugin.parse_line(TEST_LINE, doc)
107+
diag = plugin.parse_line(TEST_LINE_WITHOUT_END, doc)
99108
assert diag["message"] == '"Request" has no attribute "id"'
100109
assert diag["range"]["start"] == {"line": 278, "character": bounds[0]}
101110
assert diag["range"]["end"] == {"line": 278, "character": bounds[1]}
@@ -226,7 +235,7 @@ def test_option_overrides_dmypy(last_diagnostics_monkeypatch, workspace):
226235
"--",
227236
"--python-executable",
228237
"/tmp/fake",
229-
"--show-column-numbers",
238+
"--show-error-end",
230239
document.path,
231240
]
232241
m.assert_called_with(expected, capture_output=True, **windows_flag, encoding="utf-8")

0 commit comments

Comments
 (0)