forked from asciidoctor/sublimetext-asciidoc
-
Notifications
You must be signed in to change notification settings - Fork 1
/
completions.py
128 lines (92 loc) · 4.5 KB
/
completions.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
from itertools import chain, repeat
import sublime
from sublime import Region
from sublime_plugin import EventListener
# String that must be found in the syntax setting of the current view
# to active this plugin.
SYNTAX = 'Asciidoc'
# Selector that specifies the scope in which completions may be activated.
ADOC_SCOPE = 'text.asciidoc - source'
# Selector that specifies the scope in which attribute completions may
# be activated.
ATTR_SCOPE = 'variable.other'
# Scope of the attribute name in the attribute entry.
ATTR_ENTRY_SCOPE = 'support.variable.attribute.asciidoc'
# Selector that specifies the scope in which cross reference completions may
# be activated.
XREF_SCOPE = 'meta.xref.asciidoc'
# Scope of the anchor ID.
ANCHOR_SCOPE = 'markup.underline.blockid.id.asciidoc'
# Scope of the section titles.
SEC_TITLE_SCOPE = 'entity.name.section.asciidoc'
# Name of the plugin's settings file.
SETTINGS_NAME = 'Asciidoctor.sublime-settings'
# Global list of built-in attributes to display in the completion list.
builtin_attrs = []
def plugin_loaded():
""" Called by SublimeText when the plugin is loaded. """
global builtin_attrs
settings = sublime.load_settings(SETTINGS_NAME)
builtin_attrs = [(item, 'built-in')
for item in sorted(settings.get('built_in_attributes'))]
class AsciidocAttributeCompletions(EventListener):
def on_query_completions(self, view, prefix, locations):
""" Called by SublimeText when auto-complete pop-up box appears. """
if SYNTAX not in view.settings().get('syntax'):
return None
if not all(self.should_trigger(view, loc) for loc in locations):
return None
local_attrs = (
(attr, 'local') for attr, lno in self.declared_attrs(view)
if attr not in builtin_attrs and min(cursors_line_num(view)) > lno)
return (filter_completions(prefix, local_attrs, builtin_attrs),
sublime.INHIBIT_WORD_COMPLETIONS)
def should_trigger(self, view, point):
""" Return True if completions should be triggered at the given point. """
return (view.match_selector(point, ATTR_SCOPE) or
view.match_selector(point, ADOC_SCOPE) and lsubstr(view, point) in [':', '{'])
def declared_attrs(self, view):
""" Get attributes declared in the document.
Yields:
Tuple of attribute name and line number where it's first declared.
"""
return sorted(
{view.substr(region): view.rowcol(region.end())[0]
for region in reversed(view.find_by_selector(ATTR_ENTRY_SCOPE))}
.items())
class AsciidocCrossReferenceCompletions(EventListener):
def on_query_completions(self, view, prefix, locations):
""" Called by SublimeText when auto-complete pop-up box appears. """
if SYNTAX not in view.settings().get('syntax'):
return None
if not all(self.should_trigger(view, loc) for loc in locations):
return None
anchors = zip(find_by_scope(view, ANCHOR_SCOPE), repeat('anchor'))
titles = zip(find_by_scope(view, SEC_TITLE_SCOPE), repeat('title'))
return sorted(filter_completions(prefix, anchors, titles),
key=lambda t: t[0].lower())
def should_trigger(self, view, point):
""" Return True if completions should be triggered at the given point. """
return (view.match_selector(point, XREF_SCOPE) or
view.match_selector(point, ADOC_SCOPE) and lsubstr(view, point, 2) == '<<')
def filter_completions(prefix, *data):
""" Filter completions that starts with the given prefix and format them
for the completions list.
Arguments:
prefix (str):
*data: An iterable with tuples of a trigger (content) and a hint (text
showed on the right side of the trigger).
"""
return (("%s\t%s" % (content, hint), content)
for content, hint in chain(*data) if content.startswith(prefix))
def cursors_line_num(view):
""" Return list of 0-based line numbers of the cursor(s). """
return [view.rowcol(region.b)[0] for region in view.sel()]
def find_by_scope(view, selector):
""" Find all substrings in the file matching the given scope selector. """
return map(view.substr, view.find_by_selector(selector))
def lsubstr(view, point, length=1):
""" Return the character(s) to the left of the point on the same line. """
col = view.rowcol(point)[1]
region = Region(point - min(length, col), point)
return view.substr(region)