Skip to content

Commit 1f5cfa2

Browse files
author
steveluc
committed
Restored Sublime default brace matching and tab behavior. Enabled
change tracking to handle case of snippets with text fields. Enabled tab to move among snippet text fields without breaking change tracking. Restored default Sublime behavior for \n between paired curly braces (open line between the braces and indent). Prepared to enable Sublime auto indent to work with change tracking. Added ctrl+; and ctrl+}. The former formats the current line and will auto-indent on an empty line, placing the cursor at the correct column (Emacs tab behavior). The latter formats the innermost block (pair of curly braces) and places the cursor outside the block so that a second press of ctrl+} will format the next innermost block and so on.
1 parent b7aa2fc commit 1f5cfa2

9 files changed

+35653
-34108
lines changed

Default.sublime-keymap

+18-18
Original file line numberDiff line numberDiff line change
@@ -73,35 +73,35 @@
7373
{"key": "selector", "operator": "equal", "operand": "source.ts" }
7474
]
7575
},
76-
{ "keys": ["enter"], "command": "typescript_format_on_key", "args": { "key": "\n" },
77-
"context": [
78-
{ "key": "auto_complete_visible", "operator": "equal", "operand": false },
79-
{"key": "selector", "operator": "equal", "operand": "source.ts" }
80-
]
76+
{
77+
"keys": ["enter"], "command": "typescript_format_on_key", "args": { "key": "\n" },
78+
"context": [
79+
{ "key": "auto_complete_visible", "operator": "equal", "operand": false },
80+
{ "key": "following_text", "operator": "not_regex_match", "operand": "^\\}" },
81+
{ "key": "selector", "operator": "not_equal", "operand": "meta.scope.between-tag-pair" },
82+
{ "key": "selector", "operator": "equal", "operand": "source.ts" }
83+
]
8184
},
8285
{ "keys": ["enter"], "command": "typescript_go_to_ref",
8386
"context": [
8487
{"key": "selector", "operator": "equal", "operand": "text.find-refs" }
8588
]
8689
},
87-
{ "keys": ["enter"], "command": "commit_completion",
88-
"context": [
89-
{ "key": "auto_complete_visible","operator": "equal", "operand": true }
90-
]
90+
{
91+
"keys": ["enter"], "command": "commit_completion",
92+
"context": [
93+
{ "key": "auto_complete_visible","operator": "equal", "operand": true }
94+
]
9195
},
92-
{ "keys": ["shift+enter"], "command": "typescript_format_on_key", "args": { "key": "\n" },
96+
{ "keys": ["ctrl+;"], "command": "typescript_format_line",
9397
"context": [
94-
{"key": "selector", "operator": "equal", "operand": "source.ts" }
95-
]
96-
},
97-
{ "keys": ["keypad_enter"], "command": "typescript_format_on_key", "args": { "key": "\n" },
98-
"context": [
99-
{"key": "selector", "operator": "equal", "operand": "source.ts" }
98+
{ "key": "selector", "operator": "equal", "operand": "source.ts" }
10099
]
101100
},
102-
{ "keys": ["shift+keypad_enter"], "command": "typescript_format_on_key", "args": { "key": "\n" },
101+
{ "keys": ["ctrl+shift+]"], "command": "typescript_format_brackets",
103102
"context": [
104-
{"key": "selector", "operator": "equal", "operand": "source.ts" }
103+
{ "key": "selector", "operator": "equal", "operand": "source.ts" }
105104
]
106105
}
106+
107107
]

TypeScript.py

+102-34
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
import sys
44
import time
5+
import re
56

67
import sublime
78
import sublime_plugin
@@ -94,7 +95,7 @@ def __init__(self, filename):
9495
'syntacticDiag': [],
9596
'semanticDiag': [],
9697
}
97-
98+
self.loadHandler = None
9899

99100
# a reference to a source file, line, column; next and prev refer to the
100101
# next and previous reference in a view containing references
@@ -277,7 +278,6 @@ def __init__(self, filename, cc):
277278
self.lastModChangeCount = cc
278279
self.modCount = 0
279280

280-
281281
# region that will not change as buffer is modified
282282
class StaticRegion:
283283
def __init__(self, b, e):
@@ -290,6 +290,9 @@ def toRegion(self):
290290
def begin(self):
291291
return self.b
292292

293+
def empty(self):
294+
return self.b == self.e
295+
293296

294297
# convert a list of static regions to ordinary regions
295298
def staticRegionsToRegions(staticRegions):
@@ -334,13 +337,16 @@ def decrLocsToRegions(locs, amt):
334337
rr.append(sublime.Region(loc - amt, loc - amt))
335338
return rr
336339

337-
# right now, we must have this setting because no way to guess how to translate
338-
# tabs on the server side; so burn it in
340+
# right now, we have this setting because there is no way to know how to translate
341+
# tabs on the server side
339342
# TODO: see if we can tolerate tabs by having the editor tell the server how
340343
# to interpret them
341344
def setFilePrefs(view):
342345
settings = view.settings()
343-
settings.set('translateTabsToSpaces', True)
346+
settings.set('translate_tabs_to_spaces', True)
347+
settings.set('tab_size', 4)
348+
settings.set('detect_indentation', False)
349+
settings.set('use_tab_stops', False)
344350

345351
# given a list of regions and a (possibly zero-length) string to insert,
346352
# send the appropriate change information to the server
@@ -474,12 +480,10 @@ def showErrorMsgs(self, diagEvtBody, syntactic):
474480
filename = diagEvtBody.file
475481
if os.name == 'nt':
476482
filename = filename.replace('/', '\\')
477-
print("SEM!!! " + filename)
478483
diags = diagEvtBody.diagnostics
479484
info = self.fileMap.get(filename)
480485
if info:
481486
view = info.view
482-
print("sem: " + str(info.changeCountErrReq) + " " + str(self.change_count(view)))
483487
if info.changeCountErrReq == self.change_count(view):
484488
if syntactic:
485489
regionKey = 'syntacticDiag'
@@ -512,7 +516,6 @@ def showErrorMsgs(self, diagEvtBody, syntactic):
512516

513517
# event arrived from the server; call appropriate handler
514518
def dispatchEvent(self, ev):
515-
print("dispatch event")
516519
evtype = ev.event
517520
if evtype == 'syntaxDiag':
518521
self.showErrorMsgs(ev.body, syntactic=True)
@@ -553,7 +556,6 @@ def onIdle(self):
553556
view = active_view()
554557
ev = cli.service.getEvent()
555558
if ev is not None:
556-
print("idle got event")
557559
self.dispatchEvent(ev)
558560
self.errRefreshRequested = False
559561
# reset the timer in case more events are on the queue
@@ -567,6 +569,12 @@ def onIdle(self):
567569
# request errors
568570
self.refreshErrors(view, 500)
569571

572+
def on_load(self, view):
573+
clientInfo = cli.getOrAddFile(view.file_name())
574+
if clientInfo and clientInfo.loadHandler:
575+
clientInfo.loadHandler(view)
576+
clientInfo.loadHandler = None
577+
570578
# ST3 only
571579
# for certain text commands, learn what changed and notify the
572580
# server, to avoid sending the whole buffer during completion
@@ -654,15 +662,7 @@ def on_modified(self, view):
654662
# change handled in on_text_command
655663
info.clientInfo.changeCount = self.change_count(view)
656664
info.preChangeSent = False
657-
elif (lastCommand == "insert") and (not "\n" in args['characters']):
658-
# single-line insert, use saved cursor information to determine
659-
# what was inserted
660-
# REVIEW: consider using this only if there is a single cursor,
661-
# and only if that
662-
# cursor is an empty region; right now, the code tries to
663-
# handle multiple cursors
664-
# and non-empty selections (which will be replaced by the
665-
# string inserted)
665+
elif (lastCommand == "insert") and (not "\n" in args['characters']) and (len(info.prevSel) == 1) and (info.prevSel[0].empty()):
666666
info.clientInfo.changeCount = self.change_count(view)
667667
prevCursor = info.prevSel[0].begin()
668668
cursor = view.sel()[0].begin()
@@ -905,6 +905,12 @@ def on_done(newName):
905905
on_done, None, on_cancel)
906906

907907

908+
def locsToValue(locs):
909+
locsValue = []
910+
for loc in locs:
911+
locsValue.append(loc.toDict())
912+
return locsValue
913+
908914
# called from on_done handler in finish_rename command
909915
# on_done is called by input panel for new name
910916
class TypescriptFinishRenameCommand(sublime_plugin.TextCommand):
@@ -916,15 +922,52 @@ def run(self, text, argsJson=""):
916922
for outerLoc in outerLocs:
917923
file = outerLoc.file
918924
innerLocs = outerLoc.locs
919-
for innerLoc in innerLocs:
920-
startlc = innerLoc.start
921-
(startl, startc) = extractLineCol(startlc)
922-
endlc = innerLoc.end
923-
(endl, endc) = extractLineCol(endlc)
924-
applyEdit(text, self.view, startl, startc, endl,
925-
endc, ntext=newName)
925+
activeWindow = sublime.active_window()
926+
renameView = activeWindow.find_open_file(file)
927+
if not renameView:
928+
renameView = activeWindow.open_file(file)
929+
if renameView.is_loading():
930+
clientInfo = cli.getOrAddFile(file)
931+
innerLocsValue = locsToValue(innerLocs)
932+
def applyLocs(v):
933+
v.run_command('typescript_delayed_rename_file',
934+
{ "locs" : innerLocsValue, "name" : newName })
935+
clientInfo.loadHandler = applyLocs
936+
elif renameView != self.view:
937+
print(" got to " + renameView.file_name())
938+
innerLocsValue = locsToValue(innerLocs)
939+
print(innerLocsValue)
940+
renameView.run_command('typescript_delayed_rename_file',
941+
{ "locs" : innerLocsValue, "name" : newName })
942+
else:
943+
for innerLoc in innerLocs:
944+
startlc = innerLoc.start
945+
(startl, startc) = extractLineCol(startlc)
946+
endlc = innerLoc.end
947+
(endl, endc) = extractLineCol(endlc)
948+
applyEdit(text, self.view, startl, startc, endl,
949+
endc, ntext = newName)
926950

927951

952+
def extractLineColFromDict(lc):
953+
line = lc['line'] - 1
954+
col = lc['col'] - 1
955+
return (line, col)
956+
957+
class TypescriptDelayedRenameFile(sublime_plugin.TextCommand):
958+
def run(self, text, locs = None, name = ""):
959+
if locs and (len(name) > 0):
960+
print(name)
961+
print(locs)
962+
for innerLoc in locs:
963+
startlc = innerLoc['start']
964+
(startl, startc) = extractLineColFromDict(startlc)
965+
endlc = innerLoc['end']
966+
(endl, endc) = extractLineColFromDict(endlc)
967+
applyEdit(text, self.view, startl, startc, endl,
968+
endc, ntext = name)
969+
970+
928971
# if the FindReferences view is active, get it
929972
# TODO: generalize this so that we can find any scratch view
930973
# containing references to other files
@@ -1136,16 +1179,17 @@ def applyFormattingChanges(text, view, codeEdits):
11361179
# format on ";", "}", or "\n"; called by typing these keys in a ts file
11371180
# in the case of "\n", this is only called when no completion dialogue visible
11381181
class TypescriptFormatOnKey(sublime_plugin.TextCommand):
1139-
def run(self, text, key=""):
1182+
def run(self, text, key = "", insertKey = True):
11401183
if 0 == len(key):
11411184
return
11421185
loc = self.view.sel()[0].begin()
1143-
self.view.insert(text, loc, key)
1144-
sendReplaceChangesForRegions(self.view, [sublime.Region(loc, loc)], key)
1145-
if not cli.ST2():
1146-
clientInfo = cli.getOrAddFile(self.view.file_name())
1147-
clientInfo.changeCount = self.view.change_count()
1148-
checkUpdateView(self.view)
1186+
if insertKey:
1187+
self.view.insert(text, loc, key)
1188+
sendReplaceChangesForRegions(self.view, [sublime.Region(loc, loc)], key)
1189+
if not cli.ST2():
1190+
clientInfo = cli.getOrAddFile(self.view.file_name())
1191+
clientInfo.changeCount = self.view.change_count()
1192+
checkUpdateView(self.view)
11491193
formatResp = cli.service.formatOnKey(self.view.file_name(), getLocationFromView(self.view), key)
11501194
if formatResp.success:
11511195
codeEdits = formatResp.body
@@ -1176,14 +1220,38 @@ class TypescriptFormatDocument(sublime_plugin.TextCommand):
11761220
def run(self, text):
11771221
formatRange(text, self.view, 0, self.view.size())
11781222

1223+
nonBlankLinePattern = re.compile("[\S]+")
11791224

11801225
# command to format the current line
11811226
class TypescriptFormatLine(sublime_plugin.TextCommand):
11821227
def run(self, text):
11831228
lineRegion = self.view.line(self.view.sel()[0])
1184-
formatRange(text, self.view, lineRegion.begin(), lineRegion.end())
1185-
1229+
lineText = self.view.substr(lineRegion)
1230+
if (nonBlankLinePattern.search(lineText)):
1231+
formatRange(text, self.view, lineRegion.begin(), lineRegion.end())
1232+
else:
1233+
position = self.view.sel()[0].begin()
1234+
cursor = self.view.rowcol(position)
1235+
line = cursor[0]
1236+
if line > 0:
1237+
self.view.run_command('typescript_format_on_key', { "key": "\n", "insertKey": False });
11861238

1239+
class TypescriptFormatBrackets(sublime_plugin.TextCommand):
1240+
def run(self, text):
1241+
sel=self.view.sel()
1242+
if (len(sel) == 1):
1243+
print('format brackets')
1244+
originalPos = sel[0].begin()
1245+
bracketChar = self.view.substr(originalPos)
1246+
if bracketChar != "}":
1247+
self.view.run_command('move_to', { "to": "brackets" });
1248+
bracketPos = self.view.sel()[0].begin()
1249+
bracketChar = self.view.substr(bracketPos)
1250+
if bracketChar == "}":
1251+
self.view.run_command('move', { "by": "characters", "forward": True })
1252+
self.view.run_command('typescript_format_on_key', { "key": "}", "insertKey": False });
1253+
self.view.run_command('move', { "by": "characters", "forward": True })
1254+
11871255
# this is not always called on startup by Sublime, so we call it
11881256
# from on_activated or on_close if necessary
11891257
# TODO: get abbrev message and set up dictionary

TypeScript.sublime-settings

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
2-
"auto_complete_triggers" : [ {"selector": "source.ts",
3-
"characters": "."} ],
4-
"auto_match_enabled": false,
5-
"auto_indent" : false,
2+
"auto_complete_triggers" : [ {"selector": "source.ts", "characters": "."} ],
3+
"auto_insert": false,
64
"translate_tabs_to_spaces" : true,
5+
"tab_size": 4,
6+
"detect_indentation": false,
77
"use_tab_stops": false,
88
"auto_complete_commit_on_tab": true
99
}

