Created
August 4, 2016 20:46
-
-
Save gwenzek/1e57e481cb44c72751ef0555f75edf93 to your computer and use it in GitHub Desktop.
SublimeText plugin for inserting Doxygen style comment in code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sublime | |
import sublime_plugin | |
# inspired from https://github.com/Rapptz/DoxyDoc/blob/master/doxydoc.py | |
# with scopes instead of regex | |
CLASS_SCOPE_ACTIVATOR = ['meta.class.identifier'] | |
CLASS_NAME_SCOPE = 'entity.name.class' | |
FUN_SCOPE_ACTIVATOR = ['meta.method.identifier'] | |
FUN_NAME_SCOPE = 'entity.name.function' | |
CONSTRUCTOR_SCOPE = 'entity.name.function.constructor' | |
PROPERTY_SCOPE = 'entity.name.variable.property' | |
PROPERTY_SCOPE_ACTIVATOR = [PROPERTY_SCOPE] | |
PARAM_SCOPE = 'variable.parameter -variable.parameter.type' | |
TYPE_PARAM_SCOPE = 'variable.parameter.type' | |
VOID_STRING = ' void ' | |
class InsertDocCommand(sublime_plugin.TextCommand): | |
def run(self, edit, *args): | |
""" | |
This command try to insert C# documentation for the line below the caret | |
""" | |
point = self.get_point() | |
current_spaces = self.read_spaces(point) | |
# current_line = self.line(point) | |
next_point = self.move_point_down(point) | |
next_spaces = self.read_spaces(next_point) | |
skipped = max(next_spaces - current_spaces, 0) | |
spaces = ' ' * skipped + '/// ' | |
if self.line_contains_scope(next_point, *CLASS_SCOPE_ACTIVATOR): | |
clazz = self.get_first_member(next_point, CLASS_NAME_SCOPE, 0) | |
snippet = spaces + '<summary>${1:' + clazz + ' summary}</summary>' | |
self.write_snippet(snippet) | |
elif self.line_contains_scope(next_point, *FUN_SCOPE_ACTIVATOR): | |
function = self.get_first_member(next_point, FUN_NAME_SCOPE, 0) | |
constructor = self.get_first_member(next_point, CONSTRUCTOR_SCOPE, 0) == function | |
params = list(self.get_members(next_point, PARAM_SCOPE, 0)) | |
type_params = list(self.get_members(next_point, TYPE_PARAM_SCOPE, 0)) | |
if constructor: | |
snippet = spaces + '<summary>${1:' + function + ' constructor summary}</summary>\n' | |
else: | |
snippet = spaces + '<summary>${1:' + function + ' summary}</summary>\n' | |
index = 2 | |
for param in params: | |
snippet += spaces | |
snippet += '<param name="{0}">${{{1}:{2}}}</param>'.format(param, index, param) | |
index += 1 | |
snippet += '\n' | |
for param in type_params: | |
snippet += spaces | |
snippet += '<typeparam name="{0}">${{{1}:{2}}}</typeparam>'.format(param, index, param) | |
index += 1 | |
snippet += '\n' | |
if constructor or VOID_STRING in self.line(next_point): | |
snippet = snippet[:-1] | |
else: | |
snippet += spaces + '<returns>${{{0}:return value}}</returns>'.format(index) | |
self.write_snippet(snippet) | |
elif self.line_contains_scope(next_point, *PROPERTY_SCOPE_ACTIVATOR): | |
property = self.get_first_member(next_point, PROPERTY_SCOPE, 0) | |
snippet = spaces + '<summary>${1:' + property + ' summary}</summary>' | |
self.write_snippet(snippet) | |
else: | |
print('not above a class or a function') | |
def get_scope(self, point): | |
scope = self.view.scope_name(point) | |
return scope | |
def get_first_member(self, point, scope, skipped): | |
line = self.line(point) | |
start = skipped | |
start, point = self._next_member_start(line, start, point, scope) | |
if start < len(line): | |
end, point = self._next_member_end(line, start, point, scope) | |
return line[start:end] | |
return '' | |
def get_members(self, point, scope, skipped): | |
line = self.line(point) | |
start = skipped | |
while start < len(line): | |
start, point = self._next_member_start(line, start, point, scope) | |
if start < len(line): | |
end, point = self._next_member_end(line, start, point, scope) | |
yield line[start:end] | |
# print(start, end) | |
start = end | |
def _next_member_start(self, line, start, point, scope): | |
while start < len(line): | |
if self.match_scope(point, scope): | |
return start, point | |
start += 1 | |
point += 1 | |
return start, point | |
def _next_member_end(self, line, start, point, scope): | |
end = start | |
while end < len(line): | |
if not self.match_scope(point, scope): | |
return end, point | |
end += 1 | |
point += 1 | |
return end, point | |
def match_scope(self, point, *scopes): | |
return any(map(lambda scope: self.view.score_selector(point, scope) > 0, scopes)) | |
def line_contains_scope(self, point, *scopes): | |
end = self.view.line(point).end() | |
while point < end: | |
if self.match_scope(point, *scopes): | |
return True | |
point += 1 | |
return False | |
def get_point(self): | |
return self.view.sel()[0].begin() | |
def write_snippet(self, snippet): | |
self.view.run_command("insert_snippet", {"contents": snippet}) | |
def move_down(self): | |
self.view.run_command("move", {"by": "lines", "forward": True}) | |
def move_point_down(self, point): | |
if (point >= self.view.size()): | |
return point | |
row, col = self.view.rowcol(point) | |
return self.view.text_point(row + 1, 0) | |
def line(self, point): | |
return self.view.substr(self.view.line(point)) | |
def read_spaces(self, point): | |
line = self.line(point) | |
i = 0 | |
while i < len(line) and line[i] == ' ': | |
i += 1 | |
return i |
Note this was one of my first plugin with Sublime and I discovered new functions in the API since, so you shouldn't take that as a code example !
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I wrote and tested this plugin for C# but I tried to be language agnostic, relying on scopes rather than language specific regexes.