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

Improve interactive console #4

Open
3 tasks
blairfrandeen opened this issue Nov 19, 2022 · 4 comments
Open
3 tasks

Improve interactive console #4

blairfrandeen opened this issue Nov 19, 2022 · 4 comments
Labels
enhancement New feature or request

Comments

@blairfrandeen
Copy link
Owner

Improve interactive console. Some nice to haves a that aren't currently implemented

  • command history
  • up arrow or Ctrl+p should cycle through history
  • vim bindings for text entry

Recommend doing this by building app on a modified version of cmd2.

@blairfrandeen blairfrandeen added the enhancement New feature or request label Nov 26, 2022
@blairfrandeen
Copy link
Owner Author

I wrote up a relatively detailed question with what I want to do in the cmd2 discussions. I got some good feedback of where to start, if I want to actually use cmd2 to make this work.

What I found in a basic study is that I can use decorators to register commands, without having to keep them in the class that subclasses cmd2.Cmd. A very simple registration decorator:

def make_command(func):
    """Decorator function to add commands to the app."""
    setattr(FirstApp, f"do_{func.__name__}", func)


@make_command
def new_func(self, args):
    """A function that doesn't have to be named with 'do_'"""
    print("A new function is born.")
    print(args)

Adding functionality for pattern detection requires overriding onecmd:

    def onecmd(
        self, statement: Union[cmd2.Statement, str], *, add_to_history: bool = True
    ) -> bool:
        # ...snip...
        if func:
        # ...snip...

        ### ADDED SECTION FOR INPUT PATTERNS
        # This demo uses a check for a single pattern type (float) but could be
        # sent to a more complex command or pattern parser
        elif is_float(statement.command): 
            print(f"{float(statement.command) ** 2} {statement}")
            #  stop = func(statement)
            return False
        ### END ADDED SECTION

        else:
        # ...snip...

For functionality with an empty command, we need to override _complete_statement, and replace raise EmptyStatement at the end of the function with statement = self.statement_parser.parse("empty_statement"). We also have to add the following to the App class:

def do_empty_statement(self, args):
    pass # replace with desired behavior.

I'm uncomfortable having to override some obviously important functions of cmd2 to make this work. It means updates to cmd2 could break titr if I'm not careful.

@blairfrandeen
Copy link
Owner Author

I think the main thing that I find lacking that I want to see in what is currently datum_console is the command history. It's a reflex for me to press up arrow or ctrl+p to get to the last command, and it's disconcerting / feels like not a well thought out app when these inputs result in ^[[A or ^P, respectively.

Looking through the source code of cmd, I can't quite figure out what makes that tick. I think it has something to do with the readline library calls.

My best bet in the near term may be to take datum_console, make it more bullet-proof, write tests for it, and implement this history functionality. It could be a good learning opportunity for how stdin/stdout work, and how input gets captured from the keyboard.

I'm still at a state where it benefits me to learn things more in depth/under the hood than it does to try to use something like cmd/cmd2 out of the box

@blairfrandeen
Copy link
Owner Author

Noting that the above non-nice behavior seems to only happen in WSL (on this machine). Need to test if it happens in zsh/ubuntu next time I'm there.

Pretty sure it happens on my work computers too.

@blairfrandeen
Copy link
Owner Author

Well this could be easier than I thought:

>>>import readline
>>> readline.__doc__
'Importing this module enables command line editing using GNU readline.'

And that seems to fix it. I found this by copying the source code for cmd into a new file, and commenting out lines until the functionality broke. Talk about not obvious, but I can't complain.

I think now it's not a big jump to get datum_console to also accept tab completion using some of the functions from the readline library.

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

No branches or pull requests

1 participant