pw_console: Enable floating vertical completion menu

Use a CompletionsMenu defined in the top Float container. This lets the
menu overlay other UI panes and exist outside the python input window.

- Turn on function signature display by default.
- Clear displayed function signatures on Ctrl-C
- Disable ptpython's CompletionMenus. They can still be re-enabled
  in the F2 settings menu. If turned back on our CompletionMenu is
  hidden.

Bug: 418
No-Docs-Update-Reason: No new functionality, readability improvement.
Change-Id: Ieff37dc6547fc9b5f6a20f048514d8e0ccad47fb
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/50863
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Pigweed-Auto-Submit: Anthony DiGirolamo <tonymd@google.com>
diff --git a/pw_console/py/pw_console/console_app.py b/pw_console/py/pw_console/console_app.py
index e6c8c0e..efa586c 100644
--- a/pw_console/py/pw_console/console_app.py
+++ b/pw_console/py/pw_console/console_app.py
@@ -20,6 +20,7 @@
 from threading import Thread
 from typing import Iterable, Optional
 
+from prompt_toolkit.layout.menus import CompletionsMenu
 from prompt_toolkit.application import Application
 from prompt_toolkit.filters import Condition
 from prompt_toolkit.styles import (
@@ -39,6 +40,7 @@
     MenuItem,
 )
 from prompt_toolkit.key_binding import KeyBindings, merge_key_bindings
+from ptpython.layout import CompletionVisualisation  # type: ignore
 from ptpython.key_bindings import (  # type: ignore
     load_python_bindings, load_sidebar_bindings,
 )
@@ -242,9 +244,40 @@
                     # Callable to get width
                     width=self.help_window.content_width,
                 ),
+                # Completion menu that can overlap other panes since it lives in
+                # the top level Float container.
+                Float(
+                    xcursor=True,
+                    ycursor=True,
+                    content=ConditionalContainer(
+                        content=CompletionsMenu(
+                            scroll_offset=(lambda: self.pw_ptpython_repl.
+                                           completion_menu_scroll_offset),
+                            max_height=16,
+                        ),
+                        # Only show our completion if ptpython's is disabled.
+                        filter=Condition(lambda: self.pw_ptpython_repl.
+                                         completion_visualisation ==
+                                         CompletionVisualisation.NONE),
+                    ),
+                ),
             ],
         )
 
+        # NOTE: ptpython stores it's completion menus in this HSplit:
+        #
+        # self.pw_ptpython_repl.__pt_container__()
+        #   .children[0].children[0].children[0].floats[0].content.children
+        #
+        # Index 1 is a CompletionsMenu and is shown when:
+        #   self.pw_ptpython_repl
+        #     .completion_visualisation == CompletionVisualisation.POP_UP
+        #
+        # Index 2 is a MultiColumnCompletionsMenu and is shown when:
+        #   self.pw_ptpython_repl
+        #     .completion_visualisation == CompletionVisualisation.MULTI_COLUMN
+        #
+
         # Setup the prompt_toolkit layout with the repl pane as the initially
         # focused element.
         self.layout: Layout = Layout(
diff --git a/pw_console/py/pw_console/pw_ptpython_repl.py b/pw_console/py/pw_console/pw_ptpython_repl.py
index d1e012c..c1f6c2b 100644
--- a/pw_console/py/pw_console/pw_ptpython_repl.py
+++ b/pw_console/py/pw_console/pw_ptpython_repl.py
@@ -22,6 +22,7 @@
 
 from prompt_toolkit.buffer import Buffer
 import ptpython.repl  # type: ignore
+from ptpython.layout import CompletionVisualisation  # type: ignore
 from ptpython.completer import CompletePrivateAttributes  # type: ignore
 
 import pw_console.helpers
@@ -49,6 +50,18 @@
         self.complete_private_attributes = (
             CompletePrivateAttributes.IF_NO_PUBLIC)
 
+        # Function signature that shows args, kwargs, and types under the cursor
+        # of the input window.
+        self.show_signature: bool = True
+        # Docstring of the current completed function that appears at the bottom
+        # of the input window.
+        self.show_docstring: bool = False
+
+        # Turn off the completion menu in ptpython. The CompletionsMenu in
+        # ConsoleApp.root_container will handle this.
+        self.completion_visualisation: CompletionVisualisation = (
+            CompletionVisualisation.NONE)
+
         # Additional state variables.
         self.repl_pane = None
         self._last_result = None
diff --git a/pw_console/py/pw_console/repl_pane.py b/pw_console/py/pw_console/repl_pane.py
index 811d4bc..66d1aef 100644
--- a/pw_console/py/pw_console/repl_pane.py
+++ b/pw_console/py/pw_console/repl_pane.py
@@ -333,7 +333,10 @@
             self.interrupt_last_code_execution()
 
     def clear_input_buffer(self):
+        # Erase input buffer.
         self.pw_ptpython_repl.default_buffer.reset()
+        # Clear any displayed function signatures.
+        self.pw_ptpython_repl.on_reset()
 
     def interrupt_last_code_execution(self):
         code = self._get_currently_running_code()