/usr/lib/gedit/plugins/gdp/complete.py is in gedit-developer-plugins 0.5.15-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 | # Copyright (C) 2007-2012 - Curtis Hovey <sinzui.is at verizon.net>
# This software is licensed under the GNU General Public License version 2
# (see the file COPYING).
"""A completer for document words and python symbols."""
__all__ = [
'BaseGenerator',
'MarkupGenerator',
'PythonGenerator',
'Completer',
'TextGenerator',
]
import re
from gettext import gettext as _
from keyword import kwlist
from pydoc import TextDoc
from xml.sax import saxutils
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import GtkSource
from gdp import (
config,
ControllerMixin,
)
def get_word(document, word_pattern, end=None):
"""Return a 3-tuple of the word fragment before the cursor.
The tuple contains the (word_fragment, start_iter, end_iter) to
identify the prefix and its starting and end position in the
document.
"""
if end is None:
end = document.get_iter_at_mark(document.get_insert())
start = end.copy()
word = None
# When the preceding character is not alphanumeric,
# there is be no word before the cursor.
start_char = start.copy()
if start_char.backward_char():
char = start_char.get_char()
if not word_pattern.match(char):
return (None, start, end)
# GtkTextIter *_word_end() methods do not seek for '_' and '-', so
# we need to walk backwards through the iter to locate the word end.
count = 0
peek = start.copy()
while peek.backward_chars(1):
char = peek.get_char()
if not word_pattern.match(char):
break
else:
count += 1
if count > 0:
start.backward_chars(count)
word = document.get_text(start, end, True)
else:
word = None
return (word, start, end)
class DynamicProposal(GObject.GObject, GtkSource.CompletionProposal):
"""A common CompletionProposal for dymamically generated info."""
__gtype_name__ = "GDPDynamicProposal"
def __init__(self, word, info=None):
GObject.GObject.__init__(self)
self._word = word
self._info = info or ''
def __repr__(self):
return '<%s word="%s" at 0x%x>' % (
self.__class__.__name__, self._word, id(self))
def __eq__(self, other):
if not (isinstance(other, type(self))
or isinstance(self, type(other))):
return False
return other._word == self._word
def __lt__(self, other):
return self._word < other._word
def __hash__(self):
return hash((type(self), self._word))
def do_changed(self):
"""See `CompletionProposal`."""
return False
def do_equal(self, other):
"""See `CompletionProposal`."""
return self.__eq__(other)
def do_hash(self):
"""See `CompletionProposal`."""
return self.__hash__()
def do_get_text(self):
"""See `CompletionProposal`."""
return self._word
def do_get_label(self):
"""See `CompletionProposal`."""
return self._word
def do_get_markup(self):
"""See `CompletionProposal`."""
return saxutils.escape(self._word)
def do_get_info(self):
"""See `CompletionProposal`."""
return self._info
def do_get_icon(self):
"""See `CompletionProposal`."""
return None
class PangoDoc(TextDoc):
def bold(self, text):
return '\x86%s\x87' % text
def document(self, mod, *args):
text = TextDoc.document(self, mod)
text = saxutils.escape(text)
text = text.replace('\x86', '<b>')
text = text.replace('\x87', '</b>')
return text
class PythonProposal(DynamicProposal):
"""A proposal that provides pydoc info."""
def do_get_info(self):
"""See `CompletionProvider`."""
if not self._info:
return ''
return PangoDoc().document(self._info)
class DynamicProvider(GObject.GObject, GtkSource.CompletionProvider):
"""A common CompletionProvider for dynamically generated info."""
__gtype_name__ = "GDPDynamicProvider"
word_char = re.compile(r'[\w_-]', re.I)
def __init__(self, name, handler):
GObject.GObject.__init__(self)
self._name = name
self.last_line_no = 0
self.last_python_generator = None
self.handler = handler
self.info_widget = None
self.mark = None
theme = Gtk.IconTheme.get_default()
s, w, h = Gtk.icon_size_lookup(Gtk.IconSize.MENU)
icon_name = 'format-justify-left' # Gtk.STOCK_JUSTIFY_LEFT
self.icon = theme.load_icon(icon_name, w, 0)
def mark_position(self, it):
"""Create or move the mark of the word start."""
if not self.mark:
self.mark = it.get_buffer().create_mark(None, it, False)
else:
self.mark.get_buffer().move_mark(self.mark, it)
def get_word(self, context):
it = context.get_iter()
document = it.get_buffer()
word, start, end = get_word(document, self.word_char, it)
if word is not None:
self.mark_position(start)
else:
word = ''
return word
def is_interactive(self, context):
INTERACTIVE = GtkSource.CompletionActivation.INTERACTIVE
activation = context.get_activation() or INTERACTIVE
return activation == INTERACTIVE
def do_get_start_iter(self, context, proposal, iter_):
"""See `CompletionProvider`."""
if not self.mark or self.mark.get_deleted():
return None
return self.mark.get_buffer().get_iter_at_mark(self.mark)
def do_match(self, context):
"""See `CompletionProvider`."""
word = self.get_word(context)
if self.is_interactive(context) and len(word) < 4:
return False
return True
def get_generator(self, document, prefix):
"""Return the specialized generator for document's language."""
cursor_iter = document.get_iter_at_mark(document.get_insert())
current_line_no = cursor_iter.get_line()
is_same_line = current_line_no == self.last_line_no
language_id = None
if hasattr(document, 'get_language'):
# How can we not get a document or language?
language = document.get_language()
if language is not None:
language_id = language.get_id()
if language_id == 'python':
if is_same_line and self.last_python_generator:
# Use the last generator which caches the python compilation.
return self.last_python_generator
self.last_python_generator = PythonGenerator(
document, prefix=prefix)
self.last_line_no = current_line_no
return self.last_python_generator
if language_id in (
'xml', 'xslt', 'html', 'pt', 'mallard', 'docbook'):
return MarkupGenerator(document, prefix=prefix)
else:
# The text generator is never returned because get_proposals will
# use it in non-authoritative cases.
return None
def get_proposals(self, prefix, context):
"""See `CompletionProvider`."""
all_words = []
is_authoritative = False
document = context.get_iter().get_buffer()
generator = self.get_generator(document, prefix)
if generator:
is_authoritative, words = generator.get_words(
is_interactive=self.is_interactive(context))
all_words += words
if not is_authoritative:
is_authoritative, simple_words = TextGenerator(
document, prefix=prefix).get_words()
simple_words = [
w for w in simple_words
if PythonProposal(w._word) not in all_words]
all_words += simple_words
if prefix:
# Ensure that the prefix is first in the list.
dynamic_prefix = DynamicProposal(prefix)
if dynamic_prefix in all_words:
all_words.remove(dynamic_prefix)
all_words.insert(0, dynamic_prefix)
return all_words
def _do_populate(self, context):
word = self.get_word(context)
if self.is_interactive(context) and len(word) < 4:
# Delete was pressed while the completer was displayed.
proposals = []
else:
proposals = self.get_proposals(word, context)
if self.is_interactive(context) and len(proposals) == 1:
# Tell the completer this act is finished without suggestions.
proposals = []
return proposals
def do_populate(self, context):
"""See `CompletionProvider`."""
proposals = self._do_populate(context)
context.add_proposals(self, proposals, True)
def do_get_name(self):
"""See `CompletionProvider`."""
return self._name
def do_activate_proposal(self, proposal, piter):
"""See `CompletionProvider`."""
return self.handler(proposal, piter)
def do_get_icon(self):
"""See `CompletionProvider`."""
return self.icon
def do_get_activation(self):
"""See `CompletionProvider`."""
activation = GtkSource.CompletionActivation.USER_REQUESTED
if config.getboolean('completer', 'suggest_completions'):
activation |= GtkSource.CompletionActivation.INTERACTIVE
return activation
def do_get_info_widget(self, proposal):
"""See `DynamicProvider`."""
if self.info_widget is None:
self.info_view = Gtk.Label(label='')
self.info_view.set_alignment(0.0, 0.0)
self.info_widget = Gtk.ScrolledWindow()
self.info_widget.add_with_viewport(self.info_view)
return self.info_widget
def do_update_info(self, proposal, info):
"""See `CompletionProvider`."""
markup = proposal.get_info() or ''
self.info_view.set_markup(markup)
self.info_view.show()
self.info_widget.set_size_request(400, -1)
GObject.type_register(DynamicProposal)
GObject.type_register(DynamicProvider)
class BaseGenerator(object):
"""An abstract class representing the source of a word prefix."""
def __init__(self, document, prefix=None):
"""Create a new Generator.
:param prefix: A `str`. The word prefix used to match words.
:param document: `gedit.Document`. The source of words to search.
"""
self._prefix = prefix
self._document = document
word_char = re.compile(r'[\w_]', re.I)
@property
def string_before_cursor(self):
"""Return the string that matches `word_char` before the cursor."""
text, start_iter, end_iter = get_word(self._document, self.word_char)
if text is None:
text = ''
return text
def ensure_prefix(self, prefix):
"""Return the available prefix or an empty string."""
if prefix:
return prefix
elif self._prefix:
return self._prefix
else:
# Match all words in the text.
return ''
def get_words(self, prefix=None, is_interactive=False):
"""Return a 2-tuple of is_authoritative and unique `set` of words.
:param prefix: A `str`. The word prefix used to match words.
:return: a 2-tuple of is_authoritative and a set of words.
is_authoritative is True when the set of words are the only words
that can match the prefix. The words are a set of words.
"""
raise NotImplementedError
@property
def prefix(self):
"""The prefix use to match words to."""
return self._prefix
@property
def file_path(self):
"""The path to the file that is the word source."""
return self._document.get_uri_for_display()
@property
def text(self):
"""The text of the gedit.Document or None."""
if not self._document:
return None
start_iter = self._document.get_start_iter()
end_iter = self._document.get_end_iter()
return self._document.get_text(start_iter, end_iter, True)
class TextGenerator(BaseGenerator):
"""Generate a list of words that match a given prefix for a document."""
def get_words(self, prefix=None, is_interactive=False):
"""See `BaseGenerator.get_words`.
is_authoritative is always False because TextGenerator because it is
not for a specific document Language.
"""
prefix = self.ensure_prefix(prefix)
is_authoritative = False
if len(prefix) > 0:
# Match words that are just the prefix too.
conditional = r'*'
else:
conditional = r'+'
pattern = r'\b(%s[\w-]%s)' % (re.escape(prefix), conditional)
word_re = re.compile(pattern, re.I)
words = word_re.findall(self.text)
# Find the unique words that do not have pseudo m-dashed in them.
words = set(words)
words = [DynamicProposal(word) for word in words if '--' not in word]
return is_authoritative, words
class MarkupGenerator(BaseGenerator):
"""Generate a list of elements and attributes for a document."""
word_char = re.compile(r'[^<>]')
common_attrs = []
INSIDE_ATTRIBUTES = 'INSIDE_ATTRIBUTES'
INSIDE_CLOSE = 'INSIDE_CLOSE'
INSIDE_OPEN = 'INSIDE_OPEN'
OUTSIDE = 'OUTSIDE'
def get_cursor_context(self):
"""Return the context of the cursor in relation to the last tag."""
text, start_iter, end_iter = get_word(self._document, self.word_char)
if not start_iter.backward_char():
# The start was at the begining of the doc; no tags were found.
return self.OUTSIDE
char_iter = start_iter.copy()
char_iter.forward_char()
char = start_iter.get_char()
if char == '>':
return self.OUTSIDE
elif text and text.startswith('/'):
return self.INSIDE_CLOSE
elif text and ' ' in text:
return self.INSIDE_ATTRIBUTES
else:
return self.INSIDE_OPEN
def get_words(self, prefix=None, is_interactive=False):
"""See `BaseGenerator.get_words`."""
prefix = self.ensure_prefix(prefix)
context = self.get_cursor_context()
if context == self.OUTSIDE:
# is_authoritative is false and there are no words because the
# cursor is not in a tag to complete.
return False, set()
is_authoritative = True
if context == self.INSIDE_OPEN:
words = set(self._get_open_tags(prefix))
elif context == self.INSIDE_ATTRIBUTES:
words = set(self._get_attributes(prefix))
else:
# Close tags is not a test because it returns a ordered list.
words = self._get_close_tags(prefix)
return is_authoritative, words
def get_cardinality(self, prefix):
if prefix:
# Match words that are just the prefix too.
return r'*'
else:
return r'+'
def _get_open_tags(self, prefix):
"""Return all the tag names."""
cardinality = self.get_cardinality(prefix)
prefix = re.escape(prefix)
pattern = r'<(%s[\w_.:-]%s)' % (prefix, cardinality)
word_re = re.compile(pattern, re.I)
words = word_re.findall(self.text)
return [DynamicProposal(word) for word in words]
def _get_attributes(self, prefix):
pattern = r'<[\w_.:-]+ ([\w_.:-]*)=[^>]+>'
attrs_re = re.compile(pattern, re.I)
attr_clusters = attrs_re.findall(self.text)
attrs = set(self.common_attrs)
for attr_cluster in attr_clusters:
attr_pairs = attr_cluster.split()
for pair in attr_pairs:
attr = pair.split('=')
attrs.add(attr[0])
if prefix:
for attr in list(attrs):
if not attr.startswith(prefix):
attrs.remove(attr)
return [DynamicProposal(attr) for attr in attrs]
def _get_close_tags(self, prefix):
"""Return the tags that are still open before the cursor."""
cardinality = self.get_cardinality(prefix)
prefix = re.escape(prefix)
# Get the text before the cursor.
start_iter = self._document.get_start_iter()
end_iter = self._document.get_iter_at_mark(
self._document.get_insert())
text = self._document.get_text(start_iter, end_iter, True)
# Get all the open tags.
open_pattern = r'<(%s[\w_.:-]%s)' % (prefix, cardinality)
open_re = re.compile(open_pattern, re.I)
open_tags = open_re.findall(text)
# Get all the empty tags.
empty_pattern = r'<(%s[\w_.:-]%s)[^>]*/>' % (prefix, cardinality)
empty_re = re.compile(empty_pattern, re.I)
empty_tags = empty_re.findall(text)
# Get all the close tags.
close_pattern = r'</(%s[\w_.:-]%s)' % (prefix, cardinality)
close_re = re.compile(close_pattern, re.I)
close_tags = close_re.findall(text)
# Return only the tags that are still open.
for tag in empty_tags:
if tag in open_tags:
open_tags.remove(tag)
for tag in close_tags:
if tag in open_tags:
open_tags.remove(tag)
return [DynamicProposal(tag + '>') for tag in open_tags]
class PythonGenerator(BaseGenerator):
"""Generate a list of Python symbols that match a given prefix."""
word_char = re.compile(r'[\w_.]', re.I)
_kwlist = None
_builtin = None
def __init__(self, document, prefix=None):
super(PythonGenerator, self).__init__(document, prefix)
self._local_symsbols = None
@property
def kwlist(self):
if self._kwlist is None:
self._kwlist = [
self._get_dynamic_proposal(None, word) for word in kwlist]
return self._kwlist
@property
def builtin(self):
if self._builtin is None:
import __builtin__
self._builtin = [
self._get_dynamic_proposal(__builtin__, name)
for name in dir(__builtin__)]
return self._builtin
def get_local_symbols(self, is_interactive=False):
if self._local_symsbols:
return self._local_symsbols
try:
pyo = compile(self._get_parsable_text(), 'sc.py', 'exec')
except SyntaxError:
# This cannot be completed because of syntax errors.
# Return
if not is_interactive:
self._document.emit('syntax-error-python')
return []
co_names = ('SIGNAL_RUN_LAST', 'TYPE_NONE', 'TYPE_PYOBJECT', 'object')
self._local_symsbols = [
self._get_dynamic_proposal(None, name)
for name in pyo.co_names if name not in co_names]
return self._local_symsbols
def get_words(self, prefix=None, is_interactive=False):
"""See `BaseGenerator.get_words`.
:return: a 2-tuple of is_authoritative and a set of matching
identifiers. is_authoritative is True when the prefix is a part
of a dotted identifier.
"""
prefix = self.ensure_prefix(prefix)
local_symsbols = self.get_local_symbols(is_interactive)
is_authoritative = False
if prefix == '' and local_symsbols:
is_authoritative = True
namespaces = self.string_before_cursor.split('.')
if len(namespaces) == 1:
# The identifier is scoped to this module (the document).
symbols = local_symsbols + self.builtin + self.kwlist
symbols = [proposal for proposal in symbols
if proposal.get_text().startswith(prefix)]
return is_authoritative, symbols
# Remove the prefix to create the module's full name.
namespaces.pop()
module_name = '.'.join(namespaces)
locald = {}
try:
# Check this file first.
module_ = eval(module_name, globals(), locald)
except NameError:
# Try a true import.
try:
module_ = __import__(module_name, globals(), locald, [])
except ImportError:
return is_authoritative, []
except:
return is_authoritative, []
for symbol in namespaces[1:]:
next_module = getattr(module_, symbol, None)
if next_module is not None:
module_ = next_module
else:
break
is_authoritative = True
symbols = [self._get_dynamic_proposal(module_, name)
for name in dir(module_) if name.startswith(prefix)]
return is_authoritative, symbols
def _get_parsable_text(self):
"""Return the parsable text of the module.
The line being edited may not be valid syntax, so the line is
replaced with 'pass', or if it starts a block, it becomes 'if True:'
"""
current_iter = self._document.get_iter_at_mark(
self._document.get_insert())
index = current_iter.get_line()
text_lines = self.text.splitlines()
if index + 1 == len(text_lines):
# The current line is the last line. Add a fake line because
# the compiler will require another line to follow a comment.
text_lines.append('')
current_indentation = self._get_indentation(text_lines[index])
next_indentation = self._get_indentation(text_lines[index + 1])
if len(next_indentation) > len(current_indentation):
# Make this line compilable for the next block.
text_lines[index] = current_indentation + 'if True:'
else:
# Comment-out this line so that it is not compiled.
text_lines[index] = current_indentation + 'pass'
return '\n'.join(text_lines)
def _get_indentation(self, line):
"Return the line's indentation"
indentation_pattern = re.compile(r'^[ \t]*')
match = indentation_pattern.match(line)
if match:
return match.group()
# No match means the indentation is an empty string.
return ''
def _get_dynamic_proposal(self, module, name):
try:
if module is None:
identifier = None
elif type(module) == 'dict':
identifier = module[name]
else:
identifier = module.__dict__[name]
except KeyError:
identifier = None
return PythonProposal(name, info=identifier)
class Completer(ControllerMixin):
"""This class manages the gedit.View's interaction with completions."""
word_char = re.compile(r'[\w_-]', re.I)
def __init__(self, window):
"""Initialize the controller for the gedit.View."""
self.window = window
self.signal_ids = {}
self.view = None
self.completion = None
self.provider = None
self.completer_title = _('GDP words')
self.set_view(window.get_active_view())
def deactivate(self):
"""Clean up resources before deactivation."""
self.set_view(None)
def set_view(self, view, is_reset=False):
"""Set the view to be controlled.
Installs signal handlers for the view. Calling
document.get_uri_for_display() self.set_view(None) will effectively
remove all the control from the current view. when is_reset is True,
the current view's signals will be reset.
"""
if view is self.view and not is_reset:
return
if self.view:
# Unregister the current view before assigning the new one.
self._disconnectSignal(self.view, 'destroy')
self._disconnectSignal(self.view, 'notify::editable')
if (self.completion
and self.provider in self.completion.get_providers()):
self.completion.remove_provider(self.provider)
self.completion = None
self.provider = None
self.view = view
if view is not None:
self.signal_ids['destroy'] = view.connect(
'destroy', self.on_view_destroy)
self.signal_ids['notify::editable'] = view.connect(
'notify::editable', self.on_notify_editable)
self.provider = DynamicProvider(
self.completer_title, self.on_proposal_activated)
self.completion = self.view.get_completion()
if self.provider not in self.completion.get_providers():
self.completion.add_provider(self.provider)
def _disconnectSignal(self, obj, signal):
"""Disconnect the signal from the provided object."""
if signal in self.signal_ids:
obj.disconnect(self.signal_ids[signal])
del self.signal_ids[signal]
def show_completion(self, widget=None, data=None):
"""Show the completion proposals."""
self.view.emit('show-completion')
def get_word_prefix(self, document):
"""Return a 3-tuple of the word fragment before the cursor.
The tuple contains the (word_fragement, start_iter, end_iter) to
identify the prefix and its starting and end position in the
document.
"""
return get_word(document, self.word_char)
def insert_word(self, word, start=None):
"""Return True when the word is inserted into the Document.
The word cannot be None or an empty string.
"""
assert word, "The word cannot be None or an empty string."
document = self.view.get_buffer()
if start:
document.delete(
start, document.get_iter_at_mark(document.get_insert()))
document.insert_at_cursor(word)
def on_proposal_activated(self, proposal, piter):
"""Complete the word using the proposal."""
if not proposal:
return
document = self.view.get_buffer()
(ignored, start, end_) = self.get_word_prefix(document)
word = proposal.get_text()
self.insert_word(word, start)
return True
def on_suggest_completions_toggled(self, menu_item, data=None):
config.set(
'completer', 'suggest_completions', str(menu_item.props.active))
config.dump()
self.set_view(self.view, is_reset=True)
def on_notify_editable(self, view, param_spec):
"""Update the controller when the view editable state changes.
This method is ultimately responsible for enabling and disabling
the completion widget for completer.
"""
self.set_view(view, True)
def on_view_destroy(self, view):
"""Disconnect the controller."""
self.deactivate()
|