forked from emmetio/sublime-text-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcomment.py
149 lines (118 loc) · 5.18 KB
/
comment.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
import sublime
import sublime_plugin
from . import emmet_sublime as emmet
from . import syntax
from .utils import get_content, get_caret, narrow_to_non_space
from .telemetry import track_action
html_comment = {
'start': '<!--',
'end': '-->'
}
css_comment = {
'start': '/*',
'end': '*/'
}
comment_selector = 'comment'
# NB: use `Emmet` prefix to distinguish default `toggle_comment` action
class EmmetToggleComment(sublime_plugin.TextCommand):
def run(self, edit):
view = self.view
for s in view.sel():
pt = s.begin()
syntax_name = syntax.from_pos(view, pt)
tokens = css_comment if syntax.is_css(syntax_name) else html_comment
if view.match_selector(pt, comment_selector):
# Caret inside comment, strip it
comment_region = narrow_to_non_space(view, view.extract_scope(pt))
remove_comments(view, edit, comment_region, tokens)
elif s.empty():
# Empty region, find tag
region = get_range_for_comment(view, pt)
if region is None:
# No tag found, comment line
region = narrow_to_non_space(view, view.line(pt))
# If there are any comments inside region, remove them
comments = get_comment_regions(view, region, tokens)
if comments:
removed = 0
comments.reverse()
for c in comments:
removed += remove_comments(view, edit, c, tokens)
region = sublime.Region(region.begin(), region.end() - removed)
add_comment(view, edit, region, tokens)
else:
# Comment selection
add_comment(view, edit, s, html_comment)
pos = get_caret(view)
track_action('Toggle Comment', syntax.from_pos(view, pos))
def remove_comments(view: sublime.View, edit: sublime.Edit, region: sublime.Region, tokens: dict):
"Removes comment markers from given region. Returns amount of characters removed"
text = view.substr(region)
if text.startswith(tokens['start']) and text.endswith(tokens['end']):
start_offset = region.begin() + len(tokens['start'])
end_offset = region.end() - len(tokens['end'])
# Narrow down offsets for whitespace
if view.substr(start_offset).isspace():
start_offset += 1
if view.substr(end_offset - 1).isspace():
end_offset -= 1
start_region = sublime.Region(region.begin(), start_offset)
end_region = sublime.Region(end_offset, region.end())
# It's faster to erase the start region first
# See comment in Default/comment.py plugin
view.erase(edit, start_region)
end_region = sublime.Region(
end_region.begin() - start_region.size(),
end_region.end() - start_region.size())
view.erase(edit, end_region)
return start_region.size() + end_region.size()
return 0
def get_range_for_comment(view: sublime.View, pt: int):
"Returns tag range for given text position, if possible"
syntax_name = syntax.from_pos(view, pt)
if syntax.is_css(syntax_name):
m = emmet.match_css(get_content(view), pt)
if m:
# TODO CSS might be an inline fragment of another document
return sublime.Region(m.start, m.end)
elif syntax.is_html(syntax_name):
tag = emmet.get_tag_context(view, pt, syntax.is_xml(syntax_name))
if tag:
open_tag = tag.get('open')
close_tag = tag.get('close')
return open_tag.cover(close_tag) if close_tag else open_tag
return None
def add_comment(view: sublime.View, edit: sublime.Edit, region: sublime.Region, tokens: dict):
"Adds comments around given range"
view.insert(edit, region.end(), ' ' + tokens['end'])
view.insert(edit, region.begin(), tokens['start'] + ' ')
def get_comment_regions(view: sublime.View, region: sublime.Region, tokens: dict):
"Finds comments inside given region and returns their regions"
result = []
text = view.substr(region)
start = region.begin()
offset = 0
while True:
c_start = text.find(tokens['start'], offset)
if c_start != -1:
offset = c_start + len(tokens['start'])
# Find comment end
c_end = text.find(tokens['end'], offset)
if c_end != -1:
offset = c_end + len(tokens['end'])
result.append(sublime.Region(start + c_start, start + offset))
else:
break
return result
def allow_emmet_comments(view: sublime.View):
"Check if Emmet's Toggle Comment action can be applied at current view"
if emmet.get_settings('comment'):
selectors = emmet.get_settings('comment_scopes', [])
caret = get_caret(view)
return syntax.matches_selector(view, caret, selectors)
return False
class ToggleCommentListener(sublime_plugin.EventListener):
def on_text_command(self, view, command_name, args):
if command_name == 'toggle_comment' and allow_emmet_comments(view):
return ('emmet_toggle_comment', None)
return None