doom, whatever

This commit is contained in:
i.ortega 2020-06-17 22:43:39 +02:00
parent fb77c998d8
commit d2d1b1b682
9 changed files with 6818 additions and 4 deletions

View File

@ -0,0 +1,55 @@
(TeX-add-style-hook
"README"
(lambda ()
(TeX-add-to-alist 'LaTeX-provided-class-options
'(("article" "11pt")))
(TeX-add-to-alist 'LaTeX-provided-package-options
'(("inputenc" "utf8") ("fontenc" "T1") ("ulem" "normalem")))
(add-to-list 'LaTeX-verbatim-macros-with-braces-local "href")
(add-to-list 'LaTeX-verbatim-macros-with-braces-local "hyperref")
(add-to-list 'LaTeX-verbatim-macros-with-braces-local "hyperimage")
(add-to-list 'LaTeX-verbatim-macros-with-braces-local "hyperbaseurl")
(add-to-list 'LaTeX-verbatim-macros-with-braces-local "nolinkurl")
(add-to-list 'LaTeX-verbatim-macros-with-braces-local "url")
(add-to-list 'LaTeX-verbatim-macros-with-braces-local "path")
(add-to-list 'LaTeX-verbatim-macros-with-delims-local "path")
(TeX-run-style-hooks
"latex2e"
"article"
"art11"
"inputenc"
"fontenc"
"graphicx"
"grffile"
"longtable"
"wrapfig"
"rotating"
"ulem"
"amsmath"
"textcomp"
"amssymb"
"capt-of"
"hyperref")
(LaTeX-add-labels
"sec:org5353487"
"sec:orgd6bd057"
"sec:orgf9e85bd"
"sec:org77e416c"
"sec:orgdeecf62"
"sec:org7503de0"
"sec:org50def99"
"sec:orgd580bdf"
"sec:org9fa885d"
"sec:org0b3b8d4"
"sec:org6f2c868"
"sec:org3a32ad2"
"sec:org59d77af"
"sec:org8e3b2e1"
"sec:org55dfd9d"
"sec:org18f354e"
"sec:org7f4a24e"
"sec:orgb0c6ce7"
"sec:org7483384"
"sec:orgfe99b0a"))
:latex)

View File

@ -1,5 +1,3 @@
(setq user-full-name "inigoortega"
user-mail-address "inigoortega@tutanota.com")
;; -*- lexical-binding: t -*-
(setq doom-font (font-spec :family "monospace" :width 'normal :size 17)
doom-variable-pitch-font (font-spec :family "monospace"))
@ -157,6 +155,32 @@
(interactive)
(if (string-match-p "^ " (buffer-name))
(rename-buffer (concat " " (buffer-name)))))
(defun dfeich/ansi-terminal (&optional path name)
"Opens an ansi terminal at PATH. If no PATH is given, it uses
the value of `default-directory'. PATH may be a tramp remote path.
The ansi-term buffer is named based on `name' "
(interactive)
(unless path (setq path default-directory))
(unless name (setq name "ansi-term"))
(ansi-term "/bin/bash" name)
(let ((path (replace-regexp-in-string "^file:" "" path))
(cd-str
"fn=%s; if test ! -d $fn; then fn=$(dirname $fn); fi; cd $fn;")
(bufname (concat "*" name "*" )))
(if (tramp-tramp-file-p path)
(let ((tstruct (tramp-dissect-file-name path)))
(cond
((equal (tramp-file-name-method tstruct) "ssh")
(process-send-string bufname (format
(concat "ssh -t %s '"
cd-str
"exec bash'; exec bash; clear\n")
(tramp-file-name-host tstruct)
(tramp-file-name-localname tstruct))))
(t (error "not implemented for method %s"
(tramp-file-name-method tstruct)))))
(process-send-string bufname (format (concat cd-str " exec bash;clear\n")
path)))))
(defun ins-mode (str)
(interactive)
(setq f (gethash major-mode insert-on-mode))
@ -330,8 +354,17 @@ instead."
(map! :n "SPC f z" #'counsel-fzf)
(map! :n "SPC b j" #'ivy-switch-buffer)
(map! :n "SPC j j" #'ivy-switch-buffer)
(map! :n "SPC o s" #'ansi-term)
(map! :map 'ranger-mode-map :g "SPC b j" #'ivy-switch-buffer)
(map! :map 'ranger-mode-map :g "SPC j j" #'ivy-switch-buffer)
(map! :n "SPC o c" #'calendar)
(map! :n "SPC o s" #'ansi-term)
(map! :map 'ranger-mode-map :g "SPC o c" #'calendar)
(map! :map 'ranger-mode-map :g "SPC o s"
(lambda() (interactive)
(if (file-remote-p default-directory)
(dfeich/ansi-terminal)
(ansi-term my-term-shell))))
(map! :n "SPC j h" (lambda () (interactive) (ansi-term "htop")))
(defun mark-real-user-buffer()
(interactive)
@ -469,6 +502,9 @@ instead."
(calendar-set-date-style 'european)))
(setq calendar-week-start-day 1)
(setq flycheck-flake8-maximum-line-length 80)
(add-to-list 'load-path "~/.doom.d/lisp/")
(load "rate-sx")
(load "sunrise")
(after! haskell-mode
(progn
(setq
@ -529,3 +565,9 @@ instead."
(highlight-words-on-mode 'sh-mode sh-symbols)
;; (add-to-list 'TeX-mode-hook #'TeX-fold-buffer)
;; (add-hook! 'tex-mode-hook #'TeX-fold-buffer)
(defun my-shell ()
(interactive)
(let ((default-directory "/ssh:initega@192.168.0.28:"))
(eshell)))
(setq user-full-name "inigoortega"
user-mail-address "inigoortega@tutanota.com")

View File

@ -223,6 +223,36 @@ Xah Lee, read .bashrc:
(rename-buffer (concat " " (buffer-name)))))
#+END_SRC
Ansi-term for TRAMP:
#+BEGIN_SRC emacs-lisp
(defun dfeich/ansi-terminal (&optional path name)
"Opens an ansi terminal at PATH. If no PATH is given, it uses
the value of `default-directory'. PATH may be a tramp remote path.
The ansi-term buffer is named based on `name' "
(interactive)
(unless path (setq path default-directory))
(unless name (setq name "ansi-term"))
(ansi-term "/bin/bash" name)
(let ((path (replace-regexp-in-string "^file:" "" path))
(cd-str
"fn=%s; if test ! -d $fn; then fn=$(dirname $fn); fi; cd $fn;")
(bufname (concat "*" name "*" )))
(if (tramp-tramp-file-p path)
(let ((tstruct (tramp-dissect-file-name path)))
(cond
((equal (tramp-file-name-method tstruct) "ssh")
(process-send-string bufname (format
(concat "ssh -t %s '"
cd-str
"exec bash'; exec bash; clear\n")
(tramp-file-name-host tstruct)
(tramp-file-name-localname tstruct))))
(t (error "not implemented for method %s"
(tramp-file-name-method tstruct)))))
(process-send-string bufname (format (concat cd-str " exec bash;clear\n")
path)))))
#+END_SRC
ii mode:
#+BEGIN_SRC emacs-lisp
(defun ins-mode (str)
@ -424,11 +454,20 @@ zsh as ansi-term shell
#+BEGIN_SRC emacs-lisp
(map! :n "SPC b j" #'ivy-switch-buffer)
(map! :n "SPC j j" #'ivy-switch-buffer)
(map! :map 'ranger-mode-map :g "SPC b j" #'ivy-switch-buffer)
(map! :map 'ranger-mode-map :g "SPC j j" #'ivy-switch-buffer)
#+END_SRC
#+BEGIN_SRC emacs-lisp
(map! :n "SPC o s" #'ansi-term)
(map! :n "SPC o c" #'calendar)
(map! :n "SPC o s" #'ansi-term)
(map! :map 'ranger-mode-map :g "SPC o c" #'calendar)
(map! :map 'ranger-mode-map :g "SPC o s"
(lambda() (interactive)
(if (file-remote-p default-directory)
(dfeich/ansi-terminal)
(ansi-term my-term-shell))))
#+END_SRC
Emacs powered window manager keybinding clones:
@ -638,6 +677,12 @@ Interesting buffers filter:
#+BEGIN_SRC emacs-lisp
(setq flycheck-flake8-maximum-line-length 80)
#+END_SRC
** Personal packages
#+BEGIN_SRC emacs-lisp
(add-to-list 'load-path "~/.doom.d/lisp/")
(load "rate-sx")
(load "sunrise")
#+END_SRC
** Haskell
Haskell-mode configs:
# ;; Ancient code for hindent
@ -745,6 +790,13 @@ Highlight predule:
;; (add-to-list 'TeX-mode-hook #'TeX-fold-buffer)
;; (add-hook! 'tex-mode-hook #'TeX-fold-buffer)
#+END_SRC
** TRAMP
#+BEGIN_SRC emacs-lisp
(defun my-shell ()
(interactive)
(let ((default-directory "/ssh:initega@192.168.0.28:"))
(eshell)))
#+END_SRC
* Doom info
Here are some additional functions/macros that could help you configure Doom:

165
.doom.d/lisp/rate-sx.el Normal file
View File

@ -0,0 +1,165 @@
;;; rate-sx.el --- Show currency rates from rate.sx -*- lexical-binding: t -*-
;; Copyright 2017-2018 by Dave Pearson <davep@davep.org>
;; Author: Dave Pearson <davep@davep.org>
;; Version: 1.4
;; Keywords: comm, currency, bitcoin, money
;; URL: https://github.com/davep/rate-sx.el
;; Package-Requires: ((emacs "24.4"))
;; 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:
;;
;; `rate-sx.el' provides a main command, `rate-sx', which displays the
;; output of http://rate.sx in a buffer.
;;
;; Other commands provide ways to quickly calculate currency totals, they
;; include:
;;
;; `rate-sx-calc'
;;
;; Show the result of a currency calculation in the minibuffer. Calculations
;; are things like "1BTC+12ETH" (would show the total value, in the base
;; currency defined by `rate-sx-default-currency', of holding 1 BTC and 12
;; ETH).
;;
;; `rate-sx-calc-region'
;;
;; Same as above but takes the input from the content of the marked region.
;;
;; `rate-sx-calc-maybe-region'
;;
;; Same as above again, but performs `rate-sx-calc-region' if there is an
;; active mark, otherwise it performs `rate-sx-calc'.
;;; Code:
(require 'ansi-color)
(require 'url-util)
(defconst rate-sx-url "http://%srate.sx/%s"
"URL for rate.sx.")
(defconst rate-sx-user-agent "rate-sx.el (https://github.com/davep/rate-sx.el) (curl)"
"User agent to send to the rate.sx server.")
(defconst rate-sx-buffer "*rate.sx*"
"Name of the output buffer.")
(defvar rate-sx-currencies '(www btc eth)
"List of currencies that rate.sx can convert to.
Don't use this directly. Use the function `rate-sx-currencies'
instead.
See http://rate.sx/:help for more details.")
(defvar rate-sx-options '(btc@7d eth@7d btc eth btc@31d eth@31d btc@1y eth@1y)
"List of options that rate.sx can convert to.
Don't use this directly. Use the function `rate-sx-currencies'
instead.
See http://rate.sx/:help for more details.")
(defvar rate-sx-default-option nil
"The default display option when calling rate.sx.
If nil the default option as used by rate.sx itself will be
used")
(defvar rate-sx-default-currency nil
"The default display currency when calling rate.sx.
If nil the default currency as used by rate.sx itself will be
used. See function `rate-sx-currencies' or the help screen of rate.sx
itself for more currency options.")
(defun rate-sx-get (&optional currency params)
"Get the output from rate.sx.
Values will be acquired in CURRENCY, or the default currency of
rate.sx will be used if one isn't supplied.
PARAMS will be added to the end of `rate-sx-url' if they are supplied."
(let* ((url-mime-accept-string "text/plain")
(url-request-extra-headers `(("User-Agent" . ,rate-sx-user-agent)))
(url-show-status nil))
(with-temp-buffer
(url-insert-file-contents (format rate-sx-url
(if currency
(concat currency ".")
"")
(or params "")))
(buffer-string))))
(defun rate-sx-currencies-to-alist (rates)
"Convert RATES list from rate.sx into an assoc list."
(let ((arates))
(while rates
(when (not (string= "" (car rates)))
(setq arates (append arates (list (cons (car rates) (cadr rates))))))
(setq rates (cddr rates)))
arates))
(defun rate-sx-get-currencies ()
"Get the currency list from the rate.sx site."
(rate-sx-currencies-to-alist (split-string (rate-sx-get nil ":currencies") "\\( \\|\n\\)")))
(defun rate-sx-currencies ()
"Return the list of (non-crypto) currencies."
(or rate-sx-currencies (setq rate-sx-currencies (rate-sx-get-currencies))))
;;;###autoload
(defun rate-sx (currency option)
"Show the current output of rate.sx in a new buffer.
If CURRENCY is non-nil, this command will prompt for a display currency."
(interactive
(list (if current-prefix-arg
(completing-read "Currency: " rate-sx-currencies nil t)
rate-sx-default-currency)
(if current-prefix-arg
(completing-read "Option: " rate-sx-options nil t)
rate-sx-default-option)))
(with-help-window rate-sx-buffer
(with-current-buffer rate-sx-buffer
(insert (ansi-color-apply (rate-sx-get currency option))))))
;;;###autoload
(defun rate-sx-calc (calc)
"Evaluate CALC via rate.sx.
The result is given in `rate-sx-default-currency'."
(interactive "sCalc: ")
(message "Result: %s" (string-trim (rate-sx-get rate-sx-default-currency (url-hexify-string calc)))))
;;;###autoload
(defun rate-sx-calc-region (start end)
"Perform `rate-sx-calc' on text in region START to END."
(interactive "r")
(rate-sx-calc (buffer-substring-no-properties start end)))
;;;###autoload
(defun rate-sx-calc-maybe-region ()
"Perform a rate calculation on a region if one is active.
If one isn't active, prompt for the calculation."
(interactive)
(call-interactively (if mark-active #'rate-sx-calc-region #'rate-sx-calc)))
(provide 'rate-sx)
;;; rate-sx.el ends here

View File

@ -0,0 +1,610 @@
;;; sunrise-mirror.el --- Compressed archive support for the Sunrise Commander -*- lexical-binding: t -*-
;; Copyright (C) 2008-2012 José Alfredo Romero Latouche.
;; Author: José Alfredo Romero Latouche <escherdragon@gmail.com>
;; Štěpán Němec <stepnem@gmail.com>
;; Maintainer: José Alfredo Romero Latouche <escherdragon@gmail.com>
;; Created: 4 May 2008
;; Version: 2
;; Package-Requires: ((emacs "24.3") (cl-lib "0.5"))
;; Keywords: files, sunrise commander, archives read/write
;; URL: https://github.com/sunrise-commander/sunrise-commander
;; 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 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 de- tails.
;; 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:
;; This is an extension for the Sunrise Commander file manager (for more
;; details visit http://www.emacswiki.org/emacs/Sunrise_Commander), that
;; allows browsing compressed archives in full read-write mode. Sunrise does
;; offer means for transparent browsing archives (using AVFS), but they just
;; provide read-only navigation -- if you want to edit a file inside the
;; virtual filesystem, copy, remove, or rename anything, you still have to
;; uncompress the archive, do the stuff and compress it back yourself.
;; It uses one or unionfs-fuse or funionfs to create a writeable overlay on
;; top of the read-only filesystem provided by AVFS. You can freely add,
;; remove or modify anything inside the resulting union filesystem (a.k.a. the
;; "mirror area"), and then commit all modifications (or not) to the original
;; archive with a single keystroke. There is no preliminary uncompressing of
;; the archive and nothing happens if you don't make changes (or if you don't
;; commit them). On commit, the contents of the union fs are compressed to
;; create an updated archive to replace the original one (optionally after
;; making a backup copy of it, just in case).
;; Navigating outside a mirror area will automatically close it, so if you do
;; it you may be asked whether to commit or not to the archive all your
;; changes. In nested archives (e.g. a jar inside a zip inside a tgz), partial
;; modifications are committed silently on the fly if moving out from a
;; modified archive to one that contains it. Only if you leave the topmost
;; mirror area you will be asked for confirmation whether to modify the
;; resulting archive.
;; Be warned, though, that this method may be impractical for very large or
;; very deeply nested archives with strong compression, since the
;; uncompressing happens in the final stage and requires multiple access
;; operations through AVFS. What this means is that probably you'll have to
;; wait a looooong time if you try to commit changes to a tar.bz2 file with
;; several hundreds of megabytes in size, or under five or six other layers of
;; strong compression.
;; For this extension to work you must have:
;; 1) FUSE + AVFS support in your Sunrise Commander. If you can navigate
;; (read- only) inside compressed archives you already have this.
;; 2) One of unionfs-fuse or funionfs. Debian squeeze (stable) offers a
;; package for the first, which is currently the recommended implementation.
;; 3) Programs required for repacking archives -- at least zip and tar.
;; 4) Your AVFS mount point (and the value of variable `sunrise-avfs-root')
;; must be in a directory where you have writing access.
;; All this means is that most probably this extension will work
;; out-of-the-box on Linux (or MacOS, or other unices), but you'll have a hard
;; time to make it work on Windows. It was written on GNU Emacs 23 on Linux
;; and tested on GNU Emacs 22 and 23 for Linux.
;;; Installation and Usage:
;; 1) Put this file somewhere in your Emacs `load-path'.
;; 2) Add a (require 'sunrise-mirror) to your .emacs file, anywhere after the
;; (require 'sunrise) sexp.
;; 3) Evaluate the new expression, or reload your .emacs file, or restart
;; Emacs.
;; 4) Customize the variable `sunrise-mirror-unionfs-impl' and select your
;; preferred unionfs implementation (either unionfs-fuse or funionfs).
;; 5) Run the Sunrise Commander (M-x sunrise), select (or navigate inside) any
;; compressed directory in the active pane and press C-c C-b. This will
;; automatically take you to the mirror area for the selected archive. You can
;; make any modifications you want to the contents of the archive, or navigate
;; inside directories or other compressed archives inside it. When you're
;; done, press again C-c C-b anywhere inside the mirror area, or simply
;; navigate out of it. If there are any changes to commit (*and* if you
;; confirm) the original archive will be replaced with a new one with the
;; contents of the mirror area you've just been working on. If you don't
;; change the defaults, the original will be renamed with a ".bak" extension
;; added.
;; 6) You can add support for new archive formats by adding new entries to the
;; `sunrise-mirror-pack-commands-alist' custom variable, which contains a
;; regular expression to match against the name of the archive and a string
;; containing the shell command to execute for packing back the mirror area
;; into a compressed archive.
;; 7) Once you've gained enough confidence using this extension you can reset
;; the `sunrise-mirror-keep-backups' flag to get rid of all the backup copies
;; produced by it.
;; 8) Enjoy ;)
;;; Code:
(eval-when-compile
(require 'cl-lib))
(require 'sunrise)
(defcustom sunrise-mirror-keep-backups t
"If non-nil, keep backup files when committing changes to read-only archives."
:group 'sunrise
:type 'boolean)
(defcustom sunrise-mirror-pack-commands-alist
'(
("\\.\\(?:zip\\|xpi\\|apk\\)$" . "zip -r %f *")
("\\.[jwesh]ar$" . "zip -r %f *")
("\\.tar$" . "tar cvf %f *")
("\\.\\(?:tar\\.gz\\|tgz\\)$" . "tar cvzf %f *")
("\\.tar\\.bz2$" . "tar cvjf %f *")
("\\.\\(?:tar\\.xz\\|txz\\)$" . "tar cvJf %f *")
)
"List of shell commands to repack particular archive contents.
Used when repacking contents from a mirror area into a compressed
archive of the appropriate type. Use %f as a placeholder for the
name of the resulting archive. If no repacking command has been
registered here for a file (usually a file extension), Sunrise
will refuse to create a mirror area for it even if it is normally
browseable through AVFS."
:group 'sunrise
:type 'alist)
(defcustom sunrise-mirror-unionfs-impl 'unionfs-fuse
"Implementation of unionfs to use for creating mirror areas."
:group 'sunrise
:type '(choice (const :tag "unionfs-fuse" unionfs-fuse)
(const :tag "funionfs" funionfs)))
(defface sunrise-mirror-path-face
'((t (:background "blue" :foreground "yellow" :bold t :height 120)))
"Face of the directory path inside mirror areas."
:group 'sunrise)
(defvar sunrise-mirror-home nil
"Root directory of all mirror areas.
Set automatically by the function `sunrise-mirror-enable' and reset by
`sunrise-mirror-disable' to keep the mirror home path, as well as to
indicate mirroring support is on/off. Do not mess with it
directly - if you need to change the name of your mirror home
dir, modify `sunrise-mirror-enable'.")
(defvar sunrise-mirror-divert-goto-dir t
"Internal variable used to avoid infinite recursion.
Used when diverting `sunrise-goto-dir' calls to `sunrise-mirror-goto-dir'.
Do not touch, or else.")
(if (boundp 'sunrise-mode-map)
(define-key sunrise-mode-map "\C-c\C-b" 'sunrise-mirror-toggle))
(defun sunrise-mirror-enable ()
"Enable Sunrise mirror support.
Sets the variable `sunrise-mirror-home' to a non-nil value and
activates all advice necessary for mirror operations. This method
is called every time a new mirror area is created."
(unless sunrise-mirror-home
(setq sunrise-mirror-home (concat sunrise-avfs-root "#mirror#/"))
(ad-activate 'make-directory)
(ad-activate 'save-buffer)
(ad-activate 'sunrise-goto-dir)))
(defun sunrise-mirror-disable ()
"Disable Sunrise mirror support.
Resets `sunrise-mirror-home' and deactivates all advice used in
mirror operations. This method is called after the last mirror
area in the current mirror home is closed."
(when sunrise-mirror-home
(setq sunrise-mirror-home nil)
(ad-deactivate 'make-directory)
(ad-deactivate 'save-buffer)
(ad-deactivate 'sunrise-goto-dir)))
(defun sunrise-mirror-open ()
"Set up a mirror area in the current pane.
Uses unionfs-fuse to create a writeable filesystem overlay over
the AVFS virtual filesystem of the selected compressed archive
and displays it in the current pane. The result is a mirror of
the contents of the original archive that is fully writeable."
(interactive)
(let ((path (or (dired-get-filename nil t)
(concat (expand-file-name (dired-current-directory)) "/.")))
(sunrise-mirror-divert-goto-dir nil)
(sunrise-avfs-root (expand-file-name sunrise-avfs-root))
fname vpaths)
(if (sunrise-overlapping-paths-p sunrise-avfs-root path)
(unless (and sunrise-mirror-home
(sunrise-overlapping-paths-p sunrise-mirror-home path))
(setq path (substring path (length sunrise-avfs-root))
vpaths (split-string path "#[^/]*/")
path (car vpaths)
vpaths (cdr vpaths))))
(setq fname (file-name-nondirectory path))
(if (null (assoc-default
fname sunrise-mirror-pack-commands-alist 'string-match))
(error (concat "Sunrise: sorry, no packer was registered for "
fname)))
(sunrise-mirror-enable)
(unless (file-exists-p sunrise-mirror-home)
(make-directory sunrise-mirror-home))
(if vpaths
(mapc (lambda (x)
(let ((sunrise-mirror-divert-goto-dir nil))
(sunrise-goto-dir (sunrise-mirror-mount path))
(sunrise-follow-file x)
(setq path (dired-get-filename))))
vpaths)
(sunrise-goto-dir (sunrise-mirror-mount path)))
(sunrise-graphical-highlight 'sunrise-mirror-path-face)
(add-hook 'kill-buffer-hook 'sunrise-mirror-on-kill-buffer)
t ))
(defun sunrise-mirror-mount (path)
"Create and mount (if necessary) all the directories needed to mirror PATH.
PATH identifies the compressed archive. Returns the path to the
corresponding mirror area."
(let* ((base (sunrise-mirror-mangle path))
(virtual (sunrise-mirror-full-demangle path))
(mirror (concat sunrise-mirror-home base))
(overlay (concat sunrise-mirror-home "." base))
(command
(cl-case sunrise-mirror-unionfs-impl
(unionfs-fuse
(concat "cd ~; unionfs-fuse -o cow,kernel_cache -o allow_other "
overlay "=RW:" virtual "=RO " mirror))
(funionfs
(concat "cd ~; funionfs " overlay " " mirror
" -o dirs=" virtual "=ro")))))
(if (null virtual)
(error (concat "Sunrise: sorry, don't know how to mirror " path)))
(unless (file-directory-p mirror)
(make-directory mirror)
(make-directory overlay)
(shell-command-to-string command))
mirror))
(defun sunrise-mirror-close (&optional do-commit local-commit moving)
"Destroy the current mirror area.
Unmounts and deletes the directories it was built upon. Tries to
automatically repack the mirror and substitute the original
archive with a new one containing the modifications made to the
mirror.
If optional argument DO-COMMIT is set, then all changes made to
the mirror are unconditionally committed to the archive. If
LOCAL-COMMIT is set, then the commit is considered local (changes
effect a mirror nested inside another mirror). MOVING means that
this operation was triggered by the user moving outside of the
current mirror area (the current buffer will be killed soon)."
(interactive)
(unless sunrise-mirror-home
(error (concat "Sunrise: sorry, can't mirror " (dired-get-filename))))
(let ((here (dired-current-directory))
(sunrise-mirror-divert-goto-dir nil)
(pos) (mirror) (overlay) (vroot) (vpath) (committed))
(unless (sunrise-overlapping-paths-p sunrise-mirror-home here)
(error (concat "Sunrise: sorry, that's not a mirror area: " here)))
(setq pos (string-match "\\(?:/\\|$\\)" here
(length sunrise-mirror-home))
mirror (substring here (length sunrise-mirror-home) pos)
overlay (concat "." mirror )
vpath (substring here (1+ pos))
do-commit
(and (sunrise-mirror-files (concat sunrise-mirror-home overlay))
(or do-commit
(y-or-n-p "Sunrise: commit changes in mirror? "))))
(unless local-commit
(sunrise-unhighlight 'sunrise-mirror-path-face))
(remove-hook 'kill-buffer-hook 'sunrise-mirror-on-kill-buffer)
(sunrise-follow-file (sunrise-mirror-demangle mirror))
(setq vroot (dired-get-filename 'no-dir))
(if do-commit (setq committed (sunrise-mirror-commit mirror)))
(sunrise-mirror-unmount mirror overlay)
(unless local-commit
(if (sunrise-overlapping-paths-p
sunrise-mirror-home (dired-current-directory))
(sunrise-mirror-close committed))
(unless moving
(sunrise-find-file
(expand-file-name (concat default-directory vroot)))
(if (< 0 (length vpath)) (sunrise-goto-dir vpath)))))
(sunrise-highlight)
(if (and sunrise-mirror-home
(null (directory-files sunrise-mirror-home nil "^[^.]")))
(sunrise-mirror-disable))
t)
(defun sunrise-mirror-commit (mirror)
"Commit all modifications made to MIRROR in directory OVERLAY.
Replaces the mirrored archive with a new one built with the
current contents of the mirror. Keeps a backup of the original
archive if the variable `sunrise-mirror-backup' is non-nil (the
default)."
(condition-case err
(let ((repacked (sunrise-mirror-repack mirror))
(target (dired-get-filename)))
(if (and sunrise-mirror-keep-backups
(not (sunrise-overlapping-paths-p
sunrise-mirror-home target)))
(rename-file target (concat target ".bak") 1)
(delete-file target))
(copy-file repacked (dired-current-directory) t nil nil)
(delete-file repacked)
t)
(error (progn
(setq err (cadr err))
(if (not (yes-or-no-p (concat err ". OK to continue? ")))
(error err))))))
(defun sunrise-mirror-unmount (mirror overlay)
"Unmount and delete all directories used to mirror a compressed archive.
MIRROR is the union of the AVFS directory that holds the contents
of the archive (read-only) with OVERLAY, which contains all the
modifications made to the union in the current session."
(let* ((command (concat "cd ~; fusermount -u " sunrise-mirror-home mirror))
(err (shell-command-to-string command)))
(if (or (null err) (string= err ""))
(progn
(dired-delete-file (concat sunrise-mirror-home mirror) 'always)
(dired-delete-file (concat sunrise-mirror-home overlay) 'always)
(revert-buffer))
(error (concat "Sunrise: error unmounting mirror: " err)))))
(defun sunrise-mirror-toggle ()
"Open new or destroy the current mirror area, depending on context."
(interactive)
(let ((open-ok) (close-ok) (err-msg))
(condition-case err1
(setq open-ok (sunrise-mirror-open))
(error (condition-case err2
(progn
(setq close-ok (sunrise-mirror-close))
(setq err-msg (cadr err1)))
(error
(setq err-msg (cadr err2))) )) )
(if (and (not open-ok) (not close-ok))
(error err-msg)
(sunrise-highlight))))
(defun sunrise-mirror-repack (mirror)
"Try to repack the given MIRROR.
On success, returns a string containing the full path to the
newly packed archive, otherwise throws an error."
(message "Sunrise: repacking mirror, please wait...")
(let* ((target-home (concat sunrise-mirror-home ".repacked/"))
(archive (replace-regexp-in-string "#[a-z0-9#]*$" "" mirror))
(target (replace-regexp-in-string
"/?$" ""
(car (last (split-string archive "+")))))
(command (assoc-default archive sunrise-mirror-pack-commands-alist
'string-match)))
(if (null command)
(error (concat "Sunrise: sorry, don't know how to repack " mirror)))
(if (not (file-exists-p target-home))
(make-directory target-home))
(setq target (concat target-home target))
(setq command (replace-regexp-in-string "%f" target command))
(setq command (concat "cd " sunrise-mirror-home mirror "; " command))
(shell-command-to-string command)
target))
(defun sunrise-mirror-mangle (path)
"Transform PATH into a string naming a new mirror area."
(let ((handler (assoc-default path sunrise-avfs-handlers-alist
'string-match)))
(if (eq ?/ (string-to-char path))
(setq path (substring path 1)))
(concat (replace-regexp-in-string
"/" "+"
(replace-regexp-in-string "\\+" "{+}" path)) handler)))
(defun sunrise-mirror-demangle (path)
"Transform the given mirror area name into a regular filesystem path.
Opposite of `sunrise-mirror-mangle'."
(concat "/"
(replace-regexp-in-string
"{\\+}" "+" (replace-regexp-in-string
"\\+\\([^}]\\)" "/\\1" (replace-regexp-in-string
"#[a-z0-9#]*$" "" path)))))
(defun sunrise-mirror-full-demangle (path)
"Demangle PATH recursively to get the current path of the original archive.
This is necessary because reflecting an archive that is itself a
reflection causes deadlocks in FUSE."
(let ((reflected path)
(home-len (length sunrise-mirror-home))
(handler (assoc-default path sunrise-avfs-handlers-alist
'string-match))
(prev-path))
(while (and (not (string= reflected prev-path))
(sunrise-overlapping-paths-p sunrise-mirror-home reflected))
(setq prev-path reflected)
(setq reflected (substring reflected home-len)
reflected (sunrise-mirror-demangle reflected)))
(setq reflected (concat sunrise-avfs-root reflected handler))
reflected))
(defun sunrise-mirror-files (directory)
"Return list of pathnames constituting mirror modifications
inside overlay DIRECTORY."
(if (not (file-directory-p directory))
(ignore)
(let ((files (directory-files directory)))
(mapc (lambda (x) (setq files (delete x files)))
'("." ".." "._funionfs_control~"))
files)))
(defun sunrise-mirror-overlay-redir (dirname &optional force-root)
"Adjust DIRNAME for use with a mirror filesystem.
Analyses the given directory path and rewrites it (if necessary)
to play nicely with the mirror fs the given path belongs to. If
the path is not inside any mirror fs, it is returned unmodified."
(if (null sunrise-avfs-root)
dirname
(let ((xpdir (expand-file-name dirname))
(mirror) (pos) (target))
(if (sunrise-overlapping-paths-p sunrise-mirror-home xpdir)
(progn
(setq mirror (substring xpdir (length sunrise-mirror-home)))
(setq pos (string-match "/\\|$" mirror))
(if pos
(progn
(setq target (replace-regexp-in-string
"^/" "" (substring mirror pos)))
(setq mirror (substring mirror 0 pos))))
(if (and target
(or (> (length target) 0) force-root)
(not (eq ?. (string-to-char mirror))))
(concat sunrise-mirror-home "." mirror "/" target)
dirname))
dirname))))
(defun sunrise-mirror-surface (dir)
"Return the topmost parent of DIR under `sunrise-mirror-home', if any."
(if (and sunrise-mirror-home
(sunrise-overlapping-paths-p sunrise-mirror-home dir)
(not (sunrise-equal-dirs sunrise-mirror-home dir)))
(let ((local-dir (dired-make-relative dir sunrise-mirror-home)))
(string-match "^\\([^/]*\\)" local-dir)
(match-string 1 local-dir))))
(defun sunrise-mirror-overlapping-p (mirror1 mirror2)
"Return non-nil if MIRROR1 and MIRROR2 overlap.
Check whether the surface of MIRROR2 maps an archive nested
inside the archive mapped by the surface of MIRROR1."
(let ((surface1 (sunrise-mirror-surface mirror1))
(surface2 (sunrise-mirror-surface mirror2))
top)
(when (and surface1 surface2)
(setq top (sunrise-mirror-demangle surface1))
(sunrise-overlapping-paths-p top (sunrise-mirror-demangle surface2)))))
(defun sunrise-mirror-goto-dir (target)
"Enhance `sunrise-goto-dir' with transparent navigation inside mirror areas.
All calls to `sunrise-goto-dir' are diverted to this function."
(let* ((here (expand-file-name default-directory))
(target (expand-file-name (or target ".")))
(surface-here (sunrise-mirror-surface here))
(sunrise-mirror-divert-goto-dir nil)
surface-target)
(cond
((null surface-here)
(sunrise-goto-dir target))
((sunrise-overlapping-paths-p sunrise-avfs-root target)
(sunrise-mirror-open))
(t
(if (sunrise-equal-dirs target sunrise-mirror-home)
(setq target
(expand-file-name
(concat (sunrise-mirror-demangle surface-here) "/.."))
surface-target
(sunrise-mirror-surface (sunrise-mirror-mangle target)))
(setq surface-target (sunrise-mirror-surface target)))
(unless (equal surface-here surface-target)
(if (and surface-target
(sunrise-overlapping-paths-p sunrise-mirror-home target)
(sunrise-mirror-overlapping-p surface-target surface-here))
(sunrise-mirror-close t t)
(sunrise-mirror-close nil nil t)))
(unless (or (not (file-directory-p target))
(sunrise-equal-dirs target (dired-current-directory)))
(sunrise-goto-dir target))))
(sunrise-highlight)))
(defun sunrise-mirror-on-kill-buffer ()
"Handle navigation out of a mirror area other than through `sunrise-goto-dir'.
This includes e.g. bookmark jumps and pane synchronizations."
(when (and sunrise-mirror-home (eq major-mode 'sunrise-mode)
(null (sunrise-mirror-surface sunrise-this-directory))
(sunrise-mirror-surface (dired-current-directory)))
(sunrise-mirror-goto-dir sunrise-this-directory)
(sunrise-unhighlight 'sunrise-mirror-path-face)))
(defadvice sunrise-goto-dir
(around sunrise-mirror-advice-goto-dir (dir))
"Divert all `sunrise-goto-dir' calls to `sunrise-mirror-goto-dir'."
(if sunrise-mirror-divert-goto-dir
(sunrise-mirror-goto-dir dir)
ad-do-it))
(defadvice sunrise-clone-files
(around sunrise-mirror-advice-clone-files
(file-path-list
target-dir
clone-op
progress
&optional do-overwrite))
"Redirect all `sunrise-copy' operations to the right path under the
overlay directory."
(if (null sunrise-mirror-home)
ad-do-it
(let ((orig target-dir))
(setq target-dir (sunrise-mirror-overlay-redir target-dir t))
(if (> (length target-dir) (length orig))
(make-directory target-dir))
ad-do-it)))
(ad-activate 'sunrise-clone-files)
(defadvice make-directory
(around sunrise-mirror-advice-make-directory (dirname &optional parents))
"Redirect directory creation operations to the right path under
the overlay directory."
(setq dirname (sunrise-mirror-overlay-redir dirname))
(setq parents t)
ad-do-it)
(defadvice save-buffer
(around sunrise-mirror-advice-save-buffer (&optional args))
"Create all the subdirectories (and set their permissions)
needed for enabling the redirection of buffer saving operations
to the right path under the overlay directory."
(let* ((orig (buffer-file-name))
(target (sunrise-mirror-overlay-redir orig)))
(if (> (length target) (length orig))
(let ((default-directory "~/")
(target-dir (file-name-directory target)))
(make-directory target-dir)
(shell-command-to-string
(concat dired-chmod-program " a+x " target-dir))
(write-file target nil))
ad-do-it)))
(defun sunrise-mirror-toggle-read-only ()
"Toggle the read-only flag in all buffers opened inside a mirror area,
so they are always writeable by default."
(if sunrise-mirror-home
(let* ((orig (buffer-file-name))
(target (sunrise-mirror-overlay-redir orig)))
(if (> (length target) (length orig))
(setq buffer-read-only nil)))))
(add-hook 'find-file-hook 'sunrise-mirror-toggle-read-only)
(defun sunrise-mirror-unload-function ()
(sunrise-ad-disable "^sunrise-mirror-"))
(provide 'sunrise-mirror)
;;; sunrise-mirror.el ends here

View File

@ -0,0 +1,347 @@
;;; sunrise-modeline.el --- Navigable mode line for the Sunrise Commander -*- lexical-binding: t -*-
;; Copyright (C) 2009-2012 José Alfredo Romero Latouche.
;; Author: José Alfredo Romero Latouche <escherdragon@gmail.com>
;; Štěpán Němec <stepnem@gmail.com>
;; Maintainer: José Alfredo Romero Latouche <escherdragon@gmail.com>
;; Created: 10 Oct 2009
;; Version: 2
;; Package-Requires: ((emacs "24.3") (cl-lib "0.5"))
;; Keywords: files, sunrise commander, modeline, path mode line
;; URL: https://github.com/sunrise-commander/sunrise-commander
;; 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 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 de- tails.
;; 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:
;; This extension modifies the format of the mode lines under the Sunrise
;; Commander panes so they display only the paths to the current directories
;; (or the tail if the whole path is too long) and a row of three small icons.
;; These icons are by default plain ASCII characters, but nicer semigraphical
;; versions (in Unicode) can also be used by customizing the variable
;; `sunrise-modeline-use-utf8-marks'.
;;
;; Here is the complete list of indicator icons (in ASCII and Unicode) and
;; their respective meanings:
;;
;; (ascii) (unicode)
;; 1. Pane modes: * ☼ Normal mode
;; ! ⚡ Editable Pane mode
;; @ ☯ Virtual Directory mode
;; T ⚘ Tree View mode (with tree extension)
;;
;; 2. Navigation modes: & ⚓ Synchronized Navigation
;; $ ♻ Sticky Search
;;
;; 3. Transient states: # ♥ Contents snapshot available
;;
;; (if you can't see the icons on the right, don't use utf8 marks)
;; The regular mode line format remains available: press C-c m to toggle
;; between one format and the other.
;; The extension is provided as a minor mode, so you can enable / disable it
;; totally by issuing the command `sunrise-modeline'.
;; It was written on GNU Emacs 24 on Linux, and tested on GNU Emacs 22 and 23
;; for Linux and on EmacsW32 (version 22) for Windows.
;;; Installation and Usage:
;; 1) Put this file somewhere in your Emacs `load-path'.
;; 2) Add a (require 'sunrise-modeline) expression to your .emacs file
;; somewhere after the (require 'sunrise) one.
;; 3) Evaluate the new expression, or reload your .emacs file, or restart
;; Emacs.
;; 4) Enjoy ;-)
;;; Code:
(eval-when-compile
(require 'cl-lib))
(require 'desktop)
(require 'easymenu)
(require 'sunrise)
(defcustom sunrise-modeline-use-utf8-marks nil
"Set to t to use fancy marks (using UTF-8 glyphs) in the mode line."
:group 'sunrise
:type 'boolean)
;; slot 0 -- pane modes:
(defconst sunrise-modeline-norm-mark '("*" . ""))
(defconst sunrise-modeline-edit-mark '("!" . ""))
(defconst sunrise-modeline-virt-mark '("@" . ""))
(defconst sunrise-modeline-tree-mark '("T" . ""))
;; slot 1 -- navigation modes:
(defconst sunrise-modeline-sync-mark '("&" . ""))
(defconst sunrise-modeline-srch-mark '("$" . ""))
;; slot 2 -- transient states:
(defconst sunrise-modeline-bkup-mark '("#" . ""))
(defface sunrise-modeline-separator-face
'((t (:height 0.3)))
"Face of the string used to separate the state indicators from one another."
:group 'sunrise)
(defconst sunrise-modeline-sep
#(" " 0 1 (face sunrise-modeline-separator-face))
"Sunrise Modeline separator character.")
;;; ==========================================================================
;;; Core functions:
(defvar sunrise-modeline-mark-map (make-sparse-keymap))
(define-key sunrise-modeline-mark-map [mode-line mouse-1]
'sunrise-modeline-popup-menu)
(define-key sunrise-modeline-mark-map [mode-line mouse-2]
'sunrise-modeline-popup-menu)
(defvar sunrise-modeline-path-map (make-sparse-keymap))
(define-key sunrise-modeline-path-map [mode-line mouse-1]
'sunrise-modeline-navigate-path)
(define-key sunrise-modeline-path-map [mode-line mouse-2]
'sunrise-modeline-navigate-path)
(defun sunrise-modeline-select-mark (mark &optional slot)
"Select the right character for the given MARK in SLOT.
Depends on whether UTF-8 has been enabled in the mode line via
the variable `sunrise-modeline-use-utf8-marks'."
(let ((select (if sunrise-modeline-use-utf8-marks #'cdr #'car))
(slot (or slot 0)))
(cl-case slot
(0 (funcall select (cl-case mark
(edit sunrise-modeline-edit-mark)
(virt sunrise-modeline-virt-mark)
(tree sunrise-modeline-tree-mark)
(t sunrise-modeline-norm-mark))))
(1 (cond ((or (memq 'sunrise-sticky-post-isearch isearch-mode-end-hook)
(memq 'sunrise-tree-post-isearch isearch-mode-end-hook))
(funcall select sunrise-modeline-srch-mark))
(sunrise-synchronized
(funcall select sunrise-modeline-sync-mark))
(t " ")))
(t (if (buffer-live-p sunrise-backup-buffer)
(funcall select sunrise-modeline-bkup-mark)
" ")))))
(defun sunrise-modeline-select-mode (mode)
"Assemble the indicators section on the left of the modeline."
(concat sunrise-modeline-sep (sunrise-modeline-select-mark mode 0)
sunrise-modeline-sep (sunrise-modeline-select-mark mode 1)
sunrise-modeline-sep (sunrise-modeline-select-mark mode 2)
sunrise-modeline-sep))
(defun sunrise-modeline-setup ()
"Determine the mode indicator (character) to display in the mode line.
On success, sets the mode line format by calling
`sunrise-modeline-set'."
(let ((mode nil))
(cl-case major-mode
(sunrise-mode
(setq mode (sunrise-modeline-select-mode
(if buffer-read-only 'norm 'edit))))
(sunrise-tree-mode
(setq mode (sunrise-modeline-select-mode 'tree)))
(sunrise-virtual-mode
(setq mode (sunrise-modeline-select-mode 'virt))))
(if mode (sunrise-modeline-set mode))))
(defun sunrise-modeline-set (mark)
"Adjust the current mode line format.
Uses the given mode indicator and the path to the current
directory of the pane. Truncates the path if it is longer than
the available width of the pane."
(let ((path (expand-file-name default-directory))
(path-length (length default-directory))
(max-length (- (window-width) 12)))
(if (< max-length path-length)
(setq path
(concat "..." (substring path (- path-length max-length)))))
(eval
`(setq mode-line-format
'("%[" ,(sunrise-modeline-mark mark) "%] "
,(sunrise-modeline-path path))))))
(defun sunrise-modeline-mark (marks-string)
"Propertize MARKS-STRING for use in displaying the mode line indicators."
(let ((mode-name "") (marks (split-string marks-string "|")))
(setq mode-name
(concat
(cond ((member (sunrise-modeline-select-mark 'edit) marks)
"Editable Pane Mode")
((member (sunrise-modeline-select-mark 'virt) marks)
"Virtual Directory Mode")
((member (sunrise-modeline-select-mark 'tree) marks)
"Tree View Mode")
(t "Normal Mode"))
(if sunrise-synchronized " | Synchronized Navigation" "")
(if (or (memq 'sunrise-sticky-post-isearch isearch-mode-end-hook)
(memq 'sunrise-tree-post-isearch isearch-mode-end-hook))
" | Sticky Search"
"")
(if (buffer-live-p sunrise-backup-buffer)
" | Snapshot Available" "")))
(propertize marks-string
'font 'bold
'mouse-face 'mode-line-highlight
'help-echo (format "Sunrise Commander: %s" mode-name)
'local-map sunrise-modeline-mark-map)))
(defun sunrise-modeline-path (path)
"Propertize the string PATH for use in the mode line format.
PATH is the current directory in the file system."
(propertize path
'local-map sunrise-modeline-path-map
'mouse-face 'mode-line-highlight
'help-echo "Click to navigate directory path"
'sunrise-selected-window sunrise-selected-window))
(defun sunrise-modeline-navigate-path ()
"Handle click events occuring on the mode line directory path.
Analyzes all click events detected on the directory path and
modifies the current directory of the corresponding panel
accordingly."
(interactive)
(let* ((event (caddr (cddadr last-input-event)))
(path (car event)) (pos (cdr event)) (slash) (levels))
(or (eq sunrise-selected-window
(get-text-property 0 'sunrise-selected-window path))
(sunrise-change-window))
(setq slash (string-match "/" path pos)
levels (- (length (split-string (substring path slash) "/")) 2))
(if (< 0 levels)
(sunrise-dired-prev-subdir levels)
(sunrise-beginning-of-buffer))))
;;; ==========================================================================
;;; Private interface:
(defvar sunrise-modeline)
(defun sunrise-modeline-refresh ()
(setq sunrise-modeline t)
(sunrise-modeline-setup))
(defun sunrise-modeline-engage ()
"Activate and enforce the navigation mode line format."
(add-hook 'sunrise-refresh-hook 'sunrise-modeline-refresh)
(sunrise-modeline-setup)
(sunrise-in-other (sunrise-modeline-setup)))
(defun sunrise-modeline-disengage ()
"De-activate the navigation mode line format, restoring the default one."
(remove-hook 'sunrise-refresh-hook 'sunrise-modeline-refresh)
(setq mode-line-format (default-value 'mode-line-format))
(sunrise-in-other
(setq mode-line-format (default-value 'mode-line-format))))
(defun sunrise-modeline-toggle (&optional force)
;; FIXME explain the argument
"Toggle display of the navigation mode line format."
(interactive)
(cond ((and force (< 0 force)) (sunrise-modeline-engage))
((and force (> 0 force)) (sunrise-modeline-disengage))
(t
(if (eq mode-line-format (default-value 'mode-line-format))
(sunrise-modeline-engage)
(sunrise-modeline-disengage)))))
;;; ==========================================================================
;;; User interface:
(defvar sunrise-modeline-map (make-sparse-keymap))
(define-key sunrise-modeline-map "\C-cm" 'sunrise-modeline-toggle)
(define-minor-mode sunrise-modeline
"Provide navigable mode line for the Sunrise Commander.
This is a minor mode that provides a single keybinding:
C-c m .............. Toggle between navigation and default mode line formats
To totally disable this extension do: M-x sunrise-modeline <RET>"
nil (sunrise-modeline-select-mode 'norm) sunrise-modeline-map
(unless (memq major-mode
'(sunrise-mode sunrise-virtual-mode sunrise-tree-mode))
(setq sunrise-modeline nil)
(error "Sorry, this mode can be used only within the Sunrise Commander"))
(sunrise-modeline-toggle 1))
(defvar sunrise-modeline-menu
(easy-menu-create-menu
"Mode Line"
'(["Toggle navigation mode line" sunrise-modeline-toggle t]
["Navigation mode line help"
(lambda ()
(interactive)
(describe-function 'sunrise-modeline))])))
(defun sunrise-modeline-popup-menu ()
(interactive)
(popup-menu sunrise-modeline-menu))
;;; ==========================================================================
;;; Bootstrap:
(defun sunrise-modeline-menu-init ()
"Initialize the Sunrise Mode Line extension menu."
(unless (lookup-key sunrise-mode-map [menu-bar Sunrise])
(define-key sunrise-mode-map [menu-bar Sunrise]
(cons "Sunrise" (make-sparse-keymap))))
(let ((menu-map (make-sparse-keymap "Mode Line")))
(define-key sunrise-mode-map [menu-bar Sunrise mode-line]
(cons "Mode Line" menu-map))
(define-key menu-map [help]
'("Help" . (lambda ()
(interactive)
(describe-function 'sunrise-modeline))))
(define-key menu-map [disable]
'("Toggle" . sunrise-modeline-toggle))))
(defun sunrise-modeline-start-once ()
"Bootstrap the navigation mode line on the first execution of
the Sunrise Commander, after module installation."
(sunrise-modeline t)
(sunrise-modeline-menu-init)
(remove-hook 'sunrise-start-hook 'sunrise-modeline-start-once)
(unintern 'sunrise-modeline-menu-init obarray)
(unintern 'sunrise-modeline-start-once obarray))
(add-hook 'sunrise-start-hook 'sunrise-modeline-start-once)
;;; ==========================================================================
;;; Desktop support:
(add-to-list 'desktop-minor-mode-table '(sunrise-modeline nil))
(defun sunrise-modeline-desktop-restore-function (&rest _)
"Call this instead of `sunrise-modeline' when restoring a desktop."
(sunrise-modeline-refresh))
(add-to-list 'desktop-minor-mode-handlers
'(sunrise-modeline . sunrise-modeline-desktop-restore-function))
(provide 'sunrise-modeline)
;;; sunrise-modeline.el ends here

View File

@ -0,0 +1,746 @@
;;; sunrise-tabs.el --- Tabs for the Sunrise Commander -*- lexical-binding: t -*-
;; Copyright (C) 2009-2012 José Alfredo Romero Latouche.
;; Author: José Alfredo Romero Latouche <escherdragon@gmail.com>
;; Štěpán Němec <stepnem@gmail.com>
;; Maintainer: José Alfredo Romero Latouche <escherdragon@gmail.com>
;; Created: 24 Oct 2009
;; Version: 1
;; Package-Requires: ((emacs "24.3") (cl-lib "0.5"))
;; Keywords: files, sunrise commander, tabs
;; URL: https://github.com/sunrise-commander/sunrise-commander
;; 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 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 de- tails.
;; 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:
;; This extension brings tab-based navigation to the Sunrise Commander. It
;; adds to the list of optional mechanisms already available in Sunrise for
;; moving around the file system (like regular bookmarks, checkpoints, history
;; rings, materialized virtual buffers, navigable paths and file-following)
;; another way to maintain a list of selected locations one wants to return
;; later on, or to compose "breadcrumb trails" for complex repetitive
;; operations.
;; The main difference between tabs and other mechanisms is that once a buffer
;; has been assigned to a tab, it will not be killed automatically by Sunrise,
;; so it's possible to keep it around as long as necessary with all its marks
;; and state untouched. Tabs can be persisted across sessions using the
;; DeskTop feature.
;; Creating, using and destroying tabs are fast and easy operations, either
;; with mouse or keyboard:
;; * Press C-j (or select Sunrise > Tabs > Add Tab in the menu) to create a
;; new tab or to rename an already existing tab.
;; * Press C-k (or right-click the tab) to kill an existing tab. Combine with
;; M- (M-C-k) to kill the tab on the passive pane. Prefix with a digit to kill
;; tabs by relative order (e.g. 2 C-k kills the second tab in the current
;; pane, while 4 M-C-k kills the fourth tab in the passive pane).
;; * Press C-n and C-p to move from tab to tab ("Next", "Previous"), or simply
;; left-click on the tab to focus its assigned buffer. These two keybindings
;; can be prefixed with an integer to move faster.
;; * The last four bindings can be combined with Meta (i.e. M-C-j, M-C-k,
;; M-C-n and M-C-p) to perform the equivalent operation on the passive pane or
;; (when in synchronized navigation mode) on both panes simultaneously.
;; * Press * C-k to kill in one go all the tabs in the current pane.
;; Similarly, press * M-C-k to wipe all the tabs off the passive pane or (when
;; synchronized mode is active) on both panes simultaneously.
;; * Killing the current buffer with C-x k automatically switches to the one
;; assigned to the first available tab (if any).
;; The extension is provided as a minor mode, so you can enable / disable it
;; totally by using the command `sunrise-tabs-mode'.
;; It does *not* pretend to be a generic solution for tabs in Emacs. If you
;; need one, have a look at TabBar mode
;; (http://www.emacswiki.org/emacs/TabBarMode) by David Ponce. I wrote this
;; just because it turned out to be easier to write this than to customize
;; tabbar to behave exactly like I wanted inside the Sunrise panes. It's meant
;; to be simple and to work nicely with Sunrise with just a few tabs (up to
;; 10-15 per pane, maybe).
;; It was written on GNU Emacs 23 on Linux, and tested on GNU Emacs 22 and 23
;; for Linux and on EmacsW32 (version 23) for Windows.
;;; Installation and Usage:
;; 1) Put this file somewhere in your Emacs `load-path'.
;; 2) Add a (require 'sunrise-tabs) expression to your .emacs file somewhere
;; after the (require 'sunrise) one.
;; 3) Evaluate the new expression, or reload your .emacs file, or restart
;; Emacs.
;; 4) Enjoy ;-)
;;; Code:
(eval-when-compile
(require 'cl-lib)
(require 'desktop))
(require 'sunrise)
(defcustom sunrise-tabs-follow-panes t
"Whether tabs should be swapped too when transposing the Sunrise panes."
:group 'sunrise
:type 'boolean)
(defcustom sunrise-tabs-max-tabsize 10
"Maximum width of a Sunrise Commander tab."
:group 'sunrise
:type 'integer)
(defcustom sunrise-tabs-truncation-style 'right
"On which side should we truncate the tag a of new tab if it
happens to be longer than `sunrise-tabs-max-tabsize`."
:group 'sunrise
:type '(choice
(const :tag "Truncate from the right" right)
(const :tag "Truncate from the left" left)))
(defcustom sunrise-tabs-tag-provider
(defun sunrise-tabs-tag-provider-default (buffer-name)
"Default provider of tags based on the buffer name of a pane."
buffer-name)
"Function to use to determine the tag to use when creating a new tab.
It should take one argument which is to be interpreted as the
name of the buffer that contains the pane to be assigned to the
tab."
:group 'sunrise
:type 'function)
(defface sunrise-tabs-active-face
'((((type tty) (class color) (min-colors 88))
:background "white")
(((type tty) (class color) (min-colors 8))
:background "green" :foreground "yellow" :bold t)
(((type tty) (class mono)) :inverse-video t)
(t
:inherit variable-pitch :bold t :background "white" :height 0.9))
"Face of the currently selected tab in any of the Sunrise panes."
:group 'sunrise)
(defface sunrise-tabs-inactive-face
'((((type tty) (class color) (min-colors 88))
:background "color-84" :foreground "white")
(((type tty) (class color) (min-colors 8))
:background "white" :foreground "cyan")
(t
:inherit variable-pitch :background "gray95" :height 0.9))
"Face of all non-selected tabs in both Sunrise panes."
:group 'sunrise)
(defface sunrise-tabs-separator-face
'((t (:height 0.3)))
"Face of the string used to separate the Sunrise tabs from one another."
:group 'sunrise)
(defconst sunrise-tabs-sep
#(" " 0 1 (face sunrise-tabs-separator-face))
"Sunrise Tabs separator character.")
(defconst sunrise-tabs-ligature
#("" 0 1 (face sunrise-tabs-separator-face))
"Sunrise Tabs line separator string.")
(defconst sunrise-tabs-max-cache-length
30
"Max number of tab labels cached for reuse.")
(defvar sunrise-tabs
'((left) (right)))
(defvar sunrise-tabs-labels-cache
'((left) (right)))
(defvar sunrise-tabs-line-cache
'((left) (right)))
(defvar sunrise-tabs-mode
nil)
(defvar sunrise-tabs-on
nil)
;;; ==========================================================================
;;; Core functions:
(defun sunrise-tabs-add ()
"Assign the current buffer to exactly one tab in the active pane.
If a tab for the current buffer already exists, invoke `sunrise-tabs-rename'."
(interactive)
(let ((tab-name (buffer-name))
(tab-set (assq sunrise-selected-window sunrise-tabs)))
(if (member tab-name (cdr tab-set))
(call-interactively 'sunrise-tabs-rename)
(setcdr tab-set (cons tab-name (cdr tab-set)))))
(sunrise-tabs-refresh))
(defun sunrise-tabs-remove (&optional tab-buffer side)
"Remove the tab to which TAB-BUFFER is assigned in the active pane.
If TAB-BUFFER is nil, removes the tab to which the current buffer
is assigned, if any."
(interactive "P")
(let* ((side (or side sunrise-selected-window))
(tab-name (if (integerp tab-buffer)
(nth tab-buffer (assoc side sunrise-tabs))
(buffer-name tab-buffer)))
(tab-buffer (and tab-name (get-buffer tab-name)))
(tab-set (assq side sunrise-tabs)))
(setcdr tab-set (delete tab-name (cdr tab-set)))
(unless (or (null tab-buffer)
(eq tab-buffer (current-buffer))
(eq tab-buffer (sunrise-other 'buffer)))
(kill-buffer (get-buffer tab-name))))
(sunrise-tabs-refresh))
(defun sunrise-tabs-clean ()
"Remove all tabs from the current pane."
(interactive)
(while (nth 1 (assoc sunrise-selected-window sunrise-tabs))
(sunrise-tabs-remove 1)))
(defun sunrise-tabs-kill (&optional name side)
"Remove the tab named NAME from the active pane and kill its buffer.
The buffer is not killed when currently visible or assigned to
another tab."
(interactive)
(let ((to-kill (or (and name (get-buffer name)) (current-buffer)))
(side (or side sunrise-selected-window)))
(sunrise-tabs-remove to-kill side)
(if (and (not (memq to-kill
(list sunrise-left-buffer sunrise-right-buffer)))
(not (member to-kill
(apply 'append (mapcar 'cdr sunrise-tabs)))))
(kill-buffer to-kill))
(sunrise-tabs-refresh)))
(defun sunrise-tabs-next (&optional n)
"Move focus to the next tab (left to right) in the active pane.
With a prefix argument N, moves focus to the tab N places ahead,
or to the last one if there are fewer tabs than requested."
(interactive "p")
(sunrise-tabs-step n))
(defun sunrise-tabs-prev (&optional n)
"Move focus to the previous tab (right to left) in the active pane.
With a prefix argument N, moves focus to the tab N places behind,
or to the first one if there are fewer tabs than requested."
(interactive "p")
(sunrise-tabs-step n t))
(defun sunrise-tabs-step (count &optional back)
"Move focus from the current tab to the one COUNT places ahead or behind.
The direction depends on the value of BACK."
(let* ((stack (cdr (assq sunrise-selected-window sunrise-tabs)))
(stack (if back (reverse stack) stack))
(target (member (buffer-name) stack)))
(unless (null stack)
(if (or (null count) (zerop count))
(setq count 1))
(if (< 1 (length target))
(sunrise-tabs-switch-to-buffer
(or (nth count target) (car (last target))))
(sunrise-tabs-switch-to-buffer (car stack))))))
(defun sunrise-tabs-switch-to-buffer (to-buffer)
"Change context of the active Sunrise pane when switching buffers."
(let ((from-buffer (current-buffer))
(sunrise-current-path-faces
(with-current-buffer to-buffer sunrise-current-path-faces)))
(unless (eq from-buffer to-buffer)
(sunrise-save-aspect (switch-to-buffer to-buffer))
(setq sunrise-this-directory default-directory)
(set (sunrise-symbol sunrise-selected-window 'buffer)
(current-buffer))
(set (sunrise-symbol sunrise-selected-window 'directory)
default-directory)
(unless (eq from-buffer (sunrise-other 'buffer))
(with-current-buffer from-buffer
(set-buffer-modified-p nil)
(kill-buffer (current-buffer))))
(condition-case nil
(revert-buffer t t)
(error (ignore)))
(sunrise-history-push default-directory))
(sunrise-tabs-refresh)))
(defun sunrise-tabs-focus (name side)
"Give focus to the tab with name NAME in SIDE pane."
(unless (eq side sunrise-selected-window)
(sunrise-change-window))
(sunrise-tabs-switch-to-buffer name))
(defun sunrise-tabs-kill-and-go ()
"Kill the current Sunrise buffer and move to the next one.
This kills the buffer, removes its assigned tab (if any) and
moves to the next buffer tabbed in the active pane, unless there
are no more tabbed buffers to fall back to, in which case just
removes the tab."
(interactive)
(let ((to-kill (current-buffer))
(stack (cdr (assq sunrise-selected-window sunrise-tabs))))
(if (null stack)
(sunrise-kill-pane-buffer)
(sunrise-tabs-kill)
(setq stack (cdr stack))
(sunrise-tabs-next)
(unless (or (null stack)
(eq to-kill (current-buffer))
(eq to-kill (sunrise-other 'buffer)))
(kill-buffer to-kill)))))
(defun sunrise-tabs-rename (&optional new-name)
(interactive "sRename current tab to: ")
(let* ((key (buffer-name))
(cache (assq sunrise-selected-window sunrise-tabs-labels-cache))
(label (cadr cache)))
(if label
(sunrise-tabs-redefine-label key new-name))))
(defun sunrise-tabs-transpose ()
"Swap the sets of tabs from one pane to the other."
(interactive)
(cl-labels ((flip (side) (setcar side (cdr (assq (car side)
sunrise-side-lookup)))))
(dolist (registry (list sunrise-tabs sunrise-tabs-labels-cache))
(mapc #'flip registry)))
(sunrise-in-other (sunrise-tabs-refresh))
(sunrise-tabs-refresh))
(defadvice sunrise-transpose-panes
(after sunrise-tabs-advice-transpose-panes ())
"Synchronize the tabs with the panes if so required.
See the variable `sunrise-tabs-follow-panes'. Activated in the
function `sunrise-tabs-engage'."
(if sunrise-tabs-follow-panes (sunrise-tabs-transpose)))
;;; ==========================================================================
;;; Graphical interface:
(defun sunrise-tabs-focus-cmd (name side)
"Return a function to give focus to the named NAME in the SIDE pane."
(let ((selector (if (eq side (caar sunrise-tabs)) #'caar #'caadr)))
`(lambda ()
(interactive)
(sunrise-tabs-focus ,name (funcall ',selector sunrise-tabs)))))
(defun sunrise-tabs-rename-cmd (name)
"Return a function to rename the tab named NAME in both panes."
`(lambda (&optional new-name)
(interactive "sRename tab to: ")
(sunrise-tabs-redefine-label ,name new-name)))
(defun sunrise-tabs-kill-cmd (name side)
"Return a function to delete the tab named NAME in the SIDE pane."
(let ((selector (if (eq side (caar sunrise-tabs)) #'caar #'caadr)))
`(lambda ()
(interactive)
(if (eq sunrise-selected-window (funcall ',selector sunrise-tabs))
(sunrise-tabs-kill ,name)
(sunrise-in-other
(sunrise-tabs-kill ,name))))))
(defsubst sunrise-tabs-propertize-tag (string face keymap)
"Propertize STRING with FACE and KEYMAP so it can be used as a tab tag."
(propertize string
'face face
'help-echo
"mouse-1: select tab\n\mouse-2: rename tab\n\mouse-3: kill tab"
'local-map keymap))
(defun sunrise-tabs-make-tag (name as-active &optional tag)
"Return a propertized string for decorating a tab named NAME.
AS-ACTIVE determines whether to propertize it as an active or a
passive tab (nil = passive, t = active). The optional argument
TAG allows to provide a pretty name to label the tab."
(let ((tag (sunrise-tabs-truncate (or tag name)))
(side sunrise-selected-window)
(keymap (make-sparse-keymap)))
(setq tag (concat sunrise-tabs-sep tag sunrise-tabs-sep))
(define-key keymap [header-line mouse-1]
(sunrise-tabs-focus-cmd name side))
(define-key keymap [header-line mouse-2]
(sunrise-tabs-rename-cmd name))
(define-key keymap [header-line mouse-3]
(sunrise-tabs-kill-cmd name side))
(if as-active
(sunrise-tabs-propertize-tag tag 'sunrise-tabs-active-face keymap)
(sunrise-tabs-propertize-tag tag 'sunrise-tabs-inactive-face keymap))))
(defun sunrise-tabs-truncate (tag)
"Truncate and add an ellipsis mark to the given tag if necessary."
(if (>= sunrise-tabs-max-tabsize (length tag))
tag
(cl-case sunrise-tabs-truncation-style
(right (concat (substring tag 0 sunrise-tabs-max-tabsize) ""))
(left (concat "" (substring tag (* -1 sunrise-tabs-max-tabsize))))
(t (ignore)))))
(defun sunrise-tabs-make-label (name &optional alias)
"Return a new label for decorating a tab named NAME.
A label is a dotted pair of tags, for active and passive state.
The new label is put in cache for later reuse. The optional
argument ALIAS allows to provide a pretty name to label the tab."
(let* ((alias (or alias (apply sunrise-tabs-tag-provider (list name))))
(label (cons (sunrise-tabs-make-tag name t alias)
(sunrise-tabs-make-tag name nil alias)))
(entry (list (cons name label)))
(cache (assq sunrise-selected-window sunrise-tabs-labels-cache)))
(setcdr cache (append (cdr cache) entry))
label))
(defun sunrise-tabs-trim-label (label)
"Remove all properties and trailing whitespace from the given string."
(replace-regexp-in-string "^\\s-+\\|\\s-+$"
""
(substring-no-properties label)))
(defun sunrise-tabs-redefine-label (name alias)
"Change the name displayed on the tab with assigned buffer NAME to ALIAS.
By default, a tab is named after its assigned buffer. This function allows to
give tabs names that are more readable or simply easier to remember."
(let* ((alias (sunrise-tabs-trim-label (or alias ""))) (cache))
(when (string= "" alias)
(setq alias (buffer-name)))
(setq cache (assq sunrise-selected-window sunrise-tabs-labels-cache))
(setcdr cache (delq nil
(mapcar (lambda(x)
(and (not (equal (car x) name)) x))
(cdr cache))))
(sunrise-tabs-make-label name alias)
(sunrise-tabs-refresh)))
(defun sunrise-tabs-get-tag (name is-active)
"Retrieve the cached tag for the tab named NAME in state IS-ACTIVE.
nil = inactive, t = active. Creates new labels when needed."
(let* ((cache (assq sunrise-selected-window sunrise-tabs-labels-cache))
(label (cdr (assoc name (cdr cache)))))
(if (null label)
(setq label (sunrise-tabs-make-label name)))
(if (< sunrise-tabs-max-cache-length (length (cdr cache)))
(setcdr cache (cddr cache)))
(if is-active (car label) (cdr label))))
(defun sunrise-tabs-make-line ()
"Assemble a new tab line from cached tags and put it in the line cache."
(if (memq major-mode '(sunrise-mode sunrise-virtual-mode sunrise-tree-mode))
(let ((tab-set (cdr (assq sunrise-selected-window sunrise-tabs)))
(tab-line (if (or (cdar sunrise-tabs)
(cdr (cadr sunrise-tabs))) "" nil))
(current-name (buffer-name)))
(mapc (lambda (x)
(let ((is-current (equal current-name x)))
(setq tab-line
(concat tab-line sunrise-tabs-sep
(sunrise-tabs-get-tag x is-current)))))
tab-set)
(setcdr (assq sunrise-selected-window sunrise-tabs-line-cache)
tab-line)
tab-line)
nil))
(defsubst sunrise-tabs-empty-p (line)
(or (null line) (string= "" line)))
(defsubst sunrise-tabs-empty-mask (line)
(or (and (null line) "") line))
(defsubst sunrise-tabs-empty-null (line)
(if (sunrise-tabs-empty-p line) nil line))
(defun sunrise-tabs-nonempty-p (line-list)
"Return non-nil if LINE-LIST contains at least one non-nil element."
(or (not (sunrise-tabs-empty-p (car line-list)))
(and (cdr line-list) (sunrise-tabs-nonempty-p (cdr line-list)))))
(defun sunrise-tabs-xor (list1 list2)
"Replacement for function `set-exclusive-or'.
Used to avoid dependency on cl-seq.el."
(cond ((null list1) list2)
((null list2) list1)
((equal list1 list2) nil)
(t
(let (result)
(mapc (lambda (element)
(if (member element result)
(setq result (delete element result))
(setq result (cons element result))))
(append list1 list2))
result))))
(defun sunrise-tabs-refresh ()
"Update `header-line-format' in both panes.
Uses the line cache for the passive one, and assembles a new tab
line for the active one. In the (corner) case when both panes
contain the same buffer, glues together the tab lines with a
``double bar'' separator."
(setq sunrise-tabs-mode sunrise-tabs-on)
(sunrise-tabs-make-line)
(let ((line-list (mapcar 'cdr sunrise-tabs-line-cache))
(same-buffer (eq sunrise-left-buffer sunrise-right-buffer)))
(if same-buffer
(setq header-line-format
(and (sunrise-tabs-nonempty-p line-list)
(mapconcat 'concat line-list sunrise-tabs-ligature)))
(let ((other-buffer (sunrise-other 'buffer)))
(if (eq 'right sunrise-selected-window)
(setq line-list (nreverse line-list)))
(if (apply 'sunrise-tabs-xor (mapcar 'sunrise-tabs-empty-p line-list))
(setq line-list (mapcar 'sunrise-tabs-empty-mask line-list))
(setq line-list (mapcar 'sunrise-tabs-empty-null line-list)))
(setq header-line-format (car line-list))
(when (buffer-live-p other-buffer)
(with-current-buffer other-buffer
(setq header-line-format (cadr line-list)))))))
(force-window-update))
;;; ==========================================================================
;;; Private interface:
(defun sunrise-tabs-bury-all ()
"Bury all currently tabbed buffers."
(let ((all-buffers (apply 'append (mapcar 'cdr sunrise-tabs))))
(if all-buffers
(mapc 'bury-buffer all-buffers))))
(defun sunrise-tabs-protect-buffer ()
"Protect the current buffer from being automatically disposed
by Sunrise when moving to another directory (called from
`kill-buffer-query-functions' hook.)"
(let ((tab-name (buffer-name)))
(not (or (member tab-name (car sunrise-tabs))
(member tab-name (cadr sunrise-tabs))))))
(defun sunrise-tabs-engage ()
"Enable the Sunrise Tabs extension."
(setq sunrise-tabs-on t)
(add-hook 'sunrise-refresh-hook 'sunrise-tabs-refresh)
(add-hook 'sunrise-quit-hook 'sunrise-tabs-bury-all)
(add-hook 'kill-buffer-query-functions 'sunrise-tabs-protect-buffer)
(ad-activate 'sunrise-transpose-panes)
(ad-activate 'sunrise-editable-pane)
(sunrise-tabs-refresh))
(defun sunrise-tabs-disengage ()
"Disable the Sunrise Tabs extension."
(setq sunrise-tabs-on nil)
(remove-hook 'sunrise-refresh-hook 'sunrise-tabs-refresh)
(remove-hook 'sunrise-quit-hook 'sunrise-tabs-bury-all)
(remove-hook 'kill-buffer-query-functions 'sunrise-tabs-protect-buffer)
(ad-deactivate 'sunrise-transpose-panes)
(ad-deactivate 'sunrise-editable-pane)
(setq header-line-format (default-value 'header-line-format))
(sunrise-in-other
(setq header-line-format (default-value 'header-line-format))))
;;; ==========================================================================
;;; User interface:
(defvar sunrise-tabs-mode-map (make-sparse-keymap))
(define-key sunrise-tabs-mode-map [(control ?j)] 'sunrise-tabs-add)
(define-key sunrise-tabs-mode-map [(control ?k)] 'sunrise-tabs-remove)
(define-key sunrise-tabs-mode-map "*\C-k" 'sunrise-tabs-clean)
(define-key sunrise-tabs-mode-map [(control ?p)] 'sunrise-tabs-prev)
(define-key sunrise-tabs-mode-map [(control ?n)] 'sunrise-tabs-next)
(define-key sunrise-tabs-mode-map [(meta tab)] 'sunrise-tabs-next)
(define-key sunrise-tabs-mode-map [(control meta ?j)]
(lambda ()
(interactive)
(sunrise-in-other (sunrise-tabs-add))))
(define-key sunrise-tabs-mode-map [(control meta ?k)]
(lambda ()
(interactive)
(sunrise-in-other (call-interactively 'sunrise-tabs-remove))))
(define-key sunrise-tabs-mode-map [(control meta ?p)]
(lambda ()
(interactive)
(sunrise-in-other (sunrise-tabs-prev))))
(define-key sunrise-tabs-mode-map [(control meta ?n)]
(lambda ()
(interactive)
(sunrise-in-other (sunrise-tabs-next))))
(define-key sunrise-tabs-mode-map [(control meta tab)]
(lambda ()
(interactive)
(sunrise-in-other (sunrise-tabs-next))))
(define-key sunrise-tabs-mode-map "*\C-\M-k"
(lambda ()
(interactive)
(sunrise-in-other (sunrise-tabs-clean))))
(define-key sunrise-tabs-mode-map "\C-xk" 'sunrise-tabs-kill-and-go)
(define-key sunrise-tabs-mode-map "\M-T" 'sunrise-tabs-transpose)
(define-minor-mode sunrise-tabs-mode
"Tabs support for the Sunrise Commander file manager.
This minor mode provides the following keybindings:
C-j ........... Create new tab (or rename existing tab) in active pane.
C-k ........... Kill the tab of the current buffer in the active pane.
C-n ........... Move to the next tab in the active pane.
C-p ........... Move to the previous tab in the active pane.
C-M-j ......... Assign the current buffer to a tab in the passive pane.
C-M-k ......... Kill the tab of the current buffer in the passive pane.
C-M-n ......... Move to the next tab in the passive pane.
C-M-p ......... Move to the previous tab in the passive pane.
C-x k ......... Kill buffer and move to the next tabbed one (if any).
"
nil nil sunrise-tabs-mode-map
(unless (memq major-mode
'(sunrise-mode sunrise-virtual-mode sunrise-tree-mode))
(setq sunrise-tabs-mode nil)
(error "Sorry, this mode can be used only within the Sunrise Commander."))
(if sunrise-tabs-mode
(sunrise-tabs-engage)
(sunrise-tabs-disengage)))
(defvar sunrise-tabs-editable-dired-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map sunrise-tabs-mode-map)
(define-key map "\C-n" 'dired-next-line)
(define-key map "\C-p" 'dired-previous-line)
(define-key map "\C-tn" 'sunrise-tabs-next)
(define-key map "\C-tp" 'sunrise-tabs-prev)
map)
"Keymap for managing tabs inside Editable Dired mode panes.")
(defadvice sunrise-editable-pane (after sunrise-tabs-advice-editable-pane ())
"Install `sunrise-tabs-editable-dired-map' when in Editable Dired mode."
(add-to-list 'minor-mode-overriding-map-alist
`(sunrise-tabs-mode . ,sunrise-tabs-editable-dired-map)))
;;; ==========================================================================
;;; Bootstrap:
(defun sunrise-tabs-menu-init ()
"Initialize the Sunrise Tabs extension menu."
(unless (lookup-key sunrise-mode-map [menu-bar Sunrise])
(define-key sunrise-mode-map [menu-bar Sunrise]
(cons "Sunrise" (make-sparse-keymap))))
(let ((menu-map (make-sparse-keymap "Tabs")))
(define-key sunrise-mode-map [menu-bar Sunrise tabs]
(cons "Tabs" menu-map))
(define-key menu-map [help]
'("Help" . (lambda ()
(interactive)
(describe-function 'sunrise-tabs-mode))))
(define-key menu-map [transpose]
'("Transpose" . sunrise-tabs-transpose))
(define-key menu-map [kill]
'("Kill and go to next" . sunrise-tabs-kill-and-go))
(define-key menu-map [next]
'("Next" . sunrise-tabs-next))
(define-key menu-map [prev]
'("Previous" . sunrise-tabs-prev))
(define-key menu-map [remove]
'("Remove" . sunrise-tabs-remove))
(define-key menu-map [add]
'("Add/Rename" . sunrise-tabs-add))))
(defun sunrise-tabs-start-once ()
"Bootstrap the tabs mode on the first execution of the Sunrise Commander,
after module installation."
(sunrise-tabs-mode t)
(sunrise-tabs-menu-init)
(remove-hook 'sunrise-start-hook 'sunrise-tabs-start-once)
(unintern 'sunrise-tabs-menu-init obarray)
(unintern 'sunrise-tabs-start-once obarray))
(add-hook 'sunrise-start-hook 'sunrise-tabs-start-once)
;;; ==========================================================================
;;; Desktop support:
(defun sunrise-tabs-desktop-save-buffer (_desktop-dir)
"Return additional desktop data to save tabs of the current Sunrise buffer."
(let* ((left-tab (car (member (buffer-name) (assoc 'left sunrise-tabs))))
(left-cache (cdr (assq 'left sunrise-tabs-labels-cache)))
(left-label (cadr (assoc left-tab left-cache)))
(right-tab (car (member (buffer-name) (assoc 'right sunrise-tabs))))
(right-cache (cdr (assq 'right sunrise-tabs-labels-cache)))
(right-label (cadr (assoc right-tab right-cache))))
(delq
nil
(list
(and left-label
(cons 'left-tab (sunrise-tabs-trim-label left-label)))
(and right-label
(cons 'right-tab (sunrise-tabs-trim-label right-label)))))))
(defun sunrise-tabs-desktop-restore-buffer (_desktop-buffer-file-name
_desktop-buffer-name
desktop-buffer-misc)
"Restore all tabs in a Sunrise (normal or VIRTUAL) buffer from a desktop file."
(mapc (lambda (side)
(let* ((sunrise-selected-window side)
(tab-symbol (intern (concat (symbol-name side) "-tab")))
(name (buffer-name))
(label (cdr (assq tab-symbol desktop-buffer-misc)))
(tab-set (assq side sunrise-tabs)))
(when label
(setcdr tab-set (cons name (cdr tab-set)))
(sunrise-tabs-make-label name label))))
'(left right))
(unless sunrise-tabs-on
(sunrise-tabs-engage)))
(defun sunrise-tabs-reset-state ()
"Reset some environment variables that control the behavior of
tabs in the Sunrise Commander (used for desktop support)."
(mapc (lambda (x) (setcdr x nil)) sunrise-tabs-labels-cache)
(mapc (lambda (x) (setcdr x nil)) sunrise-tabs)
nil)
;; Append the previous functions to the generic desktop support in Sunrise:
(add-to-list 'sunrise-desktop-save-handlers
'sunrise-tabs-desktop-save-buffer)
(add-to-list 'sunrise-desktop-restore-handlers
'sunrise-tabs-desktop-restore-buffer)
;; Activate tabs support after desktop restoration:
(add-hook
'desktop-after-read-hook
(defun sunrise-tabs-desktop-after-read-function ()
(unless (assq 'sunrise-tabs-on desktop-globals-to-clear)
(add-to-list 'desktop-globals-to-clear
'(sunrise-tabs-on . (sunrise-tabs-reset-state))))))
(defun sunrise-tabs-unload-function ()
(sunrise-ad-disable "^sunrise-tabs-"))
(provide 'sunrise-tabs)
;;; sunrise-tabs.el ends here

4795
.doom.d/lisp/sunrise.el Normal file

File diff suppressed because it is too large Load Diff

View File

@ -55,5 +55,7 @@
(package! highlight)
(package! yafolding)
(package! dired-quick-sort)
(package! fish-mode)
;; (package! image-dired+)
(package! ido-vertical-mode) ; not being used
(package! tramp-term)