diff --git a/UltiSnips/rst.snippets b/UltiSnips/rst.snippets index 7174510..88e0caa 100644 --- a/UltiSnips/rst.snippets +++ b/UltiSnips/rst.snippets @@ -1,61 +1,314 @@ - +# -*- coding: utf-8 -*- ########################################################################### # General Stuff # ########################################################################### +global !p +import vim +from os import path as ospath +from string import Template +import re +from collections import Counter + +#http://docutils.sourceforge.net/docs/ref/rst/roles.html +TEXT_ROLES = ['emphasis','literal','code','math', + 'pep-reference','rfc-reference', + 'strong','subscript','superscript', + 'title-reference','raw'] +TEXT_ROLES_REGEX = r'\.\.\srole::?\s(w+)' + +#http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions +SPECIFIC_ADMONITIONS = ["attention", "caution", "danger", + "error", "hint", "important", "note", + "tip", "warning"] +#http://docutils.sourceforge.net/docs/ref/rst/directives.html +DIRECTIVES = ['topic','sidebar','math','epigraph', + 'parsed-literal','code','highlights', + 'pull-quote','compound','container', + 'list-table','class','sectnum', + 'role','default-role','unicode', + 'raw'] + +NONE_CONTENT_DIRECTIVES = ['rubric', 'contents', 'header', + 'footer', 'date', 'include', 'title'] + +INCLUDABLE_DIRECTIVES = ['image', 'figure', 'include'] +# CJK chars +# http://stackoverflow.com/questions/2718196/find-all-chinese-text-in-a-string-using-python-and-regex +CJK_RE = re.compile(u'[⺀-⺙⺛-⻳⼀-⿕々〇〡-〩〸-〺〻㐀-䶵一-鿃豈-鶴侮-頻並-龎]', re.UNICODE) + + +def has_cjk(char): + """ + Detect char contains CJK character + + :param char: characters needs to be detect + """ + try: + CJK_RE.finditer(char).next() + except StopIteration: + return False + else: + return True + +def real_filename(filename): + """peal extension name off if possible + # i.e. "foo.bar.png will return "foo.bar" + """ + return ospath.splitext(filename)[0] + +def check_file_exist(rst_path, relative_path): + """ + For RST file, it can just include files as relative path. + + :param rst_path: absolute path to rst file + :param relative_path: path related to rst file + :return: relative file's absolute path if file exist + """ + abs_path = ospath.join(ospath.dirname(rst_path), relative_path) + if ospath.isfile(abs_path): + return abs_path + + +def rst_char_len(char): + """ + return len of string which fit in rst + For instance:chinese "我" decode as only one character, + However, the rst interpreter needs 2 "=" instead of 1. + + :param: char needs to be count + """ + return len(re.findall(r'[^\u4e00-\u9fff\s]', char))+len(char) + +def make_items(times, leading='+'): + """ + make lines with leading char multitimes + + :param: times, how many times you need + :param: leading, leading character + """ + times = int(times) + if leading == 1: + msg = "" + for x in xrange(1, times+1): + msg += "%s. Item\n" % x + return msg + else: + return ("%s Item\n" % leading) * times + + +def look_up_directives(regex, fpath): + """ + find all directive args in given file + :param: regex, the regex that needs to match + :param: path, to path to rst file + + :return: list, empty list if nothing match + """ + try: + with open(fpath) as source: + match = re.findall(regex, source.read()) + except IOError: + match = [] + return match + + +def get_popular_code_type(): + """ + find most popular code type in the given rst + + :param path: file to detect + + :return: string, most popular code type in file + """ + buf = "".join(vim.current.buffer) + types = re.findall(r'[:|\.\.\s]code::?\s(\w+)', buf) + try: + popular_type = Counter(types).most_common()[0][0] + except IndexError: + popular_type = "lua" # Don't break default + return popular_type + + +def complete(t, opts): + """ + get options that start with t + + :param t: query string + :param opts: list that needs to be completed + + :return: a string that start with t + """ + msg = "({0})" + if t: + opts = [ m[len(t):] for m in opts if m.startswith(t) ] + if len(opts) == 1: + return opts[0] + + if not len(opts): + msg = "{0}" + return msg.format("|".join(opts)) + +endglobal + snippet part "Part" b -`!p snip.rv = len(t[1])*'#'` +`!p snip.rv = rst_char_len(t[1])*'#'` ${1:Part name} -`!p snip.rv = len(t[1])*'#'` +`!p snip.rv = rst_char_len(t[1])*'#'` $0 endsnippet snippet sec "Section" b ${1:Section name} -`!p snip.rv = len(t[1])*'='` +`!p snip.rv = rst_char_len(t[1])*'='` $0 endsnippet snippet ssec "Subsection" b ${1:Section name} -`!p snip.rv = len(t[1])*'-'` +`!p snip.rv = rst_char_len(t[1])*'-'` $0 endsnippet snippet sssec "Subsubsection" b ${1:Section name} -`!p snip.rv = len(t[1])*'^'` +`!p snip.rv = rst_char_len(t[1])*'^'` $0 endsnippet snippet chap "Chapter" b -`!p snip.rv = len(t[1])*'*'` +`!p snip.rv = rst_char_len(t[1])*'*'` ${1:Chapter name} -`!p snip.rv = len(t[1])*'*'` +`!p snip.rv = rst_char_len(t[1])*'*'` $0 endsnippet snippet para "Paragraph" b ${1:Paragraph name} -`!p snip.rv = len(t[1])*'"'` +`!p snip.rv = rst_char_len(t[1])*'"'` $0 endsnippet +snippet em "Emphasize string" i +`!p +# dirty but works with CJK charactor detection +if has_cjk(vim.current.line): + snip.rv ="\ "`*${1:${VISUAL:Em}}*`!p +if has_cjk(vim.current.line): + snip.rv ="\ " +else: + snip.rv = " " +`$0 +endsnippet + +snippet st "Strong string" i +`!p +if has_cjk(vim.current.line): + snip.rv ="\ "`**${1:${VISUAL:Strong}}**`!p +if has_cjk(vim.current.line): + snip.rv ="\ " +else: + snip.rv = " " +`$0 +endsnippet + +snippet "li(st)? (?P\d+)" "List" br +$0 +`!p +# usage: li 4 +# which will extand into a unordered list contains 4 items +snip.rv = make_items(match.groupdict()['num']) +` +endsnippet + +snippet "ol(st)? (?P\d+)" "Order List" br +$0 +`!p +# usage: ol 4 +# which will extand into a ordered list contains 4 items +snip.rv = make_items(match.groupdict()['num'], 1) +` +endsnippet ########################################################################### # More Specialized Stuff. # ########################################################################### snippet cb "Code Block" b -.. code-block:: ${1:lua} +.. code-block:: ${1:`!p snip.rv = get_popular_code_type()`} ${2:code} $0 endsnippet +# match snippets : +# img, inc, fig +snippet id "Includable Directives" b +`!p +real_name=real_filename(ospath.basename(t[2])) +di=t[1][:2] + +link="" +content="" + +if di == 'im': + link = "|{0}|".format(real_name) + +if di == 'fi': + content=""" + :alt: {0} + {0}""".format(real_name) +` +..`!p snip.rv = " %s" % link if link else ""` $1`!p snip.rv=complete(t[1], INCLUDABLE_DIRECTIVES)`:: ${2:file}`!p if content: + snip.rv +=" "+content` +`!p +# Tip of whether file is exist in comment type +if not check_file_exist(path, t[2]): + snip.rv='.. FILE {0} does not exist'.format(t[2]) +else: + snip.rv="" +`$0 +endsnippet + +snippet di "Directives" b +.. $1`!p snip.rv=complete(t[1], DIRECTIVES)`:: $2 + + ${3:Content} +$0 +endsnippet + +snippet nd "None Content Directives" b +.. $1`!p snip.rv=complete(t[1], NONE_CONTENT_DIRECTIVES)`:: $2 +$0 +endsnippet + +snippet sa "Specific Admonitions" b +.. $1`!p snip.rv =complete(t[1], SPECIFIC_ADMONITIONS)`:: + + ${2:Content} + +$0 +endsnippet + +#it will be trigger at start of line or after a word +snippet ro "Text Roles" w +\ :$1`!p snip.rv=complete(t[1], + TEXT_ROLES+look_up_directives(TEXT_ROLES_REGEX, + path))`:\`$2\`\ +endsnippet + +############ +# Sphinx # +############ + +snippet sid "SideBar" b +.. sidebar:: ${1:SideBar Title} + + ${2:SideBar Content} +endsnippet # vim:ft=snippets: