Archived
1
0
Fork 0

Compare commits

..

No commits in common. "fad5a5f91dd55277246675f43ce279a14cf482f4" and "cb49eaf0afd1e2e004a6865bf4d7dd93a67187f5" have entirely different histories.

577 changed files with 23012 additions and 59193 deletions

View file

@ -1,6 +1,6 @@
(define-package "all-the-icons" "20240623.1800" "A library for inserting Developer icons"
(define-package "all-the-icons" "20240108.559" "A library for inserting Developer icons"
'((emacs "24.3"))
:commit "39ef44f810c34e8900978788467cc675870bcd19" :authors
:commit "ee414384938ccf2ce93c77d717b85dc5538a257d" :authors
'(("Dominic Charlesworth" . "dgc336@gmail.com"))
:maintainers
'(("Dominic Charlesworth" . "dgc336@gmail.com"))

View file

@ -168,12 +168,6 @@
("dll" all-the-icons-faicon "cogs" :face all-the-icons-silver)
("ds_store" all-the-icons-faicon "cogs" :face all-the-icons-silver)
;; Source Codes
("ada" all-the-icons-fileicon "ada" :v-adjust 0.0 :face all-the-icons-blue)
("adb" all-the-icons-fileicon "ada" :v-adjust 0.0 :face all-the-icons-blue)
("adc" all-the-icons-fileicon "ada" :v-adjust 0.0 :face all-the-icons-blue)
("ads" all-the-icons-fileicon "ada" :v-adjust 0.0 :face all-the-icons-blue)
("gpr" all-the-icons-fileicon "ada" :v-adjust 0.0 :face all-the-icons-green)
("cgpr" all-the-icons-fileicon "ada" :v-adjust 0.0 :face all-the-icons-green)
("scpt" all-the-icons-fileicon "apple" :face all-the-icons-pink)
("aup" all-the-icons-fileicon "audacity" :face all-the-icons-yellow)
("elm" all-the-icons-fileicon "elm" :face all-the-icons-blue)
@ -190,6 +184,7 @@
("eclass" all-the-icons-fileicon "gentoo" :face all-the-icons-blue)
("go" all-the-icons-fileicon "go" :height 1.0 :face all-the-icons-blue)
("jl" all-the-icons-fileicon "julia" :face all-the-icons-purple :v-adjust 0.0)
("magik" all-the-icons-faicon "magic" :face all-the-icons-blue)
("matlab" all-the-icons-fileicon "matlab" :face all-the-icons-orange)
("nix" all-the-icons-fileicon "nix" :face all-the-icons-blue)
("pl" all-the-icons-alltheicon "perl" :face all-the-icons-lorange)
@ -688,8 +683,6 @@ for performance sake.")
(perl-mode all-the-icons-alltheicon "perl" :face all-the-icons-lorange)
(cperl-mode all-the-icons-alltheicon "perl" :face all-the-icons-lorange)
(php-mode all-the-icons-fileicon "php" :face all-the-icons-lsilver)
(php-ts-mode all-the-icons-fileicon "php" :face all-the-icons-lsilver)
(phps-mode all-the-icons-fileicon "php" :face all-the-icons-lsilver)
(prolog-mode all-the-icons-alltheicon "prolog" :height 1.1 :face all-the-icons-lmaroon)
(python-mode all-the-icons-alltheicon "python" :height 1.0 :face all-the-icons-dblue)
(python-ts-mode all-the-icons-alltheicon "python" :height 1.0 :face all-the-icons-dblue)
@ -702,10 +695,6 @@ for performance sake.")
(scheme-mode all-the-icons-fileicon "scheme" :height 1.2 :face all-the-icons-red)
(swift-mode all-the-icons-alltheicon "swift" :height 1.0 :v-adjust -0.1 :face all-the-icons-green)
(svelte-mode all-the-icons-fileicon "svelte" :v-adjust 0.0 :face all-the-icons-red)
(ada-mode all-the-icons-fileicon "ada" :v-adjust 0.0 :face all-the-icons-blue)
(ada-ts-mode all-the-icons-fileicon "ada" :v-adjust 0.0 :face all-the-icons-blue)
(gpr-mode all-the-icons-fileicon "ada" :v-adjust 0.0 :face all-the-icons-green)
(gpr-ts-mode all-the-icons-fileicon "ada" :v-adjust 0.0 :face all-the-icons-green)
(c-mode all-the-icons-alltheicon "c-line" :face all-the-icons-blue)
(c-ts-mode all-the-icons-alltheicon "c-line" :face all-the-icons-blue)
(c++-mode all-the-icons-alltheicon "cplusplus-line" :v-adjust -0.2 :face all-the-icons-blue)
@ -784,6 +773,8 @@ for performance sake.")
(emms-tag-editor-mode all-the-icons-faicon "music" :face all-the-icons-silver)
(emms-playlist-mode all-the-icons-faicon "music" :face all-the-icons-silver)
(lilypond-mode all-the-icons-faicon "music" :face all-the-icons-green)
(magik-session-mode all-the-icons-alltheicon "terminal" :face all-the-icons-blue)
(magik-cb-mode all-the-icons-faicon "book" :face all-the-icons-blue)
(meson-mode all-the-icons-fileicon "meson" :face all-the-icons-purple)
(man-common all-the-icons-fileicon "man-page" :face all-the-icons-blue)
(ess-r-mode all-the-icons-fileicon "R" :face all-the-icons-lblue)))

File diff suppressed because it is too large Load diff

View file

@ -138,11 +138,6 @@ Same as `byte-compile-file' but asynchronous.
(fn FILE)" t)
(register-definition-prefixes "async-bytecomp" '("async-"))
;;; Generated autoloads from async-package.el
(register-definition-prefixes "async-package" '("async-package-"))
;;; Generated autoloads from dired-async.el

View file

@ -60,11 +60,19 @@ all packages are always compiled asynchronously."
(defvar async-bytecomp-load-variable-regexp "\\`load-path\\'"
"The variable used by `async-inject-variables' when (re)compiling async.")
(defun async-bytecomp--file-to-comp-buffer (file-or-dir &optional quiet type)
(let ((bn (file-name-nondirectory file-or-dir))
(action-name (pcase type
('file "File")
('directory "Directory"))))
;;;###autoload
(defun async-byte-recompile-directory (directory &optional quiet)
"Compile all *.el files in DIRECTORY asynchronously.
All *.elc files are systematically deleted before proceeding."
(cl-loop with dir = (directory-files directory t "\\.elc\\'")
unless dir return nil
for f in dir
when (file-exists-p f) do (delete-file f))
;; Ensure async is reloaded when async.elc is deleted.
;; This happen when recompiling its own directory.
(load "async")
(let ((call-back
(lambda (&optional _ignore)
(if (file-exists-p async-byte-compile-log-file)
(let ((buf (get-buffer-create byte-compile-log-buffer))
(n 0))
@ -81,26 +89,10 @@ all packages are always compiled asynchronously."
(while (re-search-forward "^.*:Error:" nil t)
(cl-incf n)))
(if (> n 0)
(message "Failed to compile %d files in directory `%s'" n bn)
(message "%s `%s' compiled asynchronously with warnings"
action-name bn)))))
(message "Failed to compile %d files in directory `%s'" n directory)
(message "Directory `%s' compiled asynchronously with warnings" directory)))))
(unless quiet
(message "%s `%s' compiled asynchronously with success" action-name bn)))))
;;;###autoload
(defun async-byte-recompile-directory (directory &optional quiet)
"Compile all *.el files in DIRECTORY asynchronously.
All *.elc files are systematically deleted before proceeding."
(cl-loop with dir = (directory-files directory t "\\.elc\\'")
unless dir return nil
for f in dir
when (file-exists-p f) do (delete-file f))
;; Ensure async is reloaded when async.elc is deleted.
;; This happen when recompiling its own directory.
(load "async")
(let ((call-back
(lambda (&optional _ignore)
(async-bytecomp--file-to-comp-buffer directory quiet 'directory))))
(message "Directory `%s' compiled asynchronously with success" directory))))))
(async-start
`(lambda ()
(require 'bytecomp)
@ -148,10 +140,13 @@ All *.elc files are systematically deleted before proceeding."
(memq cur-package (async-bytecomp--get-package-deps
async-bytecomp-allowed-packages)))
(progn
;; FIXME: Why do we use (eq cur-package 'async) once
;; and (string= cur-package "async") afterwards?
(when (eq cur-package 'async)
(fmakunbound 'async-byte-recompile-directory)
(fmakunbound 'async-byte-recompile-directory))
;; Add to `load-path' the latest version of async and
;; reload it when reinstalling async.
(when (string= cur-package "async")
(cl-pushnew pkg-dir load-path)
(load "async-bytecomp"))
;; `async-byte-recompile-directory' will add directory
@ -178,13 +173,28 @@ Same as `byte-compile-file' but asynchronous."
(interactive "fFile: ")
(let ((call-back
(lambda (&optional _ignore)
(async-bytecomp--file-to-comp-buffer file nil 'file))))
(let ((bn (file-name-nondirectory file)))
(if (file-exists-p async-byte-compile-log-file)
(let ((buf (get-buffer-create byte-compile-log-buffer))
start)
(with-current-buffer buf
(goto-char (setq start (point-max)))
(let ((inhibit-read-only t))
(insert-file-contents async-byte-compile-log-file)
(compilation-mode))
(display-buffer buf)
(delete-file async-byte-compile-log-file)
(save-excursion
(goto-char start)
(if (re-search-forward "^.*:Error:" nil t)
(message "Failed to compile `%s'" bn)
(message "`%s' compiled asynchronously with warnings" bn)))))
(message "`%s' compiled asynchronously with success" bn))))))
(async-start
`(lambda ()
(require 'bytecomp)
,(async-inject-variables async-bytecomp-load-variable-regexp)
(let ((default-directory ,(file-name-directory file))
error-data)
(let ((default-directory ,(file-name-directory file)))
(add-to-list 'load-path default-directory)
(byte-compile-file ,file)
(when (get-buffer byte-compile-log-buffer)

View file

@ -1,6 +1,6 @@
(define-package "async" "20240719.640" "Asynchronous processing in Emacs"
(define-package "async" "20240312.1716" "Asynchronous processing in Emacs"
'((emacs "24.4"))
:commit "93957f5b0f2ed22aeef02972de860fb9c45b4422" :authors
:commit "a368df08d34ae0c0a36fc3e90b1ecda252915257" :authors
'(("John Wiegley" . "jwiegley@gmail.com"))
:maintainers
'(("Thierry Volpiatto" . "thievol@posteo.net"))

View file

@ -50,13 +50,6 @@ When this is nil child Emacs will hang forever when a user interaction
for password is required unless a password is stored in a \".authinfo\" file."
:type 'boolean)
(defvar async-process-noquery-on-exit nil
"Used as the :noquery argument to `make-process'.
Intended to be let-bound around a call to `async-start' or
`async-start-process'. If non-nil, the child Emacs process will
be silently killed if the user exits the parent Emacs.")
(defvar async-debug nil)
(defvar async-send-over-pipe t)
(defvar async-in-child-emacs nil)
@ -117,17 +110,14 @@ is returned unmodified."
collect elm))
(t object)))
(defvar async-inject-variables-exclude-regexps '("-syntax-table\\'")
"A list of regexps that `async-inject-variables' should ignore.")
(defun async-inject-variables
(include-regexp &optional predicate exclude-regexp noprops)
"Return a `setq' form that replicates part of the calling environment.
It sets the value for every variable matching INCLUDE-REGEXP and
also PREDICATE. It will not perform injection for any variable
matching EXCLUDE-REGEXP (if present) and variables matching one of
`async-inject-variables-exclude-regexps'.
matching EXCLUDE-REGEXP (if present) or representing a `syntax-table'
i.e. ending by \"-syntax-table\".
When NOPROPS is non nil it tries to strip out text properties of each
variable's value with `async-variables-noprops-function'.
@ -146,16 +136,14 @@ It is intended to be used as follows:
,@(let (bindings)
(mapatoms
(lambda (sym)
(let ((sname (and (boundp sym) (symbol-name sym)))
value)
(let* ((sname (and (boundp sym) (symbol-name sym)))
(value (and sname (symbol-value sym))))
(when (and sname
(or (null include-regexp)
(string-match include-regexp sname))
(or (null exclude-regexp)
(not (string-match exclude-regexp sname)))
(cl-loop for re in async-inject-variables-exclude-regexps
never (string-match-p re sname)))
(setq value (symbol-value sym))
(not (string-match "-syntax-table\\'" sname)))
(unless (or (stringp value)
(memq value '(nil t))
(numberp value)
@ -438,8 +426,7 @@ working directory."
:name name
:buffer buf
:stderr buf-err
:command (cons program program-args)
:noquery async-process-noquery-on-exit))))
:command (cons program program-args)))))
(set-process-sentinel
(get-buffer-process buf-err)
(lambda (proc _change)

View file

@ -387,7 +387,6 @@ ESC or `q' to not overwrite any of the remaining files,
(dired-copy-preserve-time
,dired-copy-preserve-time)
(dired-create-destination-dirs ',create-dir)
(dired-vc-rename-file ,dired-vc-rename-file)
auth-source-save-behavior)
(setq overwrite-backup-query nil)
;; Inline `backup-file' as long as it is not

View file

@ -1,132 +0,0 @@
;;; async-package.el --- Fetch packages asynchronously -*- lexical-binding: t -*-
;; Copyright (C) 2014-2022 Free Software Foundation, Inc.
;; Author: Thierry Volpiatto <thievol@posteo.net>
;; Keywords: dired async byte-compile package
;; X-URL: https://github.com/jwiegley/emacs-async
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Provide the function `async-package-do-action' to
;; (re)install/upgrade packages asynchronously.
;;; Code:
(eval-when-compile (require 'cl-lib))
(require 'async-bytecomp)
(require 'dired-async)
(require 'package)
(define-minor-mode async-package--modeline-mode
"Notify mode-line that an async process run."
:group 'async
:global t
:lighter (:eval (propertize (format " [%s async job Installing package(s)]"
(length (dired-async-processes
'async-pkg-install)))
'face 'async-package-message))
(unless async-package--modeline-mode
(let ((visible-bell t)) (ding))))
(defface async-package-message
'((t (:foreground "yellow")))
"Face used for mode-line message.")
(defun async-package-do-action (action packages error-file)
"Execute ACTION asynchronously on PACKAGES.
Argument ACTION can be one of \\='install, \\='upgrade, \\='reinstall.
Argument PACKAGES is a list of packages (symbols).
Argument ERROR-FILE is the file where errors are logged, if some."
(require 'async-bytecomp)
(let ((fn (pcase action
('install 'package-install)
('upgrade 'package-upgrade)
('reinstall 'package-reinstall)))
(action-string (pcase action
('install "Installing")
('upgrade "Upgrading")
('reinstall "Reinstalling"))))
(message "%s %s package(s)..." action-string (length packages))
(process-put
(async-start
`(lambda ()
(require 'bytecomp)
(setq package-archives ',package-archives
package-pinned-packages ',package-pinned-packages
package-archive-contents ',package-archive-contents
package-alist ',package-alist
load-path ',load-path)
(prog1
(condition-case err
(mapc ',fn ',packages)
(error
(with-temp-file ,error-file
(insert
(format
"%S:\n Please refresh package list before %s"
err ,action-string)))))
(let (error-data)
(when (get-buffer byte-compile-log-buffer)
(setq error-data (with-current-buffer byte-compile-log-buffer
(buffer-substring-no-properties
(point-min) (point-max))))
(unless (string= error-data "")
(with-temp-file ,async-byte-compile-log-file
(erase-buffer)
(insert error-data)))))))
(lambda (result)
(if (file-exists-p error-file)
(let ((buf (find-file-noselect error-file)))
(pop-to-buffer
buf '(nil . ((window-height . fit-window-to-buffer))))
(special-mode)
(delete-file error-file)
(async-package--modeline-mode -1))
(when result
(let ((pkgs (if (listp result) result (list result))))
(when (eq action 'install)
(customize-save-variable
'package-selected-packages
(delete-dups (append pkgs package-selected-packages))))
(package-load-all-descriptors) ; refresh package-alist.
(mapc #'package-activate pkgs) ; load packages.
(async-package--modeline-mode -1)
(message "%s %s packages done" action-string (length packages))
(run-with-timer
0.1 nil
(lambda (lst str)
(dired-async-mode-line-message
"%s %d package(s) done"
'async-package-message
str (length lst)))
packages action-string)
(when (file-exists-p async-byte-compile-log-file)
(let ((buf (get-buffer-create byte-compile-log-buffer)))
(with-current-buffer buf
(goto-char (point-max))
(let ((inhibit-read-only t))
(insert-file-contents async-byte-compile-log-file)
(compilation-mode))
(display-buffer buf)
(delete-file async-byte-compile-log-file)))))))))
'async-pkg-install t)
(async-package--modeline-mode 1)))
(provide 'async-package)
;;; async-package.el ends here

View file

@ -1,14 +1,12 @@
;;; centaur-tabs-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; centaur-tabs-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "centaur-tabs" "centaur-tabs.el" (0 0 0 0))
;;; Generated autoloads from centaur-tabs.el
(autoload 'centaur-tabs-local-mode "centaur-tabs" "\
@ -20,7 +18,8 @@ the tab bar. The tab bar is locally hidden otherwise. When turned
off, if a local header line is hidden or the tab bar is locally
hidden, it is shown again. Signal an error if Centaur-Tabs mode is off.
(fn &optional ARG)" t)
\(fn &optional ARG)" t nil)
(defvar centaur-tabs-mode nil "\
Non-nil if Centaur-Tabs mode is enabled.
See the `centaur-tabs-mode' command
@ -28,7 +27,9 @@ for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `centaur-tabs-mode'.")
(custom-autoload 'centaur-tabs-mode "centaur-tabs" nil)
(autoload 'centaur-tabs-mode "centaur-tabs" "\
Toggle display of a tab bar in the header line.
With prefix argument ARG, turn on if positive, otherwise off.
@ -36,50 +37,67 @@ Returns non-nil if the new state is enabled.
\\{centaur-tabs-mode-map}
(fn &optional ARG)" t)
\(fn &optional ARG)" t nil)
(register-definition-prefixes "centaur-tabs" '("centaur-tabs-"))
;;;***
;;;### (autoloads nil "centaur-tabs-elements" "centaur-tabs-elements.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from centaur-tabs-elements.el
(register-definition-prefixes "centaur-tabs-elements" '("centaur-tabs-"))
(register-definition-prefixes "centaur-tabs-elements" '("cent"))
;;;***
;;;### (autoloads nil "centaur-tabs-functions" "centaur-tabs-functions.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from centaur-tabs-functions.el
(autoload 'centaur-tabs-backward "centaur-tabs-functions" "\
Select the previous available tab.
Depend on the setting of the option `centaur-tabs-cycle-scope'." t)
Depend on the setting of the option `centaur-tabs-cycle-scope'." t nil)
(autoload 'centaur-tabs-forward "centaur-tabs-functions" "\
Select the next available tab.
Depend on the setting of the option `centaur-tabs-cycle-scope'." t)
Depend on the setting of the option `centaur-tabs-cycle-scope'." t nil)
(autoload 'centaur-tabs-backward-group "centaur-tabs-functions" "\
Go to selected tab in the previous available group." t)
Go to selected tab in the previous available group." t nil)
(autoload 'centaur-tabs-forward-group "centaur-tabs-functions" "\
Go to selected tab in the next available group." t)
Go to selected tab in the next available group." t nil)
(autoload 'centaur-tabs-backward-tab "centaur-tabs-functions" "\
Select the previous visible tab." t)
Select the previous visible tab." t nil)
(autoload 'centaur-tabs-forward-tab "centaur-tabs-functions" "\
Select the next visible tab." t)
Select the next visible tab." t nil)
(register-definition-prefixes "centaur-tabs-functions" '("centaur-tabs-"))
;;;***
;;;### (autoloads nil "centaur-tabs-interactive" "centaur-tabs-interactive.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from centaur-tabs-interactive.el
(autoload 'centaur-tabs-counsel-switch-group "centaur-tabs-interactive" "\
Display a list of current buffer groups using Counsel." t)
Display a list of current buffer groups using Counsel." t nil)
(register-definition-prefixes "centaur-tabs-interactive" '("centaur-tabs-"))
;;;***
;;; End of scraped data
(provide 'centaur-tabs-autoloads)
;;;### (autoloads nil nil ("centaur-tabs-pkg.el") (0 0 0 0))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; coding: utf-8
;; End:
;;; centaur-tabs-autoloads.el ends here

View file

@ -0,0 +1,865 @@
;;; centaur-tabs-elements.el --- centaur-tabs visual components and customizations -*- lexical-binding: t; -*-
;; Copyright (C) 2019-2020 Emmanuel Bustos
;; Package-Requires: ((emacs "24.4") (powerline "2.4") (cl-lib "0.5"))
;; This file is not part of GNU Emacs.
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;; Commentary:
;; This file contains the visual components of centaur-tabs
;;; Code:
;;
;;; Requires
;;
(require 'color)
(require 'powerline)
;;; Faces
;;
(defface centaur-tabs-default
'((t
(:background "black" :foreground "black")))
"Default face used in the tab bar."
:group 'centaur-tabs)
(defface centaur-tabs-unselected
'((t
(:background "#3D3C3D" :foreground "grey50")))
"Face used for unselected tabs."
:group 'centaur-tabs)
(defface centaur-tabs-selected
'((t (:background "#31343E" :foreground "white")))
"Face used for the selected tab."
:group 'centaur-tabs)
(defface centaur-tabs-unselected-modified
'((t
(:background "#3D3C3D" :foreground "grey50")))
"Face used for unselected-modified tabs."
:group 'centaur-tabs)
(defface centaur-tabs-selected-modified
'((t (:background "#31343E" :foreground "white")))
"Face used for the selected-modified tab."
:group 'centaur-tabs)
(defface centaur-tabs-close-unselected
'((t
(:inherit centaur-tabs-unselected)))
"Face used for unselected close button."
:group 'centaur-tabs)
(defface centaur-tabs-close-selected
'((t (:inherit centaur-tabs-selected)))
"Face used for selected close button."
:group 'centaur-tabs)
(defface centaur-tabs-name-mouse-face
'((t nil))
"Face used for tab name when hovered with the mouse."
:group 'centaur-tabs)
(defface centaur-tabs-close-mouse-face
'((t (:inherit underline)))
"Face used for close button when hovered with the mouse."
:group 'centaur-tabs)
(defface centaur-tabs-modified-marker-selected
`((t (:inherit centaur-tabs-selected)))
"Face used for selected modified marker."
:group 'centaur-tabs)
(defface centaur-tabs-modified-marker-unselected
`((t (:inherit centaur-tabs-unselected)))
"Face used for unselected modified marker."
:group 'centaur-tabs)
(defface centaur-tabs-active-bar-face
'((t (:background "cyan")))
"Face used for selected tab bar."
:group 'centaur-tabs)
(defface centaur-tabs-jump-identifier-selected
'((t (:inherit centaur-tabs-modified-marker-selected :weight extra-bold)))
"Face used for selected tab identifiers when centaur-tabs-ace-jump is invoked."
:group 'centaur-tabs)
(defface centaur-tabs-jump-identifier-unselected
'((t (:inherit centaur-tabs-modified-marker-unselected :weight extra-bold)))
"Face used for unselected tab identifiers when centaur-tabs-ace-jump is invoked."
:group 'centaur-tabs)
(defface centaur-tabs-dim-buffer-face
'((t (:foreground "gray40")))
"Face for the buffer when centaur-tabs-ace-jump is invoked.")
;;; Tabs' display line
;;
(defvar centaur-tabs-display-line
(if (boundp 'tab-line-format)
'tab-line
'header-line))
(defvar centaur-tabs-display-line-format
(if (boundp 'tab-line-format)
'tab-line-format
'header-line-format))
;;; Tabs' characteristics
;;
(defcustom centaur-tabs-style "bar"
"The style of tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-label-fixed-length 0
"Fixed length of label. Set to 0 if dynamic."
:group 'centaur-tabs
:type 'int)
(defcustom centaur-tabs-background-color
(face-background 'centaur-tabs-default nil 'default)
"*Background color of the tab bar.
By default, use the background color specified for the
`centaur-tabs-default' face (or inherited from another face), or the
background color of the `default' face otherwise."
:group 'centaur-tabs
:type 'face)
(defcustom centaur-tabs-height 22
"The height of tab."
:group 'centaur-tabs
:type 'int)
(defcustom centaur-tabs-bar-height (+ 8 centaur-tabs-height)
"The height of bar."
:group 'centaur-tabs
:type 'int)
(defcustom centaur-tabs-mouse-pointer 'hand
"Cursor to display when hovering the tabs.
Default is 'hand. The following scopes are possible:
- arrow
- hand
- vdrag
- hdrag
- modeline
- hourglass"
:group 'centaur-tabs
:type 'variable)
;;; Icons
;;
(defcustom centaur-tabs-set-icons nil
"When non nil, display an icon based on `centaur-tabs-icon-type' alongside the tab name."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-icon-type (and centaur-tabs-set-icons
(or (require 'all-the-icons nil t)
(require 'nerd-icons nil t)))
"Icon type. It should be one of `all-the-icons' and `nerd-icons'."
:group 'centaur-tabs
:type 'symbol
:set
(lambda (k v)
(pcase v
('all-the-icons
(unless (require 'all-the-icons nil t)
(setq v nil)))
('nerd-icons
(unless (require 'nerd-icons nil t)
(setq v nil)))
(type
(if (require 'all-the-icons nil t)
(setq v 'all-the-icons)
(setq v nil))))
(set k v)))
(defvar centaur-tabs-icon-scale-factor
1.0
"The base scale factor for the `height' face property of tab icons.")
(defvar centaur-tabs-icon-v-adjust
0.01
"The vertical adjust for tab icons.")
(defcustom centaur-tabs-gray-out-icons nil
"When non nil, enable gray icons for unselected buffer."
:group 'centaur-tabs
:type '(choice :tag "Gray out icons for unselected..."
(const :tag "Buffer" buffer)))
(defcustom centaur-tabs-plain-icons nil
"When non nil, tab icons' color will be the same as tabs' foreground color."
:group 'centaur-tabs
:type 'boolean)
(defun centaur-tabs--icon-for-file (file &rest args)
"Get the formatted icon for FILE.
ARGS should be a plist containing `:height', `:v-adjust', or `:face' properties."
(pcase centaur-tabs-icon-type
('all-the-icons (apply #'all-the-icons-icon-for-file file args))
('nerd-icons (apply #'nerd-icons-icon-for-file file args))))
(defun centaur-tabs--icon-for-mode (mode &rest args)
"Get the formatted icon for MODE.
ARG-OVERRIDES should be a plist containining `:height',
`:v-adjust' or `:face' properties like in the normal icon
inserting functions."
(pcase centaur-tabs-icon-type
('all-the-icons (apply #'all-the-icons-icon-for-mode mode args))
('nerd-icons (apply #'nerd-icons-icon-for-mode mode args))))
(defun centaur-tabs--auto-mode-match? (&optional file)
"Whether or not FILE's `major-mode' match against its `auto-mode-alist'."
(pcase centaur-tabs-icon-type
('all-the-icons (apply #'all-the-icons-auto-mode-match? file))
('nerd-icons (apply #'nerd-icons-auto-mode-match? file))))
(defun centaur-tabs-icon (tab face selected)
"Generate icon for TAB using FACE's background.
If icon gray out option enabled, gray out icon if not SELECTED."
(if centaur-tabs-icon-type
(with-current-buffer (car tab)
(let* ((icon
(if (and (buffer-file-name)
(centaur-tabs--auto-mode-match?))
(centaur-tabs--icon-for-file
(file-name-nondirectory (buffer-file-name))
:v-adjust centaur-tabs-icon-v-adjust
:height centaur-tabs-icon-scale-factor)
(centaur-tabs--icon-for-mode
major-mode
:v-adjust centaur-tabs-icon-v-adjust
:height centaur-tabs-icon-scale-factor)))
(background (face-background face nil 'default))
(inactive (cond ((and (not selected)
(eq centaur-tabs-gray-out-icons 'buffer))
(face-foreground 'mode-line-inactive nil 'default))
(centaur-tabs-plain-icons
(face-foreground 'centaur-tabs-selected nil 'default))
(t 'unspecified)))
(underline (and (eq (if (display-graphic-p) centaur-tabs-set-bar) 'under)
(face-attribute face :underline)))
(overline (and (eq (if (display-graphic-p) centaur-tabs-set-bar) 'over)
(face-attribute face :overline))))
(if (stringp icon)
(progn
(propertize icon 'face `(:inherit ,(get-text-property 0 'face icon)
:foreground ,inactive
:background ,background
:underline ,underline
:overline ,overline)))
"")))
""))
;;; Ace-window style tab switching
;;
(defcustom centaur-tabs-show-jump-identifier 'prompted
"Whether to show the tab identifier for centaur-tabs-ace-jump.
It has 3 options:
- 'nil, never show the jump identifier.
- 'prompted, only show it when using centaur-tabs-ace-jump.
- 'always, always show it regardless of the status."
:group 'centaur-tabs
:type '(choice :tag "show identifier when..."
(const :tag "Never" nil)
(const :tag "Only when prompted" prompted)
(const :tag "Always" always)))
(defcustom centaur-tabs-ace-jump-dim-buffer t
"Whether to dim the current buffer when centaur-ace-jump is activated.")
(defvar centaur-tabs-ace-jump-keys
'(?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)
"Buffer jump keys used by centaur-tabs-ace-jump.")
(defvar centuar-tabs-ace-dispatch-alist
'((?q exit "Exit")
(?\C-g exit "Exit")
(?j jump-to-tab "Jump to tab")
(?x close-tab "Close tab")
(?s swap-tab "Swap tab")
(?\[ backward-group "Previous group")
(?\] forward-group "Next group")
(?? show-help "Show dispatch help"))
"Action keys used by centaur-tabs-ace-jump.
The value of each element must be in the form:
\(key keyword docstring), where keyword must be one of the follows:
\(exit, jump-to-tab, close-tab, swap-tab, backward-group,
forward-group, show-help).")
;;; Close buttons, modified marker and edges' margins
;;
(defcustom centaur-tabs-set-close-button t
"When non nil, display a clickable close button on the right side of the tabs."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-set-left-close-button nil
"When non nil, display a clickable close button on the left side of the tabs."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-close-button (make-string 1 #x00D7)
"Display appearance of the close buttons, if enabled."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-set-modified-marker nil
"When non nil, display a marker when the buffer is modified."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-modified-marker (make-string 1 #x23FA)
"Display appearance of the modified marker, if enabled."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-left-edge-margin " "
"Text to display at the left edge of the tabs, or nil for no added margin."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-right-edge-margin " "
"Text to display at the right edge of the tabs, or nil for no added margin."
:group 'centaur-tabs
:type 'string)
;;; Selected tab bar
;;
(defcustom centaur-tabs-set-bar nil
"When non nil, display a bar to show the currently selected tab.
There are three options:
- 'left: displays the bar at the left of the currently selected tab.
- 'under: displays the bar under the currently selected tab.
- 'over: displays the bar over the currently selected tab."
:group 'centaur-tabs
:type '(choice :tag "Display bar at..."
(const :tag "Put bar on the left" left)
(const :tag "Put bar as an underline" under)
(const :tag "Put bar as an overline" over)))
(defun centaur-tabs--make-xpm (face width height)
"Create an XPM bitmap via FACE WIDTH and HEIGHT.
Taken from `doom-modeline'."
(when (and (display-graphic-p)
(image-type-available-p 'xpm))
(propertize
" " 'display
(let ((data (make-list height (make-list width 1)))
(color (or (face-background face nil t) "None")))
(ignore-errors
(create-image
(concat
(format
"/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\","
(length (car data)) (length data) color color)
(apply #'concat
(cl-loop with idx = 0
with len = (length data)
for dl in data
do (cl-incf idx)
collect
(concat
"\""
(cl-loop for d in dl
if (= d 0) collect (string-to-char " ")
else collect (string-to-char "."))
(if (eq idx len) "\"};" "\",\n")))))
'xpm t :ascent 'center))))))
(defvar centaur-tabs-active-bar
(centaur-tabs--make-xpm 'centaur-tabs-active-bar-face
2
centaur-tabs-bar-height))
;;; Navigation buttons
;;
(defcustom centaur-tabs-show-navigation-buttons nil
"When non-nil, show the buttons for backward/forward tabs."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-down-tab-text ""
"Text icon to show in the down button tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-backward-tab-text ""
"Text icon to show in the backward button tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-forward-tab-text ""
"Text icon to show in the forward button tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-show-count nil
"When non-nil, show the current index and count of tabs in the current group."
:group 'centaur-tabs
:type 'boolean)
;;; New tab button
;;
(defcustom centaur-tabs-show-new-tab-button t
"When non-nil, show the button to create a new tab."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-new-tab-text " + "
"Text icon to show in the new-tab button."
:group 'centaur-tabs
:type 'string)
;;; Separators
;;
(defvar centaur-tabs-style-left nil)
(defvar centaur-tabs-style-right nil)
(defvar ns-use-srgb-colorspace)
(defvar centaur-tabs-image-apple-rgb
(and (eq (window-system) 'ns)
ns-use-srgb-colorspace
(< 11
(string-to-number
(and (string-match "darwin\\([0-9]+\\)" system-configuration)
(match-string-no-properties 1 system-configuration)))))
"Boolean variable to determine whether to use Apple RGB colorspace.
used to render images.
t on macOS 10.7+ and `ns-use-srgb-colorspace' is t, nil otherwise.
This variable is automatically set, there's no need to modify it.")
(defun centaur-tabs-separator-interpolate (color1 color2)
"Interpolate between COLOR1 and COLOR2.
COLOR1 and COLOR2 must be supplied as hex strings with a leading #."
(let* ((c1 (color-name-to-rgb color1))
(c2 (color-name-to-rgb color2))
(red (/ (+ (nth 0 c1) (nth 0 c2)) 2))
(green (/ (+ (nth 1 c1) (nth 1 c2)) 2))
(blue (/ (+ (nth 2 c1) (nth 2 c2)) 2)))
(color-rgb-to-hex red green blue)))
(defun centaur-tabs-separator-color-xyz-to-apple-rgb (X Y Z)
"Convert CIE X Y Z colors to Apple RGB color space."
(let ((r (+ (* 3.2404542 X) (* -1.5371385 Y) (* -0.4985314 Z)))
(g (+ (* -0.9692660 X) (* 1.8760108 Y) (* 0.0415560 Z)))
(b (+ (* 0.0556434 X) (* -0.2040259 Y) (* 1.0572252 Z))))
(list (expt r (/ 1.8)) (expt g (/ 1.8)) (expt b (/ 1.8)))))
(defun centaur-tabs-separator-color-srgb-to-apple-rgb (red green blue)
"Convert RED GREEN BLUE colors from sRGB color space to Apple RGB.
RED, GREEN and BLUE should be between 0.0 and 1.0, inclusive."
(apply #'centaur-tabs-separator-color-xyz-to-apple-rgb (color-srgb-to-xyz red green blue)))
(defun centaur-tabs-separator-hex-color (color)
"Get the hexadecimal value of COLOR."
(when color
(let ((srgb-color (color-name-to-rgb color)))
(if centaur-tabs-image-apple-rgb
(apply #'color-rgb-to-hex (apply #'centaur-tabs-separator-color-srgb-to-apple-rgb srgb-color))
(apply #'color-rgb-to-hex srgb-color)))))
(defun centaur-tabs-separator-pattern (lst)
"Turn LST into an infinite pattern."
(when lst
(let ((pattern (cl-copy-list lst)))
(setcdr (last pattern) pattern))))
(defun centaur-tabs-separator-pattern-to-string (pattern)
"Convert a PATTERN into a string that can be used in an XPM."
(concat "\"" (mapconcat #'number-to-string pattern "") "\","))
(defun centaur-tabs-separator-reverse-pattern (pattern)
"Reverse each line in PATTERN."
(cl-mapcar 'reverse pattern))
(defun centaur-tabs-separator-row-pattern (fill total &optional fade)
"Make a list that has FILL 0s out of TOTAL 1s with FADE 2s to the right of the fill."
(unless fade
(setq fade 0))
(let ((fill (min fill total))
(fade (min fade (max (- total fill) 0))))
(append (make-list fill 0)
(make-list fade 2)
(make-list (- total fill fade) 1))))
(defun centaur-tabs-separator-pattern-bindings-body (patterns height-exp pattern-height-sym
second-pattern-height-sym)
"Create let-var bindings and a function body from PATTERNS.
The `car' and `cdr' parts of the result can be passed to the
function `centaur-tabs-separator-wrap-defun' as its `let-vars'
and `body' arguments,respectively. HEIGHT-EXP is an expression
calculating the image height and it should contain a free variable `height'.
PATTERN-HEIGHT-SYM and SECOND-PATTERN-HEIGHT-SYM are symbols used
for let-var binding variables."
(let* ((pattern (centaur-tabs-separator-pattern (cl-mapcar 'centaur-tabs-separator-pattern-to-string (car patterns))))
(header (cl-mapcar 'centaur-tabs-separator-pattern-to-string (nth 1 patterns)))
(footer (cl-mapcar 'centaur-tabs-separator-pattern-to-string (nth 2 patterns)))
(second-pattern (centaur-tabs-separator-pattern (cl-mapcar 'centaur-tabs-separator-pattern-to-string (nth 3 patterns))))
(center (cl-mapcar 'centaur-tabs-separator-pattern-to-string (nth 4 patterns)))
(reserve (+ (length header) (length footer) (length center))))
(when pattern
(cons `((,pattern-height-sym (max (- ,height-exp ,reserve) 0))
(,second-pattern-height-sym (/ ,pattern-height-sym 2))
(,pattern-height-sym ,(if second-pattern `(ceiling ,pattern-height-sym 2) `,pattern-height-sym)))
(list (when header `(mapconcat 'identity ',header ""))
`(mapconcat 'identity
(cl-subseq ',pattern 0 ,pattern-height-sym) "")
(when center `(mapconcat 'identity ',center ""))
(when second-pattern
`(mapconcat 'identity
(cl-subseq ',second-pattern
0 ,second-pattern-height-sym) ""))
(when footer `(mapconcat 'identity ',footer "")))))))
(defun centaur-tabs-separator-pattern-defun (name dir width &rest patterns)
"Create a powerline function of NAME in DIR with WIDTH for PATTERNS.
PATTERNS is of the form (PATTERN HEADER FOOTER SECOND-PATTERN CENTER
PATTERN-2X HEADER-2X FOOTER-2X SECOND-PATTERN-2X CENTER-2X).
PATTERN is required, all other components are optional.
The first 5 components are for the standard resolution image.
The remaining ones are for the high resolution image where both
width and height are doubled. If PATTERN-2X is nil or not given,
then the remaining components are ignored and the standard
resolution image with magnification and interpolation will be
used in high resolution environments
All generated functions generate the form:
HEADER
PATTERN ...
CENTER
SECOND-PATTERN ...
FOOTER
PATTERN and SECOND-PATTERN repeat infinitely to fill the space needed to
generate a full height XPM.
PATTERN, HEADER, FOOTER, SECOND-PATTERN, CENTER are of the form
\((COLOR ...) (COLOR ...) ...).
COLOR can be one of 0, 1, or 2, where 0 is the source color, 1 is the
destination color, and 2 is the interpolated color between 0 and 1."
(when (eq dir 'right)
(setq patterns (cl-mapcar 'centaur-tabs-separator-reverse-pattern patterns)))
(let ((bindings-body (centaur-tabs-separator-pattern-bindings-body patterns
'height
'pattern-height
'second-pattern-height))
(bindings-body-2x (centaur-tabs-separator-pattern-bindings-body (nthcdr 5 patterns)
'(* height 2)
'pattern-height-2x
'second-pattern-height-2x)))
(centaur-tabs-separator-wrap-defun name dir width
(append (car bindings-body) (car bindings-body-2x))
(cdr bindings-body) (cdr bindings-body-2x))))
(defun centaur-tabs-separator-background-color (face)
"Set the separator background color using FACE."
(face-attribute face
(if (face-attribute face :inverse-video nil 'default)
:foreground
:background)
nil
'default))
(defun centaur-tabs-separator-wrap-defun (name dir width let-vars body &optional body-2x)
"Generate a powerline function of name NAME in dir DIR.
This is made with WIDTH using LET-VARS and BODY.
BODY-2X is an optional argument."
(let* ((src-face (if (eq dir 'left) 'face1 'face2))
(dst-face (if (eq dir 'left) 'face2 'face1)))
`(defun ,(intern (format "powerline-%s-%s" name (symbol-name dir)))
(face1 face2 &optional height)
(when window-system
(unless height (setq height centaur-tabs-height))
(let* ,(append `((color1 (when ,src-face
(centaur-tabs-separator-hex-color (centaur-tabs-separator-background-color ,src-face))))
(color2 (when ,dst-face
(centaur-tabs-separator-hex-color (centaur-tabs-separator-background-color ,dst-face))))
(colori (when (and color1 color2) (centaur-tabs-separator-interpolate color1 color2)))
(color1 (or color1 "None"))
(color2 (or color2 "None"))
(colori (or colori "None")))
let-vars)
(apply #'create-image
,(append `(concat (format "/* XPM */ static char * %s_%s[] = { \"%s %s 3 1\", \"0 c %s\", \"1 c %s\", \"2 c %s\","
,(replace-regexp-in-string "-" "_" name)
(symbol-name ',dir)
,width
height
color1
color2
colori))
body
'("};"))
'xpm t
:ascent 'center
:face (when (and face1 face2)
,dst-face)
,(and body-2x
`(and (featurep 'mac)
(list :data-2x
,(append `(concat (format "/* XPM */ static char * %s_%s_2x[] = { \"%s %s 3 1\", \"0 c %s\", \"1 c %s\", \"2 c %s\","
,(replace-regexp-in-string "-" "_" name)
(symbol-name ',dir)
(* ,width 2)
(* height 2)
color1
color2
colori))
body-2x
'("};")))))))))))
(defun centaur-tabs-separator-alternate (dir)
"Generate an alternating pattern XPM function for DIR."
(centaur-tabs-separator-pattern-defun "alternate" dir 4
'((2 2 1 1)
(0 0 2 2))
nil nil nil nil
;; 2x
'((2 2 2 2 1 1 1 1)
(2 2 2 2 1 1 1 1)
(0 0 0 0 2 2 2 2)
(0 0 0 0 2 2 2 2))))
(defun centaur-tabs-separator-bar (dir)
"Generate a bar XPM function for DIR."
(centaur-tabs-separator-pattern-defun "bar" dir 2
'((2 2))))
(defun centaur-tabs-separator-box (dir)
"Generate a box XPM function for DIR."
(centaur-tabs-separator-pattern-defun "box" dir 2
'((0 0)
(0 0)
(1 1)
(1 1))
nil nil nil nil
;; 2x
'((0 0 0 0)
(0 0 0 0)
(0 0 0 0)
(0 0 0 0)
(1 1 1 1)
(1 1 1 1)
(1 1 1 1)
(1 1 1 1))))
(defun centaur-tabs-separator-chamfer (dir)
"Generate a chamfer XPM function for DIR."
(centaur-tabs-separator-pattern-defun "chamfer" dir 3
'((0 0 0))
'((1 1 1)
(0 1 1)
(0 0 1))
nil nil nil
;; 2x
'((0 0 0 0 0 0))
'((1 1 1 1 1 1)
(0 1 1 1 1 1)
(0 0 1 1 1 1)
(0 0 0 1 1 1)
(0 0 0 0 1 1)
(0 0 0 0 0 1))))
(defun centaur-tabs-separator-rounded (dir)
"Generate a rounded XPM function for DIR."
(centaur-tabs-separator-pattern-defun "rounded" dir 6
'((0 0 0 0 0 0))
'((2 1 1 1 1 1)
(0 0 2 1 1 1)
(0 0 0 0 1 1)
(0 0 0 0 2 1)
(0 0 0 0 0 1)
(0 0 0 0 0 2))
nil nil nil
;; 2x
'((0 0 0 0 0 0 0 0 0 0 0 0))
'((1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 2 1 1 1 1 1 1 1 1 1)
(0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 0 0 2 1 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1 1)
(0 0 0 0 0 0 0 0 0 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1)
(0 0 0 0 0 0 0 0 0 0 2 1)
(0 0 0 0 0 0 0 0 0 0 0 1)
(0 0 0 0 0 0 0 0 0 0 0 1))))
(defun centaur-tabs-separator-slant (dir)
"Generate a slant XPM function for DIR."
(let* ((row-modifier (if (eq dir 'left) 'identity 'reverse)))
(centaur-tabs-separator-wrap-defun "slant" dir 'width
'((width (1- (ceiling height 2))))
`((cl-loop for i from 0 to (1- height)
concat (centaur-tabs-separator-pattern-to-string (,row-modifier (centaur-tabs-separator-row-pattern (/ i 2) width)))))
`((cl-loop for i from 0 to (1- (* height 2))
concat (centaur-tabs-separator-pattern-to-string (,row-modifier (centaur-tabs-separator-row-pattern (/ i 2) (* width 2)))))))))
(defun centaur-tabs-separator-wave (dir)
"Generate a wave XPM function for DIR."
(centaur-tabs-separator-pattern-defun "wave" dir 11
'((0 0 0 0 0 0 1 1 1 1 1))
'((2 1 1 1 1 1 1 1 1 1 1)
(0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 2 1 1 1 1 1 1 1)
(0 0 0 0 1 1 1 1 1 1 1)
(0 0 0 0 2 1 1 1 1 1 1)
(0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 2 1 1 1 1 1))
'((0 0 0 0 0 0 2 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1)
(0 0 0 0 0 0 0 2 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1)
(0 0 0 0 0 0 0 0 2 1 1)
(0 0 0 0 0 0 0 0 0 0 2))
nil nil
;; 2x
'((0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1))
'((1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1))
'((0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0))))
(defun centaur-tabs-separator-zigzag (dir)
"Generate a zigzag pattern XPM function for DIR."
(centaur-tabs-separator-pattern-defun "zigzag" dir 3
'((1 1 1)
(0 1 1)
(0 0 1)
(0 0 0)
(0 0 1)
(0 1 1))
nil nil nil nil
;; 2x
'((1 1 1 1 1 1)
(0 1 1 1 1 1)
(0 0 1 1 1 1)
(0 0 0 1 1 1)
(0 0 0 0 1 1)
(0 0 0 0 0 1)
(0 0 0 0 0 0)
(0 0 0 0 0 1)
(0 0 0 0 1 1)
(0 0 0 1 1 1)
(0 0 1 1 1 1)
(0 1 1 1 1 1))))
(defun centaur-tabs-separator-memoize (func)
"Memoize FUNC.
If argument is a symbol then install the memoized function over
the original function. Use frame-local memoization."
(cl-typecase func
(symbol (fset func (centaur-tabs-separator-memoize-wrap-frame-local (symbol-function func))) func)
(function (centaur-tabs-separator-memoize-wrap-frame-local func))))
(defun centaur-tabs-separator-memoize-wrap-frame-local (func)
"Return the memoized version of FUNC.
The memoization cache is frame-local."
(let ((funcid (cl-gensym)))
`(lambda (&rest args)
,(concat (documentation func) (format "\n(memoized function %s)" funcid))
(let* ((cache (centaur-tabs-separator-create-or-get-cache))
(key (cons ',funcid args))
(val (gethash key cache)))
(if val
val
(puthash key (apply ,func args) cache))))))
(defun centaur-tabs-separator-create-or-get-cache ()
"Return a frame-local hash table that acts as a memoization cache.
The cache is for the powerline.
Create one if the frame doesn't have one yet."
(let ((table (frame-parameter nil 'powerline-cache)))
(if (hash-table-p table) table (centaur-tabs-separator-reset-cache))))
(defun centaur-tabs-separator-reset-cache ()
"Reset and return the frame-local hash table used for a memoization cache."
(let ((table (make-hash-table :test 'equal)))
;; Store it as a frame-local variable
(modify-frame-parameters nil `((powerline-cache . ,table)))
table))
(centaur-tabs-separator-memoize (centaur-tabs-separator-alternate 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-alternate 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-bar 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-bar 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-box 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-box 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-chamfer 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-chamfer 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-rounded 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-rounded 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-slant 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-slant 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-wave 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-wave 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-zigzag 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-zigzag 'right))
(defun centaur-tabs-select-separator-style (tab-style)
"Set the separator style to TAB-STYLE."
(setq centaur-tabs-style-left (funcall (intern (format "powerline-%s-right" tab-style)) 'centaur-tabs-default nil centaur-tabs-height))
(setq centaur-tabs-style-right (funcall (intern (format "powerline-%s-left" tab-style)) nil 'centaur-tabs-default centaur-tabs-height)))
(provide 'centaur-tabs-elements)
;;; centaur-tabs-elements.el ends here

View file

@ -1,10 +1,10 @@
;;; centaur-tabs-functions.el --- centaur-tabs logic components -*- lexical-binding: t; -*-
;; Copyright (C) 2019-2024 Emmanuel Bustos
;; Copyright (C) 2024 Jen-Chieh Shen
;; Copyright (C) 2019-2020 Emmanuel Bustos
;; This file is not part of GNU Emacs.
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2, or
@ -19,32 +19,33 @@
;; along with this program; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;; Commentary:
;;
;; This file contains functions that control the logic of centaur-tabs
;;
;;; Code:
;;; Require
(require 'cl-lib)
(require 'seq)
(require 'centaur-tabs-elements)
;; Compiler pacifier
(declare-function vterm "ext:vterm.el")
(declare-function ivy-read "ext:ivy.el" t t)
(declare-function helm-build-sync-source "ext:helm-source.el" t t)
(declare-function all-the-icons-match? "ext:all-the-icons.el" t t)
(declare-function all-the-icons-auto-mode-match? "ext:all-the-icons.el" t t)
(declare-function all-the-icons-icon-for-file "ext:all-the-icons.el" t t)
(declare-function all-the-icons-icon-for-mode "ext:all-the-icons.el" t t)
(declare-function nerd-icons-match? "ext:nerd-icons.el" t t)
(declare-function nerd-icons-auto-mode-match? "ext:nerd-icons.el" t t)
(declare-function nerd-icons-icon-for-file "ext:nerd-icons.el" t t)
(declare-function nerd-icons-icon-for-mode "ext:nerd-icons.el" t t)
(declare-function projectile-project-root "ext:projectile.el" t t)
(declare-function projectile-project-name "ext:projectile.el" t t)
(defvar helm-source-centaur-tabs-group)
(declare-function centaur-tabs-move-current-tab-to-right "nerd-icons.el")
(declare-function centaur-tabs-move-current-tab-to-left "nerd-icons.el")
(defvar centaur-tabs--buffer-show-groups)
(defvar centaur-tabs-ace-jump-active)
(declare-function centaur-tabs-mode "centaur-tabs.el")
;;
;;; Customs
;;
(defcustom centaur-tabs-cycle-scope nil
"*Specify the scope of cyclic navigation through tabs.
The following scopes are possible:
@ -70,8 +71,7 @@ visible."
:type 'boolean)
(defcustom centaur-tabs-common-group-name "Common"
"If the current buffer does not belong to any project the group name uses the
name of this variable."
"If the current buffer does not belong to any project the group name uses the name of this variable."
:group 'centaur-tabs
:type 'string)
@ -95,9 +95,6 @@ name of this variable."
"*Helm"
" *which"
"*Compile-Log*"
"*Choices"
"*Process"
"*Calc"
"*lsp"
"*LSP"
"*company"
@ -106,17 +103,15 @@ name of this variable."
"*ediff"
"*tramp"
" *Mini"
"*help"
"*straight"
" *temp")
" *temp"
"*Help")
"List of prefixes that indicates which buffers should not be included as tabs.
Buffers that have names that start with any of these strings will be ignored."
:type '(repeat string)
:group 'centaur-tabs)
(defvar centaur-tabs-hide-predicate #'ignore
"Predicate function to hide the entire tab line.
Ths tab line will hide if this function returns t.")
(defvar centaur-tabs-hide-tab-function 'centaur-tabs-hide-tab
"Function to hide tabs.
This function filters tabs. The tab will hide if this function returns t.")
@ -173,10 +168,10 @@ tab(B), move A to the left of B" t)
(defun centaur-tabs-headline-match ()
"Make headline use centaur-tabs-default-face."
(set-face-attribute
centaur-tabs-display-line nil
:background (face-background 'centaur-tabs-unselected nil 'default)
:box nil :overline nil :underline nil))
(set-face-attribute centaur-tabs-display-line nil :background (face-background 'centaur-tabs-unselected nil 'default)
:box nil
:overline nil
:underline nil))
;; Change the font and height for all tab faces
(defun centaur-tabs-change-fonts (family height)
@ -246,8 +241,13 @@ When not specified, ELLIPSIS defaults to ...."
(define-key map (vector centaur-tabs-display-line 'mouse-4 ) 'centaur-tabs-backward)
(define-key map (vector centaur-tabs-display-line 'wheel-down) 'centaur-tabs-forward )
(define-key map (vector centaur-tabs-display-line 'wheel-up ) 'centaur-tabs-backward)
;;; Use right click to show the rest of groups
(define-key map (vector centaur-tabs-display-line 'mouse-3) 'centaur-tabs--tab-menu )
;;; Use double click to maximize window
(define-key map (vector centaur-tabs-display-line 'double-mouse-1) 'delete-other-windows)
map)
"Keymap to use in Centaur-Tabs mode.")
@ -294,9 +294,87 @@ When not specified, ELLIPSIS defaults to ...."
map)
"Keymap used for setting mouse events for new tab button.")
;;; Events and event functions
;;
;;; Tab and tab sets
(defun centaur-tabs-buffer-close-tab (tab)
"Function for closing TAB."
(let ((buffer (centaur-tabs-tab-value tab)))
(with-current-buffer buffer
(kill-buffer buffer))
(centaur-tabs-display-update)))
(defun centaur-tabs-get-tab-from-event (event)
"Given a mouse EVENT, extract the tab at the mouse point."
(let ((pos (posn-string (event-start event))))
(get-text-property (cdr pos) 'centaur-tabs-tab (car pos))))
(defun centaur-tabs-do-select (event)
"Given a mouse EVENT, select the tab at the mouse point."
(interactive "e")
(select-window (posn-window (event-start event)))
(centaur-tabs-buffer-select-tab `,(centaur-tabs-get-tab-from-event event)))
(defun centaur-tabs-do-close (event)
"Given a mouse EVENT, close the tab at the mouse point."
(interactive "e")
(let ((window (posn-window (event-start event))))
(with-selected-window window
(select-window window)
(let ((foreground-buffer-name (buffer-name)))
(centaur-tabs-buffer-select-tab `,(centaur-tabs-get-tab-from-event event))
(let* ((buffer (window-buffer window))
(target-buffer-name (buffer-name))
(same-target-check (string-equal foreground-buffer-name target-buffer-name))
(window-num (- (length (get-buffer-window-list buffer))
(if same-target-check 0 1))))
(if (> window-num 1)
(delete-window window)
(centaur-tabs-buffer-close-tab `,(centaur-tabs-get-tab-from-event event))))))))
(defun centaur-tabs-backward--button (event)
"Same as centaur-tabs-backward, but changing window to EVENT source."
(interactive "e")
(select-window (posn-window (event-start event)))
(centaur-tabs-backward))
(defun centaur-tabs-forward--button (event)
"Same as centaur-tabs-forward, but changing window to EVENT source."
(interactive "e")
(select-window (posn-window (event-start event)))
(centaur-tabs-forward))
(defun centaur-tabs-new-tab--button (event)
"Same as centaur-tabs--create-new-tab, but changing window to EVENT source."
(interactive "e")
(select-window (posn-window (event-start event)))
(centaur-tabs--create-new-tab))
(defun centaur-tabs-move-current-tab-to-left--button (evt)
"Same as centaur-tabs-move-current-tab-to-left, but ensuring the tab will remain visible. The active window will the the EVT source."
(interactive "e")
(centaur-tabs-move-current-tab-to-left)
(centaur-tabs--button-ensure-selected-tab-is-visible evt))
(defun centaur-tabs-move-current-tab-to-right--button (evt)
"Same as centaur-tabs-move-current-tab-to-right, but ensuring the tab will remain visible. The active window will the the EVT source."
(interactive "e")
(centaur-tabs-move-current-tab-to-right)
(centaur-tabs--button-ensure-selected-tab-is-visible evt))
(defun centaur-tabs--button-ensure-selected-tab-is-visible (evt)
"This is a nasty trick to make the current tab visible, since centaur-tabs--track-selected or centaur-tabs-auto-scroll-flag seems not to work. EVT is used to change the active window."
;;; This works if the tab has not reached the last position
(centaur-tabs-forward--button evt)
(centaur-tabs-backward--button evt)
;;; Just in case the tab has the tab reached the last position
(centaur-tabs-backward--button evt)
(centaur-tabs-forward--button evt))
;;; Tab and tab sets
;;
(defsubst centaur-tabs-make-tab (object tabset)
"Return a new tab with value OBJECT.
TABSET is the tab set the tab belongs to."
@ -324,9 +402,6 @@ TABSET is the tab set the tab belongs to."
"Hook run after tab bar data has been initialized.
You should use this hook to initialize dependent data.")
(defvar centaur-tabs-display-hash (make-hash-table :test 'equal)
"Display format cache.")
(defsubst centaur-tabs-init-tabsets-store ()
"Initialize the tab set store."
(setq centaur-tabs-tabsets (make-vector 31 0)
@ -364,15 +439,14 @@ The result is a list just as long as the number of existing tab sets."
(defun centaur-tabs-make-tabset (name &rest objects)
"Make a new tab set whose name is the string NAME.
It is initialized with tabs build from the list of OBJECTS."
(when name ; some buffers don't have a tabset (e.g. org-agenda)
(let* ((tabset (intern name centaur-tabs-tabsets))
(tabs (mapcar #'(lambda (object)
(tabs (cl-mapcar #'(lambda (object)
(centaur-tabs-make-tab object tabset))
objects)))
(set tabset tabs)
(centaur-tabs-put-cache tabset 'select (car tabs))
(put tabset 'start 0)
tabset)))
tabset))
(defsubst centaur-tabs-get-tabset (name)
"Return the tab set whose name is the string NAME.
@ -390,22 +464,22 @@ That is, remove it from the tab sets store."
(defsubst centaur-tabs-tab-values (tabset)
"Return the list of tab values in TABSET."
(mapcar 'centaur-tabs-tab-value (centaur-tabs-tabs tabset)))
(cl-mapcar 'centaur-tabs-tab-value (centaur-tabs-tabs tabset)))
(defun centaur-tabs-get-cache (cache key)
"Return the cached value of KEY in CACHE."
(when-let* ((cache (format "%s" cache))
(cached-hash (gethash cache centaur-tabs-display-hash))
((hash-table-p cached-hash)))
(gethash key cached-hash nil)))
"Return the per-frame cached value of KEY in CACHE."
(let
((cached-hash (frame-parameter nil cache)))
(if (hash-table-p cached-hash)
(gethash key cached-hash nil))))
(defun centaur-tabs-put-cache (cache key value)
"Set the cached value of KEY in CACHE to VALUE."
(let* ((cache (format "%s" cache))
(cached-hash (gethash cache centaur-tabs-display-hash))
"Set the per-frame cached value of KEY in CACHE to VALUE."
(let*
((cached-hash (frame-parameter nil cache))
(hash (if (hash-table-p cached-hash) cached-hash (make-hash-table))))
(puthash key value hash)
(puthash cache hash centaur-tabs-display-hash))
(set-frame-parameter nil cache hash))
value)
(defsubst centaur-tabs-get-tab (object tabset)
@ -420,14 +494,14 @@ Return the tab found, or nil if not found."
(defsubst centaur-tabs-template (tabset)
"Return the cached visual representation of TABSET.
That is, a `centaur-tabs-display-line-format' template, or nil if
the cache is empty."
That is, a `centaur-tabs-display-line-format' template, or nil if the cache is
empty."
(centaur-tabs-get-cache tabset 'template))
(defsubst centaur-tabs-set-template (tabset template)
"Set the cached visual representation of TABSET to TEMPLATE.
TEMPLATE must be a valid `centaur-tabs-display-line-format' template,
or nil to cleanup the cache."
TEMPLATE must be a valid `centaur-tabs-display-line-format' template, or nil to
cleanup the cache."
(centaur-tabs-put-cache tabset 'template template))
(defsubst centaur-tabs-selected-tab (tabset)
@ -477,7 +551,7 @@ Otherwise insert it."
tabs
(let* ((tab (centaur-tabs-make-tab object tabset))
(selected (centaur-tabs-selected-tab tabset))
(selected-index (cl-position (car selected) (mapcar 'car tabs))))
(selected-index (cl-position (car selected) (cl-mapcar 'car tabs))))
(centaur-tabs-set-template tabset nil)
(set tabset (centaur-tabs-insert-at tabs selected-index tab))))))
@ -542,132 +616,26 @@ current cached copy."
(centaur-tabs-set-template centaur-tabs-tabsets-tabset nil)
centaur-tabs-tabsets-tabset)
(defun centaur-tabs-after-focus (&rest _)
"Focus hook."
(when (frame-focus-state)
(ignore-errors (centaur-tabs-buffer-update-groups))
(ignore-errors (centaur-tabs-display-update))))
(defun centaur-tabs-on-window-buffer-change (frame &rest _)
"Function to be run after window buffer is changed in FRAME."
(unless (frame-parent frame)
(ignore-errors (centaur-tabs-buffer-update-groups))))
;; Functions for modification hooks and advices
(defun centaur-tabs-on-saving-buffer ()
"Function to be run after the buffer is saved."
(centaur-tabs-set-template centaur-tabs-current-tabset nil)
(centaur-tabs-display-update))
(defun centaur-tabs-on-modifying-buffer (&rest _)
(defun centaur-tabs-on-modifying-buffer ()
"Function to be run after the buffer is first changed."
(set-buffer-modified-p (buffer-modified-p))
(centaur-tabs-set-template centaur-tabs-current-tabset nil)
(centaur-tabs-display-update))
(defun centaur-tabs-after-modifying-buffer (&rest _)
"Function to be run after the buffer is changed.
BEGIN, END and LENGTH are just standard arguments for after-changes-function
hooked functions"
(set-buffer-modified-p (buffer-modified-p))
(centaur-tabs-set-template centaur-tabs-current-tabset nil)
(centaur-tabs-display-update))
(defun centaur-tabs-re-enable ()
"Re-enable `centaur-tabs-mode'."
(centaur-tabs-mode -1)
(centaur-tabs-mode 1))
(defun centaur-tabs--after-load-theme (&rest _)
"Function to be run after the theme changed."
(setq centaur-tabs-style-right nil
centaur-tabs-style-left nil)
;; XXX: Dirty hack, is there a way to improve this?
(centaur-tabs-re-enable))
;;
;;; Events and event functions
(defun centaur-tabs-buffer-close-tab (tab)
"Function for closing TAB."
(let ((buffer (centaur-tabs-tab-value tab)))
(kill-buffer buffer)
(centaur-tabs-buffer-update-groups)
(centaur-tabs-display-update)))
(defun centaur-tabs-get-tab-from-event (event)
"Given a mouse EVENT, extract the tab at the mouse point."
(let ((pos (posn-string (event-start event))))
(get-text-property (cdr pos) 'centaur-tabs-tab (car pos))))
(defun centaur-tabs-do-select (event)
"Given a mouse EVENT, select the tab at the mouse point."
(interactive "e")
(select-window (posn-window (event-start event)))
(centaur-tabs-buffer-select-tab `,(centaur-tabs-get-tab-from-event event)))
(defun centaur-tabs-do-close (event)
"Given a mouse EVENT, close the tab at the mouse point."
(interactive "e")
(let ((window (posn-window (event-start event))))
(with-selected-window window
(select-window window)
(let ((foreground-buffer-name (buffer-name)))
(centaur-tabs-buffer-select-tab `,(centaur-tabs-get-tab-from-event event))
(let* ((buffer (window-buffer window))
(target-buffer-name (buffer-name))
(same-target-check (string-equal foreground-buffer-name target-buffer-name))
(window-num (- (length (get-buffer-window-list buffer))
(if same-target-check 0 1))))
(if (> window-num 1)
(delete-window window)
(centaur-tabs-buffer-close-tab `,(centaur-tabs-get-tab-from-event event))))))))
(defun centaur-tabs-backward--button (event)
"Same as centaur-tabs-backward, but changing window to EVENT source."
(interactive "e")
(select-window (posn-window (event-start event)))
(centaur-tabs-backward))
(defun centaur-tabs-forward--button (event)
"Same as centaur-tabs-forward, but changing window to EVENT source."
(interactive "e")
(select-window (posn-window (event-start event)))
(centaur-tabs-forward))
(defun centaur-tabs-new-tab--button (event)
"Same as centaur-tabs--create-new-tab, but changing window to EVENT source."
(interactive "e")
(select-window (posn-window (event-start event)))
(centaur-tabs--create-new-tab))
(defun centaur-tabs-move-current-tab-to-left--button (evt)
"Same as centaur-tabs-move-current-tab-to-left, but ensuring the tab will
remain visible. The active window will the the EVT source."
(interactive "e")
(centaur-tabs-move-current-tab-to-left)
(centaur-tabs--button-ensure-selected-tab-is-visible evt))
(defun centaur-tabs-move-current-tab-to-right--button (evt)
"Same as centaur-tabs-move-current-tab-to-right, but ensuring the tab will
remain visible. The active window will the the EVT source."
(interactive "e")
(centaur-tabs-move-current-tab-to-right)
(centaur-tabs--button-ensure-selected-tab-is-visible evt))
(defun centaur-tabs--button-ensure-selected-tab-is-visible (evt)
"This is a nasty trick to make the current tab visible, since
`centaur-tabs--track-selected' or `centaur-tabs-auto-scroll-flag' seems not
to work. EVT is used to change the active window."
;; This works if the tab has not reached the last position
(centaur-tabs-forward--button evt)
(centaur-tabs-backward--button evt)
;; Just in case the tab has the tab reached the last position
(centaur-tabs-backward--button evt)
(centaur-tabs-forward--button evt))
(defun centaur-tabs-refill-tabs ()
"Refill current tab line."
(centaur-tabs-buffer-update-groups)
(force-window-update (selected-window))
(centaur-tabs--button-ensure-selected-tab-is-visible nil))
;;
;;; Tabs display
;;
(defsubst centaur-tabs-line-tab (tab)
"Return the display representation of tab TAB.
That is, a propertized string used as an `centaur-tabs-display-line-format'
@ -708,20 +676,23 @@ Call `centaur-tabs-tab-label-function' to obtain a label for TAB."
(when (or (not centaur-tabs-style-left)
(not centaur-tabs-style-right))
(centaur-tabs-select-separator-style centaur-tabs-style))
(concat (centaur-tabs-separator-render centaur-tabs-style-left face)
(concat
(centaur-tabs-separator-render centaur-tabs-style-left face)
bar
;; left margin
(when centaur-tabs-left-edge-margin
(propertize centaur-tabs-left-edge-margin
(if centaur-tabs-left-edge-margin
(propertize
centaur-tabs-left-edge-margin
'face face
'centaur-tabs-tab tab
'pointer centaur-tabs-mouse-pointer
'local-map centaur-tabs-default-map))
;; left close button
(when centaur-tabs-set-left-close-button
(propertize centaur-tabs-close-button
(if centaur-tabs-set-left-close-button
(propertize
centaur-tabs-close-button
'face (if selected-p
'centaur-tabs-close-selected
'centaur-tabs-close-unselected)
@ -733,7 +704,9 @@ Call `centaur-tabs-tab-label-function' to obtain a label for TAB."
;; icon
(if (= (length icon) 0) ""
(concat (propertize centaur-tabs-icons-prefix
(concat
(propertize
" "
'face face
'centaur-tabs-tab tab
'pointer centaur-tabs-mouse-pointer
@ -741,7 +714,8 @@ Call `centaur-tabs-tab-label-function' to obtain a label for TAB."
icon))
;; tab name
(propertize (concat
(propertize
(concat
(if centaur-tabs-tab-label-function
(funcall centaur-tabs-tab-label-function tab)
(buffer-name buf))
@ -755,24 +729,22 @@ Call `centaur-tabs-tab-label-function' to obtain a label for TAB."
;; tab identifier
(when centaur-tabs-show-jump-identifier
(when (or (eq centaur-tabs-show-jump-identifier 'always)
centaur-tabs-ace-jump-active)
(when-let ((position (nth (cl-position tab (centaur-tabs-view (centaur-tabs-current-tabset t)))
centaur-tabs-ace-jump-keys)))
(when (or (eq centaur-tabs-show-jump-identifier 'always) centaur-tabs-ace-jump-active)
(propertize
(format "%c" position)
(format "%c" (nth (cl-position tab (centaur-tabs-view (centaur-tabs-current-tabset t))) centaur-tabs-ace-jump-keys))
'centaur-tabs-tab tab
'face (if selected-p
'centaur-tabs-jump-identifier-selected
'centaur-tabs-jump-identifier-unselected)
'pointer centaur-tabs-mouse-pointer
'help-echo buf-file-name
'local-map centaur-tabs-default-map))))
'local-map centaur-tabs-default-map)))
;; close button and/or modified marker
(unless centaur-tabs-ace-jump-active
(if centaur-tabs-set-close-button
(propertize (if use-mod-mark-p
(propertize
(if use-mod-mark-p
centaur-tabs-modified-marker
centaur-tabs-close-button)
'face (if use-mod-mark-p
@ -786,7 +758,8 @@ Call `centaur-tabs-tab-label-function' to obtain a label for TAB."
'mouse-face 'centaur-tabs-close-mouse-face
'local-map centaur-tabs-close-map)
(if (and centaur-tabs-set-modified-marker modified-p)
(propertize centaur-tabs-modified-marker
(propertize
centaur-tabs-modified-marker
'face mod-mark-face
'pointer centaur-tabs-mouse-pointer
'centaur-tabs-tab tab
@ -795,8 +768,9 @@ Call `centaur-tabs-tab-label-function' to obtain a label for TAB."
"" )))
;; right margin
(when centaur-tabs-right-edge-margin
(propertize centaur-tabs-right-edge-margin
(if centaur-tabs-right-edge-margin
(propertize
centaur-tabs-right-edge-margin
'face face
'centaur-tabs-tab tab
'pointer centaur-tabs-mouse-pointer
@ -809,7 +783,9 @@ Call `centaur-tabs-tab-label-function' to obtain a label for TAB."
That is, a propertized string used as an `centaur-tabs-display-line-format'
template element."
(let* ((face 'centaur-tabs-unselected))
(concat (propertize button
(concat
(propertize
button
'face face
'mouse-face 'highlight))))
@ -820,7 +796,7 @@ template element."
(padcolor centaur-tabs-background-color)
(all-tabs (centaur-tabs-tabs tabset))
(total-tabs (length all-tabs))
(sel-index (+ (cl-position (car sel) (mapcar 'car all-tabs)) 1))
(sel-index (+ (cl-position (car sel) (cl-mapcar 'car all-tabs)) 1))
atsel elts)
;; Track the selected tab to ensure it is always visible.
(when centaur-tabs--track-selected
@ -851,7 +827,7 @@ template element."
(apply #'insert elts)
(goto-char (point-min))
(> (vertical-motion 1) 0)))
(centaur-tabs-scroll tabset -1)
(centaur-tabs-scroll tabset 1)
(setq elts (cdr elts))))))
(setq elts (nreverse elts))
(setq centaur-tabs--track-selected nil))
@ -869,14 +845,13 @@ template element."
(propertize "% "
'face (list :background padcolor)
'pointer 'arrow)
(centaur-tabs-line-format--new-button)))))
(centaur-tabs-line-format--new-button)))
))
(defun centaur-tabs-count (index count)
"Return a centaur-tabs-button-tab with the current tab INDEX and the total
tabs COUNT."
"Return a centaur-tabs-button-tab with the current tab INDEX and the total tabs COUNT."
(if centaur-tabs-show-count
(propertize (centaur-tabs-button-tab (format centaur-tabs-count-format
index count))
(propertize (centaur-tabs-button-tab (format " [%d/%d] " index count))
'help-echo "Tabs count")
""))
@ -908,9 +883,8 @@ tabs COUNT."
"Return the header line templates that represent the tab bar.
Inhibit display of the tab bar in current window where
`centaur-tabs-hide-tab-function' return t."
(cond ((or (centaur-tabs-hide-tab-cached (current-buffer))
(and centaur-tabs-hide-predicate
(funcall centaur-tabs-hide-predicate)))
(cond
((centaur-tabs-hide-tab-cached (current-buffer))
;; Don't show the tab bar.
(set centaur-tabs-display-line-format nil))
((centaur-tabs-current-tabset t)
@ -921,9 +895,8 @@ Inhibit display of the tab bar in current window where
(defconst centaur-tabs-header-line-format '(:eval (centaur-tabs-line))
"The tab bar header line format.")
;;
;;; Cyclic navigation through tabs
;;
(defun centaur-tabs-cycle (&optional backward)
"Cycle to the next available tab.
The scope of the cyclic navigation through tabs is specified by the
@ -949,7 +922,8 @@ instead."
;; to the first/last visible tab.
(unless tab
(setq tabset (centaur-tabs-tabs tabset)
tab (car (if backward (last tabset) tabset)))))
tab (car (if backward (last tabset) tabset))))
)
;; Cycle through tab groups only.
((eq cycle 'groups)
(setq tab (centaur-tabs-tab-next ttabset selected backward))
@ -957,7 +931,8 @@ instead."
;; to the first/last available group.
(unless tab
(setq tabset (centaur-tabs-tabs ttabset)
tab (car (if backward (last tabset) tabset)))))
tab (car (if backward (last tabset) tabset))))
)
(t
;; Cycle through visible tabs then tab groups.
(setq tab (centaur-tabs-tab-next tabset selected backward))
@ -972,7 +947,8 @@ instead."
tab (car (if backward (last tabset) tabset))))
;; Select the first/last visible tab of the new group.
(setq tabset (centaur-tabs-tabs (centaur-tabs-tab-tabset tab))
tab (car (if backward (last tabset) tabset))))))
tab (car (if backward (last tabset) tabset))))
))
(centaur-tabs-buffer-select-tab tab))))
;;;###autoload
@ -1021,23 +997,32 @@ Depend on the setting of the option `centaur-tabs-cycle-scope'."
(let ((centaur-tabs-cycle-scope 'tabs))
(centaur-tabs-cycle)))
;;
;;; Buffer tabs
;;
(defgroup centaur-tabs-buffer nil
"Display buffers in the tab bar."
:group 'centaur-tabs)
(defun centaur-tabs-filter-out (condp lst)
"Filter list LST with using CONDP as the filtering condition."
(delq nil
(cl-mapcar (lambda (x) (if (funcall condp x) nil x)) lst)))
(defun centaur-tabs-buffer-list ()
"Return the list of buffers to show in tabs.
Exclude buffers whose name starts with a space, when they are not
visiting a file."
(seq-filter (lambda (b)
(cond ((eq (current-buffer) b) b)
visiting a file. The current buffer is always included."
(centaur-tabs-filter-out
'centaur-tabs-hide-tab-cached
(delq nil
(cl-mapcar #'(lambda (b)
(cond
;; Always include the current buffer.
((eq (current-buffer) b) b)
((buffer-file-name b) b)
((char-equal ?\ (aref (buffer-name b) 0)) nil)
((buffer-live-p b) b)))
(buffer-list)))
(buffer-list)))))
(defun centaur-tabs-buffer-mode-derived-p (mode parents)
"Return non-nil if MODE derives from a mode in PARENTS."
@ -1048,20 +1033,15 @@ visiting a file."
(setq mode (get mode 'derived-mode-parent))))
derived))
;;
;;; Group buffers in tab sets.
;;
(defvar centaur-tabs--buffers nil)
(defun centaur-tabs-buffer-groups-result ()
"Return the first group the current buffer belongs to."
(car (nth 2 (assq (current-buffer) centaur-tabs--buffers))))
(defun centaur-tabs-buffer-update-groups ()
"Update tabsets from groups of existing buffers.
Return the the first group where the current buffer is."
(let ((bl (sort
(mapcar
(cl-mapcar
#'(lambda (b)
(with-current-buffer b
(list (current-buffer)
@ -1095,25 +1075,19 @@ Return the the first group where the current buffer is."
(dolist (tab (centaur-tabs-tabs tabset))
(let ((e (assq (centaur-tabs-tab-value tab) bl)))
(or (and e (memq tabset
(mapcar 'centaur-tabs-get-tabset
(cl-mapcar 'centaur-tabs-get-tabset
(nth 2 e))))
(centaur-tabs-delete-tab tab))))
;; Return empty tab sets
(unless (centaur-tabs-tabs tabset)
tabset))))
;; The new cache becomes the current one.
(setq centaur-tabs--buffers bl))))
(setq centaur-tabs--buffers bl)))
;; Return the first group the current buffer belongs to.
(car (nth 2 (assq (current-buffer) centaur-tabs--buffers))))
(defun centaur-tabs-buffer-update-groups-cache ()
"Don't call function `centaur-tabs-buffer-update-groups' too often."
(let ((result (centaur-tabs-buffer-groups-result)))
(when (or (null result)
(null centaur-tabs--buffers))
(centaur-tabs-buffer-update-groups))
(centaur-tabs-buffer-groups-result)))
;;
;;; Tab bar callbacks
;;
(defsubst centaur-tabs-buffer-show-groups (flag)
"Set display of tabs for groups of buffers to FLAG."
@ -1121,7 +1095,7 @@ Return the the first group where the current buffer is."
(defun centaur-tabs-buffer-tabs ()
"Return the buffers to display on the tab bar, in a tab set."
(let ((tabset (centaur-tabs-get-tabset (centaur-tabs-buffer-update-groups-cache))))
(let ((tabset (centaur-tabs-get-tabset (centaur-tabs-buffer-update-groups))))
(centaur-tabs-select-tab-value (current-buffer) tabset)
(when centaur-tabs--buffer-show-groups
(setq tabset (centaur-tabs-get-tabsets-tabset))
@ -1146,8 +1120,10 @@ That is, a string used to represent it on the tab bar."
(defun centaur-tabs-separator-render (item face)
"Render ITEM using FACE."
(cond ((and (listp item) (eq 'image (car item)))
(propertize " " 'display item 'face face))
(cond
((and (listp item) (eq 'image (car item)))
(propertize " " 'display item
'face face))
(t item)))
(defvar centaur-tabs-last-focused-buffer nil
@ -1164,7 +1140,8 @@ That is, a string used to represent it on the tab bar."
(setq centaur-tabs-last-focused-buffer buffer)
(setq centaur-tabs-last-focused-buffer-group group)
;; (centaur-tabs-buffer-show-groups nil)
(centaur-tabs-display-update)))
(centaur-tabs-display-update)
))
(defun centaur-tabs-buffer-track-killed ()
"Hook run just before actually killing a buffer.
@ -1208,8 +1185,7 @@ first."
(nreverse (centaur-tabs-insert-after (nreverse list) bef-el el)))
(defun centaur-tabs-adjust-buffer-order ()
"Put the two buffers switched to the adjacent position after current
buffer changed."
"Put the two buffers switched to the adjacent position after current buffer changed."
;; Don't trigger by centaur-tabs command, it's annoying.
;; This feature should be trigger by search plugins, such as ibuffer, helm or ivy.
(unless (or (string-prefix-p "centaur-tabs" (format "%s" this-command))
@ -1230,7 +1206,7 @@ buffer changed."
(when (string= current-group centaur-tabs-last-focused-buffer-group)
(let* ((bufset (centaur-tabs-get-tabset current-group))
(current-group-tabs (centaur-tabs-tabs bufset))
(current-group-buffers (mapcar 'car current-group-tabs))
(current-group-buffers (cl-mapcar 'car current-group-tabs))
(current-buffer-index (cl-position current current-group-buffers))
(previous-buffer-index (cl-position previous current-group-buffers)))
@ -1254,7 +1230,8 @@ buffer changed."
(setq new-group-tabs (centaur-tabs-insert-before base-group-tabs previous-tab current-tab)))))
(set bufset new-group-tabs)
(centaur-tabs-set-template bufset nil)
(centaur-tabs-display-update)))))
(centaur-tabs-display-update)
))))
;; Update the group name of the last accessed tab.
(setq centaur-tabs-last-focused-buffer-group current-group)))))
@ -1276,10 +1253,10 @@ buffer changed."
;; Just continue if two buffers are in the same group.
(when (string= current-group centaur-tabs-last-focused-buffer-group)
(let* ((bufset (centaur-tabs-get-tabset current-group))
(current-group-tabs (centaur-tabs-tabs bufset))
(new-group-tabs (sort current-group-tabs
(current-group-tabs (centaur-tabs-tabs bufset)))
(setq new-group-tabs (sort current-group-tabs
(lambda (x y)
(string< (buffer-name (car x)) (buffer-name (car y)))))))
(string< (buffer-name (car x)) (buffer-name (car y))))))
(set bufset new-group-tabs)
(centaur-tabs-set-template bufset nil)
(centaur-tabs-display-update)))
@ -1294,17 +1271,17 @@ buffer changed."
(setq centaur-tabs-adjust-buffer-order-function 'centaur-tabs-adjust-buffer-order-alphabetically)
(add-hook 'post-command-hook centaur-tabs-adjust-buffer-order-function))
;;
;;; Buffer grouping and tab hiding
;;
(defun centaur-tabs-project-name ()
"Get project name for tabs."
(when-let* (((buffer-file-name))
(project-current (project-current))
(let* ((project-current (project-current))
(project-name (if (proper-list-p project-current)
(car (last project-current))
(cdr project-current))))
(format "Project: %s" (expand-file-name project-name))))
(if project-name
(format "Project: %s" (expand-file-name project-name))
centaur-tabs-common-group-name)))
;; Rules to control buffer's group rules.
(defvar centaur-tabs-groups-hash (make-hash-table :test 'equal))
@ -1314,8 +1291,14 @@ buffer changed."
"Get group name of buffer BUF."
(let ((group-name (gethash buf centaur-tabs-groups-hash)))
;; Return group name cache if it exists for improve performance.
(or group-name
centaur-tabs-common-group-name)))
(if group-name
group-name
;; Otherwise try get group name with `project-current'.
;; `project-current' is very slow, it will slow down Emacs if you call it when switch buffer.
(with-current-buffer buf
(let ((project-name (centaur-tabs-project-name)))
(puthash buf project-name centaur-tabs-groups-hash)
project-name)))))
(defun centaur-tabs-buffer-groups ()
"`centaur-tabs-buffer-groups' control buffers' group rules.
@ -1326,8 +1309,6 @@ All buffer name start with * will group to \"Emacs\".
Other buffer group by `centaur-tabs-get-group-name' with project name."
(list
(cond
((when-let ((project-name (centaur-tabs-project-name)))
project-name))
((or (string-equal "*" (substring (buffer-name) 0 1))
(memq major-mode '(magit-process-mode
magit-status-mode
@ -1335,13 +1316,17 @@ Other buffer group by `centaur-tabs-get-group-name' with project name."
magit-log-mode
magit-file-mode
magit-blob-mode
magit-blame-mode)))
magit-blame-mode
)))
"Emacs")
((derived-mode-p 'shell-mode) "Shell")
((derived-mode-p 'eshell-mode) "EShell")
((derived-mode-p 'emacs-lisp-mode) "Elisp")
((derived-mode-p 'dired-mode) "Dired")
((memq major-mode '( org-mode org-agenda-mode diary-mode)) "OrgMode")
((derived-mode-p 'eshell-mode)
"EShell")
((derived-mode-p 'emacs-lisp-mode)
"Elisp")
((derived-mode-p 'dired-mode)
"Dired")
((memq major-mode '(org-mode org-agenda-mode diary-mode))
"OrgMode")
(t
(centaur-tabs-get-group-name (current-buffer))))))
@ -1383,7 +1368,8 @@ Other buffer group by `centaur-tabs-get-group-name' with project name."
;; Is not magit buffer.
(and (string-prefix-p "magit" name)
(not (file-name-extension name))))))
(not (file-name-extension name)))
)))
(defun centaur-tabs-hide-tab-cached (buf)
"Cached vesion of `centaur-tabs-hide-tab' to improve performance.
@ -1398,7 +1384,7 @@ Operates over buffer BUF"
(defun centaur-tabs-get-groups ()
"Refresh tabs groups."
(set centaur-tabs-tabsets-tabset (centaur-tabs-map-tabsets 'centaur-tabs-selected-tab))
(mapcar #'(lambda (group)
(cl-mapcar #'(lambda (group)
(format "%s" (cdr group)))
(centaur-tabs-tabs centaur-tabs-tabsets-tabset)))
@ -1410,13 +1396,13 @@ Operates over buffer BUF"
(with-current-buffer buffer
(when (string-equal 'current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(when (buffer-file-name buffer)
(add-to-list 'extension-names (file-name-extension (buffer-file-name buffer)))))))
(add-to-list 'extension-names (file-name-extension (buffer-file-name buffer))))
)))
(buffer-list))
extension-names))
(defcustom centaur-tabs-enable-ido-completion t
"Non-nil means use `ido-completing-read' for completing reads
else `completing-read'."
"Non-nil means use `ido-completing-read' for completing reads else `completing-read'."
:group 'centaur-tabs
:type 'boolean)
@ -1434,8 +1420,10 @@ Refer to the variable `centaur-tabs-enable-ido-completion'."
(add-hook hook (lambda ()
(if (boundp 'tab-line-format)
(setq-local tab-line-format nil)
(setq-local header-line-format nil)))))
(setq-local header-line-format nil))
)))
centaur-tabs-hide-tabs-hooks)
(provide 'centaur-tabs-functions)
;;; centaur-tabs-functions.el ends here

View file

@ -1,10 +1,10 @@
;;; centaur-tabs-interactive.el --- centaur-tabs interactive functions and plugins support lexical-binding: t; -*-
;; Copyright (C) 2019-2024 Emmanuel Bustos
;; Copyright (C) 2024 Jen-Chieh Shen
;; Copyright (C) 2019-2020 Emmanuel Bustos
;; This file is not part of GNU Emacs.
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2, or
@ -19,60 +19,20 @@
;; along with this program; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;; Commentary:
;;
;; This file contains centaur-tabs interactive functions and plugins support
;;
;;; Code:
(require 'cl-lib)
;;; Requires
(require 'centaur-tabs-elements)
;; Compiler pacifier
(declare-function ivy-read "ext:ivy.el" t t)
(declare-function helm-build-sync-source "ext:helm-source.el" t t)
(defvar helm-source-centaur-tabs-group)
(declare-function projectile-project-root "ext:projectile.el" t t)
(declare-function projectile-project-name "ext:projectile.el" t t)
(declare-function centaur-tabs-do-select "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-tab-from-event "centaur-tabs-functions.el")
(declare-function centaur-tabs-tab-value "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-tabset "centaur-tabs-functions.el")
(declare-function centaur-tabs-buffer-show-groups "centaur-tabs-functions.el")
(declare-function centaur-tabs-buffer-close-tab "centaur-tabs-functions.el")
(declare-function centaur-tabs-view "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-extensions "centaur-tabs-functions.el")
(declare-function centaur-tabs-selected-tab "centaur-tabs-functions.el")
(declare-function centaur-tabs-display-update "centaur-tabs-functions.el")
(declare-function centaur-tabs-set-template "centaur-tabs-functions.el")
(declare-function centaur-tabs-buffer-select-tab "centaur-tabs-functions.el")
(declare-function centaur-tabs-tabs "centaur-tabs-functions.el")
(declare-function centaur-tabs-tab-tabset "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-tabsets-tabset "centaur-tabs-functions.el")
(declare-function centaur-tabs-current-tabset "centaur-tabs-functions.el")
(declare-function centaur-tabs-completing-read "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-groups "centaur-tabs-functions.el")
(declare-function centaur-tabs-forward-group "centaur-tabs-functions.el")
(declare-function centaur-tabs-backward-group "centaur-tabs-functions.el")
(declare-function centaur-tabs-forward-tab "centaur-tabs-functions.el")
(declare-function centaur-tabs-backward-tab "centaur-tabs-functions.el")
(defvar centaur-tabs-cycle-scope)
(defvar centaur-tabs-current-tabset)
(defvar centaur-tabs-last-focused-buffer-group)
(defvar centaur-tabs-buffer-list-function)
(defvar centaur-tabs-buffer-groups-function)
(defvar centaur-tabs--buffer-show-groups)
(require 'centaur-tabs-functions)
;;;;;;;;;;;;;;;;;;;;;;; Interactive functions ;;;;;;;;;;;;;;;;;;;;;;;
(defun centaur-tabs-switch-group (&optional groupname)
"Switch tab groups using ido. GROUPNAME can optionaly be provided."
(interactive)
(let* ((tab-buffer-list (mapcar
(let* ((tab-buffer-list (cl-mapcar
#'(lambda (b)
(with-current-buffer b
(list (current-buffer)
@ -82,7 +42,8 @@
(groups (centaur-tabs-get-groups))
(group-name (or groupname (centaur-tabs-completing-read "Groups: " groups))) )
(catch 'done
(mapc #'(lambda (group)
(mapc
#'(lambda (group)
(when (equal group-name (car (car (cdr (cdr group)))))
(throw 'done (switch-to-buffer (car (cdr group))))))
tab-buffer-list) )))
@ -111,8 +72,7 @@ TYPE is default option."
(defun centaur-tabs-backward-tab-other-window (&optional reversed)
"Move to left tab in other window.
Optional argument REVERSED default is move backward, if reversed is non-nil
move forward."
Optional argument REVERSED default is move backward, if reversed is non-nil move forward."
(interactive)
(other-window 1)
(if reversed
@ -187,7 +147,8 @@ move forward."
(with-current-buffer buffer
(when (string-equal current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(when (funcall ,match-rule buffer)
(kill-buffer buffer)))))
(kill-buffer buffer))
)))
(buffer-list))))
(defun centaur-tabs-kill-all-buffers-in-current-group ()
@ -198,7 +159,8 @@ move forward."
(centaur-tabs-kill-buffer-match-rule
(lambda (_buffer) t))
;; Switch to next group.
(centaur-tabs-forward-group)))
(centaur-tabs-forward-group)
))
(defun centaur-tabs-kill-other-buffers-in-current-group ()
"Kill all buffers except current buffer in current group."
@ -207,7 +169,8 @@ move forward."
(currentbuffer (current-buffer)))
;; Kill all buffers in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer) (not (equal buffer currentbuffer))))))
(lambda (buffer) (not (equal buffer currentbuffer))))
))
(defun centaur-tabs-kill-unmodified-buffers-in-current-group ()
"Kill all unmodified buffer in current group."
@ -216,7 +179,9 @@ move forward."
(currentbuffer (current-buffer)))
;; Kill all buffers in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer) (not (buffer-modified-p buffer))))))
(lambda (buffer) (not (buffer-modified-p buffer))))
))
(defun centaur-tabs-kill-match-buffers-in-current-group ()
"Kill all buffers match extension in current group."
@ -230,10 +195,12 @@ move forward."
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer)
(let ((filename (buffer-file-name buffer)))
(and filename (string-equal (file-name-extension filename) match-extension)))))
(and filename (string-equal (file-name-extension filename) match-extension))
)))
;; Switch to next group if last file killed.
(when (equal (length extension-names) 1)
(centaur-tabs-forward-group))))
(centaur-tabs-forward-group))
))
(defun centaur-tabs-keep-match-buffers-in-current-group ()
"Keep all buffers match extension in current group."
@ -247,10 +214,12 @@ move forward."
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer)
(let ((filename (buffer-file-name buffer)))
(and filename (not (string-equal (file-name-extension filename) match-extension))))))
(and filename (not (string-equal (file-name-extension filename) match-extension)))
)))
;; Switch to next group if last file killed.
(when (equal (length extension-names) 1)
(centaur-tabs-forward-group))))
(centaur-tabs-forward-group))
))
(defun centaur-tabs-select-visible-nth-tab (tab-index)
"Select visible tab with TAB-INDEX'.
@ -285,7 +254,7 @@ not the actual logical index position of the current group."
;; ace-jump style tab switching
(defvar centaur-tabs-ace-jump-active nil
"Set to t if `centaur-tabs-ace-jump' is invoked.")
"t if centaur-tabs-ace-jump is invoked.")
(defvar centaur-tabs-dim-overlay nil
"Holds the overlay for dimming buffer when invoking centaur-tabs-ace-jump.")
@ -320,7 +289,7 @@ TAB has to be in the same group as the current tab."
(defun centaur-tabs-ace-action (action)
"Preform ACTION on a visible tab. Ace-jump style.
ACTION has to be one of value in `centaur-tabs-ace-dispatch-alist'"
ACTION has to be one of value in `centuar-tabs-ace-dispatch-alist'"
(when (centaur-tabs-current-tabset t)
(when centaur-tabs-ace-jump-dim-buffer
(centaur-tabs--dim-window))
@ -351,7 +320,7 @@ ACTION has to be one of value in `centaur-tabs-ace-dispatch-alist'"
(centaur-tabs-swap-tab sel))))
(throw 'done nil))
;; actions
((setq action-cache (assoc char centaur-tabs-ace-dispatch-alist))
((setq action-cache (assoc char centuar-tabs-ace-dispatch-alist))
(setq action-cache (cadr action-cache))
(cond ((eq action-cache 'exit) ; exit
(message "Quit")
@ -369,7 +338,7 @@ ACTION has to be one of value in `centaur-tabs-ace-dispatch-alist'"
(lambda (elem) (format "%s: %s"
(key-description (vector (car elem)))
(caddr elem)))
centaur-tabs-ace-dispatch-alist
centuar-tabs-ace-dispatch-alist
"\n")))
(t (setq action action-cache) ; other actions
(cond ((eq action-cache 'jump-to-tab)
@ -389,9 +358,11 @@ ACTION has to be one of value in `centaur-tabs-ace-dispatch-alist'"
(defun centaur-tabs-ace-jump (&optional arg)
"Select a tab and perform an action. Ace-jump style.
If no ARG is provided, select that tab. If prefixed with one
`universal-argument', swap the current tab with the selected tab.
If prefixed with two `universal-argument's, close selected tab."
If no ARG is provided, select that tab.
If prefixed with one `universal-argument', swap the current
tab with the selected tab.
If prefixed with two `universal-argument's, close
selected tab."
(interactive "p")
(cond ((eq arg 1)
(centaur-tabs-ace-action 'jump-to-tab))
@ -424,16 +395,13 @@ Should be buffer local and speed up calculation of buffer groups.")
((string-equal "*" (substring (buffer-name) 0 1)) '("Misc"))
((condition-case _err
(projectile-project-root)
(error nil))
(list (projectile-project-name)))
(error nil)) (list (projectile-project-name)))
((memq major-mode '(emacs-lisp-mode python-mode emacs-lisp-mode c-mode
c++-mode javascript-mode js-mode
js2-mode makefile-mode
lua-mode vala-mode))
'("Coding"))
lua-mode vala-mode)) '("Coding"))
((memq major-mode '(nxhtml-mode html-mode
mhtml-mode css-mode))
'("HTML"))
mhtml-mode css-mode)) '("HTML"))
((memq major-mode '(org-mode calendar-mode diary-mode)) '("Org"))
((memq major-mode '(dired-mode)) '("Dir"))
(t '("Other"))))
@ -478,9 +446,9 @@ Should be buffer local and speed up calculation of buffer groups.")
:action #'centaur-tabs-switch-group
:caller 'centaur-tabs-counsel-switch-group)))
(defun centaur-tabs-extract-window-to-new-frame()
"Kill the current window in the current frame, and open the current buffer
in a new frame."
"Kill the current window in the current frame, and open the current buffer in a new frame."
(interactive)
(unless (centaur-tabs--one-window-p)
(let ((buffer (current-buffer)))
@ -499,6 +467,7 @@ in a new frame."
(kill-new filename)
(message "Copied buffer file name '%s' to the kill ring." filename))))
(defun centaur-tabs-open-directory-in-external-application ()
"Open the current directory in a external application."
(interactive)
@ -507,7 +476,7 @@ in a new frame."
(defun centaur-tabs-open-in-external-application ()
"Open the file of the current buffer according to its mime type."
(interactive)
(let ((path (or (buffer-file-name) default-directory)))
(let ((path (if (buffer-file-name) (buffer-file-name) default-directory)))
(centaur-tabs--open-externally path)))
(defun centaur-tabs--open-externally (file-or-path)
@ -526,6 +495,7 @@ Modified copy of `treemacs-visit-node-in-external-application`."
(start-process "" nil "xdg-open" path)))
(_ (message "Don't know how to open files on %s." (symbol-name system-type))))))
(defun centaur-tabs--copy-directory-name-to-clipboard ()
"Copy the current directory name to the clipboard."
(interactive)
@ -537,6 +507,7 @@ Modified copy of `treemacs-visit-node-in-external-application`."
"Menu definition with a list of tab groups."
(mapcar (lambda (s) `[,s ,s]) (sort (centaur-tabs-get-groups) #'string<)))
(defun centaur-tabs--tab-submenu-tabs-definition ()
"Menu definition with a list of tabs for the current group."
(let* ((tabset (centaur-tabs-get-tabset centaur-tabs-last-focused-buffer-group))
@ -545,9 +516,13 @@ Modified copy of `treemacs-visit-node-in-external-application`."
(sorted-tabnames (sort (mapcar #'buffer-name buffers) #'string<)))
(mapcar (lambda (s) `[,s ,s]) sorted-tabnames)))
(defvar centaur-tabs--groups-submenu-key "Tab groups")
(defvar centaur-tabs--tabs-submenu-key "Go to tab of group")
(defun centaur-tabs--kill-this-buffer-dont-ask()
"Kill the current buffer without confirmation."
(interactive)
@ -555,6 +530,7 @@ Modified copy of `treemacs-visit-node-in-external-application`."
(centaur-tabs-display-update)
(redisplay t))
(defun centaur-tabs--tab-menu-definition ()
"Definition of the context menu of a tab."
`(["Kill this buffer" centaur-tabs--kill-this-buffer-dont-ask]
@ -582,7 +558,8 @@ Modified copy of `treemacs-visit-node-in-external-application`."
:active default-directory]
"----"
,( append (list centaur-tabs--groups-submenu-key) (centaur-tabs--tab-submenu-groups-definition))
,( append (list centaur-tabs--tabs-submenu-key) (centaur-tabs--tab-submenu-tabs-definition))))
,( append (list centaur-tabs--tabs-submenu-key) (centaur-tabs--tab-submenu-tabs-definition))
))
(defun centaur-tabs--one-window-p ()
"Like `one-window-p`, but taking into account side windows like treemacs."
@ -597,16 +574,20 @@ Modified copy of `treemacs-visit-node-in-external-application`."
(lambda (tab) (string= tabname (buffer-name (centaur-tabs-tab-value tab))))
seq)))
(defun centaur-tabs--tab-menu (event)
"Show a context menu for the clicked tab or button.
The clicked tab, identified by EVENT, is selected."
"Show a context menu for the clicked tab or button. The clicked tab, identified by EVENT, is selected."
(interactive "e" )
(let ((click-on-tab-p (ignore-errors (centaur-tabs-get-tab-from-event event))))
(when (not click-on-tab-p)
(centaur-tabs--groups-menu))
(when click-on-tab-p
(centaur-tabs-do-select event)
(redisplay t)
(let*
((menu (easy-menu-create-menu nil (centaur-tabs--tab-menu-definition)))
(choice (x-popup-menu t menu))
@ -615,7 +596,7 @@ The clicked tab, identified by EVENT, is selected."
(when action-is-command-p
(call-interactively action))
(when (not action-is-command-p)
(let* ((menu-key (cl-first choice))
(let* ((menu-key (first choice))
(choice-is-group-p (string= centaur-tabs--groups-submenu-key (symbol-name menu-key)))
(name (car (last choice)))
(name-as-string (symbol-name name)))
@ -623,12 +604,14 @@ The clicked tab, identified by EVENT, is selected."
(centaur-tabs-switch-group name-as-string)
(switch-to-buffer name-as-string))))))))
(defun centaur-tabs--groups-menu ()
"Show a popup menu with the centaur tabs groups."
(interactive)
(let* ((sorted-groups (centaur-tabs--tab-submenu-groups-definition))
(menu (easy-menu-create-menu "Tab groups"
(centaur-tabs--tab-submenu-groups-definition)))
(let*
((sorted-groups (centaur-tabs--tab-submenu-groups-definition))
(menu (easy-menu-create-menu "Tab groups" (centaur-tabs--tab-submenu-groups-definition)))
(choice (x-popup-menu t menu))
(action (lookup-key menu (apply 'vector choice)))
(action-is-command-p (and (commandp action) (functionp action))))
@ -638,5 +621,7 @@ The clicked tab, identified by EVENT, is selected."
(let ((group (car (last choice))))
(centaur-tabs-switch-group (format "%s" group))))))
(provide 'centaur-tabs-interactive)
;;; centaur-tabs-interactive.el ends here

View file

@ -0,0 +1,14 @@
(define-package "centaur-tabs" "20230607.1501" "Aesthetic, modern looking customizable tabs plugin"
'((emacs "24.4")
(powerline "2.4")
(cl-lib "0.5"))
:commit "0bb1aa18d475319df85f192dce3327802866c3c3" :authors
'(("Emmanuel Bustos" . "ema2159@gmail.com"))
:maintainers
'(("Emmanuel Bustos" . "ema2159@gmail.com"))
:maintainer
'("Emmanuel Bustos" . "ema2159@gmail.com")
:url "https://github.com/ema2159/centaur-tabs")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -1,18 +1,18 @@
;;; centaur-tabs.el --- Aesthetic, modern looking customizable tabs plugin -*- lexical-binding: t; -*-
;; Copyright (C) 2019-2024 Emmanuel Bustos
;; Copyright (C) 2024 Jen-Chieh Shen
;; Copyright (C) 2019 Emmanuel Bustos
;; Filename: centaur-tabs.el
;; Description: Provide an out of box configuration to use highly customizable tabs.
;; URL: https://github.com/ema2159/centaur-tabs
;; Author: Emmanuel Bustos <ema2159@gmail.com>
;; Maintainer: Jen-Chieh Shen <jcs090218@gmail.com>
;; Maintainer: Emmanuel Bustos <ema2159@gmail.com>
;; Created: 2019-21-19 22:14:34
;; Version: 3.3
;; Version: 5
;; Known Compatibility: GNU Emacs 26.2
;; Package-Requires: ((emacs "27.1") (powerline "2.4"))
;; Keywords: frames
;; Package-Requires: ((emacs "24.4") (powerline "2.4") (cl-lib "0.5"))
;;
;;
;;; This file is NOT part of GNU Emacs
@ -54,15 +54,10 @@
;;
;;; Code:
;;; Requires
(require 'centaur-tabs-elements)
(require 'centaur-tabs-functions)
(require 'centaur-tabs-interactive)
;; Compiler pacifier
(declare-function undo-tree-undo-1 "ext:undo-tree.el")
(declare-function undo-tree-redo-1 "ext:undo-tree.el")
;;;;;;;;;;;;;;;;;;;;;;; Centaur-Tabs source code ;;;;;;;;;;;;;;;;;;;;;;;
(defgroup centaur-tabs nil
@ -71,17 +66,15 @@
(defvar centaur-tabs--buffer-show-groups nil)
;;
;;; Minor modes
;;
(defsubst centaur-tabs-mode-on-p ()
"Return non-nil if Centaur-Tabs mode is on."
(eq (default-value centaur-tabs-display-line-format)
centaur-tabs-header-line-format))
;;
;;; Centaur-Tabs-Local mode
;;
(defvar centaur-tabs--local-hlf nil)
;;;###autoload
@ -155,20 +148,19 @@ Returns non-nil if the new state is enabled.
(buffer-list))
;; Restore previous `centaur-tabs-display-line-format'.
(set-default centaur-tabs-display-line-format centaur-tabs--global-hlf)
(centaur-tabs-free-tabsets-store)))
;; Make sure it refresh every windows!
(force-window-update))
(centaur-tabs-free-tabsets-store))
))
;;
;;; Tab bar buffer setup
;;
(defun centaur-tabs-buffer-init ()
"Initialize tab bar buffer data.
Run as `centaur-tabs-init-hook'."
(setq centaur-tabs--buffers nil
centaur-tabs-current-tabset-function 'centaur-tabs-buffer-tabs
centaur-tabs-tab-label-function 'centaur-tabs-buffer-tab-label
centaur-tabs-select-tab-function 'centaur-tabs-buffer-select-tab)
centaur-tabs-select-tab-function 'centaur-tabs-buffer-select-tab
)
;; If set, initialize selected overline
(when (eq centaur-tabs-set-bar 'under)
(set-face-attribute 'centaur-tabs-selected nil
@ -196,15 +188,12 @@ Run as `centaur-tabs-init-hook'."
(set-face-attribute 'centaur-tabs-unselected-modified nil
:overline nil
:underline nil))
(add-function :after after-focus-change-function #'centaur-tabs-after-focus)
(add-hook 'window-buffer-change-functions #'centaur-tabs-on-window-buffer-change)
(add-hook 'after-save-hook #'centaur-tabs-on-saving-buffer)
(add-hook 'first-change-hook #'centaur-tabs-on-modifying-buffer)
(add-hook 'kill-buffer-hook #'centaur-tabs-buffer-track-killed)
(advice-add #'undo :after #'centaur-tabs-on-modifying-buffer)
(advice-add #'undo-tree-undo-1 :after #'centaur-tabs-on-modifying-buffer)
(advice-add #'undo-tree-redo-1 :after #'centaur-tabs-on-modifying-buffer)
(advice-add 'load-theme :after #'centaur-tabs--after-load-theme))
(advice-add #'undo :after #'centaur-tabs-after-modifying-buffer)
(advice-add #'undo-tree-undo-1 :after #'centaur-tabs-after-modifying-buffer)
(advice-add #'undo-tree-redo-1 :after #'centaur-tabs-after-modifying-buffer))
(defun centaur-tabs-buffer-quit ()
"Quit tab bar buffer.
@ -212,19 +201,18 @@ Run as `centaur-tabs-quit-hook'."
(setq centaur-tabs--buffers nil
centaur-tabs-current-tabset-function nil
centaur-tabs-tab-label-function nil
centaur-tabs-select-tab-function nil)
(remove-function after-focus-change-function #'centaur-tabs-after-focus)
(remove-hook 'window-buffer-change-functions #'centaur-tabs-on-window-buffer-change)
(remove-hook 'after-save-hook 'centaur-tabs-on-modifying-buffer)
centaur-tabs-select-tab-function nil
)
(remove-hook 'after-save-hook 'centaur-tabs-after-modifying-buffer)
(remove-hook 'first-change-hook 'centaur-tabs-on-modifying-buffer)
(remove-hook 'kill-buffer-hook 'centaur-tabs-buffer-track-killed)
(advice-remove #'undo #'centaur-tabs-on-modifying-buffer)
(advice-remove #'undo-tree-undo-1 #'centaur-tabs-on-modifying-buffer)
(advice-remove #'undo-tree-redo-1 #'centaur-tabs-on-modifying-buffer)
(advice-remove 'load-theme #'centaur-tabs--after-load-theme))
(advice-remove #'undo #'centaur-tabs-after-modifying-buffer)
(advice-remove #'undo-tree-undo-1 #'centaur-tabs-after-modifying-buffer)
(advice-remove #'undo-tree-redo-1 #'centaur-tabs-after-modifying-buffer))
(add-hook 'centaur-tabs-init-hook #'centaur-tabs-buffer-init)
(add-hook 'centaur-tabs-quit-hook #'centaur-tabs-buffer-quit)
(provide 'centaur-tabs)
;;; centaur-tabs.el ends here

View file

@ -1,891 +0,0 @@
;;; centaur-tabs-elements.el --- centaur-tabs visual components and customizations -*- lexical-binding: t; -*-
;; Copyright (C) 2019-2024 Emmanuel Bustos
;; Copyright (C) 2024 Jen-Chieh Shen
;; This file is not part of GNU Emacs.
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;; Commentary:
;;
;; This file contains the visual components of centaur-tabs
;;
;;; Code:
(require 'custom)
(require 'color)
(require 'powerline)
;; Compiler pacifier
(declare-function all-the-icons-icon-for-file "ext:all-the-icons.el" t t)
(declare-function all-the-icons-icon-for-mode "ext:all-the-icons.el" t t)
(declare-function nerd-icons-icon-for-file "ext:nerd-icons.el" t t)
(declare-function nerd-icons-icon-for-mode "ext:nerd-icons.el" t t)
;;
;;; Faces
(defface centaur-tabs-default
'((t (:background "black" :foreground "black")))
"Default face used in the tab bar."
:group 'centaur-tabs)
(defface centaur-tabs-unselected
'((t (:background "#3D3C3D" :foreground "grey50")))
"Face used for unselected tabs."
:group 'centaur-tabs)
(defface centaur-tabs-selected
'((t (:background "#31343E" :foreground "white")))
"Face used for the selected tab."
:group 'centaur-tabs)
(defface centaur-tabs-unselected-modified
'((t (:background "#3D3C3D" :foreground "grey50")))
"Face used for unselected-modified tabs."
:group 'centaur-tabs)
(defface centaur-tabs-selected-modified
'((t (:background "#31343E" :foreground "white")))
"Face used for the selected-modified tab."
:group 'centaur-tabs)
(defface centaur-tabs-close-unselected
'((t (:inherit centaur-tabs-unselected)))
"Face used for unselected close button."
:group 'centaur-tabs)
(defface centaur-tabs-close-selected
'((t (:inherit centaur-tabs-selected)))
"Face used for selected close button."
:group 'centaur-tabs)
(defface centaur-tabs-name-mouse-face
'((t nil))
"Face used for tab name when hovered with the mouse."
:group 'centaur-tabs)
(defface centaur-tabs-close-mouse-face
'((t (:inherit underline)))
"Face used for close button when hovered with the mouse."
:group 'centaur-tabs)
(defface centaur-tabs-modified-marker-selected
`((t (:inherit centaur-tabs-selected)))
"Face used for selected modified marker."
:group 'centaur-tabs)
(defface centaur-tabs-modified-marker-unselected
`((t (:inherit centaur-tabs-unselected)))
"Face used for unselected modified marker."
:group 'centaur-tabs)
(defface centaur-tabs-active-bar-face
'((t (:background "cyan")))
"Face used for selected tab bar."
:group 'centaur-tabs)
(defface centaur-tabs-jump-identifier-selected
'((t (:inherit centaur-tabs-modified-marker-selected :weight extra-bold)))
"Face used for selected tab identifiers when centaur-tabs-ace-jump is invoked."
:group 'centaur-tabs)
(defface centaur-tabs-jump-identifier-unselected
'((t (:inherit centaur-tabs-modified-marker-unselected :weight extra-bold)))
"Face used for unselected tab identifiers when centaur-tabs-ace-jump is invoked."
:group 'centaur-tabs)
(defface centaur-tabs-dim-buffer-face
'((t (:foreground "gray40")))
"Face for the buffer when centaur-tabs-ace-jump is invoked."
:group 'centaur-tabs)
;;
;;; Tabs' display line
(defvar centaur-tabs-display-line
(if (boundp 'tab-line-format)
'tab-line
'header-line))
(defvar centaur-tabs-display-line-format
(if (boundp 'tab-line-format)
'tab-line-format
'header-line-format))
;;
;;; Tabs' characteristics
(defcustom centaur-tabs-style "bar"
"The style of tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-label-fixed-length 0
"Fixed length of label. Set to 0 if dynamic."
:group 'centaur-tabs
:type 'int)
(defcustom centaur-tabs-background-color
(face-background 'centaur-tabs-default nil 'default)
"*Background color of the tab bar.
By default, use the background color specified for the
`centaur-tabs-default' face (or inherited from another face), or the
background color of the `default' face otherwise."
:group 'centaur-tabs
:type 'face)
(defcustom centaur-tabs-height 22
"The height of tab."
:group 'centaur-tabs
:type 'int)
(defcustom centaur-tabs-bar-height (+ 8 centaur-tabs-height)
"The height of bar."
:group 'centaur-tabs
:type 'int)
(defcustom centaur-tabs-mouse-pointer 'hand
"Cursor to display when hovering the tabs.
Default is `'hand'. The following scopes are possible:
- arrow
- hand
- vdrag
- hdrag
- modeline
- hourglass"
:group 'centaur-tabs
:type 'variable)
(defcustom centaur-tabs-set-bar nil
"When non nil, display a bar to show the currently selected tab.
There are three options:
- `'left': displays the bar at the left of the currently selected tab.
- `'under': displays the bar under the currently selected tab.
- `'over': displays the bar over the currently selected tab."
:group 'centaur-tabs
:type '(choice :tag "Display bar at..."
(const :tag "Put bar on the left" left)
(const :tag "Put bar as an underline" under)
(const :tag "Put bar as an overline" over)))
;;
;;; Icons
(defcustom centaur-tabs-set-icons nil
"When non nil, display an icon based on `centaur-tabs-icon-type' alongside
the tab name."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-icon-type (and centaur-tabs-set-icons
(or (require 'all-the-icons nil t)
(require 'nerd-icons nil t)))
"Icon type; it should be one of `all-the-icons' and `nerd-icons'."
:group 'centaur-tabs
:type 'symbol
:set
(lambda (k v)
(pcase v
('all-the-icons
(unless (require 'all-the-icons nil t)
(setq v nil)))
('nerd-icons
(unless (require 'nerd-icons nil t)
(setq v nil)))
('type
(if (require 'all-the-icons nil t)
(setq v 'all-the-icons)
(setq v nil))))
(set k v)))
(defvar centaur-tabs-icon-scale-factor 1.0
"The base scale factor for the `height' face property of tab icons.")
(defvar centaur-tabs-icon-v-adjust 0.01
"The vertical adjust for tab icons.")
(defcustom centaur-tabs-gray-out-icons nil
"When non nil, enable gray icons for unselected buffer."
:group 'centaur-tabs
:type '(choice :tag "Gray out icons for unselected..."
(const :tag "Buffer" buffer)))
(defcustom centaur-tabs-plain-icons nil
"When non nil, tab icons' color will be the same as tabs' foreground color."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-icons-prefix " "
"Prefix string before icons."
:group 'centaur-tabs
:type 'string)
(defun centaur-tabs--icon-for-file (file &rest args)
"Get the formatted icon for FILE.
ARGS should be a plist containing `:height', `:v-adjust', or `:face' properties."
(pcase centaur-tabs-icon-type
('all-the-icons (apply #'all-the-icons-icon-for-file file args))
('nerd-icons (apply #'nerd-icons-icon-for-file file args))))
(defun centaur-tabs--icon-for-mode (mode &rest args)
"Get the formatted icon for MODE.
ARGS should be a plist containining `:height', `:v-adjust' or `:face' properties
like in the normal icon inserting functions."
(pcase centaur-tabs-icon-type
('all-the-icons (apply #'all-the-icons-icon-for-mode mode args))
('nerd-icons (apply #'nerd-icons-icon-for-mode mode args))))
(defun centaur-tabs-icon (tab face selected)
"Generate icon for TAB using FACE's background.
If icon gray out option enabled, gray out icon if not SELECTED."
(if centaur-tabs-icon-type
(with-current-buffer (car tab)
(let* ((icon
(or (ignore-errors
(centaur-tabs--icon-for-file
(file-name-nondirectory (buffer-file-name))
:v-adjust centaur-tabs-icon-v-adjust
:height centaur-tabs-icon-scale-factor))
(ignore-errors
(centaur-tabs--icon-for-mode
major-mode
:v-adjust centaur-tabs-icon-v-adjust
:height centaur-tabs-icon-scale-factor))))
(background (face-background face nil 'default))
(inactive (cond ((and (not selected)
(eq centaur-tabs-gray-out-icons 'buffer))
(face-foreground 'mode-line-inactive nil 'default))
(centaur-tabs-plain-icons
(face-foreground 'centaur-tabs-selected nil 'default))
(t 'unspecified)))
(underline (and (eq (if (display-graphic-p) centaur-tabs-set-bar) 'under)
(face-attribute face :underline)))
(overline (and (eq (if (display-graphic-p) centaur-tabs-set-bar) 'over)
(face-attribute face :overline))))
(if (stringp icon)
(progn
(propertize icon 'face `(:inherit ,(get-text-property 0 'face icon)
:foreground ,inactive
:background ,background
:underline ,underline
:overline ,overline)))
"")))
""))
;;
;;; Ace-window style tab switching
(defcustom centaur-tabs-show-jump-identifier 'prompted
"Whether to show the tab identifier for centaur-tabs-ace-jump.
It has 3 options:
- `'nil', never show the jump identifier.
- `'prompted', only show it when using centaur-tabs-ace-jump.
- `'always', always show it regardless of the status."
:group 'centaur-tabs
:type '(choice :tag "show identifier when..."
(const :tag "Never" nil)
(const :tag "Only when prompted" prompted)
(const :tag "Always" always)))
(defcustom centaur-tabs-ace-jump-dim-buffer t
"Whether to dim the current buffer when centaur-ace-jump is activated."
:type 'boolean
:group 'centaur-tabs)
(defvar centaur-tabs-ace-jump-keys
'(?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)
"Buffer jump keys used by centaur-tabs-ace-jump.")
(defvar centaur-tabs-ace-dispatch-alist
'((?q exit "Exit")
(?\C-g exit "Exit")
(?j jump-to-tab "Jump to tab")
(?x close-tab "Close tab")
(?s swap-tab "Swap tab")
(?\[ backward-group "Previous group")
(?\] forward-group "Next group")
(?? show-help "Show dispatch help"))
"Action keys used by centaur-tabs-ace-jump.
The value of each element must be in the form:
\(key keyword docstring), where keyword must be one of the follows:
\(exit, jump-to-tab, close-tab, swap-tab, backward-group,
forward-group, show-help).")
;;
;;; Close buttons, modified marker and edges' margins
(defcustom centaur-tabs-set-close-button t
"When non nil, display a clickable close button on the right side of the tabs."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-set-left-close-button nil
"When non nil, display a clickable close button on the left side of the tabs."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-close-button (make-string 1 #x00D7)
"Display appearance of the close buttons, if enabled."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-set-modified-marker nil
"When non nil, display a marker when the buffer is modified."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-modified-marker (make-string 1 #x23FA)
"Display appearance of the modified marker, if enabled."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-left-edge-margin " "
"Text to display at the left edge of the tabs, or nil for no added margin."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-right-edge-margin " "
"Text to display at the right edge of the tabs, or nil for no added margin."
:group 'centaur-tabs
:type 'string)
;;
;;; Selected tab bar
(defun centaur-tabs--make-xpm (face width height)
"Create an XPM bitmap via FACE WIDTH and HEIGHT.
Taken from `doom-modeline'."
(when (and (display-graphic-p)
(image-type-available-p 'xpm))
(propertize
" " 'display
(let ((data (make-list height (make-list width 1)))
(color (or (face-background face nil t) "None")))
(ignore-errors
(create-image
(concat
(format
"/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\","
(length (car data)) (length data) color color)
(apply #'concat
(cl-loop with idx = 0
with len = (length data)
for dl in data
do (cl-incf idx)
collect
(concat
"\""
(cl-loop for d in dl
if (= d 0) collect (string-to-char " ")
else collect (string-to-char "."))
(if (eq idx len) "\"};" "\",\n")))))
'xpm t :ascent 'center))))))
(defvar centaur-tabs-active-bar
(centaur-tabs--make-xpm 'centaur-tabs-active-bar-face
2
centaur-tabs-bar-height))
;;
;;; Navigation buttons
(defcustom centaur-tabs-show-navigation-buttons nil
"When non-nil, show the buttons for backward/forward tabs."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-down-tab-text ""
"Text icon to show in the down button tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-backward-tab-text ""
"Text icon to show in the backward button tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-forward-tab-text ""
"Text icon to show in the forward button tab."
:group 'centaur-tabs
:type 'string)
(defcustom centaur-tabs-show-count nil
"When non-nil, show the current index and count of tabs in the current group."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-count-format " [%d/%d] "
"Format text to display count."
:group 'centaur-tabs
:type 'string)
;;
;;; New tab button
(defcustom centaur-tabs-show-new-tab-button t
"When non-nil, show the button to create a new tab."
:group 'centaur-tabs
:type 'boolean)
(defcustom centaur-tabs-new-tab-text " + "
"Text icon to show in the new-tab button."
:group 'centaur-tabs
:type 'string)
;;
;;; Separators
(defvar centaur-tabs-style-left nil)
(defvar centaur-tabs-style-right nil)
(defvar ns-use-srgb-colorspace)
(defvar centaur-tabs-image-apple-rgb
(and (eq (window-system) 'ns)
ns-use-srgb-colorspace
(< 11
(string-to-number
(and (string-match "darwin\\([0-9]+\\)" system-configuration)
(match-string-no-properties 1 system-configuration)))))
"Boolean variable to determine whether to use Apple RGB colorspace.
used to render images.
t on macOS 10.7+ and `ns-use-srgb-colorspace' is t, nil otherwise.
This variable is automatically set, there's no need to modify it.")
(defun centaur-tabs-separator-interpolate (color1 color2)
"Interpolate between COLOR1 and COLOR2.
COLOR1 and COLOR2 must be supplied as hex strings with a leading #."
(let* ((c1 (color-name-to-rgb color1))
(c2 (color-name-to-rgb color2))
(red (/ (+ (nth 0 c1) (nth 0 c2)) 2))
(green (/ (+ (nth 1 c1) (nth 1 c2)) 2))
(blue (/ (+ (nth 2 c1) (nth 2 c2)) 2)))
(color-rgb-to-hex red green blue)))
(defun centaur-tabs-separator-color-xyz-to-apple-rgb (X Y Z)
"Convert CIE X Y Z colors to Apple RGB color space."
(let ((r (+ (* 3.2404542 X) (* -1.5371385 Y) (* -0.4985314 Z)))
(g (+ (* -0.9692660 X) (* 1.8760108 Y) (* 0.0415560 Z)))
(b (+ (* 0.0556434 X) (* -0.2040259 Y) (* 1.0572252 Z))))
(list (expt r (/ 1.8)) (expt g (/ 1.8)) (expt b (/ 1.8)))))
(defun centaur-tabs-separator-color-srgb-to-apple-rgb (red green blue)
"Convert RED GREEN BLUE colors from sRGB color space to Apple RGB.
RED, GREEN and BLUE should be between 0.0 and 1.0, inclusive."
(apply #'centaur-tabs-separator-color-xyz-to-apple-rgb (color-srgb-to-xyz red green blue)))
(defun centaur-tabs-separator-hex-color (color)
"Get the hexadecimal value of COLOR."
(when color
(let ((srgb-color (color-name-to-rgb color)))
(if centaur-tabs-image-apple-rgb
(apply #'color-rgb-to-hex (apply #'centaur-tabs-separator-color-srgb-to-apple-rgb srgb-color))
(apply #'color-rgb-to-hex srgb-color)))))
(defun centaur-tabs-separator-pattern (lst)
"Turn LST into an infinite pattern."
(when lst
(let ((pattern (cl-copy-list lst)))
(setcdr (last pattern) pattern))))
(defun centaur-tabs-separator-pattern-to-string (pattern)
"Convert a PATTERN into a string that can be used in an XPM."
(concat "\"" (mapconcat #'number-to-string pattern "") "\","))
(defun centaur-tabs-separator-reverse-pattern (pattern)
"Reverse each line in PATTERN."
(mapcar 'reverse pattern))
(defun centaur-tabs-separator-row-pattern (fill total &optional fade)
"Make a list that has FILL 0s out of TOTAL 1s with FADE 2s to the right of
the fill."
(unless fade (setq fade 0))
(let ((fill (min fill total))
(fade (min fade (max (- total fill) 0))))
(append (make-list fill 0)
(make-list fade 2)
(make-list (- total fill fade) 1))))
(defun centaur-tabs-separator-pattern-bindings-body (patterns height-exp pattern-height-sym
second-pattern-height-sym)
"Create let-var bindings and a function body from PATTERNS.
The `car' and `cdr' parts of the result can be passed to the
function `centaur-tabs-separator-wrap-defun' as its `let-vars'
and `body' arguments,respectively. HEIGHT-EXP is an expression
calculating the image height and it should contain a free variable `height'.
PATTERN-HEIGHT-SYM and SECOND-PATTERN-HEIGHT-SYM are symbols used
for let-var binding variables."
(let* ((pattern (centaur-tabs-separator-pattern (mapcar 'centaur-tabs-separator-pattern-to-string (car patterns))))
(header (mapcar 'centaur-tabs-separator-pattern-to-string (nth 1 patterns)))
(footer (mapcar 'centaur-tabs-separator-pattern-to-string (nth 2 patterns)))
(second-pattern (centaur-tabs-separator-pattern (mapcar 'centaur-tabs-separator-pattern-to-string (nth 3 patterns))))
(center (mapcar 'centaur-tabs-separator-pattern-to-string (nth 4 patterns)))
(reserve (+ (length header) (length footer) (length center))))
(when pattern
(cons `((,pattern-height-sym (max (- ,height-exp ,reserve) 0))
(,second-pattern-height-sym (/ ,pattern-height-sym 2))
(,pattern-height-sym ,(if second-pattern `(ceiling ,pattern-height-sym 2) `,pattern-height-sym)))
(list (when header `(mapconcat 'identity ',header ""))
`(mapconcat 'identity
(cl-subseq ',pattern 0 ,pattern-height-sym) "")
(when center `(mapconcat 'identity ',center ""))
(when second-pattern
`(mapconcat 'identity
(cl-subseq ',second-pattern
0 ,second-pattern-height-sym) ""))
(when footer `(mapconcat 'identity ',footer "")))))))
(defun centaur-tabs-separator-pattern-defun (name dir width &rest patterns)
"Create a powerline function of NAME in DIR with WIDTH for PATTERNS.
PATTERNS is of the form (PATTERN HEADER FOOTER SECOND-PATTERN CENTER
PATTERN-2X HEADER-2X FOOTER-2X SECOND-PATTERN-2X CENTER-2X).
PATTERN is required, all other components are optional.
The first 5 components are for the standard resolution image.
The remaining ones are for the high resolution image where both
width and height are doubled. If PATTERN-2X is nil or not given,
then the remaining components are ignored and the standard
resolution image with magnification and interpolation will be
used in high resolution environments
All generated functions generate the form:
HEADER
PATTERN ...
CENTER
SECOND-PATTERN ...
FOOTER
PATTERN and SECOND-PATTERN repeat infinitely to fill the space needed to
generate a full height XPM.
PATTERN, HEADER, FOOTER, SECOND-PATTERN, CENTER are of the form
\((COLOR ...) (COLOR ...) ...).
COLOR can be one of 0, 1, or 2, where 0 is the source color, 1 is the
destination color, and 2 is the interpolated color between 0 and 1."
(when (eq dir 'right)
(setq patterns (mapcar 'centaur-tabs-separator-reverse-pattern patterns)))
(let ((bindings-body (centaur-tabs-separator-pattern-bindings-body patterns
'height
'pattern-height
'second-pattern-height))
(bindings-body-2x (centaur-tabs-separator-pattern-bindings-body (nthcdr 5 patterns)
'(* height 2)
'pattern-height-2x
'second-pattern-height-2x)))
(centaur-tabs-separator-wrap-defun name dir width
(append (car bindings-body) (car bindings-body-2x))
(cdr bindings-body) (cdr bindings-body-2x))))
(defun centaur-tabs-separator-background-color (face)
"Set the separator background color using FACE."
(face-attribute face
(if (face-attribute face :inverse-video nil 'default)
:foreground
:background)
nil
'default))
(defun centaur-tabs-separator-wrap-defun (name dir width let-vars body &optional body-2x)
"Generate a powerline function of name NAME in dir DIR.
This is made with WIDTH using LET-VARS and BODY.
BODY-2X is an optional argument."
(let* ((src-face (if (eq dir 'left) 'face1 'face2))
(dst-face (if (eq dir 'left) 'face2 'face1)))
`(defun ,(intern (format "powerline-%s-%s" name (symbol-name dir)))
(face1 face2 &optional height)
(when window-system
(unless height (setq height centaur-tabs-height))
(let* ,(append `((color1 (when ,src-face
(centaur-tabs-separator-hex-color (centaur-tabs-separator-background-color ,src-face))))
(color2 (when ,dst-face
(centaur-tabs-separator-hex-color (centaur-tabs-separator-background-color ,dst-face))))
(colori (when (and color1 color2) (centaur-tabs-separator-interpolate color1 color2)))
(color1 (or color1 "None"))
(color2 (or color2 "None"))
(colori (or colori "None")))
let-vars)
(apply #'create-image
,(append `(concat (format "/* XPM */ static char * %s_%s[] = { \"%s %s 3 1\", \"0 c %s\", \"1 c %s\", \"2 c %s\","
,(replace-regexp-in-string "-" "_" name)
(symbol-name ',dir)
,width
height
color1
color2
colori))
body
'("};"))
'xpm t
:ascent 'center
:face (when (and face1 face2)
,dst-face)
,(and body-2x
`(and (featurep 'mac)
(list :data-2x
,(append `(concat (format "/* XPM */ static char * %s_%s_2x[] = { \"%s %s 3 1\", \"0 c %s\", \"1 c %s\", \"2 c %s\","
,(replace-regexp-in-string "-" "_" name)
(symbol-name ',dir)
(* ,width 2)
(* height 2)
color1
color2
colori))
body-2x
'("};")))))))))))
(defun centaur-tabs-separator-alternate (dir)
"Generate an alternating pattern XPM function for DIR."
(centaur-tabs-separator-pattern-defun "alternate" dir 4
'((2 2 1 1)
(0 0 2 2))
nil nil nil nil
;; 2x
'((2 2 2 2 1 1 1 1)
(2 2 2 2 1 1 1 1)
(0 0 0 0 2 2 2 2)
(0 0 0 0 2 2 2 2))))
(defun centaur-tabs-separator-bar (dir)
"Generate a bar XPM function for DIR."
(centaur-tabs-separator-pattern-defun "bar" dir 2
'((2 2))))
(defun centaur-tabs-separator-box (dir)
"Generate a box XPM function for DIR."
(centaur-tabs-separator-pattern-defun "box" dir 2
'((0 0)
(0 0)
(1 1)
(1 1))
nil nil nil nil
;; 2x
'((0 0 0 0)
(0 0 0 0)
(0 0 0 0)
(0 0 0 0)
(1 1 1 1)
(1 1 1 1)
(1 1 1 1)
(1 1 1 1))))
(defun centaur-tabs-separator-chamfer (dir)
"Generate a chamfer XPM function for DIR."
(centaur-tabs-separator-pattern-defun "chamfer" dir 3
'((0 0 0))
'((1 1 1)
(0 1 1)
(0 0 1))
nil nil nil
;; 2x
'((0 0 0 0 0 0))
'((1 1 1 1 1 1)
(0 1 1 1 1 1)
(0 0 1 1 1 1)
(0 0 0 1 1 1)
(0 0 0 0 1 1)
(0 0 0 0 0 1))))
(defun centaur-tabs-separator-rounded (dir)
"Generate a rounded XPM function for DIR."
(centaur-tabs-separator-pattern-defun "rounded" dir 6
'((0 0 0 0 0 0))
'((2 1 1 1 1 1)
(0 0 2 1 1 1)
(0 0 0 0 1 1)
(0 0 0 0 2 1)
(0 0 0 0 0 1)
(0 0 0 0 0 2))
nil nil nil
;; 2x
'((0 0 0 0 0 0 0 0 0 0 0 0))
'((1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 2 1 1 1 1 1 1 1 1 1)
(0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 0 0 2 1 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1 1)
(0 0 0 0 0 0 0 0 0 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1)
(0 0 0 0 0 0 0 0 0 0 2 1)
(0 0 0 0 0 0 0 0 0 0 0 1)
(0 0 0 0 0 0 0 0 0 0 0 1))))
(defun centaur-tabs-separator-slant (dir)
"Generate a slant XPM function for DIR."
(let* ((row-modifier (if (eq dir 'left) 'identity 'reverse)))
(centaur-tabs-separator-wrap-defun "slant" dir 'width
'((width (1- (ceiling height 2))))
`((cl-loop for i from 0 to (1- height)
concat (centaur-tabs-separator-pattern-to-string (,row-modifier (centaur-tabs-separator-row-pattern (/ i 2) width)))))
`((cl-loop for i from 0 to (1- (* height 2))
concat (centaur-tabs-separator-pattern-to-string (,row-modifier (centaur-tabs-separator-row-pattern (/ i 2) (* width 2)))))))))
(defun centaur-tabs-separator-wave (dir)
"Generate a wave XPM function for DIR."
(centaur-tabs-separator-pattern-defun "wave" dir 11
'((0 0 0 0 0 0 1 1 1 1 1))
'((2 1 1 1 1 1 1 1 1 1 1)
(0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 2 1 1 1 1 1 1 1)
(0 0 0 0 1 1 1 1 1 1 1)
(0 0 0 0 2 1 1 1 1 1 1)
(0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 2 1 1 1 1 1))
'((0 0 0 0 0 0 2 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1)
(0 0 0 0 0 0 0 2 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1)
(0 0 0 0 0 0 0 0 2 1 1)
(0 0 0 0 0 0 0 0 0 0 2))
nil nil
;; 2x
'((0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1))
'((1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1))
'((0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1)
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0))))
(defun centaur-tabs-separator-zigzag (dir)
"Generate a zigzag pattern XPM function for DIR."
(centaur-tabs-separator-pattern-defun "zigzag" dir 3
'((1 1 1)
(0 1 1)
(0 0 1)
(0 0 0)
(0 0 1)
(0 1 1))
nil nil nil nil
;; 2x
'((1 1 1 1 1 1)
(0 1 1 1 1 1)
(0 0 1 1 1 1)
(0 0 0 1 1 1)
(0 0 0 0 1 1)
(0 0 0 0 0 1)
(0 0 0 0 0 0)
(0 0 0 0 0 1)
(0 0 0 0 1 1)
(0 0 0 1 1 1)
(0 0 1 1 1 1)
(0 1 1 1 1 1))))
(defun centaur-tabs-separator-memoize (func)
"Memoize FUNC.
If argument is a symbol then install the memoized function over
the original function. Use frame-local memoization."
(cl-typecase func
(symbol (fset func (centaur-tabs-separator-memoize-wrap-frame-local (symbol-function func))) func)
(function (centaur-tabs-separator-memoize-wrap-frame-local func))))
(defun centaur-tabs-separator-memoize-wrap-frame-local (func)
"Return the memoized version of FUNC.
The memoization cache is frame-local."
(let ((funcid (cl-gensym)))
`(lambda (&rest args)
,(concat (documentation func) (format "\n(memoized function %s)" funcid))
(let* ((cache (centaur-tabs-separator-create-or-get-cache))
(key (cons ',funcid args))
(val (gethash key cache)))
(or val
(puthash key (apply ,func args) cache))))))
(defun centaur-tabs-separator-create-or-get-cache ()
"Return a frame-local hash table that acts as a memoization cache.
The cache is for the powerline.
Create one if the frame doesn't have one yet."
(if-let* ((table (frame-parameter nil 'powerline-cache))
((hash-table-p table)))
table
(centaur-tabs-separator-reset-cache)))
(defun centaur-tabs-separator-reset-cache ()
"Reset and return the frame-local hash table used for a memoization cache."
(let ((table (make-hash-table :test 'equal)))
;; Store it as a frame-local variable
(modify-frame-parameters nil `((powerline-cache . ,table)))
table))
(centaur-tabs-separator-memoize (centaur-tabs-separator-alternate 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-alternate 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-bar 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-bar 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-box 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-box 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-chamfer 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-chamfer 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-rounded 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-rounded 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-slant 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-slant 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-wave 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-wave 'right))
(centaur-tabs-separator-memoize (centaur-tabs-separator-zigzag 'left))
(centaur-tabs-separator-memoize (centaur-tabs-separator-zigzag 'right))
(defun centaur-tabs-select-separator-style (tab-style)
"Set the separator style to TAB-STYLE."
(let* ((theme (or (car custom-enabled-themes) "default"))
(name (intern (format "centaur-tabs--%s-%s-face" theme tab-style)))
(face (copy-face 'centaur-tabs-default name)))
(setq centaur-tabs-style-left
(funcall (intern (format "powerline-%s-right" tab-style))
face nil centaur-tabs-height))
(setq centaur-tabs-style-right
(funcall (intern (format "powerline-%s-left" tab-style))
nil face centaur-tabs-height))))
(provide 'centaur-tabs-elements)
;;; centaur-tabs-elements.el ends here

View file

@ -1,642 +0,0 @@
;;; centaur-tabs-interactive.el --- centaur-tabs interactive functions and plugins support lexical-binding: t; -*-
;; Copyright (C) 2019-2024 Emmanuel Bustos
;; Copyright (C) 2024 Jen-Chieh Shen
;; This file is not part of GNU Emacs.
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;; Commentary:
;;
;; This file contains centaur-tabs interactive functions and plugins support
;;
;;; Code:
(require 'cl-lib)
(require 'centaur-tabs-elements)
;; Compiler pacifier
(declare-function ivy-read "ext:ivy.el" t t)
(declare-function helm-build-sync-source "ext:helm-source.el" t t)
(defvar helm-source-centaur-tabs-group)
(declare-function projectile-project-root "ext:projectile.el" t t)
(declare-function projectile-project-name "ext:projectile.el" t t)
(declare-function centaur-tabs-do-select "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-tab-from-event "centaur-tabs-functions.el")
(declare-function centaur-tabs-tab-value "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-tabset "centaur-tabs-functions.el")
(declare-function centaur-tabs-buffer-show-groups "centaur-tabs-functions.el")
(declare-function centaur-tabs-buffer-close-tab "centaur-tabs-functions.el")
(declare-function centaur-tabs-view "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-extensions "centaur-tabs-functions.el")
(declare-function centaur-tabs-selected-tab "centaur-tabs-functions.el")
(declare-function centaur-tabs-display-update "centaur-tabs-functions.el")
(declare-function centaur-tabs-set-template "centaur-tabs-functions.el")
(declare-function centaur-tabs-buffer-select-tab "centaur-tabs-functions.el")
(declare-function centaur-tabs-tabs "centaur-tabs-functions.el")
(declare-function centaur-tabs-tab-tabset "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-tabsets-tabset "centaur-tabs-functions.el")
(declare-function centaur-tabs-current-tabset "centaur-tabs-functions.el")
(declare-function centaur-tabs-completing-read "centaur-tabs-functions.el")
(declare-function centaur-tabs-get-groups "centaur-tabs-functions.el")
(declare-function centaur-tabs-forward-group "centaur-tabs-functions.el")
(declare-function centaur-tabs-backward-group "centaur-tabs-functions.el")
(declare-function centaur-tabs-forward-tab "centaur-tabs-functions.el")
(declare-function centaur-tabs-backward-tab "centaur-tabs-functions.el")
(defvar centaur-tabs-cycle-scope)
(defvar centaur-tabs-current-tabset)
(defvar centaur-tabs-last-focused-buffer-group)
(defvar centaur-tabs-buffer-list-function)
(defvar centaur-tabs-buffer-groups-function)
(defvar centaur-tabs--buffer-show-groups)
;;;;;;;;;;;;;;;;;;;;;;; Interactive functions ;;;;;;;;;;;;;;;;;;;;;;;
(defun centaur-tabs-switch-group (&optional groupname)
"Switch tab groups using ido. GROUPNAME can optionaly be provided."
(interactive)
(let* ((tab-buffer-list (mapcar
#'(lambda (b)
(with-current-buffer b
(list (current-buffer)
(buffer-name)
(funcall centaur-tabs-buffer-groups-function) )))
(funcall centaur-tabs-buffer-list-function)))
(groups (centaur-tabs-get-groups))
(group-name (or groupname (centaur-tabs-completing-read "Groups: " groups))) )
(catch 'done
(mapc #'(lambda (group)
(when (equal group-name (car (car (cdr (cdr group)))))
(throw 'done (switch-to-buffer (car (cdr group))))))
tab-buffer-list) )))
(defun centaur-tabs-select-end-tab ()
"Select end tab of current tabset."
(interactive)
(centaur-tabs-select-beg-tab t))
(defun centaur-tabs-select-beg-tab (&optional backward)
"Select beginning tab of current tabs.
If BACKWARD is non-nil, move backward, otherwise move forward.
TYPE is default option."
(interactive)
(let* ((tabset (centaur-tabs-current-tabset t))
(ttabset (centaur-tabs-get-tabsets-tabset))
(_cycle (if (and (eq centaur-tabs-cycle-scope 'groups)
(not (cdr (centaur-tabs-tabs ttabset))))
'tabs
centaur-tabs-cycle-scope))
_selected tab)
(when tabset
(setq tabset (centaur-tabs-tabs tabset)
tab (car (if backward (last tabset) tabset)))
(centaur-tabs-buffer-select-tab tab))))
(defun centaur-tabs-backward-tab-other-window (&optional reversed)
"Move to left tab in other window.
Optional argument REVERSED default is move backward, if reversed is non-nil
move forward."
(interactive)
(other-window 1)
(if reversed
(centaur-tabs-forward-tab)
(centaur-tabs-backward-tab))
(other-window -1))
(defun centaur-tabs-forward-tab-other-window ()
"Move to right tab in other window."
(interactive)
(centaur-tabs-backward-tab-other-window t))
(defun centaur-tabs-move-current-tab-to-right ()
"Move current tab one place right, unless it's already the rightmost."
(interactive)
(let* ((bufset (centaur-tabs-current-tabset t))
(old-bufs (centaur-tabs-tabs bufset))
(new-bufs (list))
the-buffer)
(while (and
old-bufs
(not (string= (buffer-name) (format "%s" (car (car old-bufs))))))
(push (car old-bufs) new-bufs)
(setq old-bufs (cdr old-bufs)))
(if old-bufs ; if this is false, then the current tab's buffer name is mysteriously missing
(progn
(setq the-buffer (car old-bufs))
(setq old-bufs (cdr old-bufs))
(if old-bufs ; if this is false, then the current tab is the rightmost
(push (car old-bufs) new-bufs))
(push the-buffer new-bufs)) ; this is the tab that was to be moved
(error "Error: current buffer's name was not found in Centaur-Tabs's buffer list"))
(setq new-bufs (reverse new-bufs))
(setq new-bufs (append new-bufs (cdr old-bufs)))
(set bufset new-bufs)
(centaur-tabs-set-template bufset nil)
(centaur-tabs-display-update)))
(defun centaur-tabs-move-current-tab-to-left ()
"Move current tab one place left, unless it's already the leftmost."
(interactive)
(let* ((bufset (centaur-tabs-current-tabset t))
(old-bufs (centaur-tabs-tabs bufset))
(first-buf (car old-bufs))
(new-bufs (list))
not-yet-this-buf)
(if (string= (buffer-name) (format "%s" (car first-buf)))
old-bufs ; the current tab is the leftmost
(setq not-yet-this-buf first-buf)
(setq old-bufs (cdr old-bufs))
(while (and
old-bufs
(not (string= (buffer-name) (format "%s" (car (car old-bufs))))))
(push not-yet-this-buf new-bufs)
(setq not-yet-this-buf (car old-bufs))
(setq old-bufs (cdr old-bufs)))
(if old-bufs ; if this is false, then the current tab's buffer name is mysteriously missing
(progn
(push (car old-bufs) new-bufs) ; this is the tab that was to be moved
(push not-yet-this-buf new-bufs)
(setq new-bufs (reverse new-bufs))
(setq new-bufs (append new-bufs (cdr old-bufs))))
(error "Error: current buffer's name was not found in Centaur-Tabs's buffer list"))
(set bufset new-bufs)
(centaur-tabs-set-template bufset nil)
(centaur-tabs-display-update))))
(defmacro centaur-tabs-kill-buffer-match-rule (match-rule)
"If buffer match MATCH-RULE, kill it."
`(save-excursion
(mapc #'(lambda (buffer)
(with-current-buffer buffer
(when (string-equal current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(when (funcall ,match-rule buffer)
(kill-buffer buffer)))))
(buffer-list))))
(defun centaur-tabs-kill-all-buffers-in-current-group ()
"Kill all buffers in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t)))))
;; Kill all buffers in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (_buffer) t))
;; Switch to next group.
(centaur-tabs-forward-group)))
(defun centaur-tabs-kill-other-buffers-in-current-group ()
"Kill all buffers except current buffer in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(currentbuffer (current-buffer)))
;; Kill all buffers in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer) (not (equal buffer currentbuffer))))))
(defun centaur-tabs-kill-unmodified-buffers-in-current-group ()
"Kill all unmodified buffer in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(currentbuffer (current-buffer)))
;; Kill all buffers in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer) (not (buffer-modified-p buffer))))))
(defun centaur-tabs-kill-match-buffers-in-current-group ()
"Kill all buffers match extension in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(extension-names (centaur-tabs-get-extensions))
match-extension)
;; Read extension need to kill.
(setq match-extension (centaur-tabs-completing-read "Kill buffers suffix with: " extension-names))
;; Kill all buffers match extension in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer)
(let ((filename (buffer-file-name buffer)))
(and filename (string-equal (file-name-extension filename) match-extension)))))
;; Switch to next group if last file killed.
(when (equal (length extension-names) 1)
(centaur-tabs-forward-group))))
(defun centaur-tabs-keep-match-buffers-in-current-group ()
"Keep all buffers match extension in current group."
(interactive)
(let* ((current-group-name (cdr (centaur-tabs-selected-tab (centaur-tabs-current-tabset t))))
(extension-names (centaur-tabs-get-extensions))
match-extension)
;; Read extension need to kill.
(setq match-extension (centaur-tabs-completing-read "Just keep buffers suffix with: " extension-names))
;; Kill all buffers match extension in current group.
(centaur-tabs-kill-buffer-match-rule
(lambda (buffer)
(let ((filename (buffer-file-name buffer)))
(and filename (not (string-equal (file-name-extension filename) match-extension))))))
;; Switch to next group if last file killed.
(when (equal (length extension-names) 1)
(centaur-tabs-forward-group))))
(defun centaur-tabs-select-visible-nth-tab (tab-index)
"Select visible tab with TAB-INDEX'.
Example, when `tab-index' is 1, this function will select the leftmost label in
the visible area, instead of the first label in the current group.
If `tab-index' more than length of visible tabs, selet the last tab.
If `tab-index' is 0, select last tab."
(let ((visible-tabs (centaur-tabs-view centaur-tabs-current-tabset)))
(switch-to-buffer
(car
(if (or (equal tab-index 0)
(> tab-index (length visible-tabs)))
(car (last visible-tabs))
(nth (- tab-index 1) visible-tabs))))))
(defun centaur-tabs-select-visible-tab ()
"Bind this function with number keystroke, such as s-1, s-2, s-3 ... etc.
This function automatically recognizes the number at the end of the keystroke
and switches to the tab of the corresponding index.
Note that this function switches to the visible range,
not the actual logical index position of the current group."
(interactive)
(let* ((event last-command-event)
(key (make-vector 1 event))
(key-desc (key-description key)))
(centaur-tabs-select-visible-nth-tab
(string-to-number (car (last (split-string key-desc "-")))))))
;; ace-jump style tab switching
(defvar centaur-tabs-ace-jump-active nil
"Set to t if `centaur-tabs-ace-jump' is invoked.")
(defvar centaur-tabs-dim-overlay nil
"Holds the overlay for dimming buffer when invoking centaur-tabs-ace-jump.")
(defun centaur-tabs--dim-window ()
"Create a dim background overlay for the current window."
(when centaur-tabs-ace-jump-dim-buffer
(when centaur-tabs-dim-overlay
(delete-overlay centaur-tabs-dim-overlay))
(setq centaur-tabs-dim-overlay
(let ((ol (make-overlay (window-start) (window-end))))
(overlay-put ol 'face 'centaur-tabs-dim-buffer-face)
ol))))
(defun centaur-tabs-swap-tab (tab)
"Swap the position of current tab with TAB.
TAB has to be in the same group as the current tab."
(if (eq (centaur-tabs-tab-tabset tab) (centaur-tabs-current-tabset t))
(let* ((group (centaur-tabs-current-tabset t))
(tabs (cl-copy-list (centaur-tabs-tabs group)))
(current (centaur-tabs-selected-tab group))
(current-index (cl-position current tabs))
(target-index (cl-position tab tabs)))
(if (eq tab current)
(message "Can't swap with current tab itself.")
(setcar (nthcdr current-index tabs) tab)
(setcar (nthcdr target-index tabs) current)
(set group tabs)
(centaur-tabs-set-template (centaur-tabs-current-tabset t) nil)
(centaur-tabs-display-update)))
(message "Error: %s is not in the same group as the current tab." tab)))
(defun centaur-tabs-ace-action (action)
"Preform ACTION on a visible tab. Ace-jump style.
ACTION has to be one of value in `centaur-tabs-ace-dispatch-alist'"
(when (centaur-tabs-current-tabset t)
(when centaur-tabs-ace-jump-dim-buffer
(centaur-tabs--dim-window))
(cond ((eq action 'jump-to-tab)
(message "Jump to tab: "))
((eq action 'close-tab)
(message "Close tab: "))
((eq action 'swap-tab)
(message "Swap current tab with: ")))
(let ((centaur-tabs-ace-jump-active t))
(catch 'done
(while t
(centaur-tabs-set-template (centaur-tabs-current-tabset t) nil)
(centaur-tabs-display-update)
(let ((char (read-key)) (action-cache))
(cond
;; tab keys
((memq char centaur-tabs-ace-jump-keys)
(let ((sel (nth (cl-position char centaur-tabs-ace-jump-keys) (centaur-tabs-view (centaur-tabs-current-tabset t)))))
(cond ((eq sel nil)
(message "Tab %s does not exist" (key-description (vector char))))
((eq action 'jump-to-tab)
(centaur-tabs-buffer-select-tab sel))
((eq action 'close-tab)
(centaur-tabs-buffer-close-tab sel))
((eq action 'swap-tab)
(centaur-tabs-swap-tab sel))))
(throw 'done nil))
;; actions
((setq action-cache (assoc char centaur-tabs-ace-dispatch-alist))
(setq action-cache (cadr action-cache))
(cond ((eq action-cache 'exit) ; exit
(message "Quit")
(throw 'done nil))
((eq action-cache 'forward-group) ; forward group
(message "Forward group")
(centaur-tabs-forward-group)
(centaur-tabs--dim-window))
((eq action-cache 'backward-group) ; backward group
(message "Backward group")
(centaur-tabs-backward-group)
(centaur-tabs--dim-window))
((eq action-cache 'show-help) ; help menu
(message "%s" (mapconcat
(lambda (elem) (format "%s: %s"
(key-description (vector (car elem)))
(caddr elem)))
centaur-tabs-ace-dispatch-alist
"\n")))
(t (setq action action-cache) ; other actions
(cond ((eq action-cache 'jump-to-tab)
(message "Jump to tab: "))
((eq action-cache 'close-tab)
(message "Close tab: "))
((eq action-cache 'swap-tab)
(message "Swap current tab with: "))))))
;; no match, repeat
(t
(message "No such candidate: %s, hit ? for help." (key-description (vector char)))))))))
(centaur-tabs-set-template (centaur-tabs-current-tabset t) nil)
(when centaur-tabs-ace-jump-dim-buffer
(delete-overlay centaur-tabs-dim-overlay)
(setq centaur-tabs-dim-overlay nil))
(centaur-tabs-display-update)))
(defun centaur-tabs-ace-jump (&optional arg)
"Select a tab and perform an action. Ace-jump style.
If no ARG is provided, select that tab. If prefixed with one
`universal-argument', swap the current tab with the selected tab.
If prefixed with two `universal-argument's, close selected tab."
(interactive "p")
(cond ((eq arg 1)
(centaur-tabs-ace-action 'jump-to-tab))
((eq arg 4)
(centaur-tabs-ace-action 'swap-tab))
((eq arg 16)
(centaur-tabs-ace-action 'close-tab))
(t
(centaur-tabs-ace-action 'jump-to-tab))))
(defun centaur-tabs-group-buffer-groups ()
"Use centaur-tabs's own buffer grouping function."
(interactive)
(setq centaur-tabs-buffer-groups-function 'centaur-tabs-buffer-groups)
(centaur-tabs-display-update))
;; Projectile integration. Taken from tabbar-ruler
(defvar centaur-tabs-projectile-buffer-group-calc nil
"Set buffer groups for projectile.
Should be buffer local and speed up calculation of buffer groups.")
(defun centaur-tabs-projectile-buffer-groups ()
"Return the list of group names BUFFER belongs to."
(if centaur-tabs-projectile-buffer-group-calc
(symbol-value 'centaur-tabs-projectile-buffer-group-calc)
(set (make-local-variable 'centaur-tabs-projectile-buffer-group-calc)
(cond
((or (get-buffer-process (current-buffer)) (memq major-mode '(comint-mode compilation-mode))) '("Term"))
((string-equal "*" (substring (buffer-name) 0 1)) '("Misc"))
((condition-case _err
(projectile-project-root)
(error nil))
(list (projectile-project-name)))
((memq major-mode '(emacs-lisp-mode python-mode emacs-lisp-mode c-mode
c++-mode javascript-mode js-mode
js2-mode makefile-mode
lua-mode vala-mode))
'("Coding"))
((memq major-mode '( nxhtml-mode html-mode
mhtml-mode css-mode))
'("HTML"))
((memq major-mode '(org-mode calendar-mode diary-mode)) '("Org"))
((memq major-mode '(dired-mode)) '("Dir"))
(t '("Other"))))
(symbol-value 'centaur-tabs-projectile-buffer-group-calc)))
(defun centaur-tabs-group-by-projectile-project()
"Group by projectile project."
(interactive)
(setq centaur-tabs-buffer-groups-function 'centaur-tabs-projectile-buffer-groups)
(centaur-tabs-display-update))
;; Show groups instead of tabs
(defun centaur-tabs-toggle-groups ()
"Show group names on the tabs instead of buffer names."
(interactive)
(centaur-tabs-buffer-show-groups (not centaur-tabs--buffer-show-groups))
(centaur-tabs-display-update))
;; Helm source for switching group in helm.
(defun centaur-tabs-build-helm-source ()
"Display a list of current buffer groups in Helm."
(interactive)
(setq helm-source-centaur-tabs-group
(when (featurep 'helm)
(require 'helm)
(helm-build-sync-source "Centaur-Tabs Group"
:candidates #'centaur-tabs-get-groups
:action '(("Switch to group" . centaur-tabs-switch-group))))))
;; Ivy source for switching group in ivy.
;;;###autoload
(defun centaur-tabs-counsel-switch-group ()
"Display a list of current buffer groups using Counsel."
(interactive)
(when (featurep 'ivy)
(require 'ivy)
(ivy-read
"Centaur Tabs Groups:"
(centaur-tabs-get-groups)
:action #'centaur-tabs-switch-group
:caller 'centaur-tabs-counsel-switch-group)))
(defun centaur-tabs-extract-window-to-new-frame()
"Kill the current window in the current frame, and open the current buffer
in a new frame."
(interactive)
(unless (centaur-tabs--one-window-p)
(let ((buffer (current-buffer)))
(delete-window)
(display-buffer-pop-up-frame buffer nil))))
(defun centaur-tabs--copy-file-name-to-clipboard ()
"Copy the current buffer file name to the clipboard."
;;; From https://emacsredux.com/blog/2013/03/27/copy-filename-to-the-clipboard/
(interactive)
(let* ((filename (if (equal major-mode 'dired-mode)
default-directory
(buffer-file-name)))
(filename (expand-file-name filename)))
(when filename
(kill-new filename)
(message "Copied buffer file name '%s' to the kill ring." filename))))
(defun centaur-tabs-open-directory-in-external-application ()
"Open the current directory in a external application."
(interactive)
(centaur-tabs--open-externally default-directory))
(defun centaur-tabs-open-in-external-application ()
"Open the file of the current buffer according to its mime type."
(interactive)
(let ((path (or (buffer-file-name) default-directory)))
(centaur-tabs--open-externally path)))
(defun centaur-tabs--open-externally (file-or-path)
"Open FILE-OR-PATH according to its mime type in an external application.
FILE-OR-PATH is expanded with `expand-file-name`.
Modified copy of `treemacs-visit-node-in-external-application`."
(let ((path (expand-file-name file-or-path)))
(pcase system-type
('windows-nt
(declare-function w32-shell-execute "w32fns.c")
(w32-shell-execute "open" (replace-regexp-in-string "/" "\\" path t t)))
('darwin
(shell-command (format "open \"%s\"" path)))
('gnu/linux
(let ((process-connection-type nil))
(start-process "" nil "xdg-open" path)))
(_ (message "Don't know how to open files on %s." (symbol-name system-type))))))
(defun centaur-tabs--copy-directory-name-to-clipboard ()
"Copy the current directory name to the clipboard."
(interactive)
(when default-directory
(kill-new default-directory)
(message "Copied directory name '%s' to the kill ring." (expand-file-name default-directory))))
(defun centaur-tabs--tab-submenu-groups-definition ()
"Menu definition with a list of tab groups."
(mapcar (lambda (s) `[,s ,s]) (sort (centaur-tabs-get-groups) #'string<)))
(defun centaur-tabs--tab-submenu-tabs-definition ()
"Menu definition with a list of tabs for the current group."
(let* ((tabset (centaur-tabs-get-tabset centaur-tabs-last-focused-buffer-group))
(tabs-in-group (centaur-tabs-tabs tabset))
(buffers (mapcar #'centaur-tabs-tab-value tabs-in-group))
(sorted-tabnames (sort (mapcar #'buffer-name buffers) #'string<)))
(mapcar (lambda (s) `[,s ,s]) sorted-tabnames)))
(defvar centaur-tabs--groups-submenu-key "Tab groups")
(defvar centaur-tabs--tabs-submenu-key "Go to tab of group")
(defun centaur-tabs--kill-this-buffer-dont-ask()
"Kill the current buffer without confirmation."
(interactive)
(kill-buffer (current-buffer))
(centaur-tabs-display-update)
(redisplay t))
(defun centaur-tabs--tab-menu-definition ()
"Definition of the context menu of a tab."
`(["Kill this buffer" centaur-tabs--kill-this-buffer-dont-ask]
["Kill other buffers of group" centaur-tabs-kill-other-buffers-in-current-group]
["Kill unmodified buffers of group" centaur-tabs-kill-unmodified-buffers-in-current-group]
"----"
["Split below" split-window-below]
["Split right" split-window-right]
"----"
["Maximize tab" delete-other-windows
:active (null (centaur-tabs--one-window-p))]
["Extract to new frame" centaur-tabs-extract-window-to-new-frame
:active (null (centaur-tabs--one-window-p))]
["Duplicate in new frame" make-frame-command]
"----"
["Copy filepath" centaur-tabs--copy-file-name-to-clipboard
:active (buffer-file-name)]
["Copy directory path" centaur-tabs--copy-directory-name-to-clipboard
:active default-directory]
["Open in external application" centaur-tabs-open-in-external-application
:active (or (buffer-file-name) default-directory)]
["Open directory in dired" dired-jump
:active (not (eq major-mode 'dired-mode))]
["Open directory externally" centaur-tabs-open-directory-in-external-application
:active default-directory]
"----"
,( append (list centaur-tabs--groups-submenu-key) (centaur-tabs--tab-submenu-groups-definition))
,( append (list centaur-tabs--tabs-submenu-key) (centaur-tabs--tab-submenu-tabs-definition))))
(defun centaur-tabs--one-window-p ()
"Like `one-window-p`, but taking into account side windows like treemacs."
(let* ((mainwindow (window-main-window))
(child-count (window-child-count mainwindow)))
(= 0 child-count)))
(defun centaur-tabs--get-tab-from-name (tabname)
"Get the tab from the current group given de TABNAME."
(let ((seq (centaur-tabs-tabs (centaur-tabs-get-tabset centaur-tabs-last-focused-buffer-group))))
(cl-find-if
(lambda (tab) (string= tabname (buffer-name (centaur-tabs-tab-value tab))))
seq)))
(defun centaur-tabs--tab-menu (event)
"Show a context menu for the clicked tab or button.
The clicked tab, identified by EVENT, is selected."
(interactive "e" )
(let ((click-on-tab-p (ignore-errors (centaur-tabs-get-tab-from-event event))))
(when (not click-on-tab-p)
(centaur-tabs--groups-menu))
(when click-on-tab-p
(centaur-tabs-do-select event)
(redisplay t)
(let*
((menu (easy-menu-create-menu nil (centaur-tabs--tab-menu-definition)))
(choice (x-popup-menu t menu))
(action (lookup-key menu (apply 'vector choice)))
(action-is-command-p (and (commandp action) (functionp action))))
(when action-is-command-p
(call-interactively action))
(when (not action-is-command-p)
(let* ((menu-key (cl-first choice))
(choice-is-group-p (string= centaur-tabs--groups-submenu-key (symbol-name menu-key)))
(name (car (last choice)))
(name-as-string (symbol-name name)))
(if choice-is-group-p
(centaur-tabs-switch-group name-as-string)
(switch-to-buffer name-as-string))))))))
(defun centaur-tabs--groups-menu ()
"Show a popup menu with the centaur tabs groups."
(interactive)
(let* ((sorted-groups (centaur-tabs--tab-submenu-groups-definition))
(menu (easy-menu-create-menu "Tab groups"
(centaur-tabs--tab-submenu-groups-definition)))
(choice (x-popup-menu t menu))
(action (lookup-key menu (apply 'vector choice)))
(action-is-command-p (and (commandp action) (functionp action))))
(when action-is-command-p
(call-interactively action))
(when (not action-is-command-p)
(let ((group (car (last choice))))
(centaur-tabs-switch-group (format "%s" group))))))
(provide 'centaur-tabs-interactive)
;;; centaur-tabs-interactive.el ends here

View file

@ -1,15 +0,0 @@
(define-package "centaur-tabs" "20240726.625" "Aesthetic, modern looking customizable tabs plugin"
'((emacs "27.1")
(powerline "2.4"))
:commit "49b9f6b813dfb1fe78aa782f76b4a7333dd8f980" :authors
'(("Emmanuel Bustos" . "ema2159@gmail.com"))
:maintainers
'(("Jen-Chieh Shen" . "jcs090218@gmail.com"))
:maintainer
'("Jen-Chieh Shen" . "jcs090218@gmail.com")
:keywords
'("frames")
:url "https://github.com/ema2159/centaur-tabs")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -1,6 +1,6 @@
(define-package "dash" "20240510.1327" "A modern list library for Emacs"
(define-package "dash" "20240103.1301" "A modern list library for Emacs"
'((emacs "24"))
:commit "1de9dcb83eacfb162b6d9a118a4770b1281bcd84" :authors
:commit "e32a70ca636bad42232b6c79f1491dc86802a721" :authors
'(("Magnar Sveen" . "magnars@gmail.com"))
:maintainers
'(("Magnar Sveen" . "magnars@gmail.com"))

View file

@ -2108,7 +2108,7 @@ last item in second form, etc."
Insert X at the position signified by the symbol `it' in the first
form. If there are more forms, insert the first form at the position
signified by `it' in the second form, etc."
signified by `it' in in second form, etc."
(declare (debug (form body)))
`(-as-> ,x it ,@forms))
@ -3298,8 +3298,6 @@ Return the sorted list. LIST is NOT modified by side effects.
COMPARATOR is called with two elements of LIST, and should return non-nil
if the first element should sort before the second."
(declare (important-return-value t))
;; Not yet worth changing to (sort list :lessp comparator);
;; still seems as fast or slightly faster.
(sort (copy-sequence list) comparator))
(defmacro --sort (form list)

View file

@ -1,4 +1,4 @@
This is dash.info, produced by makeinfo version 6.8 from dash.texi.
This is dash.info, produced by makeinfo version 6.7 from dash.texi.
This manual is for Dash version 2.19.1.
@ -2427,7 +2427,7 @@ readability.
Insert X at the position signified by the symbol it in the first
form. If there are more forms, insert the first form at the
position signified by it in the second form, etc.
position signified by it in in second form, etc.
(--> "def" (concat "abc" it "ghi"))
⇒ "abcdefghi"
@ -4892,53 +4892,53 @@ Node: Threading macros84441
Ref: ->84666
Ref: ->>85154
Ref: -->85657
Ref: -as->86214
Ref: -some->86668
Ref: -some->>87053
Ref: -some-->87500
Ref: -doto88067
Node: Binding88620
Ref: -when-let88827
Ref: -when-let*89288
Ref: -if-let89817
Ref: -if-let*90183
Ref: -let90806
Ref: -let*96896
Ref: -lambda97833
Ref: -setq98639
Node: Side effects99440
Ref: -each99634
Ref: -each-while100161
Ref: -each-indexed100781
Ref: -each-r101373
Ref: -each-r-while101815
Ref: -dotimes102459
Node: Destructive operations103012
Ref: !cons103230
Ref: !cdr103434
Node: Function combinators103627
Ref: -partial103831
Ref: -rpartial104349
Ref: -juxt104997
Ref: -compose105449
Ref: -applify106056
Ref: -on106486
Ref: -flip107258
Ref: -rotate-args107782
Ref: -const108411
Ref: -cut108753
Ref: -not109233
Ref: -orfn109777
Ref: -andfn110570
Ref: -iteratefn111357
Ref: -fixfn112059
Ref: -prodfn113633
Node: Development114784
Node: Contribute115073
Node: Contributors116085
Node: FDL118178
Node: GPL143498
Node: Index181247
Ref: -as->86213
Ref: -some->86667
Ref: -some->>87052
Ref: -some-->87499
Ref: -doto88066
Node: Binding88619
Ref: -when-let88826
Ref: -when-let*89287
Ref: -if-let89816
Ref: -if-let*90182
Ref: -let90805
Ref: -let*96895
Ref: -lambda97832
Ref: -setq98638
Node: Side effects99439
Ref: -each99633
Ref: -each-while100160
Ref: -each-indexed100780
Ref: -each-r101372
Ref: -each-r-while101814
Ref: -dotimes102458
Node: Destructive operations103011
Ref: !cons103229
Ref: !cdr103433
Node: Function combinators103626
Ref: -partial103830
Ref: -rpartial104348
Ref: -juxt104996
Ref: -compose105448
Ref: -applify106055
Ref: -on106485
Ref: -flip107257
Ref: -rotate-args107781
Ref: -const108410
Ref: -cut108752
Ref: -not109232
Ref: -orfn109776
Ref: -andfn110569
Ref: -iteratefn111356
Ref: -fixfn112058
Ref: -prodfn113632
Node: Development114783
Node: Contribute115072
Node: Contributors116084
Node: FDL118177
Node: GPL143497
Node: Index181246

End Tag Table

View file

@ -1,14 +0,0 @@
(define-package "dash" "20240510.1327" "A modern list library for Emacs"
'((emacs "24"))
:commit "1de9dcb83eacfb162b6d9a118a4770b1281bcd84" :authors
'(("Magnar Sveen" . "magnars@gmail.com"))
:maintainers
'(("Magnar Sveen" . "magnars@gmail.com"))
:maintainer
'("Magnar Sveen" . "magnars@gmail.com")
:keywords
'("extensions" "lisp")
:url "https://github.com/magnars/dash.el")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View file

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -1,10 +1,9 @@
(define-package "dashboard" "20240529.2058" "A startup screen extracted from Spacemacs"
(define-package "dashboard" "20240319.915" "A startup screen extracted from Spacemacs"
'((emacs "26.1"))
:commit "3852301f9c6f3104d9cc98389612b5ef3452a7de" :authors
:commit "e34ce1b4730fb88a33a571b9065114b7a77645f0" :authors
'(("Rakan Al-Hneiti" . "rakan.alhneiti@gmail.com"))
:maintainers
'(("Jesús Martínez" . "jesusmartinez93@gmail.com")
("Jen-Chieh" . "jcs090218@gmail.com"))
'(("Jesús Martínez" . "jesusmartinez93@gmail.com"))
:maintainer
'("Jesús Martínez" . "jesusmartinez93@gmail.com")
:keywords

View file

@ -70,6 +70,15 @@
(declare-function string-pixel-width "subr-x.el") ; TODO: remove this after 29.1
(declare-function shr-string-pixel-width "shr.el") ; TODO: remove this after 29.1
(make-obsolete-variable 'dashboard-set-navigator
'dashboard-startupify-list "1.9.0")
(make-obsolete-variable 'dashboard-set-init-info
'dashboard-startupify-list "1.9.0")
(make-obsolete-variable 'dashboard-set-footer
'dashboard-startupify-list "1.9.0")
(defvar recentf-list nil)
(defvar dashboard-buffer-name)
@ -124,27 +133,6 @@ See `create-image' and Info node `(elisp)Image Descriptors'."
:type 'boolean
:group 'dashboard)
(defcustom dashboard-set-navigator nil
"When non nil, a navigator will be displayed under the banner."
:type 'boolean
:group 'dashboard)
(make-obsolete-variable 'dashboard-set-navigator
'dashboard-startupify-list "1.9.0")
(defcustom dashboard-set-init-info t
"When non nil, init info will be displayed under the banner."
:type 'boolean
:group 'dashboard)
(make-obsolete-variable 'dashboard-set-init-info
'dashboard-startupify-list "1.9.0")
(defcustom dashboard-set-footer t
"When non nil, a footer will be displayed at the bottom."
:type 'boolean
:group 'dashboard)
(make-obsolete-variable 'dashboard-set-footer
'dashboard-startupify-list "1.9.0")
(defcustom dashboard-footer-messages
'("The one true editor, Emacs!"
"Who the hell uses VIM anyway? Go Evil!"
@ -193,7 +181,7 @@ The value can be one of: `all-the-icons', `nerd-icons'."
Will be of the form `(list-type . icon-name-string)`.
If nil it is disabled. Possible values for list-type are:
`recents' `bookmarks' `projects' `agenda' `registers'"
:type '(alist :key-type symbol :value-type string)
:type '(repeat (alist :key-type symbol :value-type string))
:group 'dashboard)
(defcustom dashboard-heading-icon-height 1.2
@ -257,16 +245,7 @@ The format is: `icon title help action face prefix suffix`.
Example:
`((\"\" \"Star\" \"Show stars\" (lambda (&rest _)
(show-stars)) warning \"[\" \"]\"))"
:type '(repeat (repeat (list string
string
string
function
(choice face
(repeat :tag "Anonymous face" sexp))
(choice string
(const nil))
(choice string
(const nil)))))
:type '(repeat (repeat (list string string string function symbol string string)))
:group 'dashboard)
(defcustom dashboard-init-info
@ -356,10 +335,8 @@ ARGS should be a plist containing `:height', `:v-adjust', or `:face' properties.
:v-adjust -0.05
:face 'dashboard-footer-icon-face)))
(propertize ">" 'face 'dashboard-footer-icon-face))
"Footer's icon.
It can be a string or a string list for display random icons."
:type '(choice string
(repeat string))
"Footer's icon."
:type 'string
:group 'dashboard)
(defcustom dashboard-heading-shorcut-format " (%s)"
@ -434,9 +411,7 @@ installed."
Will be of the form `(list-type . list-size)'.
If nil it is disabled. Possible values for list-type are:
`recents' `bookmarks' `projects' `agenda' `registers'."
:type '(repeat (choice
symbol
(cons symbol integer)))
:type '(repeat (alist :key-type symbol :value-type integer))
:group 'dashboard)
(defcustom dashboard-item-shortcuts
@ -448,8 +423,8 @@ If nil it is disabled. Possible values for list-type are:
"Association list of items and their corresponding shortcuts.
Will be of the form `(list-type . keys)' as understood by `(kbd keys)'.
If nil, shortcuts are disabled. If an entry's value is nil, that item's
shortcut is disabled. See `dashboard-items' for possible values of list-type.'"
:type '(alist :key-type symbol :value-type string)
shortcut is disbaled. See `dashboard-items' for possible values of list-type.'"
:type '(repeat (alist :key-type symbol :value-type string))
:group 'dashboard)
(defcustom dashboard-item-names nil
@ -599,8 +574,7 @@ Optionally, provide NO-NEXT-LINE to move the cursor forward a line."
`(progn
(eval-when-compile (defvar dashboard-mode-map))
(defun ,sym nil
,(concat "Jump to " name ".
This code is dynamically generated in `dashboard-insert-shortcut'.")
,(concat "Jump to " name ". This code is dynamically generated in `dashboard-insert-shortcut'.")
(interactive)
(unless (search-forward ,search-label (point-max) t)
(search-backward ,search-label (point-min) t))
@ -625,12 +599,9 @@ If MESSAGEBUF is not nil then MSG is also written in message buffer."
"Insert a page break line in dashboard buffer."
(dashboard-append dashboard-page-separator))
(defun dashboard-insert-newline (&optional times)
"When called without an argument, insert a newline.
When called with TIMES return a function that insert TIMES number of newlines."
(if times
(lambda ()
(insert (make-string times (string-to-char "\n") t)))
(defun dashboard-insert-newline (&optional n)
"Insert N times of newlines."
(dotimes (_ (or n 1))
(insert "\n")))
(defun dashboard-insert-heading (heading &optional shortcut icon)
@ -744,9 +715,7 @@ When called with TIMES return a function that insert TIMES number of newlines."
(list :text (dashboard-get-banner-path 1)))))
((and
(pred listp)
(pred (lambda (c)
(and (not (proper-list-p c))
(not (null c)))))
(pred (lambda (c) (not (proper-list-p c))))
`(,img . ,txt))
(list :image (if (dashboard--image-supported-p img)
img
@ -756,16 +725,12 @@ When called with TIMES return a function that insert TIMES number of newlines."
txt
(message "could not find banner %s, use default instead" txt)
(dashboard-get-banner-path 1))))
((and
(pred proper-list-p)
(pred (lambda (l) (not (null l)))))
((pred proper-list-p)
(let* ((max (length banner))
(choose (nth (random max) banner)))
(dashboard-choose-banner choose)))
(_
(user-error "Unsupported banner type: `%s'" banner)
nil)))
(message "unsupported banner config %s" banner))))
(defun dashboard--image-animated-p (image-path)
"Return if image is a gif or webp.
@ -885,8 +850,7 @@ Argument IMAGE-PATH path to the image."
(when (and icon title
(not (string-equal icon ""))
(not (string-equal title "")))
(propertize " " 'face `(:inherit (variable-pitch
,face))))
(propertize " " 'face 'variable-pitch))
(when title (propertize title 'face face)))
:help-echo help
:action action
@ -908,10 +872,7 @@ ACTION is theaction taken when the user activates the widget button.
WIDGET-PARAMS are passed to the \"widget-create\" function."
`(progn
(dashboard-insert-heading ,section-name
(when (and ,list
,shortcut-char
dashboard-show-shortcuts)
,shortcut-char))
(if (and ,list ,shortcut-char dashboard-show-shortcuts) ,shortcut-char))
(if ,list
(when (and (dashboard-insert-section-list
,section-name
@ -967,19 +928,10 @@ to widget creation."
"Return a random footer from `dashboard-footer-messages'."
(nth (random (length dashboard-footer-messages)) dashboard-footer-messages))
(defun dashboard-footer-icon ()
"Return footer icon or a random icon if `dashboard-footer-messages' is a list."
(if (and (not (null dashboard-footer-icon))
(listp dashboard-footer-icon))
(dashboard-replace-displayable
(nth (random (length dashboard-footer-icon))
dashboard-footer-icon))
(dashboard-replace-displayable dashboard-footer-icon)))
(defun dashboard-insert-footer ()
"Insert footer of dashboard."
(when-let ((footer (dashboard-random-footer))
(footer-icon (dashboard-footer-icon)))
(footer-icon (dashboard-replace-displayable dashboard-footer-icon)))
(dashboard-insert-center
(if (string-empty-p footer-icon) footer-icon
(concat footer-icon " "))
@ -1388,9 +1340,7 @@ Any custom function would receives the tags from `org-get-tags'"
(defun dashboard-agenda-entry-format ()
"Format agenda entry to show it on dashboard.
Also,it set text properties that latter are used to sort entries and perform
different actions."
Also,it set text properties that latter are used to sort entries and perform different actions."
(let* ((scheduled-time (org-get-scheduled-time (point)))
(deadline-time (org-get-deadline-time (point)))
(entry-timestamp (dashboard-agenda--entry-timestamp (point)))

View file

@ -143,15 +143,10 @@ Avalaible functions:
`dashboard-insert-items'
`dashboard-insert-footer'
It must be a function or a cons cell where specify function and
its arg.
Also you can add your custom function or a lambda to the list.
You can also add your custom function or a lambda to the list.
example:
(lambda () (delete-char -1))"
:type '(repeat (choice
function
(cons function sexp)))
:type '(repeat function)
:group 'dashboard)
(defcustom dashboard-navigation-cycle nil
@ -159,10 +154,8 @@ example:
:type 'boolean
:group 'dashboard)
(defcustom dashboard-buffer-name "*dashboard*"
"Dashboard's buffer name."
:type 'string
:group 'dashboard)
(defconst dashboard-buffer-name "*dashboard*"
"Dashboard's buffer name.")
(defvar dashboard-force-refresh nil
"If non-nil, force refresh dashboard buffer.")
@ -198,8 +191,8 @@ example:
(save-excursion
(if-let* ((sep (dashboard--separator))
((and (search-backward sep nil t)
(search-forward sep nil t)))
(ln (thing-at-point 'line t)))
(search-forward sep nil t))))
(let ((ln (thing-at-point 'line)))
(cond ((string-match-p "Recent Files:" ln) 'recents)
((string-match-p "Bookmarks:" ln) 'bookmarks)
((string-match-p "Projects:" ln) 'projects)
@ -207,7 +200,7 @@ example:
((string-match-p "Registers:" ln) 'registers)
((string-match-p "List Directories:" ln) 'ls-directories)
((string-match-p "List Files:" ln) 'ls-files)
(t (user-error "Unknown section from dashboard")))
(t (user-error "Unknown section from dashboard"))))
(user-error "Failed searching dashboard section"))))
;;
@ -507,11 +500,8 @@ See `dashboard-item-generators' for all items available."
(erase-buffer)
(setq dashboard--section-starts nil)
(mapc (lambda (entry)
(if (and (listp entry)
(not (functionp entry)))
(apply (car entry) `(,(cdr entry)))
(funcall entry)))
(mapc (lambda (fn)
(funcall fn))
dashboard-startupify-list)
(when dashboard-vertically-center-content

File diff suppressed because it is too large Load diff

View file

@ -1,566 +0,0 @@
;;; dashboard.el --- A startup screen extracted from Spacemacs -*- lexical-binding: t -*-
;; Copyright (c) 2016-2024 emacs-dashboard maintainers
;;
;; Author : Rakan Al-Hneiti <rakan.alhneiti@gmail.com>
;; Maintainer : Jesús Martínez <jesusmartinez93@gmail.com>
;; Shen, Jen-Chieh <jcs090218@gmail.com>
;; URL : https://github.com/emacs-dashboard/emacs-dashboard
;;
;; This file is not part of GNU Emacs.
;;
;;; License: GPLv3
;;
;; Created: October 05, 2016
;; Package-Version: 1.9.0-SNAPSHOT
;; Keywords: startup, screen, tools, dashboard
;; Package-Requires: ((emacs "26.1"))
;;; Commentary:
;; An extensible Emacs dashboard, with sections for
;; bookmarks, projects (projectile or project.el), org-agenda and more.
;;; Code:
(require 'ffap)
(require 'recentf)
(require 'dashboard-widgets)
;;
;;; Externals
(declare-function bookmark-get-filename "ext:bookmark.el")
(declare-function bookmark-all-names "ext:bookmark.el")
(declare-function dashboard-ls--dirs "ext:dashboard-ls.el")
(declare-function dashboard-ls--files "ext:dashboard-ls.el")
(declare-function page-break-lines-mode "ext:page-break-lines.el")
(declare-function projectile-remove-known-project "ext:projectile.el")
(declare-function project-forget-projects-under "ext:project.el")
(declare-function linum-mode "linum.el")
(declare-function dashboard-refresh-buffer "dashboard.el")
;;
;;; Customization
(defgroup dashboard nil
"Extensible startup screen."
:group 'applications)
;; Custom splash screen
(defvar dashboard-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-p") #'dashboard-previous-line)
(define-key map (kbd "C-n") #'dashboard-next-line)
(define-key map (kbd "<up>") #'dashboard-previous-line)
(define-key map (kbd "<down>") #'dashboard-next-line)
(define-key map (kbd "k") #'dashboard-previous-line)
(define-key map (kbd "j") #'dashboard-next-line)
(define-key map [tab] #'widget-forward)
(define-key map (kbd "C-i") #'widget-forward)
(define-key map [backtab] #'widget-backward)
(define-key map (kbd "RET") #'dashboard-return)
(define-key map [mouse-1] #'dashboard-mouse-1)
(define-key map (kbd "}") #'dashboard-next-section)
(define-key map (kbd "{") #'dashboard-previous-section)
(define-key map (kbd "<backspace>") #'dashboard-remove-item-under)
(define-key map (kbd "<delete>") #'dashboard-remove-item-under)
(define-key map (kbd "DEL") #'dashboard-remove-item-under)
(define-key map (kbd "1") #'dashboard-section-1)
(define-key map (kbd "2") #'dashboard-section-2)
(define-key map (kbd "3") #'dashboard-section-3)
(define-key map (kbd "4") #'dashboard-section-4)
(define-key map (kbd "5") #'dashboard-section-5)
(define-key map (kbd "6") #'dashboard-section-6)
(define-key map (kbd "7") #'dashboard-section-7)
(define-key map (kbd "8") #'dashboard-section-8)
(define-key map (kbd "9") #'dashboard-section-9)
map)
"Keymap for dashboard mode.")
(defcustom dashboard-before-initialize-hook nil
"Hook that is run before dashboard buffer is initialized."
:group 'dashboard
:type 'hook)
(defcustom dashboard-after-initialize-hook nil
"Hook that is run after dashboard buffer is initialized."
:group 'dashboard
:type 'hook)
(defcustom dashboard-hide-cursor nil
"Whether to hide the cursor in the dashboard."
:type 'boolean
:group 'dashboard)
(define-derived-mode dashboard-mode special-mode "Dashboard"
"Dashboard major mode for startup screen."
:group 'dashboard
:syntax-table nil
:abbrev-table nil
(buffer-disable-undo)
(when (featurep 'whitespace) (whitespace-mode -1))
(when (featurep 'linum) (linum-mode -1))
(when (featurep 'display-line-numbers) (display-line-numbers-mode -1))
(when (featurep 'page-break-lines) (page-break-lines-mode 1))
(setq-local revert-buffer-function #'dashboard-refresh-buffer)
(when dashboard-hide-cursor
(setq-local cursor-type nil))
(setq inhibit-startup-screen t
buffer-read-only t
truncate-lines t))
(defcustom dashboard-center-content nil
"Whether to center content within the window."
:type 'boolean
:group 'dashboard)
(defcustom dashboard-vertically-center-content nil
"Whether to vertically center content within the window."
:type 'boolean
:group 'dashboard)
(defcustom dashboard-startupify-list
'(dashboard-insert-banner
dashboard-insert-newline
dashboard-insert-banner-title
dashboard-insert-newline
dashboard-insert-init-info
dashboard-insert-items
dashboard-insert-newline
dashboard-insert-footer)
"List of dashboard widgets (in order) to insert in dashboard buffer.
Avalaible functions:
`dashboard-insert-newline'
`dashboard-insert-page-break'
`dashboard-insert-banner'
`dashboard-insert-banner-title'
`dashboard-insert-navigator'
`dashboard-insert-init-info'
`dashboard-insert-items'
`dashboard-insert-footer'
It must be a function or a cons cell where specify function and
its arg.
Also you can add your custom function or a lambda to the list.
example:
(lambda () (delete-char -1))"
:type '(repeat (choice
function
(cons function sexp)))
:group 'dashboard)
(defcustom dashboard-navigation-cycle nil
"Non-nil cycle the section navigation."
:type 'boolean
:group 'dashboard)
(defcustom dashboard-buffer-name "*dashboard*"
"Dashboard's buffer name."
:type 'string
:group 'dashboard)
(defvar dashboard-force-refresh nil
"If non-nil, force refresh dashboard buffer.")
(defvar dashboard--section-starts nil
"List of section starting positions.")
;;
;;; Util
(defun dashboard--goto-line (line)
"Goto LINE."
(goto-char (point-min)) (forward-line (1- line)))
(defmacro dashboard--save-excursion (&rest body)
"Execute BODY save window point."
(declare (indent 0) (debug t))
`(let ((line (line-number-at-pos nil t))
(column (current-column)))
,@body
(dashboard--goto-line line)
(move-to-column column)))
;;
;;; Core
(defun dashboard--separator ()
"Return separator used to search."
(concat "\n" dashboard-page-separator))
(defun dashboard--current-section ()
"Return section symbol in dashboard."
(save-excursion
(if-let* ((sep (dashboard--separator))
((and (search-backward sep nil t)
(search-forward sep nil t)))
(ln (thing-at-point 'line t)))
(cond ((string-match-p "Recent Files:" ln) 'recents)
((string-match-p "Bookmarks:" ln) 'bookmarks)
((string-match-p "Projects:" ln) 'projects)
((string-match-p "Agenda for " ln) 'agenda)
((string-match-p "Registers:" ln) 'registers)
((string-match-p "List Directories:" ln) 'ls-directories)
((string-match-p "List Files:" ln) 'ls-files)
(t (user-error "Unknown section from dashboard")))
(user-error "Failed searching dashboard section"))))
;;
;;; Navigation
(defun dashboard-previous-section ()
"Navigate forward to next section."
(interactive)
(let* ((items-len (1- (length dashboard-items)))
(first-item (car (nth 0 dashboard-items)))
(current (or (ignore-errors (dashboard--current-section))
first-item))
(items (mapcar #'car dashboard-items))
(find (cl-position current items :test #'equal))
(prev-index (1- find))
(prev (cond (dashboard-navigation-cycle
(if (< prev-index 0) (nth items-len items)
(nth prev-index items)))
(t
(if (< prev-index 0) (nth 0 items)
(nth prev-index items))))))
(dashboard--goto-section prev)))
(defun dashboard-next-section ()
"Navigate forward to next section."
(interactive)
(let* ((items-len (1- (length dashboard-items)))
(last-item (car (nth items-len dashboard-items)))
(current (or (ignore-errors (dashboard--current-section))
last-item))
(items (mapcar #'car dashboard-items))
(find (cl-position current items :test #'equal))
(next-index (1+ find))
(next (cond (dashboard-navigation-cycle
(or (nth next-index items)
(nth 0 items)))
(t
(if (< items-len next-index)
(nth (min items-len next-index) items)
(nth next-index items))))))
(dashboard--goto-section next)))
(defun dashboard--section-lines ()
"Return a list of integer represent the starting line number of each section."
(let (pb-lst)
(save-excursion
(goto-char (point-min))
(while (search-forward (dashboard--separator) nil t)
(when (ignore-errors (dashboard--current-section))
(push (line-number-at-pos) pb-lst))))
(setq pb-lst (reverse pb-lst))
pb-lst))
(defun dashboard--goto-section-by-index (index)
"Navigate to item section by INDEX."
(let* ((pg-lst (dashboard--section-lines))
(items-id (1- index))
(items-pg (nth items-id pg-lst))
(items-len (length pg-lst)))
(when (and items-pg (< items-id items-len))
(dashboard--goto-line items-pg))))
(defun dashboard-section-1 ()
"Navigate to section 1." (interactive) (dashboard--goto-section-by-index 1))
(defun dashboard-section-2 ()
"Navigate to section 2." (interactive) (dashboard--goto-section-by-index 2))
(defun dashboard-section-3 ()
"Navigate to section 3." (interactive) (dashboard--goto-section-by-index 3))
(defun dashboard-section-4 ()
"Navigate to section 4." (interactive) (dashboard--goto-section-by-index 4))
(defun dashboard-section-5 ()
"Navigate to section 5." (interactive) (dashboard--goto-section-by-index 5))
(defun dashboard-section-6 ()
"Navigate to section 6." (interactive) (dashboard--goto-section-by-index 6))
(defun dashboard-section-7 ()
"Navigate to section 7." (interactive) (dashboard--goto-section-by-index 7))
(defun dashboard-section-8 ()
"Navigate to section 8." (interactive) (dashboard--goto-section-by-index 8))
(defun dashboard-section-9 ()
"Navigate to section 9." (interactive) (dashboard--goto-section-by-index 9))
(defun dashboard-previous-line (arg)
"Move point up and position it at that lines item.
Optional prefix ARG says how many lines to move; default is one line."
(interactive "^p")
(dashboard-next-line (- arg)))
(defun dashboard-next-line (arg)
"Move point down and position it at that lines item.
Optional prefix ARG says how many lines to move; default is one line."
;; code heavily inspired by `dired-next-line'
(interactive "^p")
(let (line-move-visual goal-column)
(line-move arg t))
;; We never want to move point into an invisible line. Dashboard doesnt
;; use invisible text currently but when it does were ready!
(while (and (invisible-p (point))
(not (if (and arg (< arg 0)) (bobp) (eobp))))
(forward-char (if (and arg (< arg 0)) -1 1)))
(beginning-of-line-text))
;;
;;; ffap
(defun dashboard--goto-section (section)
"Move to SECTION declares in variable `dashboard-item-shortcuts'."
(let ((fnc (intern (format "dashboard-jump-to-%s" section))))
(dashboard-funcall-fboundp fnc)))
(defun dashboard--current-index (section &optional pos)
"Return the idex by SECTION from POS."
(let (target-ln section-line)
(save-excursion
(when pos (goto-char pos))
(setq target-ln (line-number-at-pos))
(dashboard--goto-section section)
(setq section-line (line-number-at-pos)))
(- target-ln section-line)))
(defun dashboard--section-list (section)
"Return the list from SECTION."
(cl-case section
(`recents recentf-list)
(`bookmarks (bookmark-all-names))
(`projects (dashboard-projects-backend-load-projects))
(`ls-directories (dashboard-ls--dirs))
(`ls-files (dashboard-ls--files))
(t (user-error "Unknown section for search: %s" section))))
(defun dashboard--current-item-in-path ()
"Return the path from current dashboard section in path."
(let ((section (dashboard--current-section)) path)
(cl-case section
(`bookmarks (setq path (bookmark-get-filename path)))
(t
(let ((lst (dashboard--section-list section))
(index (dashboard--current-index section)))
(setq path (nth index lst)))))
path))
(defun dashboard--on-path-item-p ()
"Return non-nil if current point is on the item path from dashboard."
(save-excursion
(when (= (point) (line-end-position)) (ignore-errors (forward-char -1)))
(eq (get-char-property (point) 'face) 'dashboard-items-face)))
(defun dashboard--ffap-guesser--adv (fnc &rest args)
"Advice execution around function `ffap-guesser'.
Argument FNC is the adviced function.
Optional argument ARGS adviced function arguments."
(cl-case major-mode
(`dashboard-mode
(or (and (dashboard--on-path-item-p)
(dashboard--current-item-in-path))
(apply fnc args))) ; fallback
(t (apply fnc args))))
(advice-add 'ffap-guesser :around #'dashboard--ffap-guesser--adv)
;;
;;; Removal
(defun dashboard-remove-item-under ()
"Remove a item from the current item section."
(interactive)
(cl-case (dashboard--current-section)
(`recents (dashboard-remove-item-recentf))
(`bookmarks (dashboard-remove-item-bookmarks))
(`projects (dashboard-remove-item-projects))
(`agenda (dashboard-remove-item-agenda))
(`registers (dashboard-remove-item-registers)))
(dashboard--save-excursion (dashboard-refresh-buffer)))
(defun dashboard-remove-item-recentf ()
"Remove a file from `recentf-list'."
(interactive)
(let ((path (save-excursion (end-of-line) (ffap-guesser))))
(setq recentf-list (delete path recentf-list)))
(dashboard-mute-apply (recentf-save-list)))
(defun dashboard-remove-item-projects ()
"Remove a path from `project--list'."
(interactive)
(let ((path (save-excursion (end-of-line) (ffap-guesser))))
(dashboard-mute-apply
(cl-case dashboard-projects-backend
(`projectile (projectile-remove-known-project path))
(`project-el (project-forget-projects-under path))))))
(defun dashboard-remove-item-bookmarks ()
"Remove a bookmarks from `bookmark-alist'."
(interactive)) ; TODO: ..
(defun dashboard-remove-item-agenda ()
"Remove an agenda from `org-agenda-files'."
(interactive "P")
(let ((agenda-file (get-text-property (point) 'dashboard-agenda-file))
(agenda-loc (get-text-property (point) 'dashboard-agenda-loc)))
(with-current-buffer (find-file-noselect agenda-file)
(goto-char agenda-loc)
(call-interactively 'org-todo))))
(defun dashboard-remove-item-registers ()
"Remove a registers from `register-alist'."
(interactive)) ; TODO: ..
;;
;;; Confirmation
(defun dashboard-return ()
"Hit return key in dashboard buffer."
(interactive)
(let ((start-ln (line-number-at-pos)) (fd-cnt 0) diff-line entry-pt)
(save-excursion
(while (and (not diff-line)
(not (= (point) (point-min)))
(not (get-char-property (point) 'button))
(not (= (point) (point-max))))
(forward-char 1)
(setq fd-cnt (1+ fd-cnt))
(unless (= start-ln (line-number-at-pos))
(setq diff-line t)))
(unless (= (point) (point-max))
(setq entry-pt (point))))
(when (= fd-cnt 1)
(setq entry-pt (1- (point))))
(if entry-pt
(widget-button-press entry-pt)
(call-interactively #'widget-button-press))))
(defun dashboard-mouse-1 ()
"Key for keymap `mouse-1'."
(interactive)
(let ((old-track-mouse track-mouse))
(when (call-interactively #'widget-button-click)
(setq track-mouse old-track-mouse))))
;;
;;; Insertion
(defmacro dashboard--with-buffer (&rest body)
"Execute BODY in dashboard buffer."
(declare (indent 0))
`(with-current-buffer (get-buffer-create dashboard-buffer-name)
(let ((inhibit-read-only t)) ,@body)
(current-buffer)))
(defun dashboard-insert-items ()
"Function to insert dashboard items.
See `dashboard-item-generators' for all items available."
(let ((recentf-is-on (recentf-enabled-p))
(origial-recentf-list recentf-list))
(mapc (lambda (els)
(let* ((el (or (car-safe els) els))
(list-size
(or (cdr-safe els)
dashboard-items-default-length))
(item-generator
(cdr-safe (assoc el dashboard-item-generators))))
(insert "\n")
(push (point) dashboard--section-starts)
(funcall item-generator list-size)
(goto-char (point-max))
(when recentf-is-on
(setq recentf-list origial-recentf-list))))
dashboard-items)
(when dashboard-center-content
(dashboard-center-text
(if dashboard--section-starts
(car (last dashboard--section-starts))
(point))
(point-max)))
(save-excursion
(dolist (start dashboard--section-starts)
(goto-char start)
(insert dashboard-page-separator)))
(insert "\n")
(insert dashboard-page-separator)))
(defun dashboard-insert-startupify-lists ()
"Insert the list of widgets into the buffer."
(interactive)
(let ((inhibit-redisplay t)
(recentf-is-on (recentf-enabled-p))
(origial-recentf-list recentf-list)
(dashboard-num-recents (or (cdr (assoc 'recents dashboard-items)) 0)))
(when recentf-is-on
(setq recentf-list (dashboard-subseq recentf-list dashboard-num-recents)))
(dashboard--with-buffer
(when (or dashboard-force-refresh (not (eq major-mode 'dashboard-mode)))
(run-hooks 'dashboard-before-initialize-hook)
(erase-buffer)
(setq dashboard--section-starts nil)
(mapc (lambda (entry)
(if (and (listp entry)
(not (functionp entry)))
(apply (car entry) `(,(cdr entry)))
(funcall entry)))
dashboard-startupify-list)
(when dashboard-vertically-center-content
(goto-char (point-min))
(when-let* ((content-height (cdr (window-absolute-pixel-position (point-max))))
(vertical-padding (floor (/ (- (window-pixel-height) content-height) 2)))
((> vertical-padding 0))
(vertical-lines (1- (floor (/ vertical-padding (line-pixel-height)))))
((> vertical-lines 0)))
(insert (make-string vertical-lines ?\n))))
(goto-char (point-min))
(dashboard-mode)))
(when recentf-is-on
(setq recentf-list origial-recentf-list))))
;;;###autoload
(defun dashboard-open (&rest _)
"Open (or refresh) the *dashboard* buffer."
(interactive)
(let ((dashboard-force-refresh t)) (dashboard-insert-startupify-lists))
(switch-to-buffer dashboard-buffer-name))
(defalias #'dashboard-refresh-buffer #'dashboard-open)
(defun dashboard-resize-on-hook (&optional _)
"Re-render dashboard on window size change."
(let ((space-win (get-buffer-window dashboard-buffer-name))
(frame-win (frame-selected-window)))
(when (and space-win
(not (window-minibuffer-p frame-win)))
(with-selected-window space-win
(dashboard-insert-startupify-lists)))))
(defun dashboard-initialize ()
"Switch to dashboard and run `dashboard-after-initialize-hook'."
(switch-to-buffer dashboard-buffer-name)
(goto-char (point-min))
(redisplay)
(run-hooks 'dashboard-after-initialize-hook))
;;;###autoload
(defun dashboard-setup-startup-hook ()
"Setup post initialization hooks unless a command line argument is provided."
(when (< (length command-line-args) 2) ;; Assume no file name passed
(add-hook 'window-size-change-functions #'dashboard-resize-on-hook 100)
(add-hook 'window-setup-hook #'dashboard-resize-on-hook)
(add-hook 'after-init-hook #'dashboard-insert-startupify-lists)
(add-hook 'emacs-startup-hook #'dashboard-initialize)))
(provide 'dashboard)
;;; dashboard.el ends here

View file

@ -1,6 +1,6 @@
(define-package "devdocs" "20240428.711" "Emacs viewer for DevDocs"
(define-package "devdocs" "20240301.1838" "Emacs viewer for DevDocs"
'((emacs "27.1"))
:commit "c14d1306648d3ae09ee3a3b3f45592334943cfeb" :authors
:commit "3424436f333955b39a0803ce9781aaffaae003f5" :authors
'(("Augusto Stoffel" . "arstoffel@gmail.com"))
:maintainers
'(("Augusto Stoffel" . "arstoffel@gmail.com"))

View file

@ -1,12 +1,12 @@
;;; devdocs.el --- Emacs viewer for DevDocs -*- lexical-binding: t -*-
;; Copyright (C) 2021-2024 Free Software Foundation, Inc.
;; Copyright (C) 2021 Free Software Foundation, Inc.
;; Author: Augusto Stoffel <arstoffel@gmail.com>
;; Keywords: help
;; URL: https://github.com/astoff/devdocs.el
;; Package-Requires: ((emacs "27.1"))
;; Version: 0.6.1
;; Version: 0.5
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
@ -82,7 +82,7 @@ name and a count."
:type '(choice (const :tag "Count in parentheses, italicized"
#("%s (%s)" 3 7 (face italic)))
(const :tag "Invisible cookie"
#("%s#%s" 2 5 (invisible t)))
#("%s (%s)" 2 7 (invisible t)))
string))
(defcustom devdocs-fontify-code-blocks t
@ -94,9 +94,6 @@ Fontification is done using the `org-src' library, which see."
"Whether to select the DevDocs window for viewing."
:type 'boolean)
(defvar devdocs-extra-rendering-functions '()
"Extra functions for `shr-external-rendering-functions'.")
(defface devdocs-code-block '((t nil))
"Additional face to apply to code blocks in DevDocs buffers.")
@ -319,10 +316,7 @@ already installed, reinstall it."
"Go to the original position in a DevDocs buffer."
(interactive)
(goto-char (point-min))
(when-let ((frag (let-alist (car devdocs--stack)
(or .fragment (devdocs--path-fragment .path))))
(shr-target-id (url-unhex-string frag))
(pred (if (fboundp 'shr--set-target-ids) #'member t)) ;; shr change in Emacs 29
(when-let ((pred (if (fboundp 'shr--set-target-ids) #'member t)) ;; shr change in Emacs 29
(match (text-property-search-forward 'shr-target-id shr-target-id pred)))
(goto-char (prop-match-beginning match))))
@ -483,18 +477,15 @@ fragment part of ENTRY.path."
(unless (eq major-mode 'devdocs-mode)
(devdocs-mode))
(let-alist entry
(let* ((inhibit-read-only t)
(extra-rendering-functions (cdr (assoc
(intern .doc.type)
devdocs-extra-rendering-functions)))
(let ((inhibit-read-only t)
(shr-external-rendering-functions `((pre . devdocs--shr-tag-pre)
,@extra-rendering-functions
,@shr-external-rendering-functions))
(file (expand-file-name (format "%s/%s.html"
.doc.slug
(url-hexify-string (devdocs--path-file .path)))
devdocs-data-dir)))
(erase-buffer)
(setq-local shr-target-id (url-unhex-string (or .fragment (devdocs--path-fragment .path))))
;; TODO: cl-progv here for shr settings?
(shr-insert-document
(with-temp-buffer
@ -503,9 +494,7 @@ fragment part of ENTRY.path."
(set-buffer-modified-p nil)
(setq-local devdocs-current-docs (list .doc.slug))
(push entry devdocs--stack)
(setq-local list-buffers-directory (format-mode-line devdocs-header-line
nil nil
(current-buffer)))
(setq-local list-buffers-directory (format-mode-line devdocs-header-line nil nil (current-buffer)))
(devdocs-goto-target)
(current-buffer))))

View file

@ -1,648 +0,0 @@
;;; devdocs.el --- Emacs viewer for DevDocs -*- lexical-binding: t -*-
;; Copyright (C) 2021-2024 Free Software Foundation, Inc.
;; Author: Augusto Stoffel <arstoffel@gmail.com>
;; Keywords: help
;; URL: https://github.com/astoff/devdocs.el
;; Package-Requires: ((emacs "27.1"))
;; Version: 0.6.1
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; devdocs.el is a documentation viewer similar to the built-in Info
;; browser, but geared towards documentation obtained from
;; https://devdocs.io.
;; To get started, download some documentation with `devdocs-install`.
;; This will show the available documents and save the selected one to
;; disk. Once you have the desired documents at hand, use
;; `devdocs-lookup` to search for entries.
;;; Code:
(require 'seq)
(require 'shr)
(require 'url-expand)
(eval-when-compile
(require 'let-alist)
(require 'subr-x))
(unless (libxml-available-p)
(display-warning 'devdocs "This package requires Emacs to be compiled with libxml2"))
(defgroup devdocs nil
"Emacs viewer for DevDocs."
:group 'help
:prefix "devdocs-")
(defcustom devdocs-current-docs nil
"A list of documents relevant to the current buffer.
This variable is normally set by the `devdocs-lookup' command,
but you may also wish to set it via a hook or as file or
directory-local variable."
:local t
:type '(list string))
(defcustom devdocs-data-dir (expand-file-name "devdocs" user-emacs-directory)
"Directory to save documentation files."
:type 'directory)
(defvar devdocs-site-url "https://devdocs.io"
"Location of the DevDocs website.")
(defvar devdocs-cdn-url "https://documents.devdocs.io"
"Location of the DevDocs CDN.")
(defcustom devdocs-cache-timeout 900
"Number of seconds to keep cached information such as document indexes."
:type 'number)
(defcustom devdocs-separator " » "
"String used to format a documentation location, e.g. in header line."
:type 'string)
(defcustom devdocs-disambiguated-entry-format #("%s (%s)" 3 7 (face italic))
"How to disambiguate entries with identical names in `devdocs-lookup'.
This string is passed to `format' with two arguments, the entry
name and a count."
:type '(choice (const :tag "Count in parentheses, italicized"
#("%s (%s)" 3 7 (face italic)))
(const :tag "Invisible cookie"
#("%s#%s" 2 5 (invisible t)))
string))
(defcustom devdocs-fontify-code-blocks t
"Whether to fontify code snippets inside pre tags.
Fontification is done using the `org-src' library, which see."
:type 'boolean)
(defcustom devdocs-window-select nil
"Whether to select the DevDocs window for viewing."
:type 'boolean)
(defvar devdocs-extra-rendering-functions '()
"Extra functions for `shr-external-rendering-functions'.")
(defface devdocs-code-block '((t nil))
"Additional face to apply to code blocks in DevDocs buffers.")
(defvar devdocs-history nil
"History of documentation entries.")
(defconst devdocs--data-format-version 1
"Version number of the saved documentation data format.")
;;; Memoization
(defvar devdocs--cache (make-hash-table :test 'equal)
"Hash table used by `devdocs--with-cache'.")
(defmacro devdocs--with-cache (&rest body)
"Evaluate BODY with memoization.
The return value is stored and reused if needed again within the
time span specified by `devdocs-cache-timeout'.
Note that the lexical environment is used to associate BODY to
its return value; take the necessary precautions."
`(if-let ((fun (lambda () ,@body))
(funrep ,(if (< emacs-major-version 28) ;; Cf. bug#32503
'(prin1-to-string fun)
'fun))
(data (gethash funrep devdocs--cache)))
(prog1 (cdr data)
(timer-set-time (car data) (time-add nil devdocs-cache-timeout)))
(let ((val (funcall fun))
(timer (run-at-time devdocs-cache-timeout nil
#'remhash funrep devdocs--cache)))
(prog1 val
(puthash funrep (cons timer val) devdocs--cache)))))
;;; Documentation management
(defalias 'devdocs--json-parse-buffer
(if (json-available-p)
(lambda () (json-parse-buffer :object-type 'alist))
(require 'json)
#'json-read))
(defun devdocs--doc-metadata (slug)
"Return the metadata of an installed document named SLUG."
(let ((file (expand-file-name (concat slug "/metadata") devdocs-data-dir)))
(unless (file-exists-p file)
(user-error "Document `%s' is not installed" slug))
(with-temp-buffer
(insert-file-contents file)
(let ((metadata (read (current-buffer))))
(unless (eq (car metadata) devdocs--data-format-version)
(user-error "Please run `devdocs-update-all'"))
(cdr metadata)))))
(defun devdocs--installed-docs ()
"Return a list of installed documents."
(mapcar #'devdocs--doc-metadata
(let ((default-directory devdocs-data-dir))
(seq-filter #'file-directory-p
(when (file-directory-p devdocs-data-dir)
(directory-files "." nil "^[^.]"))))))
(defun devdocs--available-docs ()
"Return a list of available documents.
If necessary, download data from `devdocs-site-url'."
(devdocs--with-cache
(with-temp-buffer
(url-insert-file-contents
(format "%s/docs.json" devdocs-site-url))
(devdocs--json-parse-buffer))))
(defun devdocs--doc-title (doc)
"Title of document DOC.
DOC is either a metadata alist, or the slug of an installed
document."
(let-alist (if (stringp doc) (devdocs--doc-metadata doc) doc)
(if (seq-empty-p .version) .name (concat .name " " .version))))
(defun devdocs--read-document (prompt &optional multiple available)
"Query interactively for a DevDocs document.
PROMPT is passed to `completing-read'.
Non-nil MULTIPLE allows selecting multiple documents.
Non-nil AVAILABLE means to offer a list of all available documents;
otherwise, offer only installed documents.
Return a document metadata alist if MULTIPLE is nil; otherwise, a
list of metadata alists."
(let ((cands (mapcar (lambda (it) (cons (alist-get 'slug it) it))
(if available
(devdocs--available-docs)
(or (devdocs--installed-docs)
(user-error "No documents in `%s'" devdocs-data-dir))))))
(if multiple
(delq nil (mapcar (lambda (s) (cdr (assoc s cands)))
(completing-read-multiple prompt cands)))
(cdr (assoc (completing-read prompt cands nil t) cands)))))
;;;###autoload
(defun devdocs-delete (doc)
"Delete DevDocs documentation.
DOC is a document metadata alist."
(interactive (list (devdocs--read-document "Delete documentation: ")))
(let ((dest (expand-file-name (alist-get 'slug doc) devdocs-data-dir)))
(if (and (file-directory-p dest)
(file-in-directory-p dest devdocs-data-dir))
(delete-directory dest t)
(user-error "Document `%s' is not installed" (alist-get 'slug doc)))))
;;;###autoload
(defun devdocs-install (doc)
"Download and install DevDocs documentation.
DOC is a document slug or metadata alist. If the document is
already installed, reinstall it."
(interactive (list (devdocs--read-document "Install documentation: " nil t)))
(make-directory devdocs-data-dir t)
(unless (listp doc)
(setq doc (or (seq-find (lambda (it) (string= doc (alist-get 'slug it)))
(devdocs--available-docs))
(user-error "No such document: %s" doc))))
(let* ((slug (alist-get 'slug doc))
(mtime (alist-get 'mtime doc))
(temp (make-temp-file "devdocs-" t))
pages)
(with-temp-buffer
(url-insert-file-contents (format "%s/%s/db.json?%s" devdocs-cdn-url slug mtime))
(dolist-with-progress-reporter
(entry (devdocs--json-parse-buffer))
"Installing documentation..."
(with-temp-file (expand-file-name
(url-hexify-string (format "%s.html" (car entry))) temp)
(push (symbol-name (car entry)) pages)
(insert (cdr entry)))))
(with-temp-buffer
(url-insert-file-contents (format "%s/%s/index.json?%s" devdocs-cdn-url slug mtime))
(let ((index (devdocs--json-parse-buffer)))
(push `(pages . ,(vconcat (nreverse pages))) index)
(with-temp-file (expand-file-name "index" temp)
(prin1 index (current-buffer)))))
(with-temp-file (expand-file-name "metadata" temp)
(prin1 (cons devdocs--data-format-version doc) (current-buffer)))
(let ((dest (expand-file-name slug devdocs-data-dir)))
(when (and (file-directory-p dest)
(file-in-directory-p dest devdocs-data-dir))
(delete-directory dest t))
(rename-file (file-name-as-directory temp) dest))
(message "Document `%s' installed" slug)))
;;;###autoload
(defun devdocs-update-all ()
"Reinstall all documents with a new version available."
(interactive)
(when-let ((installed (when (file-directory-p devdocs-data-dir)
(directory-files devdocs-data-dir nil "^[^.]")))
(newer (seq-filter
(lambda (doc)
(let-alist doc
(and (member .slug installed)
(< (alist-get 'mtime
(ignore-errors (devdocs--doc-metadata .slug))
0) ;; Update docs with an old data format too
.mtime))))
(devdocs--available-docs)))
((y-or-n-p (format "Update %s documents %s?"
(length newer)
(mapcar (lambda (d) (alist-get 'slug d)) newer)))))
(dolist (doc newer)
(devdocs-install doc))))
;;; Document indexes
(defun devdocs--index (doc kind)
"Return an index of document DOC, where KIND is `entries', `pages' or `types'."
(if kind
(alist-get kind (devdocs--with-cache (devdocs--index doc nil)))
(let* ((docmeta (cons 'doc doc))
(indexes (with-temp-buffer
(insert-file-contents (expand-file-name
(concat (alist-get 'slug doc) "/index")
devdocs-data-dir))
(read (current-buffer))))
(entries (alist-get 'entries indexes)))
(prog1 indexes
(seq-do-indexed (lambda (entry i) (aset entries i (cons docmeta entry)))
entries)))))
;;; Documentation viewer
(defvar-local devdocs--stack nil
"List of viewed entries, set buffer-locally when in `devdocs-mode'.")
(defvar-local devdocs--forward-stack nil
"List of viewed entries for `devdocs-go-forward'.")
(defvar devdocs-header-line
'(:eval (let-alist (car devdocs--stack)
(concat (devdocs--doc-title .doc)
(and .type devdocs-separator) .type
devdocs-separator (or .name .path)))))
(define-derived-mode devdocs-mode special-mode "DevDocs"
"Major mode for viewing DevDocs documents."
:interactive nil
(if (boundp 'browse-url-handlers) ;; Emacs ≥ 28
(setq-local browse-url-handlers
`((devdocs--internal-url-p . devdocs--internal-url-handler)
,@browse-url-handlers))
(setq-local browse-url-browser-function
`(("\\`[^:]+\\'" . devdocs--internal-url-handler)
,@(if (functionp browse-url-browser-function)
`(("" . ,browse-url-browser-function))
browse-url-browser-function))))
(setq-local
buffer-undo-list t
header-line-format devdocs-header-line
revert-buffer-function #'devdocs--revert-buffer
truncate-lines t))
(defun devdocs-goto-target ()
"Go to the original position in a DevDocs buffer."
(interactive)
(goto-char (point-min))
(when-let ((frag (let-alist (car devdocs--stack)
(or .fragment (devdocs--path-fragment .path))))
(shr-target-id (url-unhex-string frag))
(pred (if (fboundp 'shr--set-target-ids) #'member t)) ;; shr change in Emacs 29
(match (text-property-search-forward 'shr-target-id shr-target-id pred)))
(goto-char (prop-match-beginning match))))
(defun devdocs-go-back ()
"Go to the previously displayed entry in this DevDocs buffer."
(interactive)
(unless (cadr devdocs--stack)
(user-error "No previous entry"))
(push (pop devdocs--stack) devdocs--forward-stack)
(devdocs--render (pop devdocs--stack)))
(defun devdocs-go-forward ()
"Go to the next entry in this DevDocs buffer."
(interactive)
(unless (car devdocs--forward-stack)
(user-error "No next entry"))
(devdocs--render (pop devdocs--forward-stack)))
(defun devdocs-next-entry (count)
"Go forward COUNT entries in this document.
Note that this refers to the index order, which may not coincide
with the order of appearance in the text."
(interactive "p")
(let-alist (car devdocs--stack)
(let* ((entries (devdocs--index .doc 'entries))
(pred (lambda (entry _) (string= (alist-get 'path entry) .path)))
(current (seq-position entries nil pred)))
(unless current (user-error "No current entry"))
(devdocs--render
(or (ignore-error args-out-of-range (elt entries (+ count current)))
(user-error "No %s entry" (if (< count 0) "previous" "next")))))))
(defun devdocs-previous-entry (count)
"Go backward COUNT entries in this document."
(interactive "p")
(devdocs-next-entry (- count)))
(defun devdocs-goto-page (doc page)
"Go to a given PAGE (a number or path) of DOC.
Interactively, read a page name with completion."
(interactive (let-alist (car devdocs--stack)
(list .doc (completing-read "Go to page: "
(append (devdocs--index .doc 'pages) nil)
nil t nil 'devdocs-history))))
(let* ((path (cond ((stringp page) page)
((numberp page) (elt (devdocs--index doc 'pages) page))))
(entry (or (seq-find (lambda (entry) (string= (alist-get 'path entry) path))
(devdocs--index doc 'entries))
`((doc . ,doc) (path . ,path)))))
(devdocs--render entry)))
(defun devdocs-first-page (doc)
"Go to first page of DOC."
(interactive (list (alist-get 'doc (car devdocs--stack))))
(devdocs-goto-page doc 0))
(defun devdocs-last-page (doc)
"Go to last page of DOC."
(interactive (list (alist-get 'doc (car devdocs--stack))))
(devdocs-goto-page doc (1- (length (devdocs--index doc 'pages)))))
(defun devdocs-next-page (count)
"Go forward COUNT pages in this document."
(interactive "p")
(let-alist (car devdocs--stack)
(let* ((pages (devdocs--index .doc 'pages))
(dest (+ count (seq-position pages (devdocs--path-file .path)))))
(cond ((< dest 0) (user-error "No previous page"))
((<= (length pages) dest) (user-error "No next page")))
(devdocs-goto-page .doc dest))))
(defun devdocs-previous-page (count)
"Go backward COUNT entries in this document."
(interactive "p")
(devdocs-next-page (- count)))
(defun devdocs-copy-url ()
"Copy the URL of the current DevDocs page to the kill ring."
(interactive)
(let-alist (or (car devdocs--stack)
(user-error "Not in a DevDocs buffer"))
(let ((url (url-encode-url
(format "%s/%s/%s"
devdocs-site-url
.doc.slug
(if .fragment
(concat (devdocs--path-file .path) "#" .fragment)
.path)))))
(kill-new url)
(message "Copied %s" url))))
(let ((map devdocs-mode-map))
(define-key map [tab] #'forward-button)
(define-key map [backtab] #'backward-button)
(define-key map "d" #'devdocs-peruse)
(define-key map "i" #'devdocs-lookup)
(define-key map "p" #'devdocs-previous-entry)
(define-key map "n" #'devdocs-next-entry)
(define-key map "g" #'devdocs-goto-page)
(define-key map "[" #'devdocs-previous-page)
(define-key map "]" #'devdocs-next-page)
(define-key map "<" #'devdocs-first-page)
(define-key map ">" #'devdocs-last-page)
(define-key map "l" #'devdocs-go-back)
(define-key map "r" #'devdocs-go-forward)
(define-key map "w" #'devdocs-copy-url)
(define-key map "." #'devdocs-goto-target))
;;; Rendering
(defun devdocs--path-file (path)
"Return the non-fragment part of PATH."
(substring path 0 (string-match "#" path)))
(defun devdocs--path-fragment (path)
"Return the fragment part of PATH, or nil if absent."
(when-let ((i (string-match "#" path)))
(substring path (1+ i))))
(defun devdocs--path-expand (path base)
"Expand PATH relative to a BASE path."
(pcase (string-to-char path)
('?/ path)
('?# (concat (devdocs--path-file base) path))
(_ (string-remove-prefix
"/"
(url-expander-remove-relative-links ;; undocumented function!
(concat (file-name-directory base) path))))))
(defun devdocs--shr-tag-pre (dom)
"Insert and fontify pre-tag represented by DOM."
(let ((start (point)))
(if-let ((lang (and devdocs-fontify-code-blocks
(dom-attr dom 'data-language)))
(mode (or (cdr (assoc lang '(("cpp" . c++-mode)
("shell" . sh-mode))))
(intern (concat lang "-mode"))))
(buffer (and (fboundp mode) (current-buffer))))
(insert
(with-temp-buffer
(shr-tag-pre dom)
(let ((inhibit-message t)
(message-log-max nil))
(ignore-errors (delay-mode-hooks (funcall mode)))
(font-lock-ensure))
(buffer-string)))
(shr-tag-pre dom))
(add-face-text-property start (point) 'devdocs-code-block t)))
(defun devdocs--render (entry)
"Render a DevDocs documentation entry, returning a buffer.
ENTRY is an alist like those in the entry index of the document,
possibly with an additional ENTRY.fragment which overrides the
fragment part of ENTRY.path."
(with-current-buffer (get-buffer-create "*devdocs*")
(unless (eq major-mode 'devdocs-mode)
(devdocs-mode))
(let-alist entry
(let* ((inhibit-read-only t)
(extra-rendering-functions (cdr (assoc
(intern .doc.type)
devdocs-extra-rendering-functions)))
(shr-external-rendering-functions `((pre . devdocs--shr-tag-pre)
,@extra-rendering-functions
,@shr-external-rendering-functions))
(file (expand-file-name (format "%s/%s.html"
.doc.slug
(url-hexify-string (devdocs--path-file .path)))
devdocs-data-dir)))
(erase-buffer)
;; TODO: cl-progv here for shr settings?
(shr-insert-document
(with-temp-buffer
(insert-file-contents file)
(libxml-parse-html-region (point-min) (point-max)))))
(set-buffer-modified-p nil)
(setq-local devdocs-current-docs (list .doc.slug))
(push entry devdocs--stack)
(setq-local list-buffers-directory (format-mode-line devdocs-header-line
nil nil
(current-buffer)))
(devdocs-goto-target)
(current-buffer))))
(defun devdocs--revert-buffer (&rest _args)
"Refresh DevDocs buffer."
(devdocs--render (pop devdocs--stack)))
(defun devdocs--internal-url-p (url)
"Return t if URL seems to be an internal DevDocs link."
(not (string-match-p "\\`[a-z]+:" url)))
(defun devdocs--internal-url-handler (url &rest _)
"Open URL of an internal link in a DevDocs document."
(let-alist (car devdocs--stack)
(let* ((dest (devdocs--path-expand url .path))
(file (devdocs--path-file dest))
(frag (devdocs--path-fragment dest))
(entry (seq-find (lambda (it)
(let-alist it
(or (string= .path dest)
(string= .path file))))
(devdocs--index .doc 'entries))))
(unless entry (error "Can't find `%s'" dest))
(when frag (push `(fragment . ,frag) entry))
(devdocs--render entry))))
;;; Lookup commands
(defun devdocs--entries (documents)
"A list of entries in DOCUMENTS, as propertized strings."
(let* ((counts (make-hash-table :test 'equal))
(mkentry (lambda (it)
(let* ((name (alist-get 'name it))
(count (1+ (gethash name counts 0))))
(puthash name count counts)
`(,name ,count . ,it))))
(entries (mapcan (lambda (doc)
(mapcar mkentry
(devdocs--index doc 'entries)))
documents)))
(mapcar (pcase-lambda (`(,name ,count . ,it))
(propertize (if (= 1 (gethash name counts))
name
(format devdocs-disambiguated-entry-format name count))
'devdocs--data it))
entries)))
(defun devdocs--get-data (str)
"Get data stored as a string property in STR."
(get-text-property 0 'devdocs--data str))
(defun devdocs--annotate (cand)
"Return an annotation for `devdocs--read-entry' candidate CAND."
(let-alist (devdocs--get-data cand)
(concat " " (propertize " " 'display '(space :align-to 40))
(devdocs--doc-title .doc) devdocs-separator .type)))
(defun devdocs--relevant-docs (ask)
"Return a list of relevant documents for the current buffer.
May ask interactively for the desired documents, remembering the
choice for this buffer. If ASK is non-nil, ask unconditionally."
(if ask
(let ((docs (devdocs--read-document "Documents for this buffer: " t)))
(prog1 docs
(setq-local devdocs-current-docs
(mapcar (lambda (d) (alist-get 'slug d)) docs))))
(or (mapcar #'devdocs--doc-metadata devdocs-current-docs)
(devdocs--relevant-docs t)
(user-error "No documents"))))
(defun devdocs--read-entry (prompt documents initial-input)
"Read the name of an entry in one of the DOCUMENTS, using PROMPT.
INITIAL-INPUT is passed to `completing-read'"
(let* ((cands (devdocs--with-cache
(devdocs--entries documents)))
(metadata '(metadata
(category . devdocs)
(annotation-function . devdocs--annotate)))
(coll (lambda (string predicate action)
(if (eq action 'metadata)
metadata
(complete-with-action action cands string predicate))))
(cand (completing-read prompt coll nil t initial-input
'devdocs-history
(thing-at-point 'symbol))))
(devdocs--get-data (or (car (member cand cands))
(user-error "Not an entry!")))))
;;;###autoload
(defun devdocs-lookup (&optional ask-docs initial-input)
"Look up a DevDocs documentation entry.
Display entries in the documents `devdocs-current-docs' for
selection. With a prefix argument (or, from Lisp, if ASK-DOCS is
non-nil), first read the name of one or more installed documents
and set `devdocs-current-docs' for this buffer.
If INITIAL-INPUT is not nil, insert it into the minibuffer."
(interactive "P")
(let* ((entry (devdocs--read-entry "Go to documentation: "
(devdocs--relevant-docs ask-docs)
initial-input))
(buffer (devdocs--render entry))
(window (display-buffer buffer)))
(when window
(with-selected-window window
(devdocs-goto-target)
(recenter 0))
(when devdocs-window-select
(select-window window)))))
;;;###autoload
(defun devdocs-peruse (doc)
"Read a document from the first page."
(interactive (list (devdocs--read-document "Peruse documentation: ")))
(pop-to-buffer (devdocs-goto-page doc 0)))
;; Don't show devdocs-mode specific commands in M-x
(dolist (sym '(devdocs-goto-target devdocs-go-back devdocs-go-forward
devdocs-next-entry devdocs-previous-entry devdocs-goto-page
devdocs-first-page devdocs-last-page devdocs-next-page
devdocs-previous-page devdocs-copy-url))
(put sym 'completion-predicate (lambda (_ buffer)
(provided-mode-derived-p
(buffer-local-value 'major-mode buffer)
'devdocs-mode))))
;;; Compatibility with the old devdocs package
;;;###autoload
(defun devdocs-search (query)
"Search for QUERY in the DevDocs website."
(interactive (list (read-from-minibuffer
(format "Search %s: " devdocs-site-url)
nil nil nil nil (thing-at-point 'symbol))))
(browse-url (format "%s/#q=%s" devdocs-site-url (url-hexify-string query))))
(provide 'devdocs)
;;; devdocs.el ends here

View file

@ -1,6 +1,6 @@
(define-package "devdocs-browser" "20240511.306" "Browse devdocs.io documents using EWW"
(define-package "devdocs-browser" "20231231.1455" "Browse devdocs.io documents using EWW"
'((emacs "27.1"))
:commit "0655b89651458777354a3b89c1d486e0fda1928d" :authors
:commit "afc460e687bec4eb947ab85d207778fc3b9b3bbc" :authors
'(("blahgeek" . "i@blahgeek.com"))
:maintainers
'(("blahgeek" . "i@blahgeek.com"))

View file

@ -32,7 +32,6 @@
(require 'eww)
(require 'eldoc)
(require 'imenu)
(require 'seq)
(defgroup devdocs-browser nil
@ -40,40 +39,12 @@
:group 'tools
:group 'web)
;; Following are faces for <h1> to <h5> elements.
;; We do not reuse `shr-h1' etc. face because:
;; - they are only available since emacs 28
;; - devdocs documents usually have lower level of headings (e.g. H2 and H3 is more common), so we want different rules as the general version
(defface devdocs-browser-h1
'((t :height 1.3 :weight bold))
"Face for <h1> elements for devdocs-browser.")
(defface devdocs-browser-h2
'((t :height 1.2 :weight bold))
"Face for <h2> elements for devdocs-browser.")
(defface devdocs-browser-h3
'((t :height 1.1 :weight bold))
"Face for <h3> elements for devdocs-browser.")
(defface devdocs-browser-h4
'((t :weight bold))
"Face for <h4> elements for devdocs-browser.")
(defface devdocs-browser-h5
'((t :slant italic))
"Face for <h5> elements for devdocs-browser.")
(defcustom devdocs-browser-data-directory
(defcustom devdocs-browser-cache-directory
(expand-file-name "devdocs-browser" user-emacs-directory)
"Directory to store devdocs data files."
"Directory to store devdocs cache files."
:type 'directory
:group 'devdocs-browser)
(defalias 'devdocs-browser-cache-directory 'devdocs-browser-data-directory)
(defcustom devdocs-browser-base-url "https://devdocs.io/"
"Base URL to fetch json metadata files."
:type 'string)
@ -189,20 +160,37 @@ See https://prismjs.com/ for list of language names."
(insert (devdocs-browser--eww-fontify-pre dom))
(shr-ensure-newline)))
(defun devdocs-browser--eww-tag-header (level dom)
"Render function for header DOM with LEVEL (number)."
;; use h1/h2/h3 as title if not set yet
(when (and (<= level 3)
(zerop (length (plist-get eww-data :title))))
(eww-tag-title dom))
(defun devdocs-browser--eww-tag-maybe-set-title (dom)
"Maybe set DOM as title if it's not set yet."
(when (zerop (length (plist-get eww-data :title)))
(eww-tag-title dom)))
;; similar to shr-heading
(shr-ensure-paragraph)
(let ((start (point)))
(shr-fontize-dom dom (intern (concat "devdocs-browser-h" (number-to-string level))))
;; this is new since emacs 30, to support outline function
(put-text-property start (pos-eol) 'outline-level level))
(shr-ensure-paragraph))
(defun devdocs-browser--eww-tag-h1 (dom)
"Rendering function for h1 DOM. Maybe use it as title."
(devdocs-browser--eww-tag-maybe-set-title dom)
(shr-tag-h1 dom))
(defun devdocs-browser--eww-tag-h2 (dom)
"Rendering function for h2 DOM. Maybe use it as title."
(devdocs-browser--eww-tag-maybe-set-title dom)
(shr-heading dom (if shr-use-fonts
'(variable-pitch (:height 1.2 :weight bold))
'bold)))
(defun devdocs-browser--eww-tag-h3 (dom)
"Rendering function for h2 DOM. Maybe use it as title."
(devdocs-browser--eww-tag-maybe-set-title dom)
(shr-heading dom (if shr-use-fonts
'(variable-pitch (:height 1.1 :weight bold))
'bold)))
(defun devdocs-browser--eww-tag-h4 (dom)
"Rendering function for h4 DOM."
(shr-heading dom 'bold))
(defun devdocs-browser--eww-tag-h5 (dom)
"Rendering function for h5 DOM."
(shr-heading dom 'italic))
(defun devdocs-browser--eww-tag-generic-ensure-paragraph (dom)
"Rendering function for generic DOM while ensuring paragraph."
@ -296,7 +284,7 @@ Can be used as `imenu-create-index-function'."
(path (plist-get entry :path))
(url (url-generic-parse-url path))
(target (url-target url))
((equal (url-filename url) (url-filename page-url))))
(_ (equal (url-filename url) (url-filename page-url))))
(cons name (devdocs-browser--position-by-target target))))
entries))))
@ -337,12 +325,13 @@ Can be used as `imenu-create-index-function'."
(setq-local shr-external-rendering-functions
(append shr-external-rendering-functions
'((pre . devdocs-browser--eww-tag-pre)
(h1 . devdocs-browser--eww-tag-h1)
(h2 . devdocs-browser--eww-tag-h2)
(h3 . devdocs-browser--eww-tag-h3)
(h4 . devdocs-browser--eww-tag-h4)
(h5 . devdocs-browser--eww-tag-h5)
(summary . devdocs-browser--eww-tag-generic-ensure-paragraph)
(section . devdocs-browser--eww-tag-generic-ensure-paragraph))
(mapcar (lambda (level)
(cons (intern (concat "h" (number-to-string level)))
(apply-partially #'devdocs-browser--eww-tag-header level)))
(number-sequence 1 5))))
(section . devdocs-browser--eww-tag-generic-ensure-paragraph))))
(setq-local imenu-create-index-function
#'devdocs-browser--imenu-create-index)
(when (boundp 'eww-auto-rename-buffer)
@ -362,12 +351,13 @@ Can be used as `imenu-create-index-function'."
(defun devdocs-browser--completing-read (prompt collection &optional def)
"Helper function for `completing-read'.
PROMPT: same meaning, but this function will append ';' at the end;
COLLECTION: alist or hashtable of (name . props), where props is a plist with
COLLECTION: alist of (name . props), where props is a plist with
possibly the following keys: :value, :annotation, :group;
if :group is not nil and name starts with '<group>: ', its removed.
DEF: same meaning;"
;; convert collection to hashtables for faster completion. `complete-with-action' also supports that.
(let* (collection-ht
(setq collection (delq nil collection))
(let* ((collection-ht (make-hash-table :test 'equal :size (length collection)))
(annotation-function
(lambda (s)
(let ((annotation (plist-get (gethash s collection-ht) :annotation)))
@ -382,13 +372,8 @@ DEF: same meaning;"
(replace-match "" t t s)
s))
(t group))))))
(if (hash-table-p collection)
(setq collection-ht collection)
(setq collection-ht (make-hash-table :test 'equal :size (length collection)))
(mapc (lambda (elem)
(when elem
(puthash (car elem) (cdr elem) collection-ht)))
collection))
(mapc (lambda (elem) (puthash (car elem) (cdr elem) collection-ht))
collection)
(setq prompt (concat prompt
(when def
(format " (default %s)" (funcall group-function def t)))
@ -399,7 +384,7 @@ DEF: same meaning;"
(if (eq action 'metadata)
`(metadata . ((annotation-function . ,annotation-function)
(group-function . ,group-function)))
(complete-with-action action collection-ht str pred)))
(complete-with-action action collection str pred)))
nil t ;; require-match
nil nil def)))
(or (plist-get (gethash res collection-ht) :value)
@ -411,7 +396,7 @@ DEF: same meaning;"
(defun devdocs-browser--read-json (file-path)
"Read json file in FILE-PATH, if it's a relative path, find it in cache dir."
(let ((filename (expand-file-name file-path devdocs-browser-data-directory)))
(let ((filename (expand-file-name file-path devdocs-browser-cache-directory)))
(when (file-exists-p filename)
(with-temp-buffer
(insert-file-contents filename)
@ -420,7 +405,7 @@ DEF: same meaning;"
(defun devdocs-browser--fetch-json (url-path file-path &optional base-url)
"Fetch json from BASE-URL / URL-PATH, also save to FILE-PATH.
BASE-URL defaults to `devdocs-browser-base-url'."
(let ((cache-filename (expand-file-name file-path devdocs-browser-data-directory)))
(let ((cache-filename (expand-file-name file-path devdocs-browser-cache-directory)))
(unless (file-exists-p (file-name-directory cache-filename))
(make-directory (file-name-directory cache-filename) t))
(with-temp-file cache-filename
@ -462,18 +447,13 @@ To upgrade docs content, see `devdocs-browser-upgrade-doc'."
(equal (plist-get doc :name) slug-or-name)))
docs-list)))
(defcustom devdocs-browser-enable-cache t
"Whether cache doc indices in memory."
:type 'boolean
:group 'devdocs-browser)
(defvar devdocs-browser--docs-cache '() "Cached doc indices plist.")
(defvar devdocs-browser--docs-cache '() "Cached doc indexes plist.")
(defun devdocs-browser--install-doc-internal (doc)
"(Re-)install doc identified by plist DOC. Return t if success."
(let* ((slug (plist-get doc :slug))
(mtime (plist-get doc :mtime))
(docs-dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-data-directory))
(docs-dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-cache-directory))
(doc-dir (expand-file-name slug docs-dir))
success)
(unless (file-exists-p docs-dir)
@ -498,7 +478,7 @@ To upgrade docs content, see `devdocs-browser-upgrade-doc'."
(message "Failed to install devdocs doc %s" slug))
;; remove cache
(setq devdocs-browser--docs-cache
(plist-put devdocs-browser--docs-cache slug nil #'equal))
(lax-plist-put devdocs-browser--docs-cache slug nil))
success))
(defun devdocs-browser--doc-readable-name (doc)
@ -548,12 +528,12 @@ When called interactively, user can choose from the list."
(interactive (list (completing-read "Uninstall doc: "
(devdocs-browser-list-installed-slugs)
nil t)))
(let* ((docs-dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-data-directory))
(let* ((docs-dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-cache-directory))
(doc-dir (expand-file-name slug docs-dir)))
(when (file-exists-p doc-dir)
(delete-directory doc-dir t)))
(setq devdocs-browser--docs-cache
(plist-put devdocs-browser--docs-cache slug nil #'equal)))
(lax-plist-put devdocs-browser--docs-cache slug nil)))
(defun devdocs-browser--upgrade-readable-name (old-doc new-doc)
"Get human readable name for upgrade from OLD-DOC to NEW-DOC."
@ -622,7 +602,7 @@ You may need to call `devdocs-browser-update-docs' first."
(defun devdocs-browser-list-installed-slugs ()
"Get a list of installed docs' slug name."
(let ((dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-data-directory)))
(let ((dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-cache-directory)))
(when (file-exists-p dir)
(directory-files dir nil
;; ignore ".", ".." and hidden files
@ -644,9 +624,9 @@ You may need to call `devdocs-browser-update-docs' first."
(defun devdocs-browser--load-doc (slug &optional refresh-cache)
"Load doc identified by SLUG, reload cache if REFRESH-CACHE is not nil.
Result is a plist metadata, with an extra :index field at the beginning."
(or (and (not refresh-cache) (plist-get devdocs-browser--docs-cache slug #'equal))
(or (and (not refresh-cache) (lax-plist-get devdocs-browser--docs-cache slug))
(let* ((docs-dir (expand-file-name devdocs-browser--docs-dir
devdocs-browser-data-directory))
devdocs-browser-cache-directory))
(doc-dir (expand-file-name slug docs-dir))
(metadata-filename (expand-file-name devdocs-browser--metadata-filename doc-dir))
(metadata nil)
@ -658,16 +638,15 @@ Result is a plist metadata, with an extra :index field at the beginning."
(insert-file-contents metadata-filename)
(setq metadata (read (current-buffer))))
(setq res (append `(:index ,index) metadata))
(when devdocs-browser-enable-cache
(setq devdocs-browser--docs-cache
(plist-put devdocs-browser--docs-cache slug res #'equal))))
(lax-plist-put devdocs-browser--docs-cache slug res)))
res)))
(defun devdocs-browser--download-offline-data-internal (doc)
"(re-)Download and extract offline data for DOC."
(let* ((slug (plist-get doc :slug))
(mtime (plist-get doc :mtime))
(docs-dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-data-directory))
(docs-dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-cache-directory))
(doc-dir (expand-file-name slug docs-dir))
(data-dir (expand-file-name devdocs-browser--offline-data-dir-name doc-dir))
success)
@ -703,7 +682,7 @@ Result is a plist metadata, with an extra :index field at the beginning."
(defun devdocs-browser-offline-data-dir (slug)
"Return doc SLUG's offline data dir if present, return nil otherwise."
(let* ((docs-dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-data-directory))
(let* ((docs-dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-cache-directory))
(doc-dir (expand-file-name slug docs-dir))
(data-dir (expand-file-name devdocs-browser--offline-data-dir-name doc-dir)))
(when (file-exists-p data-dir)
@ -799,8 +778,7 @@ When called interactively, user can choose from the list."
(let ((current-word-regex
(when-let ((word (thing-at-point 'word t)))
(concat "\\<" (regexp-quote word) "\\>")))
(rows (make-hash-table :test 'equal))
slugs def)
slugs rows def)
(dolist (slug-or-name slug-or-name-list)
(when-let* ((doc-simple (devdocs-browser-find-installed-doc slug-or-name))
(slug (plist-get doc-simple :slug))
@ -808,11 +786,9 @@ When called interactively, user can choose from the list."
(index (plist-get doc :index))
(entries (plist-get index :entries)))
(setq slugs (push slug slugs))
(puthash (format "%s: INDEX PAGE" slug)
`(:value (,doc "index")
:group ,slug)
rows)
(seq-doseq (entry entries)
(let ((new-rows
(mapcar
(lambda (entry)
(let* ((name (plist-get entry :name))
(path (plist-get entry :path))
(type (plist-get entry :type))
@ -820,10 +796,15 @@ When called interactively, user can choose from the list."
(when (and (null def) current-word-regex)
(when (string-match-p current-word-regex name)
(setq def title)))
(puthash title `(:value (,doc ,path)
(cons title `(:value (,doc ,path)
:group ,slug
:annotation ,type)
rows)))))
:annotation ,type))))
entries)))
(setq rows (append new-rows rows))
(push (cons (format "%s: INDEX PAGE" slug)
`(:value (,doc "index")
:group ,slug))
rows))))
(let* ((selected-value
(devdocs-browser--completing-read
(format "Devdocs browser [%s]" (mapconcat #'identity slugs ","))
@ -831,12 +812,6 @@ When called interactively, user can choose from the list."
(when selected-value
(apply #'devdocs-browser--eww-open selected-value)))))
(defcustom devdocs-browser-open-fallback-to-all-docs t
"When not sure which docs to use, whether `devdocs-browser-open' should use all installed docs, or just ask the user to pick one (like `devdocs-browser-open-in')."
:type 'boolean
:group 'devdocs-browser)
;;;###autoload
(defun devdocs-browser-open ()
"Open entry in active docs.
@ -845,12 +820,7 @@ or `devdocs-browser-major-mode-docs-alist',
or the current doc type if called in a devdocs eww buffer.
When all of them are nil, all installed docs are used."
(interactive)
(if devdocs-browser-open-fallback-to-all-docs
(devdocs-browser-open-in (devdocs-browser--default-active-slugs))
(let ((slugs (devdocs-browser--default-active-slugs 'no-fallback-all)))
(if slugs
(devdocs-browser-open-in slugs)
(call-interactively 'devdocs-browser-open-in)))))
(devdocs-browser-open-in (devdocs-browser--default-active-slugs)))
(provide 'devdocs-browser)

View file

@ -1,857 +0,0 @@
;;; devdocs-browser.el --- Browse devdocs.io documents using EWW -*- lexical-binding: t; -*-
;; Copyright (C) 2021
;; Author: blahgeek <i@blahgeek.com>
;; URL: https://github.com/blahgeek/emacs-devdocs-browser
;; Version: 20210525
;; Keywords: docs, help, tools
;; Package-Requires: ((emacs "27.1"))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Browse devdocs.io documents inside Emacs using EWW.
;;; Code:
(require 'files)
(require 'shr)
(require 'eww)
(require 'eldoc)
(require 'imenu)
(require 'seq)
(defgroup devdocs-browser nil
"Browse devdocs.io."
:group 'tools
:group 'web)
;; Following are faces for <h1> to <h5> elements.
;; We do not reuse `shr-h1' etc. face because:
;; - they are only available since emacs 28
;; - devdocs documents usually have lower level of headings (e.g. H2 and H3 is more common), so we want different rules as the general version
(defface devdocs-browser-h1
'((t :height 1.3 :weight bold))
"Face for <h1> elements for devdocs-browser.")
(defface devdocs-browser-h2
'((t :height 1.2 :weight bold))
"Face for <h2> elements for devdocs-browser.")
(defface devdocs-browser-h3
'((t :height 1.1 :weight bold))
"Face for <h3> elements for devdocs-browser.")
(defface devdocs-browser-h4
'((t :weight bold))
"Face for <h4> elements for devdocs-browser.")
(defface devdocs-browser-h5
'((t :slant italic))
"Face for <h5> elements for devdocs-browser.")
(defcustom devdocs-browser-data-directory
(expand-file-name "devdocs-browser" user-emacs-directory)
"Directory to store devdocs data files."
:type 'directory
:group 'devdocs-browser)
(defalias 'devdocs-browser-cache-directory 'devdocs-browser-data-directory)
(defcustom devdocs-browser-base-url "https://devdocs.io/"
"Base URL to fetch json metadata files."
:type 'string)
(defcustom devdocs-browser-doc-base-url "https://documents.devdocs.io/"
"Base URL for doc contents."
:type 'string)
(defcustom devdocs-browser-major-mode-docs-alist
'((c++-mode . ("cpp"))
(c-mode . ("c"))
(go-mode . ("go"))
(python-mode . ("Python"))
(emacs-lisp-mode . ("elisp"))
(rust-mode . ("rust"))
(cmake-mode . ("CMake")))
"Alist of MAJOR-MODE and list of docset names.
When calling `devdocs-browser-open', this variable will be used
to pick a list of docsets based on the current MAJOR-MODE.
Docset name may be SLUG (e.g. 'python~3.8') or NAME (e.g. 'Python'),
if it's a NAME and multiple choices are possible,
one of the installed docs with the NAME will be used.
Also see `devdocs-browser-active-docs'."
:type '(alist :key-type function
:value-type (list string)))
(defvar-local devdocs-browser-active-docs
nil
"List of docset names used by `devdocs-browser-open' to pick docsets.
If this var is set to non-nil,
it have higher priority than `devdocs-browser-major-mode-docs-alist'.
See `devdocs-browser-major-mode-docs-alist' for the meaning of NAME.")
(defcustom devdocs-browser-highlight-lang-mode-alist '()
"Alist of language name and MAJOR-MODE, to highlight HTML pre blocks.
If language is not found in this alist,
`devdocs-browser-highlight-lang-mode-alist-default' will be used.
See https://prismjs.com/ for list of language names."
:type '(alist :key-type string
:value-type function))
(defvar devdocs-browser-highlight-lang-mode-alist-default
'(("html" . html-mode)
("xml" . xml-mode)
("css" . css-mode)
("clike" . c-mode)
("javascript" . js-mode)
("js" . js-mode)
("jsx" . js-mode)
("bash" . sh-mode)
("shell" . sh-mode)
("c" . c-mode)
("cpp" . c++-mode)
("cmake" . cmake-mode)
("go" . go-mode)
("haskell" . haskell-mode)
("hs" . haskell-mode)
("java" . java-mode)
("json" . js-mode)
("elisp" . elisp-mode)
("emacs" . elisp-mode)
("lua" . lua-mode)
("makefile" . makefile-mode)
("markdown" . markdown-mode)
("md" . markdown-mode)
("nginx" . conf-mode)
("objectivec" . objc-mode)
("objc" . objc-mode)
("perl" . perl-mode)
("protobuf" . protobuf-mode)
("python" . python-mode)
("py" . python-mode)
("ruby" . ruby-mode)
("rust" . rust-mode)
("rb" . ruby-mode)
("sql" . sql-mode)
("typescript" . typescript-mode))
"Default value for `devdocs-browser-highlight-lang-mode-alist'.")
(defun devdocs-browser--clear-dom-id-attr (dom)
"Clear id attribute for DOM and its children."
(dom-remove-attribute dom 'id)
(mapc #'devdocs-browser--clear-dom-id-attr (dom-non-text-children dom)))
(defun devdocs-browser--eww-fontify-pre (dom)
"Return fontified string for pre DOM."
(with-temp-buffer
(shr-generic dom)
(when (> shr-indentation 0)
(save-excursion
(goto-char (point-min))
(while (not (eobp))
(shr-indent)
(forward-line 1))))
(let* ((language (dom-attr dom 'data-language))
(mode (cdr (or (assoc language devdocs-browser-highlight-lang-mode-alist)
(assoc language devdocs-browser-highlight-lang-mode-alist-default)))))
(when (fboundp mode)
(delay-mode-hooks (funcall mode))
(font-lock-default-function mode)
(font-lock-default-fontify-region (point-min) (point-max) nil)))
(buffer-string)))
(defun devdocs-browser--eww-tag-pre (dom)
"Rendering function for pre DOM."
;; must clear all 'id' attributes in dom.
;; otherwise, shr would try to add text properties based on it, but since they are rendered in temp-buffer, the marker would be invalid
(devdocs-browser--clear-dom-id-attr dom)
(let ((shr-folding-mode 'none)
(shr-current-font 'default))
(shr-ensure-newline)
(insert (devdocs-browser--eww-fontify-pre dom))
(shr-ensure-newline)))
(defun devdocs-browser--eww-tag-header (level dom)
"Render function for header DOM with LEVEL (number)."
;; use h1/h2/h3 as title if not set yet
(when (and (<= level 3)
(zerop (length (plist-get eww-data :title))))
(eww-tag-title dom))
;; similar to shr-heading
(shr-ensure-paragraph)
(let ((start (point)))
(shr-fontize-dom dom (intern (concat "devdocs-browser-h" (number-to-string level))))
;; this is new since emacs 30, to support outline function
(put-text-property start (pos-eol) 'outline-level level))
(shr-ensure-paragraph))
(defun devdocs-browser--eww-tag-generic-ensure-paragraph (dom)
"Rendering function for generic DOM while ensuring paragraph."
(shr-ensure-paragraph)
(shr-generic dom))
(defvar-local devdocs-browser--eww-data '()
"Plist data for current eww page, contain :doc and :path.")
(defun devdocs-browser--eww-fix-url (url)
"Fix links' URL in docs by appending suffix and mtime."
;; shr-expand-url may be call in a temp buffer
;; we need to temporary bind this buffer to access the buffer-local variable.
(with-current-buffer (window-buffer)
(let ((url-parsed (url-generic-parse-url url))
(root-url-parsed (url-generic-parse-url (plist-get eww-data :url)))
(mtime (plist-get (plist-get devdocs-browser--eww-data :doc) :mtime)))
(when (and mtime
(equal (url-type url-parsed) (url-type root-url-parsed))
(equal (url-host url-parsed) (url-host root-url-parsed))
(not (string-match-p "\\.html" url)))
(setf (url-filename url-parsed)
(if (equal (url-type url-parsed) "file")
(concat (url-filename url-parsed) ".html")
(format "%s.html?%s" (url-filename url-parsed) mtime)))
(setq url (url-recreate-url url-parsed)))))
url)
(defun devdocs-browser--eww-parse-url-path (url)
"Return URL's doc :path ('hello/world#target')."
;; see devdocs-browser--eww-open for url pattern
(when-let* ((url-parsed (url-generic-parse-url url))
(doc (plist-get devdocs-browser--eww-data :doc))
(slug (plist-get doc :slug))
(filename-suffix (if (equal (url-type url-parsed) "file")
".html"
(format ".html?%s" (plist-get doc :mtime))))
(filename-prefix (if (equal (url-type url-parsed) "file")
(devdocs-browser-offline-data-dir slug)
(concat "/" slug "/")))
(path (url-filename url-parsed)))
(when (and (string-prefix-p filename-prefix path)
(string-suffix-p filename-suffix path))
(setq path (string-remove-prefix filename-prefix path))
(setq path (string-remove-suffix filename-suffix path))
(when (url-target url-parsed)
(setq path (concat path "#" (url-target url-parsed))))
path)))
(defun devdocs-browser--eww-page-path ()
"Return current page's :path ('hello/world#target')."
(devdocs-browser--eww-parse-url-path (plist-get eww-data :url)))
(defun devdocs-browser--eww-link-eldoc (&optional _)
"Show URL link or description at current point."
(when-let ((url (get-text-property (point) 'shr-url)))
(if-let ((path (devdocs-browser--eww-parse-url-path url)))
(let* ((doc (plist-get devdocs-browser--eww-data :doc))
(index (plist-get doc :index))
(entries (plist-get index :entries))
(entry (seq-find
(lambda (x) (equal (plist-get x :path) path))
entries)))
(concat
(when entry
(propertize (plist-get entry :name) 'face 'font-lock-keyword-face))
(when entry
(format " (%s): " (plist-get entry :type)))
(propertize path 'face 'italic)))
(format "External link: %s" (propertize url 'face 'italic)))))
(defun devdocs-browser--position-by-target (target)
"Find buffer position for TARGET (url hash)."
(save-excursion
(goto-char (point-min))
(when-let ((match (text-property-search-forward 'shr-target-id target #'member)))
(prop-match-beginning match))))
(defun devdocs-browser--imenu-create-index ()
"Create index alist for current buffer for imenu.
Can be used as `imenu-create-index-function'."
(when-let* ((doc (plist-get devdocs-browser--eww-data :doc))
(entries (plist-get (plist-get doc :index) :entries))
(page-path (devdocs-browser--eww-page-path))
(page-url (url-generic-parse-url page-path)))
(seq-filter
#'identity
(mapcar
(lambda (entry)
(when-let* ((name (plist-get entry :name))
(path (plist-get entry :path))
(url (url-generic-parse-url path))
(target (url-target url))
((equal (url-filename url) (url-filename page-url))))
(cons name (devdocs-browser--position-by-target target))))
entries))))
(define-obsolete-function-alias 'devdocs-browser-eww-goto-target 'imenu "20220917")
(defun devdocs-browser-eww-open-in-default-browser ()
"Open current page in devdocs.io in browser."
(interactive)
(when-let* ((doc (plist-get devdocs-browser--eww-data :doc))
(slug (plist-get doc :slug))
(path (devdocs-browser--eww-page-path))
(url (concat devdocs-browser-base-url slug "/" path)))
(browse-url-default-browser url)))
(defun devdocs-browser--eww-recenter-advice (res)
"Recenter current cursor for devdocs buffer, used for advice :filter-return (return `RES')."
(when devdocs-browser--eww-data
(recenter))
res)
(defun devdocs-browser--eww-browse-url-new-window-advice (args)
"Advice around `eww-browse-url' with ARGS, set NEW-WINDOW if URL is external."
(let ((url (car args))
(new-window (cadr args)))
(when (and devdocs-browser--eww-data
(not (devdocs-browser--eww-parse-url-path url)))
(setq new-window t))
(list url new-window)))
(define-minor-mode devdocs-browser-eww-mode
"Minor mode for browsing devdocs pages with eww."
:lighter " Devdocs"
:interactive nil
:group 'devdocs-browser
:keymap (let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c C-o") #'devdocs-browser-eww-open-in-default-browser)
map)
(setq-local shr-external-rendering-functions
(append shr-external-rendering-functions
'((pre . devdocs-browser--eww-tag-pre)
(summary . devdocs-browser--eww-tag-generic-ensure-paragraph)
(section . devdocs-browser--eww-tag-generic-ensure-paragraph))
(mapcar (lambda (level)
(cons (intern (concat "h" (number-to-string level)))
(apply-partially #'devdocs-browser--eww-tag-header level)))
(number-sequence 1 5))))
(setq-local imenu-create-index-function
#'devdocs-browser--imenu-create-index)
(when (boundp 'eww-auto-rename-buffer)
(setq-local eww-auto-rename-buffer nil))
(advice-add 'shr-expand-url :filter-return #'devdocs-browser--eww-fix-url)
(advice-add 'eww-display-html :filter-return #'devdocs-browser--eww-recenter-advice)
(advice-add 'eww-browse-url :filter-args #'devdocs-browser--eww-browse-url-new-window-advice)
(add-hook 'eldoc-documentation-functions #'devdocs-browser--eww-link-eldoc nil t)
(eldoc-mode))
(defvar devdocs-browser--docs-dir "docs")
(defvar devdocs-browser--index-json-filename "index.json")
(defvar devdocs-browser--metadata-filename "metadata.el")
(defvar devdocs-browser--offline-data-json-filename "content.json")
(defvar devdocs-browser--offline-data-dir-name "content")
(defun devdocs-browser--completing-read (prompt collection &optional def)
"Helper function for `completing-read'.
PROMPT: same meaning, but this function will append ';' at the end;
COLLECTION: alist or hashtable of (name . props), where props is a plist with
possibly the following keys: :value, :annotation, :group;
if :group is not nil and name starts with '<group>: ', its removed.
DEF: same meaning;"
;; convert collection to hashtables for faster completion. `complete-with-action' also supports that.
(let* (collection-ht
(annotation-function
(lambda (s)
(let ((annotation (plist-get (gethash s collection-ht) :annotation)))
(if annotation
(concat " " annotation)
nil))))
(group-function
(lambda (s transform)
(let ((group (plist-get (gethash s collection-ht) :group)))
(cond
(transform (if (and group (string-match (rx bos (literal group) ": ") s))
(replace-match "" t t s)
s))
(t group))))))
(if (hash-table-p collection)
(setq collection-ht collection)
(setq collection-ht (make-hash-table :test 'equal :size (length collection)))
(mapc (lambda (elem)
(when elem
(puthash (car elem) (cdr elem) collection-ht)))
collection))
(setq prompt (concat prompt
(when def
(format " (default %s)" (funcall group-function def t)))
": "))
(let ((res (completing-read
prompt
(lambda (str pred action)
(if (eq action 'metadata)
`(metadata . ((annotation-function . ,annotation-function)
(group-function . ,group-function)))
(complete-with-action action collection-ht str pred)))
nil t ;; require-match
nil nil def)))
(or (plist-get (gethash res collection-ht) :value)
res))))
(defun devdocs-browser--json-parse-buffer ()
"Same as `json-parse-buffer', with custom settings."
(json-parse-buffer :object-type 'plist :array-type 'array))
(defun devdocs-browser--read-json (file-path)
"Read json file in FILE-PATH, if it's a relative path, find it in cache dir."
(let ((filename (expand-file-name file-path devdocs-browser-data-directory)))
(when (file-exists-p filename)
(with-temp-buffer
(insert-file-contents filename)
(devdocs-browser--json-parse-buffer)))))
(defun devdocs-browser--fetch-json (url-path file-path &optional base-url)
"Fetch json from BASE-URL / URL-PATH, also save to FILE-PATH.
BASE-URL defaults to `devdocs-browser-base-url'."
(let ((cache-filename (expand-file-name file-path devdocs-browser-data-directory)))
(unless (file-exists-p (file-name-directory cache-filename))
(make-directory (file-name-directory cache-filename) t))
(with-temp-file cache-filename
(erase-buffer)
(url-insert-file-contents (concat (or base-url devdocs-browser-base-url) url-path))
(devdocs-browser--json-parse-buffer))))
(defvar devdocs-browser--docs-list-cache nil "Cached docs list.")
;;;###autoload
(defun devdocs-browser-list-docs (&optional refresh-cache)
"Get doc metadata lists, reload cache if REFRESH-CACHE."
(setq devdocs-browser--docs-list-cache
(or (and (not refresh-cache) devdocs-browser--docs-list-cache)
(and (not refresh-cache) (devdocs-browser--read-json "docs.json"))
(devdocs-browser--fetch-json "docs.json" "docs.json"))))
;;;###autoload
(defun devdocs-browser-update-metadata ()
"Update doc metadata list.
To upgrade docs content, see `devdocs-browser-upgrade-doc'."
(interactive)
(let ((count (length (devdocs-browser-list-docs t))))
(message (concat "Doc metadata updated, found total %s docs. "
"You may want to run `devdocs-browser-install-doc' "
"or `devdocs-browser-upgrade-doc'.")
count)))
;;;###autoload
(defalias 'devdocs-browser-update-docs 'devdocs-browser-update-metadata)
(make-obsolete 'devdocs-browser-update-docs 'devdocs-browser-update-metadata "20231231")
(defun devdocs-browser-find-doc (slug-or-name)
"Find doc from docs list by SLUG-OR-NAME."
(let ((docs-list (devdocs-browser-list-docs)))
(seq-find (lambda (doc)
(or (equal (plist-get doc :slug) slug-or-name)
(equal (plist-get doc :name) slug-or-name)))
docs-list)))
(defcustom devdocs-browser-enable-cache t
"Whether cache doc indices in memory."
:type 'boolean
:group 'devdocs-browser)
(defvar devdocs-browser--docs-cache '() "Cached doc indices plist.")
(defun devdocs-browser--install-doc-internal (doc)
"(Re-)install doc identified by plist DOC. Return t if success."
(let* ((slug (plist-get doc :slug))
(mtime (plist-get doc :mtime))
(docs-dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-data-directory))
(doc-dir (expand-file-name slug docs-dir))
success)
(unless (file-exists-p docs-dir)
(make-directory docs-dir t))
(when (file-exists-p doc-dir)
(delete-directory doc-dir t))
;; do not leave empty directory
(unwind-protect
(progn
(devdocs-browser--fetch-json
(format "docs/%s/index.json?%s" slug mtime)
(expand-file-name devdocs-browser--index-json-filename doc-dir))
(with-temp-file (expand-file-name devdocs-browser--metadata-filename doc-dir)
(print doc (current-buffer)))
(setq success t))
(unless success
(delete-directory doc-dir t)))
(if success
(message "Installed devdocs doc %s version %s" slug mtime)
(message "Failed to install devdocs doc %s" slug))
;; remove cache
(setq devdocs-browser--docs-cache
(plist-put devdocs-browser--docs-cache slug nil #'equal))
success))
(defun devdocs-browser--doc-readable-name (doc)
"Get human readable name for DOC."
(let ((slug (plist-get doc :slug))
(name (plist-get doc :name))
(version (plist-get doc :version))
(release (plist-get doc :release))
res)
(setq res (concat slug " (" name))
(unless (zerop (length version))
(setq res (concat res " " version)))
(unless (zerop (length release))
(setq res (concat res ", " release)))
(setq res (concat res ")"))
res))
;;;###autoload
(defun devdocs-browser-install-doc (slug-or-name &optional force)
"Install doc by SLUG-OR-NAME.
When called interactively, user can choose from the list.
When called interactively with prefix, or FORCE is t, reinstall existing doc."
(interactive
(let* ((force current-prefix-arg)
(installed-docs
(devdocs-browser-list-installed-slugs))
(selected-slug
(devdocs-browser--completing-read
"Install doc"
(mapcar (lambda (doc)
(let ((slug (plist-get doc :slug)))
(unless (and (not force)
(member slug installed-docs))
(cons (devdocs-browser--doc-readable-name doc)
`(:value ,slug)))))
(devdocs-browser-list-docs)))))
(list selected-slug force)))
(let ((doc (devdocs-browser-find-doc slug-or-name)))
(unless (and (not force)
(member (plist-get doc :slug) (devdocs-browser-list-installed-slugs)))
(devdocs-browser--install-doc-internal doc))))
;;;###autoload
(defun devdocs-browser-uninstall-doc (slug)
"Uninstall doc by SLUG.
When called interactively, user can choose from the list."
(interactive (list (completing-read "Uninstall doc: "
(devdocs-browser-list-installed-slugs)
nil t)))
(let* ((docs-dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-data-directory))
(doc-dir (expand-file-name slug docs-dir)))
(when (file-exists-p doc-dir)
(delete-directory doc-dir t)))
(setq devdocs-browser--docs-cache
(plist-put devdocs-browser--docs-cache slug nil #'equal)))
(defun devdocs-browser--upgrade-readable-name (old-doc new-doc)
"Get human readable name for upgrade from OLD-DOC to NEW-DOC."
(let ((slug (plist-get old-doc :slug))
(name (plist-get old-doc :name))
(old-version (plist-get old-doc :version))
(old-release (plist-get old-doc :release))
(old-mtime (plist-get old-doc :mtime))
(new-version (plist-get new-doc :version))
(new-release (plist-get new-doc :release))
(new-mtime (plist-get new-doc :mtime))
res)
(setq res (format "%s (%s" slug name))
(unless (equal old-version new-version)
(setq res (concat res (format " %s->%s" old-version new-version))))
(unless (equal old-release new-release)
(setq res (concat res (format ", %s->%s" old-release new-release))))
(setq res (concat res (format ", %s->%s)" old-mtime new-mtime)))
res))
(defun devdocs-browser--upgrade-readable-name-or-nil (slug)
"Get human readable name for upgrading SLUG if it needs upgrade."
(let ((old-doc (devdocs-browser--load-doc slug))
(new-doc (devdocs-browser-find-doc slug)))
(when (and new-doc
(> (plist-get new-doc :mtime) (plist-get old-doc :mtime)))
(devdocs-browser--upgrade-readable-name old-doc new-doc))))
;;;###autoload
(defun devdocs-browser-upgrade-doc (slug)
"Upgrade doc by SLUG, return t if upgrade success.
Also download new version of offline data if
there's offline data for current version.
When called interactively, user can choose from list.
You may need to call `devdocs-browser-update-docs' first."
(interactive
(let (rows)
(dolist (slug (devdocs-browser-list-installed-slugs))
(let ((desc (devdocs-browser--upgrade-readable-name-or-nil slug)))
(when desc
(push (cons desc slug) rows))))
(if (null rows)
(progn
(message "All docs up to date")
(list nil))
(list
(cdr (assoc (completing-read "Upgrade doc: " rows nil t) rows))))))
(when (and slug (devdocs-browser--upgrade-readable-name-or-nil slug))
(let* ((has-offline-data (devdocs-browser-offline-data-dir slug))
(doc (devdocs-browser-find-doc slug))
(install-success (devdocs-browser--install-doc-internal doc)))
(when (and has-offline-data install-success)
(devdocs-browser--download-offline-data-internal doc))
install-success)))
;;;###autoload
(defun devdocs-browser-upgrade-all-docs ()
"Upgrade all docs."
(interactive)
(let ((count 0))
(dolist (slug (devdocs-browser-list-installed-slugs))
(message "Processing %s..." slug)
(when (devdocs-browser-upgrade-doc slug)
(setq count (1+ count))))
(message "Upgraded %s docs" count)))
(defun devdocs-browser-list-installed-slugs ()
"Get a list of installed docs' slug name."
(let ((dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-data-directory)))
(when (file-exists-p dir)
(directory-files dir nil
;; ignore ".", ".." and hidden files
"^[^.].*"))))
(defun devdocs-browser-find-installed-doc (slug-or-name)
"Find installed doc by SLUG-OR-NAME."
(let ((docs-list (mapcar #'devdocs-browser-installed-doc-info
(devdocs-browser-list-installed-slugs))))
(seq-find (lambda (doc)
(or (equal (plist-get doc :slug) slug-or-name)
(equal (plist-get doc :name) slug-or-name)))
docs-list)))
(defun devdocs-browser-installed-doc-info (slug)
"Get plist info of installed doc identified by SLUG."
(cddr (devdocs-browser--load-doc slug)))
(defun devdocs-browser--load-doc (slug &optional refresh-cache)
"Load doc identified by SLUG, reload cache if REFRESH-CACHE is not nil.
Result is a plist metadata, with an extra :index field at the beginning."
(or (and (not refresh-cache) (plist-get devdocs-browser--docs-cache slug #'equal))
(let* ((docs-dir (expand-file-name devdocs-browser--docs-dir
devdocs-browser-data-directory))
(doc-dir (expand-file-name slug docs-dir))
(metadata-filename (expand-file-name devdocs-browser--metadata-filename doc-dir))
(metadata nil)
(index-filename (expand-file-name devdocs-browser--index-json-filename doc-dir))
(index (devdocs-browser--read-json index-filename))
res)
(when (file-exists-p metadata-filename)
(with-temp-buffer
(insert-file-contents metadata-filename)
(setq metadata (read (current-buffer))))
(setq res (append `(:index ,index) metadata))
(when devdocs-browser-enable-cache
(setq devdocs-browser--docs-cache
(plist-put devdocs-browser--docs-cache slug res #'equal))))
res)))
(defun devdocs-browser--download-offline-data-internal (doc)
"(re-)Download and extract offline data for DOC."
(let* ((slug (plist-get doc :slug))
(mtime (plist-get doc :mtime))
(docs-dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-data-directory))
(doc-dir (expand-file-name slug docs-dir))
(data-dir (expand-file-name devdocs-browser--offline-data-dir-name doc-dir))
success)
(unless (file-exists-p doc-dir)
(make-directory doc-dir t))
(when (file-exists-p data-dir)
(delete-directory data-dir t))
;; do not leave half-complete data directory
(unwind-protect
(let ((data (devdocs-browser--fetch-json
(format "%s/db.json?%s" slug mtime)
(expand-file-name devdocs-browser--offline-data-json-filename doc-dir)
devdocs-browser-doc-base-url)))
;; write data to files
(dolist (kv (seq-partition data 2))
(when-let* ((name (substring (symbol-name (car kv)) 1))
(value (cadr kv))
;; prepent "./" to fix paths starting with literal "~" (e.g. deno)
(path (expand-file-name (concat "./" name ".html") data-dir)))
(unless (file-exists-p (file-name-directory path))
(make-directory (file-name-directory path) t))
(write-region value nil path)))
(setq success t))
(unless success
(delete-directory data-dir t)))
(if success
(message "Installed devdocs offline data %s version %s" slug mtime)
(message "Failed to install devdocs offline data %s" slug))
success))
(defun devdocs-browser-offline-data-dir (slug)
"Return doc SLUG's offline data dir if present, return nil otherwise."
(let* ((docs-dir (expand-file-name devdocs-browser--docs-dir devdocs-browser-data-directory))
(doc-dir (expand-file-name slug docs-dir))
(data-dir (expand-file-name devdocs-browser--offline-data-dir-name doc-dir)))
(when (file-exists-p data-dir)
(file-name-as-directory data-dir))))
(defun devdocs-browser-download-offline-data (slug)
"Download offline data for doc SLUG.
Offline data contains full content pages,
which allows you to view docs without Internet connection.
It may take some time to download offline data.
When called interactively, user can choose from the list."
(interactive (list (completing-read
"Install offline data: "
(seq-filter
(lambda (slug) (null (devdocs-browser-offline-data-dir slug)))
(devdocs-browser-list-installed-slugs))
nil t)))
(when-let* ((doc (devdocs-browser--load-doc slug)))
(devdocs-browser--download-offline-data-internal doc)))
(defun devdocs-browser-remove-offline-data (slug)
"Remove offline data for doc SLUG.
When called interactively, user can choose from the list."
(interactive (list (completing-read
"Remove offline data: "
(seq-filter
#'devdocs-browser-offline-data-dir
(devdocs-browser-list-installed-slugs))
nil t)))
(when-let* ((data-dir (devdocs-browser-offline-data-dir slug)))
(delete-directory data-dir t)))
(defun devdocs-browser--eww-open (doc path)
"Open PATH for document DOC using eww."
(let* ((slug (plist-get doc :slug))
(mtime (plist-get doc :mtime))
base-url url)
;; cannot use format directly because `path' may contains #query
;; path: hello/world#query
;; url for offline: file:///home/path/to/devdocs/python~3.8/hello/world.html#query
;; url for online: https://documents.devdocs.io/python~3.8/hello/world.html?161818817#query
(let ((offline-data-dir (devdocs-browser-offline-data-dir slug)))
(if offline-data-dir
(progn
(setq base-url (concat "file://" offline-data-dir))
(setq url (url-generic-parse-url (concat "file://" offline-data-dir path)))
(setf (url-filename url) (concat (url-filename url) ".html")))
(setq base-url (concat devdocs-browser-doc-base-url slug "/"))
(setq url (url-generic-parse-url
(concat devdocs-browser-doc-base-url slug "/" path)))
(setf (url-filename url)
(format "%s.html?%s" (url-filename url) mtime))))
(pop-to-buffer (format "*devdocs-%s*" slug))
(if devdocs-browser-eww-mode
(eww-save-history)
(eww-mode)
(devdocs-browser-eww-mode))
(setq-local devdocs-browser--eww-data
(list :doc doc
:base-url base-url))
(eww (url-recreate-url url))
(recenter)))
(defun devdocs-browser--default-active-slugs (&optional no-fallback-all)
"Default active doc slugs for current buffer, fallback to all slugs if not NO-FALLBACK-ALL."
(if devdocs-browser--eww-data
(list (plist-get (plist-get devdocs-browser--eww-data :doc) :slug))
(let ((names (or devdocs-browser-active-docs
(alist-get major-mode devdocs-browser-major-mode-docs-alist)))
slugs)
(dolist (name names)
(when-let* ((doc (devdocs-browser-find-installed-doc name))
(slug (plist-get doc :slug)))
(setq slugs (push slug slugs))))
(or slugs
(and (not no-fallback-all) (devdocs-browser-list-installed-slugs))))))
;;;###autoload
(defun devdocs-browser-open-in (slug-or-name-list)
"Open entry in specified docs SLUG-OR-NAME-LIST.
When called interactively, user can choose from the list."
(interactive
(let ((def (devdocs-browser--default-active-slugs t)))
(list (completing-read-multiple
(concat "Select doc"
(when def (format " (default %s)" def))
": ")
(devdocs-browser-list-installed-slugs)
nil t nil nil def))))
(let ((current-word-regex
(when-let ((word (thing-at-point 'word t)))
(concat "\\<" (regexp-quote word) "\\>")))
(rows (make-hash-table :test 'equal))
slugs def)
(dolist (slug-or-name slug-or-name-list)
(when-let* ((doc-simple (devdocs-browser-find-installed-doc slug-or-name))
(slug (plist-get doc-simple :slug))
(doc (devdocs-browser--load-doc slug))
(index (plist-get doc :index))
(entries (plist-get index :entries)))
(setq slugs (push slug slugs))
(puthash (format "%s: INDEX PAGE" slug)
`(:value (,doc "index")
:group ,slug)
rows)
(seq-doseq (entry entries)
(let* ((name (plist-get entry :name))
(path (plist-get entry :path))
(type (plist-get entry :type))
(title (concat slug ": " name)))
(when (and (null def) current-word-regex)
(when (string-match-p current-word-regex name)
(setq def title)))
(puthash title `(:value (,doc ,path)
:group ,slug
:annotation ,type)
rows)))))
(let* ((selected-value
(devdocs-browser--completing-read
(format "Devdocs browser [%s]" (mapconcat #'identity slugs ","))
rows def)))
(when selected-value
(apply #'devdocs-browser--eww-open selected-value)))))
(defcustom devdocs-browser-open-fallback-to-all-docs t
"When not sure which docs to use, whether `devdocs-browser-open' should use all installed docs, or just ask the user to pick one (like `devdocs-browser-open-in')."
:type 'boolean
:group 'devdocs-browser)
;;;###autoload
(defun devdocs-browser-open ()
"Open entry in active docs.
Active docs are specified by `devdocs-browser-active-docs',
or `devdocs-browser-major-mode-docs-alist',
or the current doc type if called in a devdocs eww buffer.
When all of them are nil, all installed docs are used."
(interactive)
(if devdocs-browser-open-fallback-to-all-docs
(devdocs-browser-open-in (devdocs-browser--default-active-slugs))
(let ((slugs (devdocs-browser--default-active-slugs 'no-fallback-all)))
(if slugs
(devdocs-browser-open-in slugs)
(call-interactively 'devdocs-browser-open-in)))))
(provide 'devdocs-browser)
;;; devdocs-browser.el ends here

View file

@ -0,0 +1,23 @@
;;; dired-hacks-utils-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "dired-hacks-utils" "dired-hacks-utils.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from dired-hacks-utils.el
(register-definition-prefixes "dired-hacks-utils" '("dired-"))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; dired-hacks-utils-autoloads.el ends here

View file

@ -0,0 +1,2 @@
;;; Generated package description from dired-hacks-utils.el -*- no-byte-compile: t -*-
(define-package "dired-hacks-utils" "20221127.1247" "Utilities and helpers for dired-hacks collection" '((dash "2.5.0")) :commit "41d3eb42195d9f0894c20d18cc8e722b099aa1c1" :authors '(("Matúš Goljer" . "matus.goljer@gmail.com")) :maintainer '("Matúš Goljer" . "matus.goljer@gmail.com") :keywords '("files"))

View file

@ -0,0 +1,275 @@
;;; dired-hacks-utils.el --- Utilities and helpers for dired-hacks collection
;; Copyright (C) 2014-2015 Matúš Goljer
;; Author: Matúš Goljer <matus.goljer@gmail.com>
;; Maintainer: Matúš Goljer <matus.goljer@gmail.com>
;; Keywords: files
;; Package-Version: 20221127.1247
;; Package-Commit: 41d3eb42195d9f0894c20d18cc8e722b099aa1c1
;; Version: 0.0.1
;; Created: 14th February 2014
;; Package-Requires: ((dash "2.5.0"))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Utilities and helpers for `dired-hacks' collection of dired
;; improvements.
;; This package also provides these interactive functions:
;; * `dired-hacks-next-file' - go to next file, skipping empty and non-file lines
;; * `dired-hacks-previous-file' - go to previous file, skipping empty
;; and non-file lines
;; * `dired-utils-format-information-line-mode' - Format the information
;; (summary) line file sizes to be human readable (e.g. 1GB instead of 1048576).
;; See https://github.com/Fuco1/dired-hacks for the entire collection
;;; Code:
(require 'dash)
(require 'dired)
(defgroup dired-hacks ()
"Collection of useful dired additions."
:group 'dired
:prefix "dired-hacks-")
(defcustom dired-hacks-file-size-formatter 'file-size-human-readable
"The function used to format file sizes.
See `dired-utils-format-file-sizes'."
:type 'symbol
:group 'dired-hacks)
(defcustom dired-hacks-datetime-regexp
"\\sw\\sw\\sw....\\(?:[0-9][0-9]:[0-9][0-9]\\|.[0-9]\\{4\\}\\)"
"A regexp matching the date/time in the dired listing.
It is used to determine where the filename starts. It should
*not* match any characters after the last character of the
timestamp. It is assumed that the timestamp is preceded and
followed by at least one space character. You should only use
shy groups (prefixed with ?:) because the first group is used by
the font-lock to determine what portion of the name should be
colored."
:type 'string
:group 'dired-hacks)
(defalias 'dired-utils--string-trim
(if (and (require 'subr-x nil t)
(fboundp 'string-trim))
#'string-trim
(lambda (string)
(let ((s string))
(when (string-match "\\`[ \t\n\r]+" s)
(setq s (replace-match "" t t s)))
(when (string-match "[ \t\n\r]+\\'" s)
(setq s (replace-match "" t t s)))
s)))
"Trim STRING of trailing whitespace.
\(fn STRING)")
(defun dired-utils-get-filename (&optional localp)
"Like `dired-get-filename' but never signal an error.
Optional arg LOCALP with value `no-dir' means don't include
directory name in result."
(dired-get-filename localp t))
(defun dired-utils-get-all-files (&optional localp)
"Return all files in this dired buffer as a list.
LOCALP has same semantics as in `dired-get-filename'."
(save-excursion
(goto-char (point-min))
(let (r)
(while (= 0 (forward-line))
(--when-let (dired-utils-get-filename localp)
(push it r)))
(nreverse r))))
(defconst dired-utils-file-attributes-keywords
'(:isdir :nlinks :uid :gid :atime :mtime :ctime :size :modes :gidchg :inode :devnum)
"List of keywords to map with `file-attributes'.")
(defconst dired-utils-info-keywords
`(:name :issym :target ,@dired-utils-file-attributes-keywords)
"List of keywords available for `dired-utils-get-info'.")
(defun dired-utils--get-keyword-info (keyword)
"Get file information about KEYWORD."
(let ((filename (dired-utils-get-filename)))
(cl-case keyword
(:name filename)
(:isdir (file-directory-p filename))
(:issym (and (file-symlink-p filename) t))
(:target (file-symlink-p filename))
(t
(nth (-elem-index keyword dired-utils-file-attributes-keywords)
(file-attributes filename))))))
(defun dired-utils-get-info (&rest keywords)
"Query for info about the file at point.
KEYWORDS is a list of attributes to query.
When querying for one attribute, its value is returned. When
querying for more than one, a list of results is returned.
The available keywords are listed in
`dired-utils-info-keywords'."
(let ((attributes (mapcar 'dired-utils--get-keyword-info keywords)))
(if (> (length attributes) 1)
attributes
(car attributes))))
(defun dired-utils-goto-line (filename)
"Go to line describing FILENAME in listing.
Should be absolute file name matched against
`dired-get-filename'."
(goto-char (point-min))
(let (stop)
(while (and (not stop)
(= (forward-line) 0))
(when (equal filename (dired-utils-get-filename))
(setq stop t)
(dired-move-to-filename)))
stop))
(defun dired-utils-match-filename-regexp (filename alist)
"Match FILENAME against each car in ALIST and return first matched cons.
Each car in ALIST is a regular expression.
The matching is done using `string-match-p'."
(let (match)
(--each-while alist (not match)
(when (string-match-p (car it) filename)
(setq match it)))
match))
(defun dired-utils-match-filename-extension (filename alist)
"Match FILENAME against each car in ALIST and return first matched cons.
Each car in ALIST is a string representing file extension
*without* the delimiting dot."
(let (done)
(--each-while alist (not done)
(when (string-match-p (concat "\\." (regexp-quote (car it)) "\\'") filename)
(setq done it)))
done))
(defun dired-utils-format-information-line ()
"Format the disk space on the Dired information line."
(save-excursion
(goto-char (point-min))
(forward-line)
(let ((inhibit-read-only t)
(limit (line-end-position)))
(while (re-search-forward "\\(?:directory\\|available\\) \\(\\<[0-9]+$\\>\\)" nil t)
(replace-match
(save-match-data
(propertize (dired-utils--string-trim
(funcall dired-hacks-file-size-formatter
(* 1024 (string-to-number (match-string 1))) t))
'invisible 'dired-hide-details-information))
t nil nil 1)))))
;;; Predicates
(defun dired-utils-is-file-p ()
"Return non-nil if the line at point is a file or a directory."
(dired-utils-get-filename 'no-dir))
(defun dired-utils-is-dir-p ()
"Return non-nil if the line at point is a directory."
(--when-let (dired-utils-get-filename)
(file-directory-p it)))
;;; Interactive
;; TODO: add wrap-around option
(defun dired-hacks-next-file (&optional arg)
"Move point to the next file.
Optional prefix ARG says how many lines to move; default is one
line."
(interactive "p")
(unless arg (setq arg 1))
(if (< arg 0)
(dired-hacks-previous-file (- arg))
(--dotimes arg
(forward-line)
(while (and (or (not (dired-utils-is-file-p))
(get-text-property (point) 'invisible))
(= (forward-line) 0))))
(if (not (= (point) (point-max)))
(dired-move-to-filename)
(forward-line -1)
(dired-move-to-filename)
nil)))
(defun dired-hacks-previous-file (&optional arg)
"Move point to the previous file.
Optional prefix ARG says how many lines to move; default is one
line."
(interactive "p")
(unless arg (setq arg 1))
(if (< arg 0)
(dired-hacks-next-file (- arg))
(--dotimes arg
(forward-line -1)
(while (and (or (not (dired-utils-is-file-p))
(get-text-property (point) 'invisible))
(= (forward-line -1) 0))))
(if (not (= (point) (point-min)))
(dired-move-to-filename)
(dired-hacks-next-file)
nil)))
(defun dired-hacks-compare-files (file-a file-b)
"Test if two files FILE-A and FILE-B are the (probably) the same."
(interactive (let ((other-dir (dired-dwim-target-directory)))
(list (read-file-name "File A: " default-directory (car (dired-get-marked-files)) t)
(read-file-name "File B: " other-dir (with-current-buffer (cdr (assoc other-dir dired-buffers))
(car (dired-get-marked-files))) t))))
(let ((md5-a (with-temp-buffer
(shell-command (format "md5sum %s" file-a) (current-buffer))
(buffer-string)))
(md5-b (with-temp-buffer
(shell-command (format "md5sum %s" file-b) (current-buffer))
(buffer-string))))
(message "%s%sFiles are %s." md5-a md5-b
(if (equal (car (split-string md5-a))
(car (split-string md5-b)))
"probably the same" "different"))))
(define-minor-mode dired-utils-format-information-line-mode
"Toggle formatting of disk space in the Dired information line."
:group 'dired-utils
:lighter ""
(if dired-utils-format-information-line-mode
(add-hook 'dired-after-readin-hook #'dired-utils-format-information-line)
(remove-hook 'dired-after-readin-hook #'dired-utils-format-information-line)))
(provide 'dired-hacks-utils)
;;; dired-hacks-utils.el ends here

View file

@ -0,0 +1,27 @@
;;; dired-hacks-utils-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "dired-hacks-utils" "dired-hacks-utils.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from dired-hacks-utils.el
(register-definition-prefixes "dired-hacks-utils" '("dired-"))
;;;***
;;;### (autoloads nil nil ("dired-hacks-utils-pkg.el") (0 0 0 0))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; dired-hacks-utils-autoloads.el ends here

View file

@ -1,15 +1,13 @@
(define-package "dired-hacks-utils" "20240629.1906" "Utilities and helpers for dired-hacks collection"
'((dash "2.5.0")
(emacs "24.3"))
:commit "63b04d17936c98cb4ad7ce6bc3331cda8e30c55a" :authors
(define-package "dired-hacks-utils" "20230512.1107" "Utilities and helpers for dired-hacks collection"
'((dash "2.5.0"))
:commit "523f51b4152a3bf4e60fe57f512732c698b5c96f" :authors
'(("Matúš Goljer" . "matus.goljer@gmail.com"))
:maintainers
'(("Matúš Goljer" . "matus.goljer@gmail.com"))
:maintainer
'("Matúš Goljer" . "matus.goljer@gmail.com")
:keywords
'("files")
:url "https://github.com/Fuco1/dired-hacks")
'("files"))
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -7,8 +7,7 @@
;; Keywords: files
;; Version: 0.0.1
;; Created: 14th February 2014
;; Package-Requires: ((dash "2.5.0") (emacs "24.3"))
;; URL: https://github.com/Fuco1/dired-hacks
;; Package-Requires: ((dash "2.5.0"))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
@ -42,7 +41,6 @@
(require 'dash)
(require 'dired)
(require 'dired-aux) ;; for dired-dwim-target-directory
(defgroup dired-hacks ()
"Collection of useful dired additions."

View file

@ -1,28 +0,0 @@
;;; dired-hacks-utils-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from dired-hacks-utils.el
(register-definition-prefixes "dired-hacks-utils" '("dired-"))
;;; End of scraped data
(provide 'dired-hacks-utils-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; dired-hacks-utils-autoloads.el ends here

View file

@ -0,0 +1,22 @@
;;; dired-rainbow-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "dired-rainbow" "dired-rainbow.el" (0 0 0 0))
;;; Generated autoloads from dired-rainbow.el
(register-definition-prefixes "dired-rainbow" '("dired-rainbow-"))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; dired-rainbow-autoloads.el ends here

View file

@ -0,0 +1,2 @@
;;; Generated package description from dired-rainbow.el -*- no-byte-compile: t -*-
(define-package "dired-rainbow" "20221127.1247" "Extended file highlighting according to its type" '((dash "2.5.0") (dired-hacks-utils "0.0.1")) :commit "41d3eb42195d9f0894c20d18cc8e722b099aa1c1" :authors '(("Matus Goljer" . "matus.goljer@gmail.com")) :maintainer '("Matus Goljer" . "matus.goljer@gmail.com") :keywords '("files"))

View file

@ -5,10 +5,12 @@
;; Author: Matus Goljer <matus.goljer@gmail.com>
;; Maintainer: Matus Goljer <matus.goljer@gmail.com>
;; Keywords: files
;; Package-Commit: 41d3eb42195d9f0894c20d18cc8e722b099aa1c1
;; Package-Version: 20221127.1247
;; Package-X-Original-Version: 20170922.817
;; Version: 0.0.3
;; Created: 16th February 2014
;; Package-Requires: ((dash "2.5.0") (dired-hacks-utils "0.0.1") (emacs "24"))
;; URL: https://github.com/Fuco1/dired-hacks
;; Package-Requires: ((dash "2.5.0") (dired-hacks-utils "0.0.1"))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by

View file

@ -1,28 +0,0 @@
;;; dired-rainbow-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from dired-rainbow.el
(register-definition-prefixes "dired-rainbow" '("dired-rainbow-"))
;;; End of scraped data
(provide 'dired-rainbow-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; dired-rainbow-autoloads.el ends here

View file

@ -1,16 +0,0 @@
(define-package "dired-rainbow" "20240629.1857" "Extended file highlighting according to its type"
'((dash "2.5.0")
(dired-hacks-utils "0.0.1")
(emacs "24"))
:commit "d1a85901c892ba7ec273995070a43cbbbe5d0b37" :authors
'(("Matus Goljer" . "matus.goljer@gmail.com"))
:maintainers
'(("Matus Goljer" . "matus.goljer@gmail.com"))
:maintainer
'("Matus Goljer" . "matus.goljer@gmail.com")
:keywords
'("files")
:url "https://github.com/Fuco1/dired-hacks")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -0,0 +1,9 @@
(define-package "dockerfile-mode" "20240318.24" "Major mode for editing Docker's Dockerfiles"
'((emacs "24")
(s "1.2.0"))
:commit "f6196726342b44081933597a343805db6366e7ac" :keywords
'("docker" "languages" "processes" "tools")
:url "https://github.com/spotify/dockerfile-mode")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -1,7 +1,7 @@
;;; dockerfile-mode.el --- Major mode for editing Docker's Dockerfiles -*- lexical-binding: t -*-
;; Copyright (c) 2013 Spotify AB
;; Package-Requires: ((emacs "24"))
;; Package-Requires: ((emacs "24") (s "1.2.0"))
;; Homepage: https://github.com/spotify/dockerfile-mode
;; URL: https://github.com/spotify/dockerfile-mode
;; Version: 1.7
@ -29,6 +29,7 @@
(require 'sh-script)
(require 'rx)
(require 's)
(declare-function cygwin-convert-file-name-to-windows "cygw32.c" (file &optional absolute-p))
@ -165,7 +166,7 @@ by `dockerfile-enable-auto-indent'."
(defun dockerfile-build-arg-string ()
"Create a --build-arg string for each element in `dockerfile-build-args'."
(mapconcat (lambda (arg) (concat "--build-arg=" (replace-regexp-in-string "\\\\=" "=" (shell-quote-argument arg))))
(mapconcat (lambda (arg) (concat "--build-arg=" (s-replace "\\=" "=" (shell-quote-argument arg))))
dockerfile-build-args " "))
(defun dockerfile-standard-filename (file)

View file

@ -1,8 +0,0 @@
(define-package "dockerfile-mode" "20240324.1010" "Major mode for editing Docker's Dockerfiles"
'((emacs "24"))
:commit "39a012a27fcf6fb629c447d13b6974baf906714c" :keywords
'("docker" "languages" "processes" "tools")
:url "https://github.com/spotify/dockerfile-mode")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -1,68 +0,0 @@
;;; focus-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from focus.el
(autoload 'focus-mode "focus" "\
Dim the font color of text in surrounding sections.
This is a minor mode. If called interactively, toggle the `Focus
mode' mode. If the prefix argument is positive, enable the mode,
and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate `focus-mode'.
The mode's hook is called both when the mode is enabled and when
it is disabled.
\\{focus-mode-map}
(fn &optional ARG)" t)
(autoload 'focus-read-only-mode "focus" "\
A read-only mode optimized for `focus-mode'.
This is a minor mode. If called interactively, toggle the
`Focus-Read-Only mode' mode. If the prefix argument is positive,
enable the mode, and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate `focus-read-only-mode'.
The mode's hook is called both when the mode is enabled and when
it is disabled.
\\{focus-read-only-mode-map}
(fn &optional ARG)" t)
(register-definition-prefixes "focus" '("focus-"))
;;; End of scraped data
(provide 'focus-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; focus-autoloads.el ends here

View file

@ -1,13 +0,0 @@
(define-package "focus" "20240528.901" "Dim the font color of text in surrounding sections"
'((emacs "24.3")
(cl-lib "0.5"))
:commit "17c471544f540f2cf9a05fd6cd87e52e5de317e2" :authors
'(("Lars Tveito" . "larstvei@ifi.uio.no"))
:maintainers
'(("Lars Tveito" . "larstvei@ifi.uio.no"))
:maintainer
'("Lars Tveito" . "larstvei@ifi.uio.no")
:url "http://github.com/larstvei/Focus")
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -1,329 +0,0 @@
;;; focus.el --- Dim the font color of text in surrounding sections -*- lexical-binding: t; -*-
;; Copyright (C) 2015 Lars Tveito
;; Author: Lars Tveito <larstvei@ifi.uio.no>
;; URL: http://github.com/larstvei/Focus
;; Created: 11th May 2015
;; Version: 1.0.1
;; Package-Requires: ((emacs "24.3") (cl-lib "0.5"))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Focus provides `focus-mode` that dims the text of surrounding sections,
;; similar to [iA Writer's](https://ia.net/writer) Focus Mode.
;;
;; Enable the mode with `M-x focus-mode'.
;;; Code:
(require 'cl-lib)
(require 'thingatpt)
(defgroup focus ()
"Dim the font color of text in surrounding sections."
:group 'font-lock
:prefix "focus-")
(defcustom focus-mode-to-thing '((prog-mode . defun)
(text-mode . paragraph)
(org-mode . org-element))
"An associated list between mode and thing.
A thing is defined in thingatpt.el; the thing determines the
narrowness of the focused section.
Note that the order of the list matters. The first mode that the
current mode is derived from is used, so more modes that have
many derivatives should be placed by the end of the list.
Things that are defined include `symbol', `list', `sexp',
`defun', `filename', `url', `email', `word', `sentence',
`whitespace', `line', and `page'.
In order for changes to take effect, reenable `focus-mode'."
:type '(alist :key-type symbol :valye-type symbol)
:group 'focus)
(defcustom focus-read-only-blink-seconds 1
"The duration of a cursor blink in `focus-read-only-mode'."
:type 'number
:group 'focus)
(defcustom focus-update-idle-delay nil
"Delay (in seconds) before updating the focus after each command.
The default value of nil results in an immediate update.
Increase this value if you experience performance issues."
:type '(choice (const :tag "Immediate update" nil)
(const :tag "Delayed update (0.1s)" 0.1)
(number :tag "Custom delay"))
:group 'focus)
(defface focus-unfocused
'((t :inherit shadow))
"The face that overlays the unfocused area."
:group 'focus)
(defface focus-focused nil
"The face that overlays the focused area."
:group 'focus)
(defvar focus-cursor-type cursor-type
"Used to restore the users `cursor-type'.")
(defvar-local focus-current-thing nil
"Overrides the choice of thing dictated by `focus-mode-to-thing' if set.")
(defvar-local focus-current-thing-cache nil
"Caches the current thing to focus.")
(defvar-local focus-buffer nil
"Local reference to the buffer focus functions operate on.")
(defvar-local focus-pre-overlay nil
"The overlay that dims the text prior to the current-point.")
(defvar-local focus-mid-overlay nil
"The overlay that surrounds the text of the current-point.")
(defvar-local focus-post-overlay nil
"The overlay that dims the text past the current-point.")
(defvar-local focus-read-only-blink-timer nil
"Timer started from `focus-read-only-cursor-blink'.
The timer calls `focus-read-only-hide-cursor' after
`focus-read-only-blink-seconds' seconds.")
(defvar-local focus-update-timer nil
"Timer started from `focus-update'")
(defun focus-get-thing ()
"Return the current thing, based on `focus-mode-to-thing'.
This also sets `focus-current-thing-cache' to the current thing."
(or focus-current-thing
focus-current-thing-cache
(setq focus-current-thing-cache
(let* ((modes (mapcar 'car focus-mode-to-thing))
(mode (or (cl-find major-mode modes)
(apply #'derived-mode-p modes))))
(if mode (cdr (assoc mode focus-mode-to-thing)) 'sentence)))))
(defun focus-bounds ()
"Return the current bounds, based on `focus-get-thing'."
(let ((thing (focus-get-thing)))
(cond ((eq thing 'org-element)
(let* ((elem (org-element-at-point))
(beg (org-element-property :begin elem))
(end (org-element-property :end elem)))
(cons beg end)))
(t (bounds-of-thing-at-point thing)))))
(defun focus-move-focus (buffer)
"Move the focused section according to `focus-bounds'.
If `focus-mode' is enabled, this command fires after each
command."
(with-current-buffer buffer
(setq focus-update-timer nil)
(let* ((bounds (focus-bounds)))
(when bounds
(focus-move-overlays (car bounds) (cdr bounds))))))
(defun focus-update ()
"Trigger an update of the focus.
When `focus-update-idle-delay' is non-nil, start update after the
specified idle delay."
(if focus-update-idle-delay
(unless focus-update-timer
(setq focus-update-timer
(run-with-idle-timer focus-update-idle-delay nil
#'focus-move-focus focus-buffer)))
(focus-move-focus focus-buffer)))
(defun focus-move-overlays (low high)
"Move the overlays to highlight the region between LOW and HIGH."
(move-overlay focus-pre-overlay (point-min) low)
(move-overlay focus-mid-overlay low high)
(move-overlay focus-post-overlay high (point-max)))
(defun focus-init ()
"This function is run when command `focus-mode' is enabled.
It sets the `focus-pre-overlay', `focus-min-overlay', and
`focus-post-overlay' to overlays; these are invisible until
`focus-update' is run. It adds `focus-update' to
`post-command-hook'."
(unless (or focus-pre-overlay focus-post-overlay)
(setq focus-pre-overlay (make-overlay (point-min) (point-min))
focus-mid-overlay (make-overlay (point-min) (point-max))
focus-post-overlay (make-overlay (point-max) (point-max))
focus-buffer (current-buffer))
(overlay-put focus-mid-overlay 'face 'focus-focused)
(mapc (lambda (o) (overlay-put o 'face 'focus-unfocused))
(list focus-pre-overlay focus-post-overlay))
(setq focus-current-thing-cache nil
focus-update-timer nil)
(add-hook 'post-command-hook 'focus-update nil t)
(add-hook 'change-major-mode-hook 'focus-terminate nil t)))
(defun focus-terminate ()
"This function is run when command `focus-mode' is disabled.
The overlays pointed to by `focus-pre-overlay',
`focus-mid-overlay' and `focus-post-overlay' are deleted, and
`focus-update' is removed from `post-command-hook'."
(when (and focus-pre-overlay focus-post-overlay)
(mapc 'delete-overlay
(list focus-pre-overlay focus-mid-overlay focus-post-overlay))
(remove-hook 'post-command-hook 'focus-update t)
(when focus-update-timer
(cancel-timer focus-update-timer))
(setq focus-current-thing-cache nil
focus-update-timer nil
focus-pre-overlay nil
focus-mid-overlay nil
focus-post-overlay nil)))
(defun focus-goto-thing (bounds)
"Move point to the middle of BOUNDS."
(when bounds
(goto-char (/ (+ (car bounds) (cdr bounds)) 2))
(recenter nil)))
(defun focus-change-thing ()
"Adjust the narrowness of the focused section for the current buffer.
The variable `focus-mode-to-thing' dictates the default thing
according to major-mode. If `focus-current-thing' is set, this
default is overwritten. This function simply helps set the
`focus-current-thing'."
(interactive)
(let* ((candidates '(defun line list org-element paragraph sentence sexp symbol word))
(thing (completing-read "Thing: " candidates)))
(setq focus-current-thing (intern thing))))
(defun focus-pin ()
"Pin the focused section to its current location or the region, if active."
(interactive)
(when (bound-and-true-p focus-mode)
(when (region-active-p)
(focus-move-overlays (region-beginning) (region-end)))
(when focus-update-timer
(cancel-timer focus-update-timer))
(setq focus-update-timer nil)
(remove-hook 'post-command-hook 'focus-update t)))
(defun focus-unpin ()
"Unpin the focused section."
(interactive)
(when (bound-and-true-p focus-mode)
(add-hook 'post-command-hook 'focus-update nil t)))
(defun focus-next-thing (&optional n)
"Move the point to the middle of the Nth next thing."
(interactive "p")
(let ((current-bounds (focus-bounds))
(thing (focus-get-thing)))
(forward-thing thing n)
(when (equal current-bounds (focus-bounds))
(forward-thing thing (cl-signum n)))
(focus-goto-thing (focus-bounds))))
(defun focus-prev-thing (&optional n)
"Move the point to the middle of the Nth previous thing."
(interactive "p")
(focus-next-thing (- n)))
(defun focus-read-only-hide-cursor ()
"Hide the cursor.
This function is triggered by the `focus-read-only-blink-timer',
when `focus-read-only-mode' is activated."
(with-current-buffer focus-buffer
(when (and (bound-and-true-p focus-read-only-mode)
(not (null focus-read-only-blink-timer)))
(setq focus-read-only-blink-timer nil)
(setq cursor-type nil))))
(defun focus-read-only-cursor-blink ()
"Make the cursor visible for `focus-read-only-blink-seconds'.
This is added to the `pre-command-hook' when
`focus-read-only-mode' is active."
(with-current-buffer focus-buffer
(when (and (bound-and-true-p focus-read-only-mode)
(not (member last-command '(focus-next-thing focus-prev-thing))))
(when focus-read-only-blink-timer (cancel-timer focus-read-only-blink-timer))
(setq cursor-type focus-cursor-type)
(setq focus-read-only-blink-timer
(run-at-time focus-read-only-blink-seconds nil
'focus-read-only-hide-cursor)))))
(defun focus-read-only-init ()
"Run when `focus-read-only-mode' is activated.
Enables `read-only-mode', hides the cursor and adds
`focus-read-only-cursor-blink' to `pre-command-hook'.
Also `focus-read-only-terminate' is added to the `kill-buffer-hook'."
(read-only-mode 1)
(setq cursor-type nil
focus-buffer (current-buffer))
(add-hook 'pre-command-hook 'focus-read-only-cursor-blink nil t)
(add-hook 'kill-buffer-hook 'focus-read-only-terminate nil t))
(defun focus-read-only-terminate ()
"Run when `focus-read-only-mode' is deactivated.
Disables `read-only-mode' and shows the cursor again.
It cleans up the `focus-read-only-blink-timer' and hooks."
(read-only-mode -1)
(setq cursor-type focus-cursor-type)
(when focus-read-only-blink-timer
(cancel-timer focus-read-only-blink-timer))
(setq focus-read-only-blink-timer nil)
(remove-hook 'pre-command-hook 'focus-read-only-cursor-blink t)
(remove-hook 'kill-buffer-hook 'focus-read-only-terminate t))
(defun focus-turn-off-focus-read-only-mode ()
"Turn off `focus-read-only-mode'."
(interactive)
(focus-read-only-mode -1))
;;;###autoload
(define-minor-mode focus-mode
"Dim the font color of text in surrounding sections."
:init-value nil
:keymap (let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c C-q") 'focus-read-only-mode)
map)
(if focus-mode (focus-init) (focus-terminate)))
;;;###autoload
(define-minor-mode focus-read-only-mode
"A read-only mode optimized for `focus-mode'."
:init-value nil
:keymap (let ((map (make-sparse-keymap)))
(define-key map (kbd "n") 'focus-next-thing)
(define-key map (kbd "SPC") 'focus-next-thing)
(define-key map (kbd "p") 'focus-prev-thing)
(define-key map (kbd "S-SPC") 'focus-prev-thing)
(define-key map (kbd "i") 'focus-turn-off-focus-read-only-mode)
(define-key map (kbd "q") 'focus-turn-off-focus-read-only-mode)
map)
(when cursor-type
(setq focus-cursor-type cursor-type))
(if focus-read-only-mode (focus-read-only-init) (focus-read-only-terminate)))
(provide 'focus)
;;; focus.el ends here

View file

@ -1,115 +0,0 @@
;;; go-mode-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
;; Generated by the `loaddefs-generate' function.
;; This file is part of GNU Emacs.
;;; Code:
(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
;;; Generated autoloads from go-mode.el
(autoload 'go-mode "go-mode" "\
Major mode for editing Go source text.
This mode provides (not just) basic editing capabilities for
working with Go code. It offers almost complete syntax
highlighting, indentation that is almost identical to gofmt and
proper parsing of the buffer content to allow features such as
navigation by function, manipulation of comments or detection of
strings.
In addition to these core features, it offers various features to
help with writing Go code. You can directly run buffer content
through gofmt, read godoc documentation from within Emacs, modify
and clean up the list of package imports or interact with the
Playground (uploading and downloading pastes).
The following extra functions are defined:
- `gofmt'
- `godoc' and `godoc-at-point'
- `go-import-add'
- `go-goto-arguments'
- `go-goto-docstring'
- `go-goto-function'
- `go-goto-function-name'
- `go-goto-imports'
- `go-goto-return-values'
- `go-goto-method-receiver'
- `go-play-buffer' and `go-play-region'
- `go-download-play'
- `godef-describe' and `godef-jump'
- `go-coverage'
If you want to automatically run `gofmt' before saving a file,
add the following hook to your Emacs configuration:
(add-hook 'before-save-hook #'gofmt-before-save)
If you want to use `godef-jump' instead of etags (or similar),
consider binding godef-jump to `M-.', which is the default key
for `find-tag':
(add-hook 'go-mode-hook (lambda ()
(local-set-key (kbd \"M-.\") #'godef-jump)))
Please note that godef is an external dependency. You can install
it with
go get github.com/rogpeppe/godef
If you're looking for even more integration with Go, namely
on-the-fly syntax checking, auto-completion and snippets, it is
recommended that you look at flycheck
(see URL `https://github.com/flycheck/flycheck') or flymake in combination
with goflymake (see URL `https://github.com/dougm/goflymake'), gocode
(see URL `https://github.com/nsf/gocode'), go-eldoc
(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go
(see URL `https://github.com/dominikh/yasnippet-go')
(fn)" t)
(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode))
(autoload 'gofmt-before-save "go-mode" "\
Add this to .emacs to run gofmt on the current buffer when saving:
(add-hook 'before-save-hook 'gofmt-before-save).
Note that this will cause go-mode to get loaded the first time
you save any file, kind of defeating the point of autoloading." t)
(autoload 'godoc "go-mode" "\
Show Go documentation for QUERY, much like \\<go-mode-map>\\[man].
(fn QUERY)" t)
(autoload 'go-download-play "go-mode" "\
Download a paste from the playground and insert it in a Go buffer.
Tries to look for a URL at point.
(fn URL)" t)
(autoload 'go-dot-mod-mode "go-mode" "\
A major mode for editing go.mod files.
(fn)" t)
(add-to-list 'auto-mode-alist '("go\\.mod\\'" . go-dot-mod-mode))
(autoload 'go-dot-work-mode "go-mode" "\
A major mode for editor go.work files.
(fn)" t)
(add-to-list 'auto-mode-alist '("go\\.work\\'" . go-dot-work-mode))
(register-definition-prefixes "go-mode" '("go-" "god" "gofmt"))
;;; End of scraped data
(provide 'go-mode-autoloads)
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; no-native-compile: t
;; coding: utf-8-emacs-unix
;; End:
;;; go-mode-autoloads.el ends here

View file

@ -1,8 +0,0 @@
(define-package "go-mode" "20240620.1948" "Major mode for the Go programming language"
'((emacs "26.1"))
:commit "636d36e37a0d2b6adb2e12d802ff4794ccbba336" :keywords
'("languages" "go")
:url "https://github.com/dominikh/go-mode.el")
;; Local Variables:
;; no-byte-compile: t
;; End:

File diff suppressed because it is too large Load diff

View file

@ -156,37 +156,27 @@ fi
cat > $CONF_FILE <<EOF
(setq initial-scratch-message
";; This buffer is for text that is not saved, and for Lisp evaluation.\\n\
;; You can hit \`C-j' at end of a lisp expression to eval it.\\n\\n\
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\\n\
(setq initial-scratch-message (concat initial-scratch-message
";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\\n\
;; This Emacs is Powered by \`HELM' using\\n\
;; emacs program \"$EMACS\".\\n\
;; This is a minimal \`helm' configuration to discover \`helm' or debug it.\\n\
;; You can retrieve this minimal configuration in \"$CONF_FILE\".\\n\
;;
;; Some original Emacs commands are replaced by their \`helm' counterparts:\\n\\n\
;; - \`find-file'(C-x C-f) =>\`helm-find-files'\\n\
;; - \`occur'(M-s o) =>\`helm-occur'\\n\
;; - \`list-buffers'(C-x C-b) =>\`helm-buffers-list'\\n\
;; - \`completion-at-point'(M-tab) =>\`helm-lisp-completion-at-point'[1]\\n\
;; - \`apropos-command'(C-h a) =>\`helm-apropos'\\n\
;; - \`dabbrev-expand'(M-/) =>\`helm-dabbrev'\\n\
;; - \`execute-extended-command'(M-x) =>\`helm-M-x'\\n\\n
;; \`helm-mode' is enabled which mean that most Emacs commands using completion\\n\
;; will use helm.\\n\
;; Some other Emacs commands are \"helmized\" by \`helm-mode'.\\n\
;; [1] Coming with emacs-24.4, \`completion-at-point' is \"helmized\" by \`helm-mode'\\n\
;; To start editing a file or to create a new file, visit it with \`C-x C-f'\\n\
;; and enter text in its buffer, to save your changes hit \`C-x C-s'.
;; Find context help for most Helm commands with \`C-h m' while helm is running.\\n\
;; You can also retrieve the whole user documentation with \`C-x c h h'.\\n\
;; For online documentation see \`https://github.com/emacs-helm/helm/wiki'.\\n\
;; \(Put cursor on url, hit \`C-x C-f' and then RETurn).\\n\
;; To quit this Emacs, hit \'C-x C-c'.\\n\
;; Note about keybindings in Emacs: \`C-' means \'Control-' and \`M-' \'Alt-'.\\n\
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\\n\\n")
;; which provides Helm completion in many places like \`shell-mode'.\\n\
;; Find context help for most Helm commands with \`C-h m'.\\n\
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\\n\\n"))
(setq load-path (quote $LOAD_PATH))
@ -243,7 +233,7 @@ cat > $CONF_FILE <<EOF
(setq package-load-list
(if (equal load-packages '("all"))
'(all)
(append '((helm-core t) (helm t) (async t) (wfnames t))
(append '((helm-core t) (helm t) (async t) (popup t))
(mapcar (lambda (p) (list (intern p) t)) load-packages)))))
(package-initialize))
@ -266,6 +256,9 @@ cat > $CONF_FILE <<EOF
(define-key global-map [remap dabbrev-expand] 'helm-dabbrev)
(define-key global-map [remap execute-extended-command] 'helm-M-x)
(define-key global-map [remap apropos-command] 'helm-apropos)
(unless (boundp 'completion-in-region-function)
(define-key lisp-interaction-mode-map [remap completion-at-point] 'helm-lisp-completion-at-point)
(define-key emacs-lisp-mode-map [remap completion-at-point] 'helm-lisp-completion-at-point))
(add-hook 'kill-emacs-hook #'(lambda () (and (file-exists-p "$CONF_FILE") (delete-file "$CONF_FILE"))))
EOF

View file

@ -33,7 +33,6 @@
(declare-function all-the-icons-icon-for-file"ext:all-the-icons.el")
(declare-function all-the-icons-octicon "ext:all-the-icons.el")
(declare-function all-the-icons-match-to-alist "ext:all-the-icons.el")
(declare-function all-the-icons-faicon "ext:all-the-icons.el")
(defvar all-the-icons-dir-icon-alist)
@ -66,7 +65,7 @@
Don't use `setq' to set this."
:type 'boolean
:set (lambda (var val)
(if (require 'all-the-icons nil t)
(if (featurep 'all-the-icons)
(set var val)
(set var nil))))
@ -89,10 +88,6 @@ will be honored."
helm-bookmark-default-filtered-sources)
for fn = (intern (format "%s-builder" s))
do (set s (funcall fn)))))
(defcustom helm-bookmark-annotation-sign "*"
"Boomarks with annotation are prefixed with this string."
:type 'string)
(defgroup helm-bookmark-faces nil
"Customize the appearance of helm-bookmark."
@ -255,8 +250,7 @@ will be honored."
BOOKMARK is a bookmark name or a bookmark record."
(or (eq (bookmark-get-handler bookmark) 'bmkext-jump-gnus)
(eq (bookmark-get-handler bookmark) 'gnus-summary-bookmark-jump)
(eq (bookmark-get-handler bookmark) 'bookmarkp-jump-gnus)
(eq (bookmark-get-handler bookmark) 'bmkp-jump-gnus)))
(eq (bookmark-get-handler bookmark) 'bookmarkp-jump-gnus)))
(defun helm-bookmark-mu4e-bookmark-p (bookmark)
"Return non nil if BOOKMARK is a mu4e bookmark.
@ -269,24 +263,21 @@ BOOKMARK is a bookmark name or a bookmark record."
BOOKMARK is a bookmark name or a bookmark record."
(or (eq (bookmark-get-handler bookmark) 'bmkext-jump-w3m)
(eq (bookmark-get-handler bookmark) 'bookmark-w3m-bookmark-jump)
(eq (bookmark-get-handler bookmark) 'bookmarkp-jump-w3m)
(eq (bookmark-get-handler bookmark) 'bmkp-jump-w3m)))
(eq (bookmark-get-handler bookmark) 'bookmarkp-jump-w3m)))
(defun helm-bookmark-woman-bookmark-p (bookmark)
"Return non-nil if BOOKMARK is a Woman bookmark.
BOOKMARK is a bookmark name or a bookmark record."
(or (eq (bookmark-get-handler bookmark) 'bmkext-jump-woman)
(eq (bookmark-get-handler bookmark) 'woman-bookmark-jump)
(eq (bookmark-get-handler bookmark) 'bookmarkp-jump-woman)
(eq (bookmark-get-handler bookmark) 'bmkp-jump-woman)))
(eq (bookmark-get-handler bookmark) 'bookmarkp-jump-woman)))
(defun helm-bookmark-man-bookmark-p (bookmark)
"Return non-nil if BOOKMARK is a Man bookmark.
BOOKMARK is a bookmark name or a bookmark record."
(or (eq (bookmark-get-handler bookmark) 'bmkext-jump-man)
(eq (bookmark-get-handler bookmark) 'Man-bookmark-jump)
(eq (bookmark-get-handler bookmark) 'bookmarkp-jump-man)
(eq (bookmark-get-handler bookmark) 'bmkp-jump-man)))
(eq (bookmark-get-handler bookmark) 'bookmarkp-jump-man)))
(defun helm-bookmark-woman-man-bookmark-p (bookmark)
"Return non-nil if BOOKMARK is a Man or Woman bookmark.
@ -311,10 +302,7 @@ BOOKMARK is a bookmark name or a bookmark record.
This excludes bookmarks of a more specific kind (Info, Gnus, and W3m)."
(let* ((filename (bookmark-get-filename bookmark))
(isnonfile (equal filename helm-bookmark--non-file-filename)))
(and filename
(not isnonfile)
(not (helm-bookmark-org-file-p bookmark))
(not (bookmark-get-handler bookmark)))))
(and filename (not isnonfile) (not (bookmark-get-handler bookmark)))))
(defun helm-bookmark-org-file-p (bookmark)
(let* ((filename (bookmark-get-filename bookmark)))
@ -400,10 +388,6 @@ If `browse-url-browser-function' is set to something else than
(defalias 'bookmarkp-jump-w3m #'helm-bookmark-jump-w3m)
(defalias 'bookmarkp-jump-woman #'woman-bookmark-jump)
(defalias 'bookmarkp-jump-man #'Man-bookmark-jump)
(defalias 'bmkp-jump-gnus #'gnus-summary-bookmark-jump)
(defalias 'bmkp-jump-w3m #'helm-bookmark-jump-w3m)
(defalias 'bmkp-jump-woman #'woman-bookmark-jump)
(defalias 'bmkp-jump-man #'Man-bookmark-jump)
;;;; Filtered bookmark sources
@ -635,17 +619,15 @@ If `browse-url-browser-function' is set to something else than
all-the-icons-dir-icon-alist))
(apply (car it) (cdr it))
(all-the-icons-octicon "file-directory")))
(isw3m (all-the-icons-faicon "firefox"))
((and isfile isinfo) (all-the-icons-octicon "info"))
(isfile (all-the-icons-icon-for-file isfile))
((or iswoman isman)
(all-the-icons-fileicon "man-page"))
((or isgnus ismu4e)
(all-the-icons-octicon "mail-read"))
(isfile (all-the-icons-icon-for-file (helm-basename isfile)))))
(all-the-icons-octicon "mail-read"))))
;; Add a * if bookmark have annotation
if (and isannotation (not (string-equal isannotation "")))
do (setq trunc (concat helm-bookmark-annotation-sign
(if helm-bookmark-show-location trunc i)))
do (setq trunc (concat "*" (if helm-bookmark-show-location trunc i)))
for sep = (and helm-bookmark-show-location
(make-string (- (+ bookmark-bmenu-file-column 2)
(string-width trunc))
@ -767,43 +749,32 @@ renamed."
(setq bookmark-alist-modification-count (1+ bookmark-alist-modification-count))
(when (bookmark-time-to-save-p) (bookmark-save)))
(defun helm-bookmark-rename (old &optional new _batch)
(defun helm-bookmark-rename (old &optional new batch)
"Change bookmark's name from OLD to NEW.
Interactively:
If called from the keyboard, then prompt for OLD.
If called from the menubar, select OLD from a menu.
If NEW is nil, then prompt for its string value.
Unused arg _BATCH is kept for backward compatibility.
If BATCH is non-nil, then do not rebuild the menu list.
While the user enters the new name, repeated `C-w' inserts
consecutive words from the buffer into the new bookmark name."
(interactive (list (bookmark-completing-read "Old bookmark name")))
(bookmark-maybe-historicize-string old)
(bookmark-maybe-load-default-file)
(save-excursion
(skip-chars-forward " ") (setq bookmark-yank-point (point)))
(save-excursion (skip-chars-forward " ") (setq bookmark-yank-point (point)))
(setq bookmark-current-buffer (current-buffer))
(catch 'skip
(let ((newname
(or new (read-from-minibuffer
;; `format-prompt' is not available in old Emacs.
(format "New name [C-RET to skip] (default %s): " old) nil
(let ((newname (or new (read-from-minibuffer
"New name: " nil
(let ((now-map (copy-keymap minibuffer-local-map)))
(define-key now-map "\C-w" #'bookmark-yank-word)
(define-key now-map (kbd "C-<return>")
#'(lambda () (interactive) (throw 'skip 'skip)))
now-map)
nil 'bookmark-history old))))
nil 'bookmark-history))))
(bookmark-set-name old newname)
(setq bookmark-current-bookmark newname)
(helm-bookmark-maybe-save-bookmark) newname)))
(defun helm-bookmark-rename-marked (_candidate)
"Rename marked bookmarks."
(let* ((bmks (helm-marked-candidates))
(count 0)
(len (length bmks)))
(cl-loop for bmk in bmks
unless (eq (helm-bookmark-rename bmk) 'skip)
do (cl-incf count))
(message "(%s/%s) bookmark(s) renamed" count len)))
(unless batch (bookmark-bmenu-surreptitiously-rebuild-list))
(helm-bookmark-maybe-save-bookmark) newname))
(helm-make-command-from-action helm-bookmark-run-edit
"Run `helm-bookmark-edit-bookmark' from keyboard."
@ -837,65 +808,7 @@ E.g. prepended with *."
(dolist (i (helm-marked-candidates))
(bookmark-delete (helm-bookmark-get-bookmark-from-name i)
'batch)))
;;; bookmark annotations
;;
(defun helm-bookmark-show-annotation (bookmark-name-or-record)
"Display the annotation for BOOKMARK-NAME-OR-RECORD in a buffer."
(let ((annotation (bookmark-get-annotation bookmark-name-or-record)))
(when (and annotation (not (string-equal annotation "")))
(let ((buf (get-buffer-create "*Bookmark Annotation*")))
(with-current-buffer buf
(let ((inhibit-read-only t))
(erase-buffer)
(insert annotation)
(goto-char (point-min))
(set-buffer-modified-p nil)
(helm-bookmark-annotation-mode)
(insert (substitute-command-keys
"# Edit this buffer with \\[helm-bookmark-edit-annotation]")
(substitute-command-keys
"\n# Quit this buffer with \\[helm-bookmark-quit-annotation]\n"))
(set (make-local-variable 'bookmark-annotation-name)
bookmark-name-or-record)
(put 'bookmark-annotation-name 'permanent-local t)))
(pop-to-buffer buf)))))
(defun helm-bookmark-edit-annotation ()
"Edit bookmark annotation from the show annotation buffer."
(interactive)
(setq buffer-read-only nil)
(bookmark-edit-annotation-mode)
(save-excursion
(goto-char (point-min))
(delete-region
(point) (save-excursion (forward-line 2) (point)))
(insert (funcall bookmark-edit-annotation-text-func
bookmark-annotation-name))))
(put 'helm-bookmark-edit-annotation 'no-helm-mx t)
(defun helm-bookmark-quit-annotation ()
"Quit bookmark annotation buffer."
(interactive)
(quit-window t))
(put 'helm-bookmark-quit-annotation 'no-helm-mx t)
(defvar helm-bookmark-annotation-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map text-mode-map)
(define-key map (kbd "q") #'helm-bookmark-quit-annotation)
(define-key map (kbd "e") #'helm-bookmark-edit-annotation)
map)
"Map used in show annotation bookmark buffer.")
(define-derived-mode helm-bookmark-annotation-mode
text-mode "helm-annotation-mode"
"Mode to display bookmark annotations.
Special commands:
\\{helm-bookmark-annotation-mode-map}"
:interactive nil
(setq-local buffer-read-only t))
;;;###autoload
(defun helm-bookmarks ()
@ -905,7 +818,7 @@ Special commands:
helm-source-bookmark-set)
:buffer "*helm bookmarks*"
:default (buffer-name helm-current-buffer)))
;;;###autoload
(defun helm-filtered-bookmarks ()
"Preconfigured `helm' for bookmarks (filtered by category).

View file

@ -36,7 +36,6 @@
(defvar dired-buffers)
(defvar org-directory)
(defvar helm-ff-default-directory)
(defvar major-mode-remap-alist)
(defgroup helm-buffers nil
@ -101,10 +100,7 @@ When adding a source here it is up to you to ensure the library
of this source is accessible and properly loaded."
:type '(repeat (choice symbol)))
(defcustom helm-buffers-end-truncated-string
;; `truncate-string-ellipsis', the function is not available in 27.1
;; See issue#2673.
(if (char-displayable-p ?…) "" "...")
(defcustom helm-buffers-end-truncated-string "..."
"The string to display at end of truncated buffer names."
:type 'string)
@ -152,7 +148,7 @@ you want to keep the recentest order when narrowing candidates."
Don't use `setq' to set this."
:type 'boolean
:set (lambda (var val)
(if (require 'all-the-icons nil t)
(if (featurep 'all-the-icons)
(set var val)
(set var nil))))
@ -339,9 +335,6 @@ Note that this variable is buffer-local.")
when (string-match r candidate)
return m)))
(buffer (get-buffer-create candidate)))
(helm-aif (and (boundp 'major-mode-remap-alist)
(cdr (assq mjm major-mode-remap-alist)))
(setq mjm it))
(if mjm
(with-current-buffer buffer (funcall mjm))
(set-buffer-major-mode buffer))
@ -386,7 +379,7 @@ Note that this variable is buffer-local.")
(defun helm-buffers-get-visible-buffers ()
"Returns buffers visible on visible frames."
"Returns buffers visibles on current frame."
(let (result)
(walk-windows
(lambda (x)
@ -395,7 +388,6 @@ Note that this variable is buffer-local.")
result))
(defun helm-buffer-list-1 (&optional visibles)
"Return list of all buffers except VISIBLES buffers."
(cl-loop for b in (buffer-list)
for bn = (buffer-name b)
unless (member bn visibles)
@ -439,7 +431,7 @@ The list is reordered with `helm-buffer-list-reorder-fn'."
(cond ((eq type 'dired)
(all-the-icons-octicon "file-directory"))
(buf-fname
(all-the-icons-icon-for-file buf-name))
(all-the-icons-icon-for-file buf-fname))
(t (all-the-icons-octicon "star" :v-adjust 0.0))))))
(buf-name (propertize buf-name 'face face1
'help-echo help-echo
@ -460,7 +452,7 @@ The list is reordered with `helm-buffer-list-reorder-fn'."
(format "(%s %s in `%s')"
(process-name proc)
(process-status proc) dir)
(format "`%s'" dir))
(format "(in `%s')" dir))
'face face2)))))
(defun helm-buffer--format-mode-name (buf)
@ -1010,14 +1002,12 @@ vertically."
(defun helm-buffers-persistent-kill (_buffer)
(let ((marked (helm-marked-candidates))
(sel (helm-get-selection))
(msg "Buffer `%s' modified, please save it before kill"))
(sel (helm-get-selection)))
(unwind-protect
(cl-loop for b in marked
do (if (and (buffer-file-name b) (buffer-modified-p b))
(message msg (buffer-name b))
do (progn
;; We need to preselect each marked because
;; helm-buffers-persistent-kill-1 is deleting
;; helm-buffers-persistent-kill is deleting
;; current selection.
(helm-preselect
(format "^%s"
@ -1035,7 +1025,7 @@ vertically."
(if (or (helm-follow-mode-p)
(eql current (get-buffer helm-current-buffer))
(not (eql current (get-buffer candidate))))
(display-buffer candidate)
(switch-to-buffer candidate)
(if (and helm-persistent-action-display-window
(window-dedicated-p
(next-window helm-persistent-action-display-window 1)))
@ -1120,18 +1110,19 @@ Can be used by any source that list buffers."
(cl-assert (not helm-buffers-in-project-p)
nil "You are already browsing this project"))
;;;###autoload
(defun helm-buffers-quit-and-find-file-fn (source)
(let* ((sel (get-buffer (helm-get-selection nil nil source)))
(bname (and (bufferp sel) (buffer-name sel))))
(when bname
(let* ((sel (helm-get-selection nil nil source))
(buf (helm-aand (bufferp sel)
(get-buffer sel)
(buffer-name it))))
(when buf
(or (buffer-file-name sel)
(car (rassoc bname dired-buffers))
(and (with-current-buffer bname
(car (rassoc buf dired-buffers))
(and (with-current-buffer buf
(eq major-mode 'org-agenda-mode))
org-directory
(expand-file-name org-directory))
(with-current-buffer bname
(with-current-buffer buf
(expand-file-name default-directory))))))
;;; Candidate Transformers

View file

@ -36,7 +36,7 @@
'global
(with-current-buffer (get-buffer "*Faces*")
(buffer-substring
(next-single-char-property-change (point-min) 'category)
(next-single-char-property-change (point-min) 'face)
(point-max))))
(kill-buffer "*Faces*")))

View file

@ -55,10 +55,6 @@ This value can be toggled with
\\<helm-M-x-map>\\[helm-M-x-toggle-short-doc] while in helm-M-x session."
:type 'boolean)
(defcustom helm-M-x-history-transformer-sort t
"When nil, do not sort helm-M-x's commands history."
:type 'boolean)
;;; Faces
;;
@ -138,56 +134,42 @@ Note that SORT should not be used when fuzzy matching because
fuzzy matching is running its own sort function with a different
algorithm."
(with-helm-current-buffer
(cl-loop with local-map = (helm-M-x-current-mode-map-alist)
(cl-loop with max-len = (when helm-M-x-show-short-doc
(helm-in-buffer-get-longest-candidate))
with local-map = (helm-M-x-current-mode-map-alist)
for cand in candidates
for local-key = (car (rassq cand local-map))
for key = (substitute-command-keys (format "\\[%s]" cand))
for sym = (intern (if (consp cand) (car cand) cand))
for doc = (when helm-M-x-show-short-doc
for doc = (when max-len
(helm-get-first-line-documentation (intern-soft cand)))
for disp = (if (or (eq sym major-mode)
(and (memq sym minor-mode-list)
(boundp sym)
(buffer-local-value
sym helm-current-buffer)))
(buffer-local-value sym helm-current-buffer)))
(propertize cand 'face 'helm-command-active-mode)
cand)
unless (and (null ignore-props)
(or (get sym 'helm-only) (get sym 'no-helm-mx)
(eq sym 'helm-M-x)))
unless (and (null ignore-props) (or (get sym 'helm-only) (get sym 'no-helm-mx)))
collect
(cons (cond ((and (string-match "^M-x" key) local-key)
(propertize
(format "%s%s%s %s"
(propertize (format "%s%s%s %s"
disp
(if doc (helm-make-separator cand) "")
(if doc
(propertize
doc 'face 'helm-M-x-short-doc)
"")
(if doc (make-string (+ 1 (- max-len (length cand))) ? ) "")
(if doc (propertize doc 'face 'helm-M-x-short-doc) "")
(propertize
" " 'display
(propertize local-key 'face 'helm-M-x-key)))
'match-part disp))
((and (string-match "^M-x" key)
(not (string= key "M-x")))
(propertize
(format "%s%s%s"
((string-match "^M-x" key)
(propertize (format "%s%s%s"
disp
(if doc (helm-make-separator cand) "")
(if doc
(propertize
doc 'face 'helm-M-x-short-doc)
""))
(if doc (make-string (+ 1 (- max-len (length cand))) ? ) "")
(if doc (propertize doc 'face 'helm-M-x-short-doc) ""))
'match-part disp))
(t (propertize
(format "%s%s%s %s"
(t (propertize (format "%s%s%s %s"
disp
(if doc (helm-make-separator cand) "")
(if doc
(propertize
doc 'face 'helm-M-x-short-doc)
"")
(if doc (make-string (+ 1 (- max-len (length cand))) ? ) "")
(if doc (propertize doc 'face 'helm-M-x-short-doc) "")
(propertize
" " 'display
(propertize key 'face 'helm-M-x-key)))
@ -262,7 +244,7 @@ algorithm."
(defclass helm-M-x-class (helm-source-in-buffer helm-type-command)
((requires-pattern :initform 0)
(must-match :initform t)
(filtered-candidate-transformer :initform #'helm-M-x-transformer)
(filtered-candidate-transformer :initform 'helm-M-x-transformer-no-sort)
(persistent-help :initform "Describe this command")
(help-message :initform 'helm-M-x-help-message)
(nomark :initform t)
@ -283,19 +265,23 @@ algorithm."
(defun helm-M-x-read-extended-command (collection &optional predicate history)
"Read or execute action on command name in COLLECTION or HISTORY.
Helm completion is not provided when executing or defining kbd macros.
When `helm-M-x-use-completion-styles' is used, Emacs
`completion-styles' mechanism is used, otherwise standard helm
completion and helm fuzzy matching are used together.
Arg COLLECTION should be an `obarray'.
Arg PREDICATE is a function that default to `commandp'.
Arg HISTORY default to `extended-command-history'."
Helm completion is not provided when executing or defining kbd
macros.
Arg COLLECTION should be an `obarray' but can be any object
suitable for `try-completion'. Arg PREDICATE is a function that
default to `commandp' see also `try-completion'. Arg HISTORY
default to `extended-command-history'."
(setq helm--mode-line-display-prefarg t)
(let* ((pred (or predicate #'commandp))
(helm-fuzzy-sort-fn (lambda (candidates _source)
(if helm-M-x-history-transformer-sort
;; Sort on real candidate otherwise
;; "symbol (<binding>)" is used when sorting.
(helm-fuzzy-matching-default-sort-fn-1 candidates t)
candidates)))
(helm-fuzzy-matching-default-sort-fn-1 candidates t)))
(sources `(,(helm-make-source "Emacs Commands history" 'helm-M-x-class
:data (lambda ()
(helm-comp-read-get-candidates
@ -307,10 +293,6 @@ Arg HISTORY default to `extended-command-history'."
;; Ensure using empty string to
;; not defeat helm matching fns [1]
pred nil nil ""))
:filtered-candidate-transformer
(if helm-M-x-history-transformer-sort
#'helm-M-x-transformer
#'helm-M-x-transformer-no-sort)
:fuzzy-match helm-M-x-fuzzy-match)
,(helm-make-source "Emacs Commands" 'helm-M-x-class
:data (lambda ()
@ -318,11 +300,16 @@ Arg HISTORY default to `extended-command-history'."
;; [1] Same comment as above.
collection pred nil nil ""))
:fuzzy-match helm-M-x-fuzzy-match)))
(prompt (concat (helm-acase helm-M-x-prefix-argument
(- "-")
((guard (and (consp it) (car it)))
(if (eq guard 4) "C-u " (format "%d " guard)))
((guard (integerp it)) (format "%d " it)))
(prompt (concat (cond
((eq helm-M-x-prefix-argument '-) "- ")
((and (consp helm-M-x-prefix-argument)
(eq (car helm-M-x-prefix-argument) 4))
"C-u ")
((and (consp helm-M-x-prefix-argument)
(integerp (car helm-M-x-prefix-argument)))
(format "%d " (car helm-M-x-prefix-argument)))
((integerp helm-M-x-prefix-argument)
(format "%d " helm-M-x-prefix-argument)))
"M-x ")))
(setq helm-M-x--timer (run-at-time 1 0.1 #'helm-M-x--notify-prefix-arg))
;; Fix Bug#2250, add `helm-move-selection-after-hook' which

View file

@ -54,7 +54,7 @@ Helm buffer."
(defcustom helm-dabbrev-related-buffer-fn #'helm-dabbrev--same-major-mode-p
"A function that decide if a buffer to search in its related to `current-buffer'.
This is currently determined by comparing `major-mode' of the
This is actually determined by comparing `major-mode' of the
buffer to search and the `current-buffer'.
The function take one arg, the buffer which is current, look at

View file

@ -31,8 +31,7 @@
["Recent Files" helm-recentf t]
["Locate" helm-locate t]
["Search Files with find" helm-find t]
["Bookmarks" helm-filtered-bookmarks t]
["Locate library" helm-locate-library t])
["Bookmarks" helm-filtered-bookmarks t])
("Buffers"
["Find buffers" helm-buffers-list t])
("Projects"
@ -48,9 +47,8 @@
["Emacs Manual index" helm-info-emacs t]
["Gnus Manual index" helm-info-gnus t]
["Helm documentation" helm-documentation t])
("Packages"
["Elisp packages" helm-packages t]
["Finder" helm-finder t])
("Elpa"
["Elisp packages" helm-packages t])
("Tools"
["Occur" helm-occur t]
["Grep current directory with AG" helm-do-grep-ag t]

View file

@ -33,9 +33,6 @@
(declare-function helm-comp-read "helm-mode")
(declare-function helm-M-x-transformer-no-sort-no-props "helm-command")
(defvar helm-M-x-show-short-doc)
(defvar completions-detailed)
(defvar helm-completions-detailed)
;;; Customizable values
@ -160,9 +157,9 @@ display."
;; Called each time cursor move in helm-buffer.
(defun helm-show-completion ()
(with-helm-current-buffer
(helm-aif (helm-get-selection)
(overlay-put helm-show-completion-overlay
'display (substring-no-properties it)))))
'display (substring-no-properties
(helm-get-selection)))))
(defun helm-show-completion-init-overlay (beg end)
(setq helm-show-completion-overlay (make-overlay beg end))
@ -209,9 +206,6 @@ If `helm-turn-on-show-completion' is nil do nothing."
'helm-display-function
(or helm-show-completion-display-function
'helm-default-display-buffer))
(with-helm-after-update-hook
;; Show immediately first candidate as soon as helm popup.
(helm-show-completion))
(helm-show-completion-init-overlay ,beg ,end)
,@body)
,@body)
@ -298,10 +292,13 @@ Return a cons (beg . end)."
(when (and pos (< (point) pos))
(push-mark pos t t))))
(defvar helm-lisp-completion--cache nil)
(defvar helm-lgst-len nil)
;;;###autoload
(defun helm-lisp-completion-at-point ()
"Preconfigured Helm for Lisp symbol completion at point."
(interactive)
(setq helm-lgst-len 0)
(let* ((target (helm-thing-before-point))
(beg (car (helm-bounds-of-thing-before-point)))
(end (point))
@ -314,12 +311,17 @@ Return a cons (beg . end)."
(helm-quit-if-no-candidate t)
(helm-execute-action-at-once-if-one t)
(enable-recursive-minibuffers t))
(setq helm-lisp-completion--cache (cl-loop for sym in candidates
for len = (length sym)
when (> len helm-lgst-len)
do (setq helm-lgst-len len)
collect sym))
(if candidates
(with-helm-show-completion beg end
;; Overlay is initialized now in helm-current-buffer.
(helm
:sources (helm-build-in-buffer-source "Lisp completion"
:data candidates
:data helm-lisp-completion--cache
:persistent-action `(helm-lisp-completion-persistent-action .
,(and (eq helm-elisp-help-function
'helm-elisp-show-doc-modeline)
@ -358,17 +360,17 @@ other window according to the value of
(helm-elisp-show-help "Toggle show help for the symbol")))
(defun helm-elisp--show-help-1 (candidate &optional name)
(helm-acase (intern-soft candidate)
((guard (and (fboundp it) (boundp it)))
(if (member name `(,helm-describe-function-function
,helm-describe-variable-function))
(funcall (intern (format "helm-%s" name)) it)
(let ((sym (intern-soft candidate)))
(pcase sym
((and (pred fboundp) (pred boundp))
(if (member name `(,helm-describe-function-function ,helm-describe-variable-function))
(funcall (intern (format "helm-%s" name)) sym)
;; When there is no way to know what to describe
;; prefer describe-function.
(helm-describe-function it)))
((guard (fboundp it)) (helm-describe-function it))
((guard (boundp it)) (helm-describe-variable it))
((guard (facep it)) (helm-describe-face it))))
(helm-describe-function sym)))
((pred fboundp) (helm-describe-function sym))
((pred boundp) (helm-describe-variable sym))
((pred facep) (helm-describe-face sym)))))
(defun helm-elisp-show-help (candidate &optional name)
"Show full help for the function CANDIDATE.
@ -393,14 +395,15 @@ the same time to variable and a function."
"Helm candidates transformer for Lisp completion."
(cl-loop for c in candidates
for sym = (intern c)
for annot = (helm-acase sym
((guard (commandp it)) " (Com)")
((guard (class-p it)) " (Class)")
((guard (cl-generic-p it)) " (Gen)")
((guard (fboundp it)) " (Fun)")
((guard (boundp it)) " (Var)")
((guard (facep it)) " (Face)"))
collect (cons (concat c (helm-make-separator c) annot) c) into lst
for annot = (pcase sym
((pred commandp) " (Com)")
((pred class-p) " (Class)")
((pred cl-generic-p) " (Gen)")
((pred fboundp) " (Fun)")
((pred boundp) " (Var)")
((pred facep) " (Face)"))
for spaces = (make-string (- helm-lgst-len (length c)) ? )
collect (cons (concat c spaces annot) c) into lst
finally return (sort lst #'helm-generic-sort-fn)))
;;;###autoload
@ -412,37 +415,31 @@ If SYM is not documented, return \"Not documented\".
Argument NAME allows specifiying what function to use to display
documentation when SYM name is the same for function and variable."
(let ((doc (condition-case _err
(helm-acase sym
((guard (class-p it))
(cl--class-docstring (cl--find-class it)))
((guard (and (fboundp it) (boundp it)))
(if (string= name "describe-variable")
(documentation-property it 'variable-documentation t)
(documentation it t)))
((guard (custom-theme-p it))
(documentation-property it 'theme-documentation t))
((guard (and (helm-group-p it) (not (fboundp it))))
(documentation-property it 'group-documentation t))
((guard (fboundp it))
(documentation it t))
((guard (boundp it))
(documentation-property it 'variable-documentation t))
((guard (facep it)) (face-documentation it)))
(pcase sym
((pred class-p) (cl--class-docstring (cl--find-class sym)))
((and (pred fboundp) (pred boundp))
(pcase name
("describe-function"
(documentation sym t))
("describe-variable"
(documentation-property sym 'variable-documentation t))
(_ (documentation sym t))))
((pred custom-theme-p)
(documentation-property sym 'theme-documentation t))
((pred helm-group-p) (documentation-property
sym 'group-documentation t))
((pred fboundp) (documentation sym t))
((pred boundp) (documentation-property
sym 'variable-documentation t))
((pred facep) (face-documentation sym)))
(void-function "Void function -- Not documented"))))
(if (and doc (not (string= doc ""))
;; `documentation' return "\n\n(args...)"
;; for CL-style functions.
(not (string-match-p "\\`\n\n" doc)))
;; Some commands specify key bindings or keymap in their first line,
;; e.g.: "\<hexl-mode-map>A mode for editing binary [...]. As a result
;; (substitute-command-keys doc) returns a string like "\nUses
;; keymap...\nFirst line docstring. See
;; <https://debbugs.gnu.org/70163>.
;; Some commands specify key bindings in their first line.
(truncate-string-to-width
(helm-acase (split-string (substitute-command-keys doc) "\n")
((guard (and (string= (car it) "") (cdr it)))
(cadr guard))
(t (car it)))
(substitute-command-keys (car (split-string doc "\n")))
end-column nil nil t)
(if (or (symbol-function sym) (boundp sym) (facep sym) (helm-group-p sym))
"Not documented"
@ -460,7 +457,7 @@ documentation when SYM name is the same for function and variable."
"Preconfigured Helm to complete file name at point."
(interactive)
(require 'helm-mode)
(let* ((tap (or (thing-at-point 'filename t) ""))
(let* ((tap (or (thing-at-point 'filename) ""))
beg
(init (and tap
(or force
@ -469,7 +466,8 @@ documentation when SYM name is the same for function and variable."
(search-backward tap (pos-bol) t)
(setq beg (point))
(looking-back "[^'`( ]" (1- (point)))))
(expand-file-name tap)))
(expand-file-name
(substring-no-properties tap))))
(end (point))
(helm-quit-if-no-candidate t)
(helm-execute-action-at-once-if-one t)
@ -481,7 +479,6 @@ documentation when SYM name is the same for function and variable."
(delete-region beg end) (insert (if (string-match "^~" tap)
(abbreviate-file-name completion)
completion)))))
(make-obsolete 'helm-complete-file-name-at-point 'helm-find-files "3.9.6")
;;;###autoload
(defun helm-lisp-indent ()
@ -493,6 +490,20 @@ documentation when SYM name is the same for function and variable."
tab-always-indent)))
(indent-for-tab-command current-prefix-arg)))
;;;###autoload
(defun helm-lisp-completion-or-file-name-at-point ()
"Preconfigured Helm to complete Lisp symbol or filename at point.
Filename completion happens if string start after or between a
double quote."
(interactive)
(let* ((tap (thing-at-point 'filename)))
(if (and tap (save-excursion
(end-of-line)
(search-backward tap (pos-bol) t)
(looking-back "[^'`( ]" (1- (point)))))
(helm-complete-file-name-at-point)
(helm-lisp-completion-at-point))))
;;; Apropos
;;
@ -532,17 +543,18 @@ is only used to test DEFAULT."
(defun helm-apropos-short-doc-transformer (candidates _source)
(if helm-apropos-show-short-doc
(cl-loop for cand in candidates
(cl-loop with max-len = (helm-in-buffer-get-longest-candidate)
for cand in candidates
for doc = (helm-get-first-line-documentation (intern-soft cand))
collect (cons (format "%s%s%s"
cand
(if doc
(helm-make-separator cand)
(make-string (+ 1 (if (zerop max-len)
max-len
(- max-len (string-width cand))))
? )
"")
(if doc
(propertize
doc 'face 'helm-M-x-short-doc)
""))
(if doc (propertize doc 'face 'helm-M-x-short-doc) ""))
cand))
candidates))
@ -746,23 +758,23 @@ is only used to test DEFAULT."
("Info lookup" . helm-info-lookup-symbol))))
(defun helm-info-lookup-fallback-source (candidate)
(cl-multiple-value-bind (fn src-name)
(helm-acase (helm-symbolify candidate)
((guard (class-p it))
(list #'helm-describe-function
"Describe class"))
((guard (cl-generic-p it))
(list #'helm-describe-function
"Describe generic function"))
((guard (fboundp it))
(list #'helm-describe-function
"Describe function"))
((guard (facep it))
(list #'helm-describe-face
"Describe face"))
(let ((sym (helm-symbolify candidate))
src-name fn)
(cond ((class-p sym)
(setq fn #'helm-describe-function
src-name "Describe class"))
((cl-generic-p sym)
(setq fn #'helm-describe-function
src-name "Describe generic function"))
((fboundp sym)
(setq fn #'helm-describe-function
src-name "Describe function"))
((facep sym)
(setq fn #'helm-describe-face
src-name "Describe face"))
(t
(list #'helm-describe-variable
"Describe variable")))
(setq fn #'helm-describe-variable
src-name "Describe variable")))
(helm-build-sync-source src-name
:candidates (list candidate)
:persistent-action (lambda (candidate)
@ -798,10 +810,7 @@ is only used to test DEFAULT."
(defun helm-apropos-get-default ()
(with-syntax-table emacs-lisp-mode-syntax-table
(symbol-name (intern-soft
(helm-aand (thing-at-point 'symbol t)
(replace-regexp-in-string "\\`[~=]" "" it)
(replace-regexp-in-string "[~=]\\'" "" it))))))
(symbol-name (intern-soft (thing-at-point 'symbol)))))
;;;###autoload
(defun helm-apropos (default)
@ -844,7 +853,7 @@ a string, i.e. the `symbol-name' of any existing symbol."
:persistent-help "Toggle describe function / C-u C-j: Toggle advice"))
(defun helm-advice-candidates ()
(cl-loop for fname in ad-advised-functions
(cl-loop for (fname) in ad-advised-functions
for function = (intern fname)
append
(cl-loop for class in ad-advice-classes append
@ -893,76 +902,41 @@ a string, i.e. the `symbol-name' of any existing symbol."
;;; Locate elisp library
;;
;;
(defvar helm--locate-library-cache nil)
(defvar helm--locate-library-doc-cache (make-hash-table :test 'equal))
(defun helm-locate-library-scan-list ()
(cl-loop for dir in load-path
with load-suffixes = '(".el")
when (file-directory-p dir)
nconc (directory-files
dir nil (concat (regexp-opt (find-library-suffixes)) "\\'"))))
append (directory-files
dir t (concat (regexp-opt (get-load-suffixes))
"\\'"))))
;;;###autoload
(defun helm-locate-library (&optional arg)
"Preconfigured helm to locate elisp libraries.
When `completions-detailed' or `helm-completions-detailed' is non
nil, a description of libraries is provided. The libraries are
partially cached in the variables
`helm--locate-library-doc-cache' and
`helm--locate-library-cache'. TIP: You can make these vars
persistent for faster start with the psession package, using M-x
psession-make-persistent-variable. NOTE: The caches affect as
well `find-libray' and `locate-library' when `helm-mode' is
enabled and `completions-detailed' is non nil. There is no need
to refresh the caches, they will be updated automatically if some
new libraries are found, however when a library update its
headers and the description change you can reset the caches with
a prefix arg."
(interactive "P")
(let (done)
(when arg
(setq helm--locate-library-cache nil)
(clrhash helm--locate-library-doc-cache))
(helm :sources
(helm-build-in-buffer-source "Elisp libraries (Scan)"
(defun helm-locate-library ()
"Preconfigured helm to locate elisp libraries."
(interactive)
(helm :sources (helm-build-in-buffer-source "Elisp libraries (Scan)"
:data #'helm-locate-library-scan-list
:fuzzy-match helm-locate-library-fuzzy-match
:keymap helm-generic-files-map
:candidate-transformer
(lambda (candidates)
(cl-loop with reporter = (unless done
(make-progress-reporter
"Scanning libraries..." 0 (length candidates)))
with lgst = (helm-in-buffer-get-longest-candidate)
for c in candidates
for count from 0
for bn = (helm-basename c 2)
for sep = (helm-make-separator bn lgst)
for path = (or (assoc-default bn helm--locate-library-cache)
;; A lock file in LOAD-PATH (bug#2626).
(unless (string-match "\\`\\.#" bn)
(let ((p (find-library-name bn)))
(push (cons bn p) helm--locate-library-cache)
p)))
for doc = (and path
(or completions-detailed helm-completions-detailed)
(or (gethash bn helm--locate-library-doc-cache)
(puthash bn (helm-locate-lib-get-summary path)
helm--locate-library-doc-cache)))
for disp = (and path
(if (and doc
(or completions-detailed helm-completions-detailed))
(helm-aand (propertize doc 'face 'font-lock-warning-face)
(propertize " " 'display (concat sep it))
(concat bn it))
bn))
when (and disp path)
collect (cons disp path)
when reporter do (progress-reporter-update reporter count)
finally do (setq done t)))
:search (unless helm-locate-library-fuzzy-match
(lambda (regexp)
(re-search-forward
(if helm-ff-transformer-show-only-basename
(replace-regexp-in-string
"\\`\\^" "" regexp)
regexp)
nil t)))
:match-part (lambda (candidate)
(with-helm-buffer
(if helm-ff-transformer-show-only-basename
(helm-basename candidate) candidate)))
:filter-one-by-one (lambda (c)
(with-helm-buffer
(if helm-ff-transformer-show-only-basename
(cons (helm-basename c) c) c)))
:action (helm-actions-from-type-file))
:buffer "*helm locate library*")))
:ff-transformer-show-only-basename nil
:buffer "*helm locate library*"))
;;; Modify variables from Helm
;;

View file

@ -86,18 +86,14 @@
uid 'face 'font-lock-warning-face))
key)))
(cl-defun helm-epa--select-keys (prompt keys)
(defun helm-epa--select-keys (prompt keys)
"A helm replacement for `epa--select-keys'."
(let ((result (helm :sources (helm-make-source "Epa select keys" 'helm-epa
:candidates (lambda ()
(helm-epa-get-key-list keys))
:action (lambda (_candidate)
(helm-marked-candidates)))
(helm-epa-get-key-list keys)))
:prompt (and prompt (helm-epa--format-prompt prompt))
:buffer "*helm epa*")))
(if (or (equal result "") (null result))
(cl-return-from helm-epa--select-keys
(error "No keys selected, aborting"))
(unless (equal result "")
result)))
(defun helm-epa--format-prompt (prompt)
@ -108,23 +104,13 @@
(replace-regexp-in-string "\\.[\t ]*\\'" "" (cadr split)))
(format "%s: " (replace-regexp-in-string "\\.[\t ]*\\'" "" (car split))))))
(defun helm-epa--read-signature-type-help ()
(with-temp-buffer
(save-excursion
(insert
"n: Create a normal signature)\n"
"c: Create a cleartext signature)\n"
"d: Create a detached signature)"))
(while (re-search-forward "^\\(.\\):" nil t)
(helm-add-face-text-properties (match-beginning 1) (match-end 1)
'font-lock-variable-name-face))
(buffer-string)))
(defun helm-epa--read-signature-type ()
"A helm replacement for `epa--read-signature-type'."
(let ((answer (helm-read-answer "Signature type? [n,c,d,h]"
'("n" "c" "d")
#'helm-epa--read-signature-type-help)))
(let ((answer (helm-read-answer "Signature type:
(n - Create a normal signature)
(c - Create a cleartext signature)
(d - Create a detached signature)"
'("n" "c" "d"))))
(helm-acase answer
("n" 'normal)
("c" 'clear)
@ -159,7 +145,7 @@
(progn
(advice-add 'epa--select-keys :override #'helm-epa--select-keys)
(advice-add 'epa--read-signature-type :override #'helm-epa--read-signature-type))
(advice-remove 'epa--select-keys #'helm-epa--select-keys)
(advice-remove 'epa-select-keys #'helm-epa--select-keys)
(advice-remove 'epa--read-signature-type #'helm-epa--read-signature-type)))
(defun helm-epa-action-transformer (actions _candidate)

View file

@ -290,7 +290,7 @@ at point."
(delete-char -1) (setq del-dot t)
(setq helm-ec-target (substring helm-ec-target 0 (1- (length helm-ec-target)))))
(cond ((eq first ?\()
(helm-lisp-completion-at-point))
(helm-lisp-completion-or-file-name-at-point))
;; In eshell `pcomplete-parse-arguments' is called
;; with `pcomplete-parse-arguments-function'
;; locally bound to `eshell-complete-parse-arguments'

View file

@ -23,8 +23,7 @@
(require 'edebug)
(declare-function helm-lisp-completion-at-point "helm-elisp.el")
(declare-function helm-elisp-show-doc-modeline "helm-elisp.el")
(defvar helm-elisp-help-function)
(defgroup helm-eval nil
"Eval related Applications and libraries for Helm."
@ -84,13 +83,8 @@ Should take one arg: the string to display."
(define-key map (kbd "<left>") #'backward-char)
map))
(defclass helm-evaluation-result-class (helm-source-dummy)
((echo-input-in-header-line
:initarg :echo-input-in-header-line
:initform 'never)))
(defun helm-build-evaluation-result-source ()
(helm-make-source "Evaluation Result" 'helm-evaluation-result-class
(helm-build-dummy-source "Evaluation Result"
:multiline t
:mode-line "C-RET: nl-and-indent, M-tab: reindent, C-tab:complete, C-p/n: next/prec-line."
:filtered-candidate-transformer

View file

@ -67,15 +67,16 @@ If this variable is not set by the user, it will be calculated
automatically.")
(defun helm-external-commands-list-1 (&optional sort)
"Return a list of all external commands the user can execute.
"Returns a list of all external commands the user can execute.
If `helm-external-commands-list' is non-nil it will return its
contents. Else it calculates all external commands and sets
`helm-external-commands-list'."
(or helm-external-commands-list
(helm-aif helm-external-commands-list
it
(setq helm-external-commands-list
(cl-loop for dir in (split-string (getenv "PATH") path-separator)
when (and (file-exists-p dir)
(file-accessible-directory-p dir))
(cl-loop
for dir in (split-string (getenv "PATH") path-separator)
when (and (file-exists-p dir) (file-accessible-directory-p dir))
for lsdir = (cl-loop for i in (directory-files dir t)
for bn = (file-name-nondirectory i)
when (and (not (member bn completions))

View file

@ -132,11 +132,10 @@
(cl-assert (executable-find helm-fd-executable) nil "Could not find fd executable")
(cl-assert (not (file-remote-p directory)) nil "Fd not supported on remote directories")
(let ((default-directory directory))
(helm :sources (helm-make-source "Fd" 'helm-fd-class
:header-name
(lambda (name)
(format "%s (%s)"
name (abbreviate-file-name default-directory))))
(helm :sources (helm-make-source
(format "fd (%s)"
(abbreviate-file-name default-directory))
'helm-fd-class)
:buffer "*helm fd*")))

View file

@ -50,7 +50,6 @@ Using `setq' to modify this variable will have no effect."
(define-key map (kbd "a") 'helm-apropos)
(define-key map (kbd "e") 'helm-etags-select)
(define-key map (kbd "l") 'helm-locate)
(define-key map (kbd "L") 'helm-locate-library)
(define-key map (kbd "s") 'helm-surfraw)
(define-key map (kbd "r") 'helm-regexp)
(define-key map (kbd "m") 'helm-man-woman)
@ -85,7 +84,6 @@ Using `setq' to modify this variable will have no effect."
(define-key map (kbd "b") 'helm-resume)
(define-key map (kbd "M-g i") 'helm-gid)
(define-key map (kbd "@") 'helm-packages)
(define-key map (kbd "h p") 'helm-finder)
map)
"Default keymap for \\[helm-command-prefix] commands.
The normal global definition of the character \\[helm-command-prefix] indirects to this keymap.")

View file

@ -533,16 +533,16 @@ Have no effect when grep backend use \"--color=\"."
(and rec-com rec-com-ack-p)))))))
(defun helm-grep--pipe-command-for-grep-command (smartcase pipe-switches &optional grep-cmd)
(helm-acase (or grep-cmd (helm-grep-command))
(pcase (or grep-cmd (helm-grep-command))
;; Use grep for GNU regexp based tools.
(("grep" "zgrep" "git-grep")
((or "grep" "zgrep" "git-grep")
(format "grep --color=always%s %s"
(if smartcase " -i" "")
pipe-switches))
;; Use ack-grep for PCRE based tools.
;; Sometimes ack-grep cmd is ack only so compare by matching ack.
((guard (string-match-p "ack" it))
(format "%s --smart-case --color %s" it pipe-switches))))
;; Sometimes ack-grep cmd is ack only.
((and (pred (string-match-p "ack")) ack)
(format "%s --smart-case --color %s" ack pipe-switches))))
(defun helm-grep--prepare-cmd-line (only-files &optional include zgrep)
(let* ((default-directory (or helm-ff-default-directory
@ -1196,7 +1196,7 @@ of grep."
:initform nil
:documentation
" The grep backend that will be used.
It is currently used only as an internal flag
It is actually used only as an internal flag
and doesn't set the backend by itself.
You probably don't want to modify this.")
(candidate-number-limit :initform 9999)
@ -1243,7 +1243,7 @@ Argument DEFAULT-INPUT is use as `default' arg of `helm' and
INPUT is used as `input' arg of `helm'. See `helm' docstring.
Arg BACKEND when non-nil specifies which backend to use.
It is used currently to specify \\='zgrep' or \\='git'.
It is used actually to specify \\='zgrep' or \\='git'.
When BACKEND \\='zgrep' is used don't prompt for a choice in
recurse, and ignore EXTS, search being made recursively on files
matching `helm-zgrep-file-extension-regexp' only."
@ -1635,12 +1635,8 @@ returns if available with current AG version."
(helm-default-directory)
default-directory))
(cmd-line (helm-grep-ag-prepare-cmd-line
;; NOTE Encode directory name and pattern,
;; or it may not work with Chinese and maybe other non-utf8
;; characters on MSWindows systems issue#2677 and issue#2678.
(encode-coding-string helm-pattern locale-coding-system)
(or (file-remote-p directory 'localname)
(encode-coding-string directory locale-coding-system))
helm-pattern (or (file-remote-p directory 'localname)
directory)
type))
(start-time (float-time))
(proc-name (helm-grep--ag-command)))
@ -1697,27 +1693,18 @@ returns if available with current AG version."
proc-name
(replace-regexp-in-string "\n" "" event))))))))))
(defvar helm-grep-ag-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map helm-grep-map)
(define-key map (kbd "C-s") 'helm-grep-run-ag-grep-parent-directory)
map))
(defclass helm-grep-ag-class (helm-source-async)
((nohighlight :initform t)
(pcre :initarg :pcre :initform t
:documentation
" Backend is using pcre regexp engine when non--nil.")
(keymap :initform 'helm-grep-ag-map)
(keymap :initform 'helm-grep-map)
(history :initform 'helm-grep-ag-history)
(help-message :initform 'helm-grep-help-message)
(filtered-candidate-transformer :initform #'helm-grep-fc-transformer)
(persistent-action :initform 'helm-grep-persistent-action)
(persistent-help :initform "Jump to line (`C-u' Record in mark ring)")
(candidate-number-limit :initform 99999)
(directory :initarg :directory :initform nil
:documentation
" Directory currently searched.")
(requires-pattern :initform 2)
(nomark :initform t)
(action :initform 'helm-grep-actions)
@ -1741,30 +1728,16 @@ If INPUT is provided, use it as the search string."
:header-name (lambda (name)
(format "%s [%s]"
name (abbreviate-file-name directory)))
:directory directory
:action (append helm-grep-actions
`((,(format "%s grep parent directory"
(upcase (helm-grep--ag-command)))
. helm-grep-ag-grep-parent-directory)))
:candidates-process
(lambda () (helm-grep-ag-init directory type))))
(helm-set-local-variable 'helm-input-idle-delay helm-grep-input-idle-delay)
(helm :sources 'helm-source-grep-ag
:keymap helm-grep-map
:history 'helm-grep-ag-history
:input input
:truncate-lines helm-grep-truncate-lines
:buffer (format "*helm %s*" (helm-grep--ag-command))))
(defun helm-grep-ag-grep-parent-directory (_candidate)
"Restart helm-grep-ag in the parent of the currently searched directory."
(let* ((src (with-helm-buffer (car helm-sources)))
(directory (helm-basedir (helm-get-attr 'directory src) t))
(input helm-pattern))
(helm-grep-ag-1 directory nil input)))
(helm-make-command-from-action helm-grep-run-ag-grep-parent-directory
"Ag grep parent directory." 'helm-grep-ag-grep-parent-directory)
(defun helm-grep-ag (directory with-types)
"Start grep AG in DIRECTORY.
When WITH-TYPES is non-nil provide completion on AG types."

View file

@ -588,10 +588,10 @@ On completion (\\[helm-ff-run-complete-fn-at-point]):
Use of wildcard is supported to run an action over a set of files.
Example: You can copy all the files with \".el\" extension by using \"*.el\" and
Example: You can copy all the files with \".el\" extension by using \"*.el\" and
then run copy action.
Similarly, \"**.el\" (note the two stars) will recursively select all \".el\"
Similarly, \"**.el\" (note the two stars) will recursively select all \".el\"
files under the current directory.
Note that when recursively copying files, you may have files with same name
@ -606,7 +606,7 @@ to backup files in current directory.
This command is available only when `dired-async-mode' is active.
When using an action that involves an external backend (e.g. grep), using \"**\"
is not recommended (even though it works fine) because it will be slower to
is not recommended (even thought it works fine) because it will be slower to
select all the files. You are better off leaving the backend to do it, it will
be faster. However, if you know you have not many files it is reasonable to use
this, also using not recursive wildcard (e.g. \"*.el\") is perfectly fine for
@ -614,14 +614,14 @@ this.
The \"**\" feature is active by default in the option `helm-file-globstar'. It
is different from the Bash \"shopt globstar\" feature in that to list files with
a named extension recursively you would write \"**.el\" whereas in Bash it would
be \"**/*.el\". Directory selection with \"**/\" like Bash \"shopt globstar\"
a named extension recursively you would write \"**.el\" whereas in Bash it would
be \"**/*.el\". Directory selection with \"**/\" like Bash \"shopt globstar\"
option is not supported yet.
Helm supports different styles of wildcards:
- `sh' style, the ones supported by `file-expand-wildcards'.
e.g. \"*.el\", \"*.[ch]\" which match respectively all \".el\"
e.g. \"*.el\", \"*.[ch]\" which match respectively all \".el\"
files or all \".c\" and \".h\" files.
- `bash' style (partially) In addition to what allowed in `sh'
@ -807,7 +807,7 @@ instead its scp method if you want to avoid out of memory
problems and crash Emacs or the whole system. Moreover when using
scp method, you will hit a bug when copying more than 3 files at
the time, see [[https://github.com/emacs-helm/helm/issues/1945][bug#1945]].
The best way currently is using Rsync to copy files from or to
The best way actually is using Rsync to copy files from or to
remote, see [[Use Rsync to copy files][Use Rsync to copy files]].
Also if you often work on remote you may consider using SSHFS
instead of relying on tramp.
@ -873,7 +873,7 @@ rsync command line with a prefix arg (see above).
Since Android doesn't provide anymore mass storage for USB, it is
not simple to access files on Android, the best way to do this
currently seems to use Adb, here some hints to set this up, read
actually seems to use Adb, here some hints to set this up, read
in addition the Tramp documentation.
1) Install Adb, most distribution provide it.
@ -915,40 +915,6 @@ On remote files grep is not well supported by TRAMP unless you suspend updates b
entering the pattern and re-enable it once your pattern is ready.
To toggle suspend-update, use `\\<helm-map>\\[helm-toggle-suspend-update]'.
*** Compressing or uncompressing files from helm-find-files
**** Compressing/uncompressing using Helm commands
Helm provide commands like dired (reusing dired code)
to (un)compress files from `helm-find-files', however these
commands are asynchronous.
You can use `\\<helm-find-files-map>\\[helm-ff-run-compress-marked-files]' to compress marked files.
To compress file(s) to an archive use `\\<helm-find-files-map>\\[helm-ff-run-compress-to]'.
To quickly compress/uncompress small files without quitting Helm use `\\<helm-find-files-map>\\[helm-ff-persistent-compress]'.
NOTE: This persistent action is NOT asynchronous, IOW it will block Helm
for a while until compression/uncompression finish.
**** Compressing/uncompressing using external commands in Eshell
You can use Eshell aliases to uncompress files,
see [[Execute Eshell commands on files][Execute Eshell commands on files]] for more infos.
Here some aliases using commands from the excellent =atools= package:
alias pack2zip apack -e -F .zip $* &
alias pack2gz apack -e -F .tar.gz $* &
alias pack2bz apack -e -F .tar.bz $* &
alias pack2xz apack -e -F .tar.xz $* &
alias unpack aunpack $1 &
Note the \"&\" at end of commands that make eshell aliases asynchronous.
NOTE: Using the ampersand at end of command to make it asynchronous is broken
in all emacs versions before emacs-28 (see emacs bug#50209).
Of course you can use any other commands of your choice as aliases.
*** Execute Eshell commands on files
Setting up aliases in Eshell allows you to set up powerful customized commands.
@ -1040,9 +1006,9 @@ Starting at helm version 2.9.7 it is somewhat possible to
colorize fnames by listing files without loosing performances with
external commands (ls and awk) if your system is compatible.
For this you can use `helm-list-dir-external' as value
for `helm-list-remote-directory-fn'.
for `helm-list-directory-function'.
See `helm-list-remote-directory-fn' documentation for more infos.
See `helm-list-directory-function' documentation for more infos.
**** Completing host
@ -1301,10 +1267,7 @@ If `all-the-icons' package is installed, turning on
|\\[helm-ff-sort-by-size]|Sort by size.
|\\[helm-ff-toggle-dirs-only]|Show only directories.
|\\[helm-ff-toggle-files-only]|Show only files.
|\\[helm-ff-sort-by-ext]|Sort by extensions.
|\\[helm-ff-run-compress-to]|Compress file(s) to archive.
|\\[helm-ff-run-compress-marked-files]|Compress file(s).
|\\[helm-ff-persistent-compress]|Compress file(s) without quitting.")
|\\[helm-ff-sort-by-ext]|Sort by extensions.")
;;; Help for file-name-history
;;
@ -2288,15 +2251,6 @@ See [[Moving in `helm-buffer'][Moving in `helm-buffer']].
(defvar helm-top-help-message
"* Helm Top
** Tips
*** Auto update
You can enable auto updating in `helm-top' by turning on
`helm-top-poll-mode' either interactively or in your init file
with (helm-top-poll-mode 1).
Calling `helm-top' with a prefix arg also toggle auto updating.
** Commands
\\<helm-top-map>
|Keys|Description

View file

@ -100,7 +100,7 @@ Don't use `setq' to set this."
:group 'helm-imenu
:type 'boolean
:set (lambda (var val)
(if (require 'all-the-icons nil t)
(if (featurep 'all-the-icons)
(set var val)
(set var nil))))
@ -111,17 +111,17 @@ Don't use `setq' to set this."
:group 'helm-imenu
:type 'boolean
:set (lambda (var val)
(if (require 'all-the-icons nil t)
(if (featurep 'all-the-icons)
(set var val)
(set var nil))))
(defcustom helm-imenu-icon-type-alist
'(("Array" . (all-the-icons-material "crop" :face font-lock-builtin-face))
("Arrays" . (all-the-icons-material "crop" :face font-lock-builtin-face))
("Array" . (all-the-icons-material "crop" :face font-lock-builtin-face))
("Boolean" . (all-the-icons-material "crop" :face font-lock-builtin-face))
("Boolean" . (all-the-icons-material "crop" :face font-lock-builtin-face))
("Booleans" . (all-the-icons-material "crop" :face font-lock-builtin-face))
("Class" . (all-the-icons-octicon "package" :face font-lock-type-face))
("Classes" . (all-the-icons-octicon "package" :face font-lock-type-face))
("Class" . (all-the-icons-octicon "package" :face font-lock-type-face))
("Color" . (all-the-icons-material "color_lens" :face font-lock-builtin-face))
("Colors" . (all-the-icons-material "color_lens" :face font-lock-builtin-face))
("Constant" . (all-the-icons-material "crop" :face font-lock-builtin-face))
@ -157,7 +157,7 @@ Don't use `setq' to set this."
("Module" . (all-the-icons-faicon "angle-double-right" :face font-lock-builtin-face))
("Modules" . (all-the-icons-faicon "angle-double-right" :face font-lock-builtin-face))
("Numeric" . (all-the-icons-material "crop" :face font-lock-builtin-face))
("Numerics" . (all-the-icons-material "crop" :face font-lock-builtin-face))
("Numeric" . (all-the-icons-material "crop" :face font-lock-builtin-face))
("Object" . (all-the-icons-faicon "angle-double-right" :face font-lock-builtin-face))
("Objects" . (all-the-icons-faicon "angle-double-right" :face font-lock-builtin-face))
("Operator" . (all-the-icons-faicon "calculator" :face font-lock-builtin-face))
@ -167,7 +167,7 @@ Don't use `setq' to set this."
("Reference" . (all-the-icons-octicon "book" :face font-lock-variable-name-face))
("References" . (all-the-icons-octicon "book" :face font-lock-variable-name-face))
("Snippet" . (all-the-icons-material "border_style" :face font-lock-variable-name-face))
("Snippets" . (all-the-icons-material "border_style" :face font-lock-variable-name-face))
("Snippet" . (all-the-icons-material "border_style" :face font-lock-variable-name-face))
("String" . (all-the-icons-material "text_fields" :face font-lock-variable-name-face))
("Strings" . (all-the-icons-material "text_fields" :face font-lock-variable-name-face))
("Struct" . (all-the-icons-faicon "cog" :face font-lock-type-face))
@ -320,8 +320,7 @@ The sexp should be an `all-the-icons' function with its args."
(if (equal (cdr cur) mb)
(prog1 nil
(helm-set-pattern "")
(helm-force-update
(concat "\\_<" (regexp-quote (car cur)) "\\_>")))
(helm-force-update (concat "\\_<" (car cur) "\\_>")))
t)))
(defun helm-imenu-quit-and-find-file-fn (source)
@ -407,11 +406,12 @@ The sexp should be an `all-the-icons' function with its args."
(and (cdr elm)
;; Semantic uses overlays whereas imenu uses
;; markers (Bug#1706).
(setcdr elm (helm-acase (cdr elm) ; Same as [1].
((guard (overlayp it))
(copy-overlay it))
((guard (or (markerp it) (integerp it)))
(copy-marker it))))
(setcdr elm (pcase (cdr elm) ; Same as [1].
((and ov (pred overlayp))
(copy-overlay ov))
((and mk (or (pred markerp)
(pred integerp)))
(copy-marker mk))))
(list elm))))))
(defun helm-imenu--get-prop (item)
@ -443,9 +443,10 @@ The icon is found in `helm-imenu-icon-type-alist', if not
(cl-loop for (k . v) in candidates
;; (k . v) == (symbol-name . marker)
for bufname = (buffer-name
(helm-acase v
((guard (overlayp it)) (overlay-buffer it))
((guard (markerp it)) (marker-buffer it))))
(pcase v
((pred overlayp) (overlay-buffer v))
((or (pred markerp) (pred integerp))
(marker-buffer v))))
for types = (or (helm-imenu--get-prop k)
(list (if (with-current-buffer bufname
(derived-mode-p 'prog-mode))

Some files were not shown because too many files have changed in this diff Show more