From f807eea272426b269228ed01b2e6fac84e642c5a Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Wed, 19 Oct 2022 08:38:34 +0300 Subject: [PATCH] ENHANCED: teach sweep to jump to C code for built-in predicates * sweeprolog.el: - sweeprolog-swipl-sources: new user option. - sweeprolog-native-predicate-location: new function. - sweeprolog-predicate-location: use it. - README.org: Built-in Native Predicates: new section. --- NEWS.org | 18 +++++++++++ README.org | 38 ++++++++++++++++++++++ sweeprolog.el | 87 +++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 137 insertions(+), 6 deletions(-) diff --git a/NEWS.org b/NEWS.org index 40f95e6..4f58936 100644 --- a/NEWS.org +++ b/NEWS.org @@ -11,6 +11,24 @@ SWI-Prolog in Emacs. For further details, please consult the manual: . + +* Version 0.7.1 on 2022-10-19 + +** Jumping to source works also for built-in predicates defined in C + +~sweep~ now knows how to find and jump to the definitions of native +built-in SWI-Prolog predicates defined in C, under the condition that +the user has the SWI-Prolog sources checked out locally. + +See ~C-h v sweeprolog-swipl-sources~ and the new section "Built-in +Native Predicates" in the manual for more information about this +feature. + +** Fixes and improvements to ~sweeprolog-describe-predicate~ + +This version fixes some compatibility issues with Emacs versions prior +to 29 in ~sweeprolog-describe-predicate~. Reported by Jan Wielemaker. + * Version 0.7.0 on 2022-10-17 ** New command ~sweeprolog-describe-predicate~ diff --git a/README.org b/README.org index 711748c..28fc8cf 100644 --- a/README.org +++ b/README.org @@ -1108,6 +1108,44 @@ For example, typing =C-x C-f library(pldoc/doc_man)= will open the source of the =pldoc_man= module from the Prolog library, and likewise =C-x C-f pack(.)= will open the Prolog packages directory. +** Built-in Native Predicates +:PROPERTIES: +:CUSTOM_ID: goto-c-predicates +:DESCRIPTION: Finding and jumping to definitions of built-in SWI-Prolog predicates defined in C +:END: + +#+CINDEX: native built-in predicates +Some of the built-in predicates provided by SWI-Prolog, such as ~is/2~, +are implemented in C and included as native functions in the +SWI-Prolog runtime. It is sometimes useful to examine the +implementation of such native built-in predicates by reading its +definition in the SWI-Prolog C sources. ~sweep~ knows about SWI-Prolog +native built-ins, and can find and jump to their definitions in C when +the user has the SWI-Prolog sources checked out locally. + +#+VINDEX: sweeprolog-swipl-sources +The way ~sweep~ locates the SWI-Prolog sources depends on the user +option ~sweeprolog-swipl-sources~. When customized to a string, it is +taken to be the path to the root directory of the SWI-Prolog source +code. If instead ~sweeprolog-swipl-sources~ is set to ~t~ (the default), +~sweep~ will try to locate a local checkout of the SWI-Prolog sources +automatically among known project root directories provided by Emacs' +built-in ~project-known-project-roots~ from =project.el= (see [[info:emacs#Projects][Projects in +the Emacs manual]] for more information about =project.el= projects). +Lastly, setting ~sweeprolog-swipl-sources~ to ~nil~ disables searching for +definitions of native built-ins. + +With ~sweeprolog-swipl-sources~ set, the provided commands for finding +predicate definitions operate seamlessly on native built-ins to +display their C definitions in ~c-mode~ buffers (see [[info:ccmode#Top][the Emacs CC Mode +manual]] for information about working with C code in Emacs). These +commands include: +- ~M-x sweeprolog-find-predicate~, +- ~M-.~ (~xref-find-definitions~) in ~sweeprolog-mode~ buffers (see + [[#sweeprolog-xref][Definitions and references]]), and +- ~s~ (~help-view-source~) in the =*Help*= buffer produced by ~M-x + sweeprolog-describe-predicate~ (see [[#prolog-help][Prolog Help]]). + * Quick access to sweep commands :PROPERTIES: :CUSTOM_ID: quick-command-access diff --git a/sweeprolog.el b/sweeprolog.el index 4d32647..3c1d9d7 100644 --- a/sweeprolog.el +++ b/sweeprolog.el @@ -6,8 +6,8 @@ ;; Maintainer: Eshel Yaron <~eshel/dev@lists.sr.ht> ;; Keywords: prolog languages extensions ;; URL: https://git.sr.ht/~eshel/sweep -;; Package-Version: 0.7.0 -;; Package-Requires: ((emacs "28")) +;; Package-Version: 0.7.1 +;; Package-Requires: ((emacs "28.1")) ;; This file is NOT part of GNU Emacs. @@ -40,6 +40,24 @@ "SWI-Prolog Embedded in Emacs." :group 'prolog) +(defcustom sweeprolog-swipl-sources t + "Location of the SWI-Prolog source code root directory. + +When non-nil, the function `sweeprolog-predicate-location' will +attempt to find the C defintions of SWI-Prolog native built-in +predicates. + +The value of this option can be a string, in which case it should +be a path to the SWI-Prolog source code root directory. Any +other non-nil value instructs `sweeprolog-predicate-location' to +try and find the SWI-Prolog sources among known project roots +obtained from `project-known-project-roots', which see." + :package-version '((sweeprolog . "0.4.6")) + :type '(choice (const :tag "Detect" t) + (string :tag "Manual") + (const :tag "Disable" nil)) + :group 'sweeprolog) + (defcustom sweeprolog-module-header-comment-skeleton ?\n "Additional content for the topmost comment in module headers. @@ -415,6 +433,7 @@ Otherwise set ARGS to nil." (interactive (and current-prefix-arg + (fboundp 'split-string-shell-command) (split-string-shell-command (read-string "swipl arguments: ")))) (when-let ((top-levels (seq-filter (lambda (buffer) (eq 'sweeprolog-top-level-mode @@ -464,13 +483,68 @@ When non-nil, only predicates whose name contains PREFIX are returned." (when (sweeprolog-true-p sol) (cdr sol)))) +(defun sweeprolog--mfn-to-functor-arity (mfn) + (let ((functor-arity (split-string (car (reverse (split-string mfn ":"))) "/"))) + (cons (car functor-arity) + (string-to-number (cadr functor-arity))))) + +(defun sweeprolog--swipl-source-directory () + (when sweeprolog-swipl-sources + (if (stringp sweeprolog-swipl-sources) + sweeprolog-swipl-sources + (when (fboundp 'project-known-project-roots) + (car (seq-filter + (lambda (root) + (member (car + (reverse + (seq-filter + (lambda (s) + (not (string-empty-p s))) + (file-name-split root)))) + '("swipl" "swipl-devel"))) + (project-known-project-roots))))))) + +(defun sweeprolog-native-predicate-location (mfn) + (let ((functor-arity (sweeprolog--mfn-to-functor-arity mfn))) + (when-let ((default-directory (sweeprolog--swipl-source-directory)) + (match + (car (xref-matches-in-files + (rx (or "PRED_IMPL" "FRG") + (zero-or-more whitespace) + "(\"" + (zero-or-more whitespace) + (literal (car functor-arity)) + "\"" + (zero-or-more whitespace) + "," + (zero-or-more whitespace) + (literal (number-to-string (cdr functor-arity)))) + (project-files (project-current) + (list (expand-file-name "src" + default-directory)))))) + (location (if (fboundp 'xref-match-item-location) + (xref-match-item-location match) + (xref-item-location match)))) + (if (fboundp 'xref-file-location-file) + (cons (xref-file-location-file location) + (xref-file-location-line location)) + (with-slots ((file file) + (line line)) + location + (cons file line)))))) + (defun sweeprolog-predicate-location (mfn) - "Return the source location where the predicate MFN is defined." + "Return the source location where the predicate MFN is defined. + +For native built-in predicates, the behavior of this function +depends on the value of the user option +`sweeprolog-swipl-sources', which see." (sweeprolog-open-query "user" "sweep" "sweep_predicate_location" mfn) (let ((sol (sweeprolog-next-solution))) (sweeprolog-close-query) - (when (sweeprolog-true-p sol) - (cdr sol)))) + (if (sweeprolog-true-p sol) + (cdr sol) + (sweeprolog-native-predicate-location mfn)))) (defun sweeprolog-predicate-apropos (pattern) "Return a list of predicates whose name resembeles PATTERN." @@ -2788,7 +2862,8 @@ if-then-else constructs in SWI-Prolog." nil (font-lock-fontify-region-function . sweeprolog-colourise-some-terms))) (when sweeprolog-enable-eldoc - (setq-local eldoc-documentation-strategy #'eldoc-documentation-default) + (when (fboundp 'eldoc-documentation-default) + (setq-local eldoc-documentation-strategy #'eldoc-documentation-default)) (add-hook 'eldoc-documentation-functions #'sweeprolog-predicate-modes-doc nil t)) (when sweeprolog-enable-flymake (add-hook 'flymake-diagnostic-functions #'sweeprolog-diagnostic-function nil t) -- 2.39.2