1
# Copyright 2007 Owen Taylor
3
# This file is part of Reinteract and distributed under the terms
4
# of the BSD license. See the file COPYING in the Reinteract
5
# distribution for full details.
7
########################################################################
12
from popup import Popup
13
from doc_popup import DocPopup
14
from data_format import is_data_object
15
from shell_buffer import ADJUST_NONE
17
# Space between the line of text where the cursor is and the popup
24
# If the user is just typing, the number of characters we require before
25
# we start suggesting completions
26
SPONTANEOUS_MIN_LENGTH = 3
28
class CompletionPopup(Popup):
30
"""Class implementing a completion popup for ShellView
32
This class encapsulates the user interface logic for completion
33
popups. The actual code to determine possible completions lives in
34
tokenized_statement.py.
38
def __init__(self, view):
40
self.set_size_request(WIDTH, HEIGHT)
44
sw = gtk.ScrolledWindow()
47
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
49
self.__tree_model = gtk.ListStore(str, str, object)
50
self.__tree = gtk.TreeView(self.__tree_model)
51
self.__tree.set_headers_visible(False)
53
self.__tree.get_selection().connect('changed', self.__on_selection_changed)
54
self.__tree.connect('row-activated', self.__on_row_activated)
56
cell = gtk.CellRendererText()
57
column = gtk.TreeViewColumn(None, cell, text=0)
58
self.__tree.append_column(column)
63
self.set_default_size(WIDTH, HEIGHT)
65
# A small amount of background shows between the scrollbar and the list;
66
# which looks ugly if it is the only gray thing in the window, so change
67
# the window background to white
68
self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(65535, 65535, 65535))
70
self.__doc_popup= DocPopup(fixed_width=True, fixed_height=True, max_height=HEIGHT, can_focus=False)
72
self._in_change = False
73
self.spontaneous = False
76
def __update_completions(self, spontaneous=False):
77
buf = self.__view.get_buffer()
79
self.__in_change = True
80
self.__tree_model.clear()
81
line, offset = buf.iter_to_pos(buf.get_iter_at_mark(buf.get_insert()), adjust=ADJUST_NONE)
86
min_length = SPONTANEOUS_MIN_LENGTH
89
completions = buf.worksheet.find_completions(line, offset, min_length)
90
for display, completion, obj in completions:
91
self.__tree_model.append([display, completion, obj])
93
if len(completions) > 0:
94
self.__tree.set_cursor(0)
95
self.__in_change = False
96
self.__update_doc_popup()
98
def __update_position(self):
99
buf = self.__view.get_buffer()
101
self.position_at_location(self.__view,
102
buf.get_iter_at_mark(buf.get_insert()))
104
def __update_doc_popup(self):
106
self.__doc_popup.popdown()
109
model, iter = self.__tree.get_selection().get_selected()
111
self.__doc_popup.popdown()
114
obj = model.get_value(iter, 2)
116
# Long term it would be nice to preview the value of the
117
# object, but it's distracting to show the class docs on int
118
# for every integer constant, etc, which is what the DocPopup
120
if (obj == None or is_data_object(obj)):
121
self.__doc_popup.popdown()
124
self.__doc_popup.set_target(obj)
125
self.__doc_popup.popup()
127
def __insert_completion(self, iter):
128
completion = self.__tree_model.get_value(iter, 1)
129
obj = self.__tree_model.get_value(iter, 2)
131
buf = self.__view.get_buffer()
132
default_editable = self.__view.get_editable()
134
buf.insert_interactive_at_cursor(completion, default_editable)
135
if inspect.isclass(obj) or inspect.isroutine(obj):
136
# Show the doc popup to give the user information about what arguments
137
# are posssible/required
138
self.__view.show_doc_popup()
140
# Insert a () and put the cursor in the middle
141
buf.insert_interactive_at_cursor('(', default_editable)
142
insert = buf.get_iter_at_mark(buf.get_insert())
143
mark_between_parens = buf.create_mark(None, insert, left_gravity=True)
144
buf.insert_interactive_at_cursor(')', default_editable)
145
iter = buf.get_iter_at_mark(mark_between_parens)
146
self.__view.highlight_arg_region(iter, iter)
147
buf.place_cursor(iter)
148
buf.delete_mark(mark_between_parens)
150
def __insert_selected(self):
151
model, iter = self.__tree.get_selection().get_selected()
152
self.__insert_completion(iter)
154
def __on_selection_changed(self, selection):
155
if not self.__in_change:
156
self.__update_doc_popup()
158
def __on_row_activated(self, view, path, column):
159
self.__insert_completion(self.__tree_model.get_iter(path))
162
def popup(self, spontaneous=False):
163
"""Pop up the completion popup.
165
If there are no possibilities completion at the insert cursor
166
location, the popup is not popped up. If there is exactly one
167
possibility and the spontaneous parameter is not provided , then
168
completion is done immediately to that one possibility.
170
@param spontaneous set to True if we're popping this up as a result
171
of editing, rather than because of an explicit key shortcut.
175
self.__update_completions(spontaneous=spontaneous)
176
num_completions = len(self.__tree_model)
177
if num_completions == 0:
179
elif num_completions == 1 and not spontaneous:
180
self.__insert_selected()
183
self.__update_position()
185
self.spontaneous = spontaneous
193
self.__doc_popup.position_next_to_window(self)
194
self.__update_doc_popup()
199
"""Update the completion popup after the cursor is moved, or text is inserted.
201
If there are no completion possibilities at the cursor when this is called,
202
the popup is popped down.
209
self.__update_completions(spontaneous=self.spontaneous)
210
if len(self.__tree_model) == 0:
214
self.__update_position()
217
"""Hide the completion if it is currently showing"""
225
if self.__doc_popup.showing:
226
self.__doc_popup.popdown()
230
def on_key_press_event(self, event):
231
"""Do key press handling while the popup is active.
233
Returns True if the key press is handled, False otherwise.
237
if event.keyval == gtk.keysyms.Escape:
240
elif event.keyval in (gtk.keysyms.KP_Enter, gtk.keysyms.Return):
241
self.__insert_selected()
244
# These keys are forwarded to the popup to move the selected row
245
elif event.keyval in (gtk.keysyms.Up, gtk.keysyms.KP_Up,
246
gtk.keysyms.Down, gtk.keysyms.KP_Down,
247
gtk.keysyms.Page_Up, gtk.keysyms.KP_Page_Up,
248
gtk.keysyms.Page_Down, gtk.keysyms.KP_Page_Down):