1
# Copyright 2007-2009 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 shell_buffer import ShellBuffer, ADJUST_NONE, ADJUST_BEFORE, ADJUST_AFTER
13
from chunks import StatementChunk, CommentChunk, BlankChunk
14
from completion_popup import CompletionPopup
15
from doc_popup import DocPopup
16
from global_settings import global_settings
17
from notebook import NotebookFile
18
import sanitize_textview_ipc
20
LEFT_MARGIN_WIDTH = 10
22
ALL_WHITESPACE_RE = re.compile("^\s*$")
24
class ShellView(gtk.TextView):
26
'backspace' : 'override',
27
'expose-event': 'override',
28
'focus-out-event': 'override',
29
'button-press-event': 'override',
30
'button-release-event': 'override',
31
'motion-notify-event': 'override',
32
'key-press-event': 'override',
33
'leave-notify-event': 'override',
34
'motion-notify-event': 'override',
35
'realize': 'override',
36
'unrealize': 'override',
37
'size-allocate': 'override'
40
def __init__(self, buf):
41
self.edit_only = buf.worksheet.edit_only
43
if not self.edit_only:
44
buf.worksheet.connect('chunk-inserted', self.on_chunk_inserted)
45
buf.worksheet.connect('chunk-changed', self.on_chunk_changed)
46
buf.worksheet.connect('chunk-status-changed', self.on_chunk_status_changed)
47
buf.worksheet.connect('notify::state', self.on_notify_state)
49
# Track changes to update completion
50
buf.connect_after('insert-text', self.on_after_insert_text)
51
buf.connect_after('delete-range', self.on_after_delete_range)
52
buf.connect_after('end-user-action', self.on_after_end_user_action)
54
self.__inserted_in_user_action = False
55
self.__deleted_in_user_action = False
57
buf.connect('add-custom-result', self.on_add_custom_result)
58
buf.connect('pair-location-changed', self.on_pair_location_changed)
60
gtk.TextView.__init__(self, buf)
61
if not self.edit_only:
62
self.set_border_window_size(gtk.TEXT_WINDOW_LEFT, LEFT_MARGIN_WIDTH)
63
self.set_left_margin(2)
65
# Attach a "behavior object" to the view which, by ugly hacks, makes it
66
# do simply and reasonable things for cut-and-paste and DND
67
sanitize_textview_ipc.sanitize_view(self)
69
self.add_events(gtk.gdk.LEAVE_NOTIFY_MASK)
71
self.__completion_popup = CompletionPopup(self)
72
self.__doc_popup = DocPopup()
73
self.__mouse_over_object = None
74
self.__mouse_over_timeout = None
76
self.__mouse_over_start = buf.create_mark(None, buf.get_start_iter(), True)
78
self.__arg_highlight_start = None
79
self.__arg_highlight_end = None
80
buf.connect('mark-set', self.on_mark_set)
82
def __get_worksheet_line_yrange(self, line):
83
buffer_line = self.get_buffer().pos_to_iter(line)
84
return self.get_line_yrange(buffer_line)
86
def __get_worksheet_line_at_y(self, y, adjust):
87
buf = self.get_buffer()
88
(buffer_line, _) = self.get_line_at_y(y)
89
return buf.iter_to_pos(buffer_line, adjust)[0]
91
def paint_chunk(self, cr, area, chunk, fill_color, outline_color):
92
buf = self.get_buffer()
94
(y, _) = self.__get_worksheet_line_yrange(chunk.start)
95
(end_y, end_height) = self.__get_worksheet_line_yrange(chunk.end - 1)
96
height = end_y + end_height - y
98
(_, window_y) = self.buffer_to_window_coords(gtk.TEXT_WINDOW_LEFT, 0, y)
99
cr.rectangle(area.x, window_y, area.width, height)
100
cr.set_source_rgb(*fill_color)
103
cr.rectangle(0.5, window_y + 0.5, LEFT_MARGIN_WIDTH - 1, height - 1)
104
cr.set_source_rgb(*outline_color)
108
def do_realize(self):
109
gtk.TextView.do_realize(self)
111
if not self.edit_only:
112
self.get_window(gtk.TEXT_WINDOW_LEFT).set_background(self.style.white)
114
# While the the worksheet is executing, we want to display a watch cursor
115
# Trying to override the cursor setting of GtkTextView is really hard because
116
# of things like hiding the cursor when typing, so we take the simple approach
117
# of using an input-only "cover window" that we set the cursor on and
118
# show on top of the GtkTextView's normal window.
120
self.__watch_window = gtk.gdk.Window(self.window,
121
self.allocation.width, self.allocation.height,
122
gtk.gdk.WINDOW_CHILD,
123
(gtk.gdk.SCROLL_MASK |
124
gtk.gdk.BUTTON_PRESS_MASK |
125
gtk.gdk.BUTTON_RELEASE_MASK |
126
gtk.gdk.POINTER_MOTION_MASK |
127
gtk.gdk.POINTER_MOTION_HINT_MASK),
130
self.__watch_window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
131
self.__watch_window.set_user_data(self)
133
if self.get_buffer().worksheet.state == NotebookFile.EXECUTING:
134
self.__watch_window.show()
135
self.__watch_window.raise_()
137
def do_unrealize(self):
138
self.__watch_window.set_user_data(None)
139
self.__watch_window.destroy()
140
self.__watch_window = None
141
gtk.TextView.do_unrealize(self)
143
def do_size_allocate(self, allocation):
144
gtk.TextView.do_size_allocate(self, allocation)
145
if (self.flags() & gtk.REALIZED) != 0:
146
self.__watch_window.resize(allocation.width, allocation.height)
148
def __expose_window_left(self, event):
149
(_, start_y) = self.window_to_buffer_coords(gtk.TEXT_WINDOW_LEFT, 0, event.area.y)
150
start_line = self.__get_worksheet_line_at_y(start_y, adjust=ADJUST_AFTER)
152
(_, end_y) = self.window_to_buffer_coords(gtk.TEXT_WINDOW_LEFT, 0, event.area.y + event.area.height - 1)
153
end_line = self.__get_worksheet_line_at_y(end_y, adjust=ADJUST_BEFORE)
155
buf = self.get_buffer()
157
cr = event.window.cairo_create()
159
for chunk in buf.worksheet.iterate_chunks(start_line, end_line + 1):
160
if isinstance(chunk, StatementChunk):
162
self.paint_chunk(cr, event.area, chunk, (0, 1, 0), (0, 0.5, 0))
163
elif chunk.error_message != None:
164
self.paint_chunk(cr, event.area, chunk, (1, 0, 0), (0.5, 0, 0))
165
elif chunk.needs_compile:
166
self.paint_chunk(cr, event.area, chunk, (1, 1, 0), (0.5, 0.5, 0))
167
elif chunk.needs_execute:
168
self.paint_chunk(cr, event.area, chunk, (1, 0, 1), (0.5, 0.5, 0))
170
self.paint_chunk(cr, event.area, chunk, (0, 0, 1), (0, 0, 0.5))
172
def __draw_rect_outline(self, event, rect):
173
if (rect.y + rect.height <= event.area.y or rect.y >= event.area.y + event.area.height):
176
cr = event.window.cairo_create()
177
cr.set_line_width(1.)
178
cr.rectangle(rect.x + 0.5, rect.y + 0.5, rect.width - 1, rect.height - 1)
179
cr.set_source_rgb(0.6, 0.6, 0.6)
182
def __expose_arg_highlight(self, event):
183
buf = self.get_buffer()
185
# We want a rectangle enclosing the range between arg_highlight_start and
186
# arg_highlight_end; the method here isn't correct in the presence of
187
# RTL text, but the necessary Pango functionality isn't exposed to
188
# a GtkTextView user. RTL code is rare. We also don't handle multiple-line
189
# highlight regions right. (Return ends the highlight, so you'd need to paste)
190
rect = self.get_iter_location(buf.get_iter_at_mark (self.__arg_highlight_start))
191
rect.x, rect.y = self.buffer_to_window_coords(gtk.TEXT_WINDOW_TEXT,
194
end_rect = self.get_iter_location(buf.get_iter_at_mark (self.__arg_highlight_end))
195
end_rect.x, end_rect.y = self.buffer_to_window_coords(gtk.TEXT_WINDOW_TEXT,
196
end_rect.x, end_rect.y)
199
rect = rect.union(end_rect)
201
self.__draw_rect_outline(event, rect)
203
def __expose_pair_location(self, event):
204
pair_location = self.get_buffer().get_pair_location()
205
if pair_location == None:
208
rect = self.get_iter_location(pair_location)
210
rect.x, rect.y = self.buffer_to_window_coords(gtk.TEXT_WINDOW_TEXT, rect.x, rect.y)
212
self.__draw_rect_outline(event, rect)
214
def do_expose_event(self, event):
215
if not self.edit_only and event.window == self.get_window(gtk.TEXT_WINDOW_LEFT):
216
self.__expose_window_left(event)
219
gtk.TextView.do_expose_event(self, event)
221
if event.window == self.get_window(gtk.TEXT_WINDOW_TEXT):
222
if self.__arg_highlight_start:
223
self.__expose_arg_highlight(event)
225
self.__expose_pair_location(event)
229
def __get_line_text(self, iter):
231
start.set_line_index(0)
233
end.forward_to_line_end()
235
return start.get_slice(end)
237
# This is likely overengineered, since we're going to try as hard as possible not to
238
# have tabs in our worksheets. We don't do the funky handling of \f.
239
def __count_indent(self, text):
245
indent += 8 - (indent % 8)
251
def __find_outdent(self, iter):
252
buf = self.get_buffer()
253
line, _ = buf.iter_to_pos(iter)
255
current_indent = self.__count_indent(buf.worksheet.get_line(line))
259
line_text = buf.worksheet.get_line(line)
260
# Empty lines don't establish indentation
261
if ALL_WHITESPACE_RE.match(line_text):
264
indent = self.__count_indent(line_text)
265
if indent < current_indent:
266
return re.match(r"^[\t ]*", line_text).group(0)
270
def __find_default_indent(self, iter):
271
buf = self.get_buffer()
272
line, offset = buf.iter_to_pos(iter)
276
chunk = buf.worksheet.get_chunk(line)
277
if isinstance(chunk, StatementChunk):
278
return chunk.tokenized.get_next_line_indent(line - chunk.start)
279
elif isinstance(chunk, CommentChunk):
280
return " " * self.__count_indent(buf.worksheet.get_line(line))
284
def __reindent_line(self, iter, indent_text):
285
buf = self.get_buffer()
287
line_text = self.__get_line_text(iter)
288
prefix = re.match(r"^[\t ]*", line_text).group(0)
290
diff = self.__count_indent(indent_text) - self.__count_indent(prefix)
295
for a, b in zip(prefix, indent_text):
301
start.set_line_offset(common_len)
303
end.set_line_offset(len(prefix))
305
# Nitpicky-detail. If the selection starts at the start of the line, and we are
306
# inserting white-space there, then the whitespace should be *inside* the selection
308
if common_len == 0 and buf.get_has_selection():
309
mark = buf.get_insert()
310
if buf.get_iter_at_mark(mark).compare(start) == 0:
313
mark = buf.get_selection_bound()
314
if buf.get_iter_at_mark(mark).compare(start) == 0:
317
buf.delete(start, end)
318
buf.insert(end, indent_text[common_len:])
320
if mark_to_start != None:
321
end.set_line_offset(0)
322
buf.move_mark(mark_to_start, end)
326
def __reindent_selection(self, outdent):
327
buf = self.get_buffer()
329
bounds = buf.get_selection_bounds()
331
insert_mark = buf.get_insert()
332
bounds = buf.get_iter_at_mark(insert_mark), buf.get_iter_at_mark(insert_mark)
335
line, _ = buf.iter_to_pos(start, adjust=ADJUST_AFTER)
336
end_line, end_offset = buf.iter_to_pos(end, adjust=ADJUST_BEFORE)
337
if end_offset == 0 and end_line > line:
340
iter = buf.pos_to_iter(line)
343
indent_text = self.__find_outdent(iter)
345
indent_text = self.__find_default_indent(iter)
347
diff = self.__reindent_line(iter, indent_text)
353
iter = buf.pos_to_iter(line)
354
current_indent = self.__count_indent(self.__get_line_text(iter))
355
self.__reindent_line(iter, max(0, " " * (current_indent + diff)))
357
def __hide_completion(self):
358
if self.__completion_popup.showing:
359
self.__completion_popup.popdown()
361
def do_focus_out_event(self, event):
362
self.__hide_completion()
363
self.__doc_popup.popdown()
364
return gtk.TextView.do_focus_out_event(self, event)
366
def __rewrite_window(self, event):
367
# Mouse events on the "watch window" that covers the text view
368
# during calculation need to be forwarded to the real text window
369
# since it looks bad if keynav works, but you can't click on the
370
# window to set the cursor, select text, and so forth
372
if event.window == self.__watch_window:
373
event.window = self.get_window(gtk.TEXT_WINDOW_TEXT)
375
# Events on the left-margin window also get written so the user can
376
# click there when starting a drag selection. We need to adjust the
377
# X coordinate in that case
378
if not self.edit_only and event.window == self.get_window(gtk.TEXT_WINDOW_LEFT):
379
event.window = self.get_window(gtk.TEXT_WINDOW_TEXT)
380
if event.type == gtk.gdk._3BUTTON_PRESS:
381
# Workaround for http://bugzilla.gnome.org/show_bug.cgi?id=573664
384
event.x -= LEFT_MARGIN_WIDTH
386
def do_button_press_event(self, event):
387
self.__rewrite_window(event)
389
self.__doc_popup.popdown()
391
return gtk.TextView.do_button_press_event(self, event)
393
def do_button_release_event(self, event):
394
self.__rewrite_window(event)
396
return gtk.TextView.do_button_release_event(self, event)
398
def do_motion_notify_event(self, event):
399
self.__rewrite_window(event)
401
return gtk.TextView.do_motion_notify_event(self, event)
403
def __remove_arg_highlight(self, cursor_to_end=True):
404
buf = self.get_buffer()
406
end = buf.get_iter_at_mark (self.__arg_highlight_end)
408
buf.delete_mark(self.__arg_highlight_start)
409
self.__arg_highlight_start = None
410
buf.delete_mark(self.__arg_highlight_end)
411
self.__arg_highlight_end = None
414
# If the arg_highlight ends at closing punctuation, skip over it
417
text = buf.get_slice(end, tmp)
419
if text in (")", "]", "}"):
420
buf.place_cursor(tmp)
422
buf.place_cursor(end)
424
def do_key_press_event(self, event):
425
buf = self.get_buffer()
427
if self.__completion_popup.focused and self.__completion_popup.on_key_press_event(event):
430
if self.__doc_popup.focused:
431
self.__doc_popup.on_key_press_event(event)
434
if not self.edit_only and event.keyval in (gtk.keysyms.F2, gtk.keysyms.KP_F2):
435
self.__hide_completion()
437
if self.__doc_popup.showing:
438
self.__doc_popup.focus()
440
self.show_doc_popup(focus_popup=True)
444
if not self.__arg_highlight_start:
445
self.__doc_popup.popdown()
447
if event.keyval in (gtk.keysyms.KP_Enter, gtk.keysyms.Return):
448
self.__hide_completion()
450
if self.__arg_highlight_start:
451
self.__remove_arg_highlight()
452
self.__doc_popup.popdown()
455
increase_indent = False
456
insert = buf.get_iter_at_mark(buf.get_insert())
457
line, pos = buf.iter_to_pos(insert, adjust=ADJUST_NONE)
459
# Inserting return inside a ResultChunk would normally do nothing
460
# but we want to make it insert a line after the chunk
461
if line == None and not buf.get_has_selection():
462
line, pos = buf.iter_to_pos(insert, adjust=ADJUST_BEFORE)
463
iter = buf.pos_to_iter(line, -1)
464
buf.place_cursor(iter)
465
buf.insert_interactive(iter, "\n", True)
469
buf.begin_user_action()
471
gtk.TextView.do_key_press_event(self, event)
472
# We need the chunks to be updated when computing the line indents
473
buf.worksheet.rescan()
475
insert = buf.get_iter_at_mark(buf.get_insert())
477
self.__reindent_line(insert, self.__find_default_indent(insert))
479
# If we have two comment lines in a row, assume a block comment
481
isinstance(buf.worksheet.get_chunk(line), CommentChunk) and
482
isinstance(buf.worksheet.get_chunk(line - 1), CommentChunk)):
483
self.get_buffer().insert_interactive_at_cursor("# ", True)
485
buf.end_user_action()
488
elif event.keyval in (gtk.keysyms.Tab, gtk.keysyms.KP_Tab) and event.state & gtk.gdk.CONTROL_MASK == 0:
489
buf.begin_user_action()
490
self.__reindent_selection(outdent=False)
491
buf.end_user_action()
494
elif event.keyval == gtk.keysyms.ISO_Left_Tab and event.state & gtk.gdk.CONTROL_MASK == 0:
495
buf.begin_user_action()
496
self.__reindent_selection(outdent=True)
497
buf.end_user_action()
500
elif event.keyval == gtk.keysyms.space and event.state & gtk.gdk.CONTROL_MASK != 0:
501
if self.__completion_popup.showing:
502
if self.__completion_popup.spontaneous:
503
self.__completion_popup.popup(spontaneous=False)
505
self.__completion_popup.popdown()
507
if self.__doc_popup.showing:
508
self.__doc_popup.popdown()
509
self.__completion_popup.popup(spontaneous=False)
511
elif event.keyval in (gtk.keysyms.z, gtk.keysyms.Z) and \
512
(event.state & gtk.gdk.CONTROL_MASK) != 0 and \
513
(event.state & gtk.gdk.SHIFT_MASK) == 0:
517
# This is the gedit/gtksourceview binding to cause your hands to fall off
518
elif event.keyval in (gtk.keysyms.z, gtk.keysyms.Z) and \
519
(event.state & gtk.gdk.CONTROL_MASK) != 0 and \
520
(event.state & gtk.gdk.SHIFT_MASK) != 0:
524
# This is the familiar binding (Eclipse, etc). Firefox supports both
525
elif event.keyval in (gtk.keysyms.y, gtk.keysyms.Y) and event.state & gtk.gdk.CONTROL_MASK != 0:
530
return gtk.TextView.do_key_press_event(self, event)
532
def __show_mouse_over(self):
533
self.__mouse_over_timeout = None
535
if self.__completion_popup.showing:
538
self.__doc_popup.set_target(self.__mouse_over_object)
539
location = self.get_buffer().get_iter_at_mark(self.__mouse_over_start)
540
self.__doc_popup.position_at_location(self, location)
541
self.__doc_popup.popup()
545
def __stop_mouse_over(self):
546
if self.__mouse_over_timeout:
547
gobject.source_remove(self.__mouse_over_timeout)
548
self.__mouse_over_timeout = None
550
self.__mouse_over_object = None
552
def do_motion_notify_event(self, event):
553
# Successful mousing-over depends on knowing the types of symbols so doing the
554
# checks are pointless in edit-only mode
555
if not self.edit_only and event.window == self.get_window(gtk.TEXT_WINDOW_TEXT) and not self.__doc_popup.focused:
556
buf = self.get_buffer()
558
x, y = self.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, int(event.x), int(event.y))
559
iter, _ = self.get_iter_at_position(x, y)
560
line, offset = buf.iter_to_pos(iter, adjust=ADJUST_NONE)
562
obj, start_line, start_offset, _,_ = buf.worksheet.get_object_at_location(line, offset)
566
if not obj is self.__mouse_over_object:
567
self.__stop_mouse_over()
568
self.__doc_popup.popdown()
570
start = buf.pos_to_iter(start_line, start_offset)
571
buf.move_mark(self.__mouse_over_start, start)
573
self.__mouse_over_object = obj
575
timeout = self.get_settings().get_property('gtk-tooltip-timeout')
576
except TypeError: # GTK+ < 2.12
578
self.__mouse_over_timeout = gobject.timeout_add(timeout, self.__show_mouse_over)
580
return gtk.TextView.do_motion_notify_event(self, event)
582
def do_leave_notify_event(self, event):
583
self.__stop_mouse_over()
584
if not self.__doc_popup.focused:
585
self.__doc_popup.popdown()
588
def do_backspace(self):
589
buf = self.get_buffer()
591
insert = buf.get_iter_at_mark(buf.get_insert())
592
line, offset = buf.iter_to_pos(insert)
594
current_chunk = buf.worksheet.get_chunk(line)
595
if isinstance(current_chunk, StatementChunk) or isinstance(current_chunk, BlankChunk):
596
line_text = buf.worksheet.get_line(line)[0:offset]
598
if re.match(r"^[\t ]+$", line_text):
599
self.__reindent_line(insert, self.__find_outdent(insert))
602
return gtk.TextView.do_backspace(self)
604
def __invalidate_status(self, chunk):
605
buf = self.get_buffer()
607
(start_y, start_height) = self.__get_worksheet_line_yrange(chunk.start)
608
(end_y, end_height) = self.__get_worksheet_line_yrange(chunk.end - 1)
610
(_, window_y) = self.buffer_to_window_coords(gtk.TEXT_WINDOW_LEFT, 0, start_y)
613
left_margin_window = self.get_window(gtk.TEXT_WINDOW_LEFT)
614
left_margin_window.invalidate_rect((0, window_y, LEFT_MARGIN_WIDTH, end_y + end_height - start_y),
617
def on_chunk_inserted(self, worksheet, chunk):
618
self.__invalidate_status(chunk)
620
def on_chunk_changed(self, worksheet, chunk, changed_lines):
621
self.__invalidate_status(chunk)
623
def on_chunk_status_changed(self, worksheet, chunk):
624
self.__invalidate_status(chunk)
626
def on_notify_state(self, worksheet, param_spec):
627
if (self.flags() & gtk.REALIZED) != 0:
628
if worksheet.state == NotebookFile.EXECUTING:
629
self.__watch_window.show()
630
self.__watch_window.raise_()
632
self.__watch_window.hide()
634
def on_after_insert_text(self, buf, location, text, len):
635
if buf.worksheet.in_user_action() and not buf.in_modification():
636
self.__inserted_in_user_action = True
638
def on_after_delete_range(self, buf, start, end):
639
if buf.worksheet.in_user_action() and not buf.in_modification():
640
self.__deleted_in_user_action = True
642
def on_after_end_user_action(self, buf):
643
if not buf.worksheet.in_user_action():
644
if self.__completion_popup.showing:
645
if self.__inserted_in_user_action or self.__deleted_in_user_action:
646
self.__completion_popup.update()
648
if self.__inserted_in_user_action and global_settings.autocomplete:
649
self.__completion_popup.popup(spontaneous=True)
650
self.__inserted_in_user_action = False
651
self.__deleted_in_user_action = False
653
def on_add_custom_result(self, buf, result, anchor):
654
widget = result.create_widget()
656
self.add_child_at_anchor(widget, anchor)
658
def on_mark_set(self, buffer, iter, mark):
659
if self.__arg_highlight_start:
660
# See if the user moved the cursor out of the highlight regionb
661
buf = self.get_buffer()
662
if mark != buf.get_insert():
665
if (iter.compare(buf.get_iter_at_mark(self.__arg_highlight_start)) < 0 or
666
iter.compare(buf.get_iter_at_mark(self.__arg_highlight_end)) > 0):
667
self.__remove_arg_highlight(cursor_to_end=False)
669
def __invalidate_char_position(self, iter):
670
y, height = self.get_line_yrange(iter)
672
text_window = self.get_window(gtk.TEXT_WINDOW_TEXT)
673
width, _ = text_window.get_size()
674
text_window.invalidate_rect((0, y, width, height), False)
676
def on_pair_location_changed(self, buf, old_position, new_position):
678
self.__invalidate_char_position(old_position)
680
self.__invalidate_char_position(new_position)
683
buf = self.get_buffer()
685
buf.worksheet.calculate()
687
# This is a hack to work around the fact that scroll_mark_onscreen()
688
# doesn't wait for a size-allocate cycle, so doesn't properly handle
689
# embedded request widgets
691
self.size_allocate((self.allocation.x, self.allocation.y,
692
self.allocation.width, self.allocation.height))
694
self.scroll_mark_onscreen(buf.get_insert())
696
def copy_as_doctests(self):
697
buf = self.get_buffer()
699
bounds = buf.get_selection_bounds()
701
start, end = buf.get_iter_at_mark(buf.get_insert())
705
start_line, start_offset = buf.iter_to_pos(start, adjust=ADJUST_BEFORE)
706
end_line, end_offset = buf.iter_to_pos(end, adjust=ADJUST_BEFORE)
708
doctests = buf.worksheet.get_doctests(start_line, end_line + 1)
709
self.get_clipboard(gtk.gdk.SELECTION_CLIPBOARD).set_text(doctests)
711
def show_doc_popup(self, focus_popup=False):
712
"""Pop up the doc popup for the symbol at the insertion point, if any
714
@param focus_popup: if True, the popup will be given keyboard focus
718
buf = self.get_buffer()
720
insert = buf.get_iter_at_mark(buf.get_insert())
721
line, offset = buf.iter_to_pos(insert, adjust=ADJUST_NONE)
723
obj, start_line, start_offset, _, _ = buf.worksheet.get_object_at_location(line, offset, include_adjacent=True)
728
start = buf.pos_to_iter(start_line, start_offset)
729
self.__stop_mouse_over()
730
self.__doc_popup.set_target(obj)
731
self.__doc_popup.position_at_location(self, start)
733
self.__doc_popup.popup_focused()
735
self.__doc_popup.popup()
737
def highlight_arg_region(self, start, end):
738
"""Highlight the region between start and end for argument insertion.
739
A box will be drawn around the region as long as the cursor is inside
740
the region. If the user hits return, the cursor will be moved to the
741
end (and past a single closing punctuation at the end, if any.)
743
@param start iter at the start of the highlight region
744
@param end iter at the end of the highlight region
748
buf = self.get_buffer()
750
self.__arg_highlight_start = buf.create_mark(None, start, left_gravity=True)
751
self.__arg_highlight_end = buf.create_mark(None, end, left_gravity=False)