servicedefs.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -101,19 +101,25 @@ def __init__(self, line, col):
101101
self.line = line
102102
self.col = col
103103

104+
def toDict(self):
105+
return { "line": self.line, "col": self.col }
104106

105107
class FileSpan(object):
106108
def __init__(self, file, start, end):
107109
self.file = file
108110
self.start = Location(**start)
109111
self.end = Location(**end)
110112

111-
112113
class FileSpanWithinFile:
113114
def __init__(self, start, end):
114115
self.start = Location(**start)
115116
self.end = Location(**end)
116117

118+
def toDict(self):
119+
return {
120+
"start": self.start.toDict(),
121+
"end": self.end.toDict()
122+
}
117123

118124
class DefinitionResponse(Response):
119125
def __init__(self, body=None, **kwargs):

src/service/makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
TS = ../../../TypeScript
1+
TS = ~/src/TypeScript
22
TSLOC = $(TS)/built/local
33
BIN = $(TSLOC)/tsserver.js $(TSLOC)/tsserver.js.map $(TSLOC)/lib.d.ts $(TSLOC)/lib.es6.d.ts
44
LTSC = node $(TSLOC)/tsc.js
@@ -8,7 +8,7 @@ it: $(SRC)
88
$(LTSC) node.d.ts editorServices.ts protocol.ts server.ts -m commonjs --noImplicitAny -out protocol.js
99

1010
install:
11-
cp $(BIN) ../../clients/sublime/TypeScript/tsserver
11+
cp $(BIN) ../../tsserver
1212

1313
clean:
1414
-rm *.js

0 commit comments

Comments
 (0)