From 1038e480382d243adcbb0d173050f7c41234a0b5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gerd=20M=C3=B6llmann?= Date: Wed, 18 Oct 2023 09:24:45 +0200 Subject: [PATCH] Gud LLDB completions (bug#66604) * etc/emacs_lldb.py: Remove xcomplete. * lisp/progmodes/gud.el: Implement lldb command completions. * src/.lldbinit: Remove settings done in Gud. --- etc/emacs_lldb.py | 30 ------------- lisp/progmodes/gud.el | 102 ++++++++++++++++++++++++++++++++++++++++-- src/.lldbinit | 4 -- 3 files changed, 99 insertions(+), 37 deletions(-) diff --git a/etc/emacs_lldb.py b/etc/emacs_lldb.py index f2c7a7987c7..fa8d95d7b5b 100644 --- a/etc/emacs_lldb.py +++ b/etc/emacs_lldb.py @@ -203,35 +203,6 @@ def xdebug_print(debugger, command, result, internal_dict): """Print Lisp_Objects using safe_debug_print()""" debugger.HandleCommand(f"expr safe_debug_print({command})") -# According to SBCommanInterpreter.cpp, the return value of -# HandleCompletions is as follows: -# -# Index 1 to the end contain all the completions. -# -# At index 0: -# -# If all completions have a common prefix, this is the shortest -# completion, with the common prefix removed from it. -# -# If it is the completion for a whole word, a space is added at the -# end. -# -# So, the prefix is what could be added to make the command partially -# complete. -# -# If there is no common prefix, index 0 has an empty string "". - -def xcomplete(debugger, command, result, internal_dict): - """Print completions for COMMAND.""" - interpreter = debugger.GetCommandInterpreter() - string_list = lldb.SBStringList() - interpreter.HandleCompletion(command, len(command), len(command), - -1, string_list) - list = "" - for i in range(string_list.GetSize()): - list += '"' + string_list.GetStringAtIndex(i) + '" ' - result.AppendMessage("(" + list + ")") - ######################################################################## # Formatters @@ -336,7 +307,6 @@ def enable_type_category(debugger, category): def __lldb_init_module(debugger, internal_dict): define_command(debugger, xbacktrace) define_command(debugger, xdebug_print) - define_command(debugger, xcomplete) define_type_summary(debugger, "Lisp_Object", type_summary_Lisp_Object) define_type_synthetic(debugger, "Lisp_Object", Lisp_Object_Provider) enable_type_category(debugger, "Emacs") diff --git a/lisp/progmodes/gud.el b/lisp/progmodes/gud.el index ea5a3580629..86836e153e5 100644 --- a/lisp/progmodes/gud.el +++ b/lisp/progmodes/gud.el @@ -3850,7 +3850,7 @@ so they have been disabled.")) ;; 'gud-lldb-history' and 'gud-gud-lldb-command-name' are required -;; because gud-symbol uses their values if they are present. Their +;; because 'gud-symbol' uses their values if they are present. Their ;; names are deduced from the minor-mode name. (defvar gud-lldb-history nil) @@ -3859,7 +3859,7 @@ so they have been disabled.")) :type 'string) (defun gud-lldb-marker-filter (string) - "Deduce interesting stuff from output STRING." + "Deduce interesting stuff from process output STRING." (cond (;; Process 72668 stopped ;; * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 ;; frame #0: ...) at emacs.c:1310:9 [opt] @@ -3879,6 +3879,96 @@ so they have been disabled.")) (setq gud-overlay-arrow-position nil))) string) +;; According to SBCommanInterpreter.cpp, the return value of +;; HandleCompletions is as follows: +;; +;; Index 1 to the end contain all the completions. +;; +;; At index 0: +;; +;; If all completions have a common prefix, this is the shortest +;; completion, with the common prefix removed from it. +;; +;; If it is the completion for a whole word, a space is added at the +;; end. +;; +;; So, the prefix is what could be added to make the command partially +;; complete. +;; +;; If there is no common prefix, index 0 has an empty string "". + +(defun gud-lldb-fetch-completions (context command) + "Return the data to complete the LLDB command before point. +This is what the Python function we installed at initialzation +time returns, as a Lisp list." + (let* ((process (get-buffer-process gud-comint-buffer)) + (to-complete (concat context command)) + (output-buffer (get-buffer-create "*lldb-completions*"))) + ;; Send the completion command with output to our buffer + (with-current-buffer output-buffer + (erase-buffer)) + (comint-redirect-send-command-to-process + (format "script --language python -- gud_complete('%s')" + to-complete) + output-buffer process nil t) + ;; Wait for output + (unwind-protect + (while (not comint-redirect-completed) + (accept-process-output process)) + (comint-redirect-cleanup)) + ;; Process the completion output. + (with-current-buffer output-buffer + (goto-char (point-min)) + (when (search-forward "gud-completions:" nil t) + (read (current-buffer)))))) + +(defun gud-lldb-completions (context command) + "Completion table for LLDB commands." + (let ((completions (gud-lldb-fetch-completions context command))) + ;; If this is a cmpletion for w whole word, return a completion + ;; list that contains that word only, with a space appended. + (if (string-suffix-p " " (car completions)) + (list (concat (cadr completions) " ")) + (cdr completions)))) + +(defun gud-lldb-completion-at-point () + "Return the data to complete the LLDB command before point." + (let* ((end (point)) + (line-start (comint-line-beginning-position)) + (start (save-excursion + (skip-chars-backward "^ " line-start) + (point))) + (context (buffer-substring line-start start))) + (list (copy-marker start t) + end + (completion-table-dynamic + (apply-partially #'gud-lldb-completions context))))) + +(defvar gud-lldb-def-python-completion-function + " +def gud_complete(s): + interpreter = lldb.debugger.GetCommandInterpreter() + string_list = lldb.SBStringList() + interpreter.HandleCompletion(s, len(s), len(s), -1, string_list) + print('gud-completions: (') + for i in range(string_list.GetSize()): + print(f'\"{string_list.GetStringAtIndex(i)}\" ') + print(')') +" + "LLDB command to define a Python function for completion.") + +(defun gud-lldb-send-python (python) + (gud-basic-call "script --language python --") + (mapc #'gud-basic-call (split-string python "\n")) + (gud-basic-call "exit()")) + +(defun gud-lldb-initialize () + "Initialize the LLDB process as needed for this debug session." + (gud-lldb-send-python gud-lldb-def-python-completion-function) + (gud-basic-call "settings set stop-line-count-before 0") + (gud-basic-call "settings set stop-line-count-after 0") + (gud-basic-call "script --language python -- print('Gud initialized')")) + ;;;###autoload (defun lldb (command-line) "Run lldb passing it COMMAND-LINE as arguments. @@ -3979,11 +4069,17 @@ the buffer in which this command was invoked." nil "Run the program.") + (add-hook 'completion-at-point-functions + #'gud-lldb-completion-at-point + nil 'local) + (keymap-local-set "" #'completion-at-point) + (gud-set-repeat-map-property 'gud-gdb-repeat-map) (setq comint-prompt-regexp (rx line-start "(lldb)" (0+ blank))) + (setq comint-process-echoes t) (setq paragraph-start comint-prompt-regexp) (setq gud-running nil) - (setq gud-filter-pending-text nil) + (gud-lldb-initialize) (run-hooks 'lldb-mode-hook)) (provide 'gud) diff --git a/src/.lldbinit b/src/.lldbinit index 430c48f91f0..a5789f49122 100644 --- a/src/.lldbinit +++ b/src/.lldbinit @@ -33,8 +33,4 @@ command script import emacs_lldb # Print with children provider, depth 2. command alias xprint frame variable -P 2 -# This is for M-x lldb: don't show source lines when stopping. -settings set stop-line-count-before 0 -settings set stop-line-count-after 0 - # end. -- 2.39.2