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
|
import os
class TabComp():
"Implements a state machine for tab completions on a Gtk.TextBuffer"
def __init__(self, buffer, compfn):
self.__buffer = buffer
self.__compfn = compfn
self._tabcomp_mark = None
self._tabcomp_index = 0
def reset(self):
"""Invalidates any cached completion results. Should be called
when the user presses any key other than the key configured
for tab completions."""
self._tabcomp_mark = None
def next(self, start):
"Completes the word starting at the given start iterator."
buffer = self.__buffer
# Distinguish two cases:
# 1. This is the first time the user pressed tab.
# If so: Determine input between word start and end.
# 2. User is switching through generated matches.
# If so: Determine input between word start and _tabcomp_mark.
if self._tabcomp_mark is None:
# Reset has been called → invalidate completion cache
self._tabcomp_matches = None
self._tabcomp_index = 0
end = buffer.get_iter_at_offset(buffer.props.cursor_position)
self._tabcomp_mark = buffer.create_mark(None, end, True)
else:
end = buffer.get_iter_at_mark(self._tabcomp_mark)
# Extract text, regenerate completion results for
# given text if cache has been invalidated above.
text = buffer.get_text(start, end, True)
if self._tabcomp_matches is None:
self._tabcomp_matches = self.__compfn(text)
self._tabcomp_matches.append("") # original text
c = self._tabcomp_matches[self._tabcomp_index]
# Insert the matched completion text and delete
# text potentially remaining from older completion.
buffer.insert(end, c)
cursor = buffer.get_iter_at_offset(buffer.props.cursor_position)
buffer.delete(end, cursor)
# Advance current index in matches and wrap-around.
self._tabcomp_index = (self._tabcomp_index + 1) % len(self._tabcomp_matches)
class FileName():
"Provides file name completions relative to a given directory."
def __init__(self, cwd):
self.__cwd = cwd
def get_matches(self, input):
# Preferably, we would expand ~ to $HOME on tab since the
# application may not expand ~ itself. However, the current
# completion setup just allows appending characters to the end.
input = os.path.expanduser(input)
if input.find("/") != -1:
base = os.path.dirname(input)
prefix = os.path.basename(input)
if not os.path.isabs(input):
base = os.path.join(self.__cwd, base)
else:
base = self.__cwd
prefix = input
return self.__get_matches(base, prefix)
def __get_matches(self, base, prefix):
if not os.path.isdir(base):
return []
matches = []
with os.scandir(base) as it:
for entry in it:
name = entry.name
if prefix != "" and not name.startswith(prefix):
continue
if entry.is_dir():
name += "/"
# Strip prefix from name
name = name[len(prefix):]
matches.append(name)
# Ensure that shortest matches are suggested first
matches.sort(key=len)
return matches
|