diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index f2a27ff91dd..263b980ac18 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -51,6 +51,16 @@ ;; files inside the root must not be considered a part of it). It ;; should be consistent with `project-files'. ;; +;; `project-extra-info' is a user custom variable and a method to +;; specify extra project information like build command or equivalent. +;; The variable is a plist were the values can be strings or functions +;; that receive the project-current as argument. The +;; `project-extra-info' expect project current as first parameter and an +;; extra parameter equivalent to the plist key. The user defined plist +;; takes precedence over the backend defined methods, but all the +;; specializations are optional and the functions calling them may +;; provide conditions in case both are undefined. +;; ;; This list can change in future versions. ;; ;; Transient project: @@ -295,6 +305,52 @@ project-external-roots headers search path, load path, class path, and so on." nil) +(defcustom project-extra-info nil + "Project extra info defined in user space. + +This is intended to be set by the user. This is expected to be a plist +with key entries for compile. At the moment the implemented keys are +`:compile-command' and `:test-command'. The entries may be either +string constants, paths or functions. This custom has a symmetric +generic method with the same name that are intended to be implemented by +the project backends. When this variable is defined it takes precedence +over the backend methods." + :safe t + :version "30.1" + :type '(plist :key-type (choice (const :build-dir) + (const :compile-command)) + :value-type (choice string + directory + function))) + +(cl-defgeneric project-extra-info (_project _info) + "Return extra INFO for the current PROJECT. + +This function is intended to be defined by the backend when needed. +Otherwise this returns nil and the `project-compile' command will use +some default values. The current valid values for INFO are the same key +types in `project-extra-info': `:test-command' and `:compile-command' +This method is independent from the custom variable with same name +because project.el initializes itself lazily and variable propagation +within directories and buffers already open will require too much work +in the user side potentially more error prone." + nil) + +(defun project--get-extra-info (project info) + "Steps to get PROJECT's INFO internally. +1. Parse the user defined variable `project-extra-info'. If the key +exists: + a. Check if it is a function and call it passing project as the first +parameter. + b. If the key is a string return it as is. + c. Otherwise return nil. +2. Else call the backend defined method `project-extra-info'." + (if-let* ((value (plist-get project-extra-info info))) + (cond ((functionp value) (funcall value project)) + ((stringp info) info) + (t nil)) + (project-extra-info project info))) + (cl-defgeneric project-name (project) "A human-readable name for the PROJECT. Nominally unique, but not enforced." @@ -1477,16 +1533,32 @@ project-compilation-buffer-name-function project-prefixed-buffer-name) (function :tag "Custom function"))) -;;;###autoload -(defun project-compile () - "Run `compile' in the project root." - (declare (interactive-only compile)) - (interactive) - (let ((default-directory (project-root (project-current t))) - (compilation-buffer-name-function - (or project-compilation-buffer-name-function - compilation-buffer-name-function))) - (call-interactively #'compile))) +(defmacro project-compile-helper (name command-key) + "Run `compile' in the project root. +When the variable `project-extra-info' contains the entries +`command-key' or the project backend specializes the method +`project-extra-info' for those values; then this command uses that +instead of the default `compile-command'." + `(defun ,name () + (declare (interactive-only compile)) + (interactive) + (let* ((project (project-current t)) + (default-directory (project-root project)) + (compile-command + (or (project--get-extra-info project ,command-key) + compile-command)) + (compilation-buffer-name-function + (or project-compilation-buffer-name-function + compilation-buffer-name-function))) + (call-interactively #'compile)))) + + +;;;###autoload (autoload 'project-compile "project") +(project-compile-helper project-compile :compile-command) + +;;;###autoload (autoload 'project-test "project") +(project-compile-helper project-test :test-command) + ;;;###autoload (defun project-recompile (&optional edit-command)