Skip to content

Commit 71612a7

Browse files
author
Orta
authored
Merge pull request #661 from it6/add-code-fixes
add code fixes command
2 parents 68129f4 + d2e24f2 commit 71612a7

4 files changed

Lines changed: 142 additions & 0 deletions

File tree

TypeScript.sublime-commands

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
{ "caption" : "TypeScript: Format Selection", "command": "typescript_format_selection" },
99
{ "caption" : "TypeScript: Format Document", "command": "typescript_format_document" },
1010
{ "caption" : "TypeScript: Find References", "command": "typescript_find_references" },
11+
{ "caption" : "TypeScript: Request Code Fixes", "command": "typescript_request_code_fixes" },
1112
{ "caption" : "TypeScript: Navigate To Symbol", "command": "typescript_nav_to" },
1213
{ "caption" : "TypeScript: Rename", "command": "typescript_rename" },
1314
{ "caption" : "TypeScript: Refactor", "command": "typescript_get_applicable_refactors" },

typescript/commands/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
from .save import TypescriptSave
88
from .show_doc import TypescriptShowDoc
99
from .signature import TypescriptSignaturePanel, TypescriptSignaturePopup
10+
from .get_code_fixes import (
11+
TypescriptRequestCodeFixesCommand,
12+
ReplaceTextCommand
13+
)
1014
from .format import (
1115
TypescriptFormatBrackets,
1216
TypescriptFormatDocument,
@@ -47,6 +51,8 @@
4751
"TypescriptProjectErrorList",
4852
"TypescriptGoToError",
4953
"TypescriptFormatBrackets",
54+
"TypescriptRequestCodeFixesCommand",
55+
"ReplaceTextCommand",
5056
"TypescriptFormatDocument",
5157
"TypescriptFormatLine",
5258
"TypescriptFormatOnKey",
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import sublime_plugin
2+
from ..libs.view_helpers import *
3+
from ..libs import log
4+
from .base_command import TypeScriptBaseTextCommand
5+
6+
7+
class ReplaceTextCommand(TypeScriptBaseTextCommand):
8+
"""The replace_text command implementation."""
9+
10+
def run(self, edit, start, end, text):
11+
"""Replace the content of a region with new text.
12+
Arguments:
13+
edit (Edit):
14+
The edit object to identify this operation.
15+
start (int):
16+
The beginning of the Region to replace.
17+
end (int):
18+
The end of the Region to replace.
19+
text (string):
20+
The new text to replace the content of the Region with.
21+
"""
22+
visible_region = self.view.visible_region()
23+
region = sublime.Region(start, end)
24+
text = text.replace('\r\n', '\n').replace('\r', '\n')
25+
self.view.replace(edit, region, text)
26+
27+
28+
class TypescriptRequestCodeFixesCommand(TypeScriptBaseTextCommand):
29+
"""
30+
Code Fixes command
31+
Get all errors in the current view
32+
Use errorCodes along with cursor offsets to get codeFixes
33+
"""
34+
all_code_fixes = []
35+
all_errors = []
36+
37+
def get_errors_at_cursor(self, path, cursor):
38+
line = cursor[0] + 1
39+
column = cursor[1] + 1
40+
errors_at_cursor = list(filter(lambda error:
41+
error['start']['line'] == line and
42+
error['end']['line'] == line and
43+
error['start']['offset'] <= column and
44+
error['end']['offset'] >= column, self.all_errors))
45+
return errors_at_cursor
46+
47+
def handle_selection(self, idx):
48+
if idx == -1:
49+
return
50+
all_changes = self.all_code_fixes['body'][idx]['changes'][0]['textChanges']
51+
for change in all_changes:
52+
text = change['newText']
53+
if text[:1] == '\n':
54+
start = self.view.text_point(
55+
change['start']['line'] - 1, change['start']['offset'])
56+
end = self.view.text_point(
57+
change['end']['line'] - 1, change['end']['offset'])
58+
else:
59+
start = self.view.text_point(
60+
change['start']['line'] - 1, change['start']['offset'] - 1)
61+
end = self.view.text_point(
62+
change['end']['line'] - 1, change['end']['offset'] - 1)
63+
self.view.run_command(
64+
'replace_text', {'start': start, 'end': end, 'text': text})
65+
66+
def run(self, text):
67+
log.debug("running TypescriptRequestCodeFixesCommand")
68+
if not is_typescript(self.view):
69+
print("To run this command, please first assign a file name to the view")
70+
return
71+
check_update_view(self.view)
72+
path = self.view.file_name()
73+
74+
semantic_errors = cli.service.get_semantic_errors(path)
75+
syntactic_errors = cli.service.get_syntactic_errors(path)
76+
77+
if semantic_errors['success']:
78+
self.all_errors = self.all_errors + semantic_errors['body']
79+
80+
if syntactic_errors['success']:
81+
self.all_errors = self.all_errors + syntactic_errors['body']
82+
83+
pos = self.view.sel()[0].begin()
84+
cursor = self.view.rowcol(pos)
85+
errors = self.get_errors_at_cursor(path, cursor)
86+
87+
if len(errors):
88+
error_codes = list(map(lambda error: error['code'], errors))
89+
start_line = errors[0]['start']['line']
90+
end_line = errors[0]['end']['line']
91+
start_offset = errors[0]['start']['offset']
92+
end_offset = errors[0]['end']['offset']
93+
self.all_code_fixes = cli.service.get_code_fixes(
94+
path, start_line, start_offset, end_line, end_offset, error_codes)
95+
if self.all_code_fixes['success']:
96+
if len(self.all_code_fixes['body']):
97+
possibleFixesDescriptions = list(
98+
map(lambda fix: fix['description'], self.all_code_fixes['body']))
99+
if len(possibleFixesDescriptions) == 1:
100+
self.handle_selection(0)
101+
else:
102+
self.view.window().show_quick_panel(
103+
possibleFixesDescriptions, self.handle_selection, False, -1)

typescript/libs/service_proxy.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,3 +352,35 @@ def create_req_dict(self, command_name, args=None):
352352
if args:
353353
req_dict["arguments"] = args
354354
return req_dict
355+
356+
def get_semantic_errors(self, path):
357+
args = {
358+
"file": path
359+
}
360+
req_dict = self.create_req_dict("semanticDiagnosticsSync", args)
361+
json_str = json_helpers.encode(req_dict)
362+
response_dict = self.__comm.sendCmdSync(json_str, req_dict["seq"])
363+
return response_dict
364+
365+
def get_syntactic_errors(self, path):
366+
args = {
367+
"file": path
368+
}
369+
req_dict = self.create_req_dict("syntacticDiagnosticsSync", args)
370+
json_str = json_helpers.encode(req_dict)
371+
response_dict = self.__comm.sendCmdSync(json_str, req_dict["seq"])
372+
return response_dict
373+
374+
def get_code_fixes(self, path, startLine, startOffset, endLine, endOffset, errorCodes):
375+
args = {
376+
"file": path,
377+
"startLine": startLine,
378+
"startOffset": startOffset,
379+
"endLine": endLine,
380+
"endOffset": endOffset,
381+
"errorCodes": errorCodes
382+
}
383+
req_dict = self.create_req_dict("getCodeFixes", args)
384+
json_str = json_helpers.encode(req_dict)
385+
response_dict = self.__comm.sendCmdSync(json_str, req_dict["seq"])
386+
return response_dict

0 commit comments

Comments
 (0)