Skip to content

Commit

Permalink
Added option for hiding/showing private completions.
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanslenders committed Sep 25, 2020
1 parent 9f7819e commit a395a25
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 19 deletions.
79 changes: 68 additions & 11 deletions ptpython/completer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import ast
import keyword
import re
from typing import TYPE_CHECKING, Any, Dict, Iterable, List
from enum import Enum
from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional

from prompt_toolkit.completion import (
CompleteEvent,
Expand All @@ -12,21 +13,34 @@
from prompt_toolkit.contrib.regular_languages.compiler import compile as compile_grammar
from prompt_toolkit.contrib.regular_languages.completion import GrammarCompleter
from prompt_toolkit.document import Document
from prompt_toolkit.formatted_text import fragment_list_to_text, to_formatted_text

from ptpython.utils import get_jedi_script_from_document

if TYPE_CHECKING:
from prompt_toolkit.contrib.regular_languages.compiler import _CompiledGrammar

__all__ = ["PythonCompleter"]
__all__ = ["PythonCompleter", "CompletePrivateAttributes", "HidePrivateCompleter"]


class CompletePrivateAttributes(Enum):
"""
Should we display private attributes in the completion pop-up?
"""

NEVER = "NEVER"
IF_NO_PUBLIC = "IF_NO_PUBLIC"
ALWAYS = "ALWAYS"


class PythonCompleter(Completer):
"""
Completer for Python code.
"""

def __init__(self, get_globals, get_locals, get_enable_dictionary_completion):
def __init__(
self, get_globals, get_locals, get_enable_dictionary_completion
) -> None:
super().__init__()

self.get_globals = get_globals
Expand All @@ -35,8 +49,8 @@ def __init__(self, get_globals, get_locals, get_enable_dictionary_completion):

self.dictionary_completer = DictionaryCompleter(get_globals, get_locals)

self._path_completer_cache = None
self._path_completer_grammar_cache = None
self._path_completer_cache: Optional[GrammarCompleter] = None
self._path_completer_grammar_cache: Optional["_CompiledGrammar"] = None

@property
def _path_completer(self) -> GrammarCompleter:
Expand Down Expand Up @@ -158,7 +172,7 @@ def get_completions(

if script:
try:
completions = script.completions()
jedi_completions = script.completions()
except TypeError:
# Issue #9: bad syntax causes completions() to fail in jedi.
# https://github.com/jonathanslenders/python-prompt-toolkit/issues/9
Expand Down Expand Up @@ -196,12 +210,12 @@ def get_completions(
# Supress all other Jedi exceptions.
pass
else:
for c in completions:
for jc in jedi_completions:
yield Completion(
c.name_with_symbols,
len(c.complete) - len(c.name_with_symbols),
display=c.name_with_symbols,
style=_get_style_for_name(c.name_with_symbols),
jc.name_with_symbols,
len(jc.complete) - len(jc.name_with_symbols),
display=jc.name_with_symbols,
style=_get_style_for_name(jc.name_with_symbols),
)


Expand Down Expand Up @@ -464,6 +478,49 @@ def sort_key(name: str):
return sorted(names, key=sort_key)


class HidePrivateCompleter(Completer):
"""
Wrapper around completer that hides private fields, deponding on whether or
not public fields are shown.
(The reason this is implemented as a `Completer` wrapper is because this
way it works also with `FuzzyCompleter`.)
"""

def __init__(
self,
completer: Completer,
complete_private_attributes: Callable[[], CompletePrivateAttributes],
) -> None:
self.completer = completer
self.complete_private_attributes = complete_private_attributes

def get_completions(
self, document: Document, complete_event: CompleteEvent
) -> Iterable[Completion]:

completions = list(self.completer.get_completions(document, complete_event))
complete_private_attributes = self.complete_private_attributes()
hide_private = False

def is_private(completion: Completion) -> bool:
text = fragment_list_to_text(to_formatted_text(completion.display))
return text.startswith("_")

if complete_private_attributes == CompletePrivateAttributes.NEVER:
hide_private = True

elif complete_private_attributes == CompletePrivateAttributes.IF_NO_PUBLIC:
hide_private = any(not is_private(completion) for completion in completions)

if hide_private:
completions = [
completion for completion in completions if not is_private(completion)
]

return completions


class ReprFailedError(Exception):
" Raised when the repr() call in `DictionaryCompleter` fails. "

Expand Down
5 changes: 4 additions & 1 deletion ptpython/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,10 @@ def get_help_text():

return ConditionalContainer(
content=Window(
FormattedTextControl(get_help_text), style=token, height=Dimension(min=3)
FormattedTextControl(get_help_text),
style=token,
height=Dimension(min=3),
wrap_lines=True,
),
filter=ShowSidebar(python_input)
& Condition(lambda: python_input.show_sidebar_help)
Expand Down
46 changes: 39 additions & 7 deletions ptpython/python_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
from prompt_toolkit.validation import ConditionalValidator, Validator
from pygments.lexers import Python3Lexer as PythonLexer

from .completer import PythonCompleter
from .completer import CompletePrivateAttributes, HidePrivateCompleter, PythonCompleter
from .history_browser import PythonHistory
from .key_bindings import (
load_confirm_exit_bindings,
Expand Down Expand Up @@ -180,13 +180,17 @@ def __init__(
self.get_globals: _GetNamespace = get_globals or (lambda: {})
self.get_locals: _GetNamespace = get_locals or self.get_globals

self._completer = _completer or FuzzyCompleter(
PythonCompleter(
self.get_globals,
self.get_locals,
lambda: self.enable_dictionary_completion,
self._completer = HidePrivateCompleter(
_completer
or FuzzyCompleter(
PythonCompleter(
self.get_globals,
self.get_locals,
lambda: self.enable_dictionary_completion,
),
enable_fuzzy=Condition(lambda: self.enable_fuzzy_completion),
),
enable_fuzzy=Condition(lambda: self.enable_fuzzy_completion),
lambda: self.complete_private_attributes,
)
self._validator = _validator or PythonValidator(self.get_compiler_flags)
self._lexer = _lexer or PygmentsLexer(PythonLexer)
Expand Down Expand Up @@ -239,6 +243,9 @@ def __init__(
self.enable_syntax_highlighting: bool = True
self.enable_fuzzy_completion: bool = False
self.enable_dictionary_completion: bool = False
self.complete_private_attributes: CompletePrivateAttributes = (
CompletePrivateAttributes.ALWAYS
)
self.swap_light_and_dark: bool = False
self.highlight_matching_parenthesis: bool = False
self.show_sidebar: bool = False # Currently show the sidebar.
Expand Down Expand Up @@ -530,6 +537,31 @@ def get_values():
"off": lambda: disable("complete_while_typing"),
},
),
Option(
title="Complete private attrs",
description="Show or hide private attributes in the completions. "
"'If no public' means: show private attributes only if no public "
"matches are found or if an underscore was typed.",
get_current_value=lambda: {
CompletePrivateAttributes.NEVER: "Never",
CompletePrivateAttributes.ALWAYS: "Always",
CompletePrivateAttributes.IF_NO_PUBLIC: "If no public",
}[self.complete_private_attributes],
get_values=lambda: {
"Never": lambda: enable(
"complete_private_attributes",
CompletePrivateAttributes.NEVER,
),
"Always": lambda: enable(
"complete_private_attributes",
CompletePrivateAttributes.ALWAYS,
),
"If no public": lambda: enable(
"complete_private_attributes",
CompletePrivateAttributes.IF_NO_PUBLIC,
),
},
),
Option(
title="Enable fuzzy completion",
description="Enable fuzzy completion.",
Expand Down

0 comments on commit a395a25

Please sign in to comment.