From 7dd79e09551cf16fc272c6f52ce57350d89066ad Mon Sep 17 00:00:00 2001 From: Martin Skarzynski Date: Sat, 17 Oct 2020 18:52:29 -0400 Subject: [PATCH] provide option for tab to apply completion improve completion when completion menu is not showing improve apply completion with enter improve completion with enter in ptpython --- ptpython/key_bindings.py | 103 +++++++++++++++++++++++++++++++++++++++ ptpython/python_input.py | 5 ++ 2 files changed, 108 insertions(+) diff --git a/ptpython/key_bindings.py b/ptpython/key_bindings.py index b01762e6..bb011f1e 100644 --- a/ptpython/key_bindings.py +++ b/ptpython/key_bindings.py @@ -1,3 +1,5 @@ +from jedi import Interpreter +from prompt_toolkit.completion import CompleteEvent from prompt_toolkit.application import get_app from prompt_toolkit.document import Document from prompt_toolkit.enums import DEFAULT_BUFFER @@ -6,10 +8,13 @@ emacs_insert_mode, emacs_mode, has_focus, + has_completions, + completion_is_selected, has_selection, vi_insert_mode, ) from prompt_toolkit.key_binding import KeyBindings +from prompt_toolkit.key_binding.key_processor import KeyPress from prompt_toolkit.keys import Keys from .utils import document_is_multiline_python @@ -201,6 +206,104 @@ def _(event): " Abort when Control-C has been pressed. " event.app.exit(exception=KeyboardInterrupt, style="class:aborting") + def is_callable(text=""): + completions = Interpreter(text, [locals()]).complete() + match = next((i for i in completions if i.name == text), None) + return match.type in ("class", "function") if match else None + + @Condition + def auto_complete_selected_option_on_tab(): + return python_input.enable_auto_complete_selected_option_on_tab + + @Condition + def auto_complete_top_option_on_enter(): + return python_input.enable_auto_complete_top_option_on_enter + + @Condition + def auto_complete_top_option_on_tab(): + return python_input.enable_auto_complete_top_option_on_tab + + @Condition + def auto_complete_only_option_on_tab(): + return python_input.enable_auto_complete_only_option_on_tab + + @Condition + def auto_complete_function_parentheses(): + return python_input.enable_auto_complete_function_parentheses + + insert_mode = vi_insert_mode | emacs_insert_mode + focused_insert = insert_mode & has_focus(DEFAULT_BUFFER) + shown_not_selected = has_completions & ~completion_is_selected + alt_enter = [KeyPress(Keys.Escape), KeyPress(Keys.Enter)] + + # apply selected completion option with enter + @handle('c-j', filter=focused_insert & completion_is_selected) + @handle("enter", filter=focused_insert & completion_is_selected) + def _(event): + b = event.current_buffer + completion = b.complete_state.current_completion + b.apply_completion(completion) + if python_input.enable_auto_complete_function_parentheses: + if is_callable(completion.text) or is_callable(b.document.get_word_under_cursor()): + b.insert_text("()") + b.cursor_left() + + # apply selected completion option with tab + @handle("tab", filter=focused_insert & completion_is_selected & auto_complete_selected_option_on_tab) + @handle("c-space", filter=focused_insert & completion_is_selected & auto_complete_selected_option_on_tab) + def _(event): + b = event.current_buffer + completion = b.complete_state.current_completion + b.apply_completion(completion) + if python_input.enable_auto_complete_function_parentheses: + if is_callable(completion.text) or is_callable(b.document.get_word_under_cursor()): + b.insert_text("()") + b.cursor_left() + + # apply first completion option with enter when completion menu is showing + @handle('c-j', filter=focused_insert & shown_not_selected & auto_complete_top_option_on_enter) + @handle("enter", filter=focused_insert & shown_not_selected & auto_complete_top_option_on_enter) + def _(event): + b = event.current_buffer + text = b.text + completion = b.complete_state.completions[0] + b.apply_completion(completion) + if python_input.enable_auto_complete_function_parentheses: + if is_callable(completion.text) or is_callable(b.document.get_word_under_cursor()): + b.insert_text("()") + b.cursor_left() + if text == b.text: + event.cli.key_processor.feed_multiple(alt_enter) + + # apply first completion option with tab if completion menu is showing + @handle("tab", filter=focused_insert & shown_not_selected & auto_complete_top_option_on_tab) + @handle("c-space", filter=focused_insert & shown_not_selected & auto_complete_top_option_on_tab) + def _(event): + b = event.current_buffer + completion = b.complete_state.completions[0] + b.apply_completion(completion) + if python_input.enable_auto_complete_function_parentheses: + if is_callable(completion.text) or is_callable(b.document.get_word_under_cursor()): + b.insert_text("()") + b.cursor_left() + + # apply completion if there is only one option, otherwise start completion + @handle("tab", filter=focused_insert & ~has_completions & auto_complete_only_option_on_tab) + @handle("c-space", filter=focused_insert & ~has_completions & auto_complete_only_option_on_tab) + def _(event): + b = event.current_buffer + complete_event = CompleteEvent(completion_requested=True) + completions = list(b.completer.get_completions(b.document, complete_event)) + if len(completions) == 1: + completion = completions[0] + b.apply_completion(completion) + if python_input.enable_auto_complete_function_parentheses: + if is_callable(completion.text) or is_callable(b.document.get_word_under_cursor()): + b.insert_text("()") + b.cursor_left() + else: + b.start_completion(insert_common_part=True) + return bindings diff --git a/ptpython/python_input.py b/ptpython/python_input.py index c119e391..3ccdd50f 100644 --- a/ptpython/python_input.py +++ b/ptpython/python_input.py @@ -234,6 +234,11 @@ def __init__( self.enable_system_bindings: bool = True self.enable_input_validation: bool = True self.enable_auto_suggest: bool = False + self.enable_auto_complete_on_tab: bool = False + self.enable_auto_complete_top_option_on_enter: bool = False + self.enable_auto_complete_top_option_on_tab: bool = False + self.enable_auto_complete_only_option_on_tab: bool = False + self.enable_auto_complete_function_parentheses: bool = False self.enable_mouse_support: bool = False self.enable_history_search: bool = False # When True, like readline, going # back in history will filter the