Skip to content

Instantly share code, notes, and snippets.

@gwenzek
Created August 4, 2016 20:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gwenzek/1e57e481cb44c72751ef0555f75edf93 to your computer and use it in GitHub Desktop.
Save gwenzek/1e57e481cb44c72751ef0555f75edf93 to your computer and use it in GitHub Desktop.
SublimeText plugin for inserting Doxygen style comment in code
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
@gwenzek
Copy link
Author

gwenzek commented Aug 4, 2016

I wrote and tested this plugin for C# but I tried to be language agnostic, relying on scopes rather than language specific regexes.

@gwenzek
Copy link
Author

gwenzek commented Aug 4, 2016

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