Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Include parentheses when tab completing functions #387

Open
mskar opened this issue Oct 1, 2020 · 2 comments
Open

Feature Request: Include parentheses when tab completing functions #387

mskar opened this issue Oct 1, 2020 · 2 comments

Comments

@mskar
Copy link

mskar commented Oct 1, 2020

When completing a function,
image
ptpython does not add parentheses after the function name.
image
I would like tab completion of functions to also add parentheses and put my cursor inside the parentheses:
image

Other examples:
Tab completion in IPython shows functions with parentheses in the completion menu:
image
but when I tab complete a function the parentheses are not included:
image
It would be better if IPython would add parentheses and put my cursor inside the parentheses:
image
Adding parentheses to completed functions is the default behavior in

I think this feature would be widely appreciated by users and should be the default behavior for tab completion of functions. Some users may prefer the current behavior, so ideally it would be controlled via a setting in ptpython/config.py.

@mskar
Copy link
Author

mskar commented Oct 2, 2020

I implemented this feature in my config.py:

Click here to see the code:
from jedi import Interpreter
from prompt_toolkit import filters
from prompt_toolkit.enums import DEFAULT_BUFFER
from prompt_toolkit.completion import CompleteEvent

def configure(repl):

    focused_insert = filters.has_focus(DEFAULT_BUFFER) & filters.vi_insert_mode
    focused_insert_and_completion = focused_insert & filters.has_completions

    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

    @repl.add_key_binding("tab", filter=focused_insert)
    @repl.add_key_binding("c-space", filter=focused_insert)
    def _(event):
        b = event.current_buffer
        if b.completer is None:
            return
        complete_event = CompleteEvent(completion_requested=True)
        completions = list(b.completer.get_completions(b.document, complete_event))
        if len(completions) == 1: # only one possible completion
            completion = completions[0]
            if completion.start_position:
                b.delete_before_cursor(-completion.start_position)
        elif not b.complete_state: # no completion menu
            b.start_completion(insert_common_part=True)
            completion = None
        elif b.complete_state.current_completion: # completion menu and selection
            completion = b.complete_state.current_completion
        else: # completion menu, but no selection
            b.complete_next()
            completion = b.complete_state.current_completion
        if completion:
            b.apply_completion(completion)
            if is_callable(completion.text):
                b.insert_text("()")
                b.cursor_left()

    @repl.add_key_binding("enter", filter=focused_insert_and_completion)
    def _(event):
        b = event.current_buffer
        if b.completer is None:
            return
        if b.complete_state.current_completion: # completion menu and selection
            completion = b.complete_state.current_completion
        else: # completion menu, but no selection
            b.complete_next()
            completion = b.complete_state.current_completion
        if completion:
            b.apply_completion(completion)
            if is_callable(completion.text):
                b.insert_text("()")
                b.cursor_left()

Note: This also completely changes how completion works:
Tab:

  • does not cycle thru completion options and instead
  • accepts completion if there is only one completion option
  • starts completion if there are multiple options and the completion menu is not showing
  • accepts the selected completion option if an option is selected
  • accepts the first completion option if the completion menu is showing and no option is selected

Enter:

  • accepts the selected completion option if an option is selected
  • accepts the first completion option if the completion menu is showing and no option is selected

I decided the best solution in the end will be to use jedi to find the type from the string returned by the completer. I think it would be great if the ptython completer could provide the type. Could something like this be built into the completer?

@mskar
Copy link
Author

mskar commented Oct 7, 2020

I submitted a pull request that implements the changes described above: #390.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant