diff --git a/doom-modeline-core.el b/doom-modeline-core.el index 7d1c2da..4e8f1f3 100644 --- a/doom-modeline-core.el +++ b/doom-modeline-core.el @@ -123,9 +123,6 @@ The icons may not be showed correctly in terminal and on Windows.") "Whether display environment version or not.") (define-obsolete-variable-alias 'doom-modeline-version 'doom-modeline-env-version "1.7.4") -(defvar doom-modeline-python-executable "python" - "What executable of Python will be used (if nil nothing will be showed).") - (defvar doom-modeline-mu4e t "Whether display mu4e notifications or not. Requires `mu4e-alert' package.") diff --git a/doom-modeline-env.el b/doom-modeline-env.el index 468998e..0d65522 100644 --- a/doom-modeline-env.el +++ b/doom-modeline-env.el @@ -44,19 +44,33 @@ Example: '(\"--version\") ") "A function that returns version number from a programs --version (or similar) command. Example: 'doom-modeline-env--ruby") +(defvar doom-modeline-load-string "..." + "What to dispaly as the version while a new one is being loaded.") + +(defvar doom-modeline-after-update-env-hook nil + "Hooks that run after the modeline version string is updated.") + +(defvar doom-modeline-before-update-env-hook nil + "Hooks that run before the modeline version string is updated.") + (defun doom-modeline-update-env () "Update environment info on mode-line." (when (and doom-modeline-env-version doom-modeline-env--command - (executable-find doom-modeline-env--command) doom-modeline-env--command-args doom-modeline-env--parser) - (let ((default-directory (doom-modeline-project-root))) - (doom-modeline-env--get doom-modeline-env--command - doom-modeline-env--command-args - (lambda (prog-version) - (setq doom-modeline-env--version - (funcall doom-modeline-env--parser prog-version))))))) + (let ((default-directory (doom-modeline-project-root)) + (buffer (current-buffer))) + (run-hooks 'doom-modeline-before-update-env-hook) + (setq doom-modeline-env--version doom-modeline-load-string) + (doom-modeline-env--get + doom-modeline-env--command + doom-modeline-env--command-args + (lambda (prog-version) + (with-current-buffer buffer + (setq doom-modeline-env--version + (funcall doom-modeline-env--parser prog-version)) + (run-hooks 'doom-modeline-after-update-env-hook))))))) (add-hook 'find-file-hook #'doom-modeline-update-env) (with-no-warnings @@ -89,55 +103,62 @@ Example: (ignore-errors (funcall parser line)))))) -;; -;; Parser -;; +(cl-defmacro doom-modeline-def-env (name &key hooks command parser) + "Defines a handler for updating & displaying a version string for a language. -(defun doom-modeline-env--ruby (line) - "Parse Ruby version from LINE." - (car (split-string - (cadr - (split-string line)) - "p"))) - -(defun doom-modeline-env--elixir (line) - "Parse Elixir version from LINE." - (cadr - (split-string line))) - -(defun doom-modeline-env--rustc (line) - "Parse Rust version from LINE." - (car - (split-string - (cadr - (split-string line)) - "-"))) - -(defun doom-modeline-env--go (line) - "Parse Go version from LINE." - (cadr - (split-string - (cadr - (cdr - (split-string - line))) - "go"))) - -(defun doom-modeline-env--perl (line) - "Parse Perl version from LINE." - (cadr - (split-string - (car - (split-string - (cadr - (split-string line "(")) - ")")) - "v"))) - -(defun doom-modeline-env--python (line) - "Parse Python version from LINE." - (cadr - (split-string line))) +NAME is an unquoted symbol representing the handler's unique ID. +HOOKS is a list of hook symbols where this handler should be triggered. +COMMAND should be a function that returns a shell command and its arguments (as + a list). It is run on HOOKS. It takes no arguments. +PARSER should be a function for parsing COMMAND's output line-by-line, to + extract the version string." + (declare (indent defun)) + (unless (and hooks command parser) + (error "'%s' env is missing either :hooks, :command or :parser" name)) + (let ((parse-fn (intern (format "doom-modeline-env--%s-parse" name))) + (action-fn (intern (format "doom-modeline-env--%s-args" name))) + (setup-fn (intern (format "doom-modeline-env-setup-%s" name))) + (update-fn (intern (format "doom-modeline-env-update-%s" name))) + (enable-var (intern (format "doom-modeline-env-enable-%s" name))) + (command-var (intern (format "doom-modeline-env-%s-command" name))) + (parser-var (intern (format "doom-modeline-env-%s-parser-fn" name))) + (exe-var (intern (format "doom-modeline-env-%s-executable" name)))) + (macroexp-progn + `((defvar ,enable-var t + (format "Whether to display the version string for %s buffers." ',name)) + (defvar ,command-var ',action-fn + (concat "A function that returns the shell command and arguments (as a list) to\n" + "produce a version string.")) + (defvar ,parser-var ',parse-fn + (format "The function for parsing each line of `%s's output." ',command-var)) + (defvar ,exe-var nil + (format (concat "What executable to use for the version indicator in %s buffers.\n\n" + "If nil, the default binary for this language is used.") + ',name)) + (defalias ',parse-fn ,parser + (format "The line parser for %s buffers.\n\nUsed by `%s'." + ',name ',update-fn)) + (defalias ',action-fn ,command + (format "The command resolver for %s buffers.\n\nUsed by `%s'." + ',name ',update-fn)) + (defalias ',setup-fn + (lambda () + (if enable-local-variables + (add-hook 'hack-local-variables-hook #',update-fn nil t) + (,update-fn))) + (format "Prepares the modeline to later display the %s version string." + ',name)) + (defalias ',update-fn + (lambda () + (when ,enable-var + (when-let* ((command (funcall ,command-var))) + (setq doom-modeline-env--command (car command) + doom-modeline-env--command-args (cdr command) + doom-modeline-env--parser ,parser-var) + (doom-modeline-update-env)))) + (format "Updates the %s version string in the modeline." ',name)) + (dolist (hook ',(doom-enlist (eval hooks))) + (add-hook hook #',setup-fn)))))) ;; @@ -145,43 +166,74 @@ Example: ;; Versions, support Python, Ruby, Perl and Golang, etc. ;; -(add-hook 'python-mode-hook - (lambda () - (cond ((and (fboundp 'pipenv-project-p) (pipenv-project-p) (executable-find "pipenv")) - (setq doom-modeline-env--parser 'doom-modeline-env--python) - (setq doom-modeline-env--command "pipenv") - (setq doom-modeline-env--command-args '("run" "python" "--version"))) - ((and doom-modeline-python-executable - (executable-find doom-modeline-python-executable)) - (setq doom-modeline-env--parser 'doom-modeline-env--python) - (setq doom-modeline-env--command doom-modeline-python-executable) - (setq doom-modeline-env--command-args '("--version")))))) -(add-hook 'ruby-mode-hook - (lambda () - (setq doom-modeline-env--command "ruby") - (setq doom-modeline-env--command-args '("--version")) - (setq doom-modeline-env--parser 'doom-modeline-env--ruby))) -(add-hook 'perl-mode-hook - (lambda () - (setq doom-modeline-env--command "perl") - (setq doom-modeline-env--command-args '("--version")) - (setq doom-modeline-env--parser 'doom-modeline-env--perl))) -(add-hook 'go-mode-hook - (lambda () - (setq doom-modeline-env--command "go") - (setq doom-modeline-env--command-args '("version")) - (setq doom-modeline-env--parser 'doom-modeline-env--go))) +;;;###autoload (autoload 'doom-modeline-env-setup-python "doom-modeline-env") +(doom-modeline-def-env python + :hooks 'python-mode-hook + :command (lambda () (cond ((and (fboundp 'pipenv-project-p) + (pipenv-project-p)) + (list "pipenv" "run" + (or doom-modeline-env-python-executable + python-shell-interpreter + "python") + "--version")) + ((list (or doom-modeline-env-python-executable + python-shell-interpreter + "python") + "--version")))) + :parser (lambda (line) (cadr (split-string line)))) -(add-hook 'elixir-mode-hook - (lambda () - (setq doom-modeline-env--command "iex") - (setq doom-modeline-env--command-args '("--version")) - (setq doom-modeline-env--parser 'doom-modeline-env--elixir))) -(add-hook 'rust-mode-hook - (lambda () - (setq doom-modeline-env--command "rustc") - (setq doom-modeline-env--command-args '("--version")) - (setq doom-modeline-env--parser 'doom-modeline-env--rustc))) +;;;###autoload (autoload 'doom-modeline-env-setup-ruby "doom-modeline-env") +(doom-modeline-def-env ruby + :hooks '(ruby-mode-hook enh-ruby-mode-hook) + :command (lambda () (list (or doom-modeline-env-ruby-executable "ruby") "--version")) + :parser (lambda (line) + (car (split-string + (cadr + (split-string line)) + "p")))) + +;;;###autoload (autoload 'doom-modeline-env-setup-perl "doom-modeline-env") +(doom-modeline-def-env perl + :hooks 'perl-mode-hook + :command (lambda () (list (or doom-modeline-env-perl-executable "perl") "--version")) + :parser (lambda (line) + (cadr + (split-string + (car + (split-string + (cadr + (split-string line "(")) + ")")) + "v")))) + +;;;###autoload (autoload 'doom-modeline-env-setup-go "doom-modeline-env") +(doom-modeline-def-env go + :hooks 'go-mode-hook + :command (lambda () (list (or doom-modeline-env-go-executable "go") "version")) + :parser (lambda (line) + (cadr + (split-string + (cadr + (cdr + (split-string line))) + "go")))) + +;;;###autoload (autoload 'doom-modeline-env-setup-elixir "doom-modeline-env") +(doom-modeline-def-env elixir + :hooks 'elixir-mode-hook + :command (lambda () (list (or doom-modeline-env-elixir-executable "iex") "--version")) + :parser (lambda () (cadr (split-string line)))) + +;;;###autoload (autoload 'doom-modeline-env-setup-rust "doom-modeline-env") +(doom-modeline-def-env rust + :hooks 'rust-mode-hook + :command (lambda () (list (or doom-modeline-env-rust-executable "rustc") "--version")) + :parser (lambda (line) + (car + (split-string + (cadr + (split-string line)) + "-")))) (provide 'doom-modeline-env)