branch: externals/drepl commit b11717466dc37b6d72b5dd9678286fd496b2d9a1 Author: Augusto Stoffel <arstof...@gmail.com> Commit: Augusto Stoffel <arstof...@gmail.com>
Add drepl-ipython-config, allowing arbitrary IPython configuration --- drepl-ipython.el | 35 ++++++++++++++++--------------- drepl-ipython.py | 63 ++++++++++++++++++++++++++++++++------------------------ 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/drepl-ipython.el b/drepl-ipython.el index fec017fc79..046ced42dd 100644 --- a/drepl-ipython.el +++ b/drepl-ipython.el @@ -38,18 +38,21 @@ :group 'python :link '(url-link "https://github.com/astoff/drepl")) -(defcustom drepl-ipython-prompts - ["In [{}]: " "...: " "\e[31mOut[{}]:\e[0m " "\n" ""] - "Prompts of the Python dREPL. - -This should be a vector of 5 string: primary prompt, continuation -prompt, output prefix, input separator, output separator. The -substring \"{}\" is replaced by the execution count." - :type '(vector (string :tag "Primary prompt") - (string :tag "Continuation prompt") - (string :tag "Output prompt") - (string :tag "Input separator") - (string :tag "Output separator"))) +(defcustom drepl-ipython-config nil + "Customization options for the IPython shell. + +This should be a plist of configuration options in flat \"dotted\" +format. For example, to make the prompt look like the classic Python +one and use SVG output for plots, set this variable as follows: + + (DRepl.ps1 \">>> \" + DRepl.ps2 \"... \" + DRepl.ps3 \"\" + DRepl.separate_in \"\" + InlineBackend.figure_formats [\"svg\"]) + +Type `%config' in the shell to see a listing of all available options." + :type 'plist) (defvar drepl-ipython--start-file (expand-file-name "drepl-ipython.py" @@ -74,11 +77,9 @@ exec(stdin.read(int(stdin.readline())))")) (with-temp-buffer (insert-file-contents drepl-ipython--start-file) (process-send-string buffer (format "%s\n" (buffer-size))) - (process-send-string buffer (buffer-string))))) - -(cl-defmethod drepl--set-options ((repl drepl-ipython) _) - (drepl--communicate repl #'ignore 'setoptions - :prompts drepl-ipython-prompts)) + (process-send-region buffer (point-min) (point-max)) + (process-send-string buffer (json-serialize drepl-ipython-config)) + (process-send-string buffer "\n")))) (provide 'drepl-ipython) diff --git a/drepl-ipython.py b/drepl-ipython.py index ff061b99db..f452eac04e 100644 --- a/drepl-ipython.py +++ b/drepl-ipython.py @@ -11,6 +11,7 @@ from IPython.core.completer import provisionalcompleter, rectify_completions from IPython.core.displayhook import DisplayHook from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC from IPython.utils.tokenutil import token_at_cursor +from traitlets import Unicode def encoding_workaround(data): @@ -45,14 +46,14 @@ def readmsg(): if line.startswith("\033="): return json.loads("".join(buffer)) if not line.startswith("\033+"): - raise DreplError("Invalid input") + raise DReplError("Invalid input") -class DreplError(Exception): +class DReplError(Exception): pass -class DreplDisplayHook(DisplayHook): +class DReplDisplayHook(DisplayHook): def write_output_prompt(self): stdout.write(self.shell.separate_out) if self.do_full_cache: @@ -67,10 +68,29 @@ class DreplDisplayHook(DisplayHook): @InteractiveShellABC.register -class Drepl(InteractiveShell): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - self.current_ps1 = None +class DRepl(InteractiveShell): + ps1 = Unicode( + "In [{}]: ", + help="Primary input prompt, with '{}' replaced by the execution count.", + ).tag(config=True) + ps2 = Unicode( + "...: ", + help="Secondary input prompt, used in multiline commands.", + ).tag(config=True) + ps3 = Unicode( + "\033[31mOut[{}]:\033[0m ", + help="String prepended to return values displayed in the shell.", + ).tag(config=True) + + def __init__(self, config) -> None: + # Default settings + self.config.HistoryManager.enabled = False + # User-supplied settings + for k, v in config.items(): + k0, dot, k1 = k.rpartition(".") + cfg = getattr(self.config, k0) if dot else self.config.DRepl + setattr(cfg, k1, v) + super().__init__() self.keep_running = True self.confirm_exit = True try: @@ -83,11 +103,10 @@ class Drepl(InteractiveShell): k: self.make_mime_renderer(k, v) for k, v in MIME_TYPES.items() } self.enable_mime_rendering() - # TODO: disable history self.show_banner() system = InteractiveShell.system_raw - displayhook_class = DreplDisplayHook + displayhook_class = DReplDisplayHook def make_mime_renderer(self, type, encoder): def renderer(data, meta=None): @@ -117,8 +136,7 @@ class Drepl(InteractiveShell): self.keep_running = False def enable_gui(self, gui=None): - if gui != "inline": - print("Can't enable this GUI: {}".format(gui)) + pass def mainloop(self): while self.keep_running: @@ -130,24 +148,19 @@ class Drepl(InteractiveShell): "Do you really want to exit ([y]/n)?", "y", "n" ): self.ask_exit() - except (DreplError, KeyboardInterrupt) as e: + except (DReplError, KeyboardInterrupt) as e: print(str(e) or e.__class__.__name__) def run_once(self): "Print prompt, run REPL until a new prompt is needed." - if self.current_ps1 is None: - sendmsg(op="getoptions") - self.current_ps1, separate_in = "", "" - else: - separate_in = self.separate_in if self.current_ps1 else "" - self.current_ps1 = sys.ps1.format(self.execution_count) - print(separate_in + self.current_ps1, end="") + self.current_ps1 = self.ps1.format(self.execution_count) + stdout.write(self.separate_in + self.current_ps1) while True: data = readmsg() op = data.pop("op") fun = getattr(self, "drepl_{}".format(op), None) if fun is None: - raise DreplError("Invalid op: {}".format(op)) + raise DReplError("Invalid op: {}".format(op)) fun(**data) if op == "eval": self.execution_count += 1 @@ -176,7 +189,7 @@ class Drepl(InteractiveShell): def drepl_checkinput(self, id, code): status, indent = self.check_complete(code) - prompt = sys.ps2.format(self.execution_count).rjust(len(self.current_ps1)) + prompt = self.ps2.format(self.execution_count).rjust(len(self.current_ps1)) sendmsg(id=id, status=status, indent=indent, prompt=prompt) def drepl_describe(self, id, code, pos): @@ -194,11 +207,7 @@ class Drepl(InteractiveShell): except Exception: sendmsg(id=id) - def drepl_setoptions(self, id, prompts=None): - if prompts: - sys.ps1, sys.ps2, sys.ps3, self.separate_in, self.separate_out = prompts - sendmsg(id=id) - if __name__ == "__main__": - Drepl.instance().mainloop() + config = json.loads(stdin.readline()) + DRepl.instance(config).mainloop()