forked from mgnandt/sublime-text-surround
-
Notifications
You must be signed in to change notification settings - Fork 1
/
surround.py
156 lines (131 loc) · 6.41 KB
/
surround.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import sublime, sublime_plugin
class BaseSurroundCommand(sublime_plugin.TextCommand):
"""
class BaseSurroundCommand(sublime_plugin.TextCommand)
|
| An abstract class representing shared behavior for all
| Surround commands.
|
| Static values:
|
| MATCHING_CHARS
|
| A dictionary mapping special case surround shortcuts into
| open, close pairs. E.g. both open and close parentheses
| map to the pair (, ) to make surrounding with parens easier
|
"""
# Provides a shortcut for specifying surrounding characters as
# open, close pairs of characters. An additional special case
# is included for xml tag pairs, handled outside of this dict
MATCHING_CHARS = {
'{' : ('{', '}'),
'}' : ('{', '}'),
'[' : ('[', ']'),
']' : ('[', ']'),
'(' : ('(', ')'),
')' : ('(', ')'),
'<' : ('<', '>'),
'>' : ('<', '>'),
'/*' : ('/*', '*/'),
'#' : ('# ',''),
'//' : ('// ', '')
}
def get_matching_char_pair(self, text):
if text in self.MATCHING_CHARS:
return self.MATCHING_CHARS[text]
# If the text looks like <...> assume its an xml tag
# and generate the appropriate closing tag
if text.startswith('<') and text.endswith('>'):
if text.startswith('</'):
return [text[0] + text[2:], text]
return [text, text[0] + "/" + text[1:]]
return [text, text]
def get_word_to_surround(self, region):
if region.empty():
return self.view.word(region.a)
return region
def run(self, edit):
raise NotImplementedError('The class "BaseSurroundCommand" is an abstract class and should not be used.')
def apply_surround_chars(self, surround_chars):
raise NotImplementedError('The class "BaseSurroundCommand" is an abstract class and should not be used.')
def after_input(self, text):
raise NotImplementedError('The class "BaseSurroundCommand" is an abstract class and should not be used.')
class AddSurroundCommand(BaseSurroundCommand):
def run(self, edit):
self.view.window().show_input_panel('Surround with:', '', self.after_input, None, None)
def after_input(self, text):
if text:
surround_chars = self.get_matching_char_pair(text)
self.apply_surround_chars(surround_chars)
def apply_surround_chars(self, surround_chars):
selected_regions = self.view.sel()
for region in selected_regions:
word = self.get_word_to_surround(region)
self.insert_around_word(word, surround_chars)
def insert_around_word(self, word, surround_chars):
edit_sequence = self.view.begin_edit()
self.view.insert(edit_sequence, word.begin(), surround_chars[0])
# After inserting the prefix, the end of the word is shifted by the length of the prefix
self.view.insert(edit_sequence, word.end() + len(surround_chars[0]), surround_chars[1])
self.view.end_edit(edit_sequence)
class DeleteSurroundCommand(BaseSurroundCommand):
def run(self, edit):
self.view.window().show_input_panel('Delete surrounding:', '', self.after_input, None, None)
def after_input(self, text):
if text:
surround_chars = self.get_matching_char_pair(text)
self.apply_surround_chars(surround_chars)
def apply_surround_chars(self, surround_chars):
selected_regions = self.view.sel()
for region in selected_regions:
if (region.size() > 0):
self.delete_surrounding_regions(surround_chars, region)
else:
self.delete_surrounding_regions(surround_chars, self.get_word_to_surround(region))
def delete_surrounding_regions(self, surround_chars, region):
region_text = self.view.substr(region)
prefix = sublime.Region(max(0,region.begin() - len(surround_chars[0])), region.begin())
suffix = sublime.Region(region.end(), min(self.view.size(),region.end() + len(surround_chars[1])))
print("Suffix: %s" % suffix)
if self.view.substr(prefix) == surround_chars[0] and self.view.substr(suffix) == surround_chars[1]:
edit_sequence = self.view.begin_edit()
self.view.replace(edit_sequence, prefix, '')
# After deleting the prefix, the beginning and end of the suffix are shifted by the lenght of the prefix
self.view.replace(edit_sequence, sublime.Region(suffix.begin() - len(surround_chars[0]), suffix.end() - len(surround_chars[0])), '')
self.view.end_edit(edit_sequence)
class ReplaceSurroundCommand(BaseSurroundCommand):
replace_chars = None
with_chars = None
def run(self, edit):
self.view.window().show_input_panel('Replace: ', '', self.after_replace_input, None, None)
def after_replace_input(self, text):
self.replace_chars = self.get_matching_char_pair(text)
self.view.window().show_input_panel('With: ', '', self.after_with_input, None, None)
def after_with_input(self, text):
self.with_chars = self.get_matching_char_pair(text)
self.apply_surround_chars()
def insert_around_word(self, word, surround_chars):
edit_sequence = self.view.begin_edit()
self.view.insert(edit_sequence, word.begin(), surround_chars[0])
self.view.insert(edit_sequence, word.end() + len(surround_chars[0]), surround_chars[1])
self.view.end_edit(edit_sequence)
def replace_around_word(self, replace_chars, with_chars, region):
region_text = self.view.substr(region)
prefix = sublime.Region(max(0,region.begin() - len(replace_chars[0])), region.begin())
suffix = sublime.Region(region.end(), min(self.view.size(),region.end() + len(replace_chars[1])))
print("Suffix: %s" % suffix)
if self.view.substr(prefix) == replace_chars[0] and self.view.substr(suffix) == replace_chars[1]:
edit_sequence = self.view.begin_edit()
self.view.replace(edit_sequence, prefix, '')
self.view.replace(edit_sequence, sublime.Region(suffix.begin() - len(replace_chars[0]), suffix.end() - len(replace_chars[0])), '')
self.view.insert(edit_sequence, region.begin() - len(replace_chars[0]), with_chars[0])
self.view.insert(edit_sequence, region.end() - len(replace_chars[0]) + len(with_chars[0]), with_chars[1])
self.view.end_edit(edit_sequence)
def apply_surround_chars(self):
selected_regions = self.view.sel()
for region in selected_regions:
if (region.size() > 0):
self.replace_around_word(self.replace_chars, self.with_chars, region)
else:
self.replace_around_word(self.replace_chars, self.with_chars, self.get_word_to_surround(region))