diff --git a/.emacs.d/emacs-custom.el b/.emacs.d/emacs-custom.el index f553052..1e43e6e 100644 --- a/.emacs.d/emacs-custom.el +++ b/.emacs.d/emacs-custom.el @@ -9,7 +9,7 @@ '(livedown-port 1337) '(package-selected-packages (quote - (ox-jira use-package switch-window ox-reveal markdown-mode magit htmlize dracula-theme)))) + (dired-sidebar use-package switch-window ox-reveal ox-jira markdown-mode magit htmlize dracula-theme confluence)))) (custom-set-faces ;; custom-set-faces was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. diff --git a/.emacs.d/init.el b/.emacs.d/init.el index ab5c46f..2b0d8d1 100644 --- a/.emacs.d/init.el +++ b/.emacs.d/init.el @@ -78,6 +78,14 @@ :config (load-theme 'dracula t)) +;; Sitebar dirred +(use-package dired-sidebar + :ensure t + :commands (dired-sidebar-toggle-sidebar)) + +(require 'dired-sidebar) +(global-set-key (kbd "C-x d") 'dired-sidebar-toggle-sidebar) + ;; Magit (use-package magit :ensure t diff --git a/.emacs.d/plugins/dired-sidebar.el b/.emacs.d/plugins/dired-sidebar.el new file mode 100755 index 0000000..6308ca8 --- /dev/null +++ b/.emacs.d/plugins/dired-sidebar.el @@ -0,0 +1,1195 @@ +;;; dired-sidebar.el --- Tree browser leveraging dired -*- lexical-binding: t -*- + +;; Copyright (C) 2017 James Nguyen + +;; Author: James Nguyen +;; Maintainer: James Nguyen +;; URL: https://github.com/jojojames/dired-sidebar +;; Version: 0.0.1 +;; Package-Requires: ((emacs "25.1") (dired-subtree "0.0.1")) +;; Keywords: dired, files, tools +;; HomePage: https://github.com/jojojames/dired-sidebar + +;; 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 . + +;;; Commentary: +;; This package provides a tree browser similar to `neotree' or `treemacs' +;; but leverages `dired' to do the job of display. + +;; (use-package dired-sidebar +;; :bind (("C-x C-n" . dired-sidebar-toggle-sidebar)) +;; :ensure nil +;; :commands (dired-sidebar-toggle-sidebar)) + +;;; Code: + +(require 'dired) +(require 'dired-subtree) +(require 'face-remap) +(eval-when-compile (require 'subr-x)) ; `if-let*' and `when-let*' + +;; Compatibility + +(eval-and-compile + (with-no-warnings + (if (version< emacs-version "26") + (progn + (defalias 'dired-sidebar-if-let* #'if-let) + (defalias 'dired-sidebar-when-let* #'when-let) + (function-put #'dired-sidebar-if-let* 'lisp-indent-function 2) + (function-put #'dired-sidebar-when-let* 'lisp-indent-function 1)) + (defalias 'dired-sidebar-if-let* #'if-let*) + (defalias 'dired-sidebar-when-let* #'when-let*)))) + +;; Customizations + +(defgroup dired-sidebar nil + "A major mode leveraging `dired-mode' to display a filesystem in a tree +layout." + :group 'files) + +(defcustom dired-sidebar-use-custom-font nil + "Show `dired-sidebar' with custom font. + +This face can be customized using `dired-sidebar-face'." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-face nil + "Face used by `dired-sidebar' for custom font. + +This only takes effect if `dired-sidebar-use-custom-font' is true." + :type 'list + :group 'dired-sidebar) + +(defcustom dired-sidebar-use-custom-modeline t + "Show `dired-sidebar' with custom modeline. + +This uses format specified by `dired-sidebar-mode-line-format'." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-mode-line-format + '("%e" mode-line-front-space + mode-line-buffer-identification + " " mode-line-end-spaces) + "Mode line format for `dired-sidebar'." + :type 'list + :group 'dired-sidebar) + +(defcustom dired-sidebar-theme 'icons + "*The tree style to display. +`ascii' is the simplest style, it will use +/- to display the fold state, +it is suitable for terminal. +`icons' use `all-the-icons'. +`nerd' use the nerdtree indentation mode and arrow. +`none' use no theme. +`vscode' use `vscode' icons. + +This only takes effect if on a local connection. (e.g. Not Tramp)" + :group 'dired-sidebar + :type '(choice (const ascii) + (const icons) + (const nerd) + (const none) + (const vscode))) + +(defcustom dired-sidebar-width 35 + "Width of the `dired-sidebar' buffer." + :type 'integer + :group 'dired-sidebar) + +(defcustom dired-sidebar-refresh-on-projectile-switch t + "Refresh sidebar when `projectile' changes projects." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-should-follow-file nil + "Refresh sidebar to match current file." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-skip-subtree-parent t + "Whether to skip subtree parent directory when jumping up." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-pop-to-sidebar-on-toggle-open t + "Whether to jump to sidebar upon toggling open. + +This is used in conjunction with `dired-sidebar-toggle-sidebar'." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-follow-file-at-point-on-toggle-open t + "Whether to recursively cycle the subtree and put point on file. + +Similar to `dired-jump'. This moves point inside sidebar buffer +to where current-buffer-file is \(if it exists\) but does not necessarily +select the sidebar window." + :type 'boolean + :group 'dired-sidebar) + + + +(defcustom dired-sidebar-use-magit-integration t + "Whether to integrate with `magit-mode'. + +When true: + +When finding file to point at for +`dired-sidebar-follow-file-at-point-on-toggle-open', use file at point +in `magit' buffer. + +When finding root directory for sidebar, use directory specified by `magit'." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-use-term-integration nil + "Whether to integrate with `term-mode'. + +When true: + +When finding root directory for sidebar, use PWD of `term-mode'. This is turned +off by default due to the experimental nature of getting the PWD from the +terminal. + +Look at `dired-sidebar-term-get-pwd' for implementation." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-use-wdired-integration t + "Whether to integrate with `wdired'." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-cycle-subtree-on-click t + "Whether to cycle subtree on click." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-delay-auto-revert-updates t + "Whether to delay automatically reverting buffer. + +When true, only allow function `auto-revert-mode' to update every +`dird-sidebar-stale-buffer-time-idle-delay' seconds." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-stale-buffer-time-idle-delay 1.5 + "The time in idle seconds to wait before checking if buffer is stale." + :type 'number + :group 'dired-sidebar) + +(defcustom dired-sidebar-follow-file-idle-delay 2 + "The time in idle seconds to wait before checking if sidebar should +follow file." + :type 'number + :group 'dired-sidebar) + +(defcustom dired-sidebar-tui-update-delay 0.02 + "The time in idle seconds to wait before updating tui interface. + +This only takes effect if `all-the-icons-dired' is disabled." + :type 'number + :group 'dired-sidebar) + +(defcustom dired-sidebar-refresh-on-special-commands t + "Whether or not to trigger auto-revert after certain functions. + +Warning: This is implemented by advising specific dired functions." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-disable-dired-collapse t + "Whether or not to disable `dired-collapse' if it's enabled." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-special-refresh-commands + '(dired-do-delete + dired-do-rename + dired-do-copy + dired-do-flagged-delete + dired-create-directory + (delete-file . 5) + (save-buffer . 5) + magit-format-patch) + "A list of commands that will trigger a refresh of the sidebar. + +The command can be an alist with the CDR of the alist being the amount of time +to wait to refresh the sidebar after the CAR of the alist is called. + +Set this to nil or set `dired-sidebar-refresh-on-special-commands' to nil +to disable automatic refresh when a special command is triggered." + :type 'list + :group 'dired-sidebar) + +(defcustom dired-sidebar-toggle-hidden-commands + '(balance-windows) + "A list of commands that won't work when `dired-sidebar' is visible. + +When the command is triggered, `dired-sidebar' will hide temporarily until +command is completed. + +This functionality is implemented using advice. + +Set this to nil to disable this advice." + :type 'list + :group 'dired-sidebar) + +(defcustom dired-sidebar-alternate-select-window-function + #'dired-sidebar-default-alternate-select-window + "Function to call when using alternative window selection. + +Alternative window selection is used when `dired-sidebar-find-file' is called +with a prefix arg or when `dired-sidebar-find-file-alt' is called." + :type 'function + :group 'dired-sidebar) + +(defcustom dired-sidebar-recenter-cursor-on-follow-file t + "Whether or not to center cursor when pointing at file." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-recenter-cursor-on-tui-update nil + "Whether or not to center cursor when updating tui interface." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-display-autorevert-messages nil + "Whether or not to display `autorevert' messages." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-open-file-in-most-recently-used-window t + "Whether or not to open files in most recently used window." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-subtree-line-prefix dired-subtree-line-prefix + "The line prefix to use when subtree is cycled." + :type 'string + :group 'dired-sidebar) + +(defcustom dired-sidebar-display-alist '((side . left) (slot . -1)) + "Alist used in `display-buffer-in-side-window'. + +e.g. (display-buffer-in-side-window buffer '((side . left) (slot . -1)))" + :type 'alist + :group 'dired-sidebar) + +(defcustom dired-sidebar-close-sidebar-on-file-open nil + "Whether or not to close sidebar when `dired-sidebar-find-file' is called. + +This behavior only triggers if `dired-sidebar-find-file' is triggered on +a file." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-icon-scale .18 + "The scale of icons \(currently only applies to vscode theme.\)." + :type 'number + :group 'dired-sidebar) + +(defcustom dired-sidebar-no-delete-other-windows nil + "Whether or not to add `no-delete-other-window' parameter to window. + +If this is true, when calling `delete-other-windows', `dired-sidebar' window +will continue showing. + +For more information, look up `delete-other-windows'." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-one-instance-p nil + "Only show one buffer instance for dired-sidebar for each frame." + :type 'boolean + :group 'dired-sidebar) + +;; Internal + +(defvar dired-sidebar-basedir (file-name-directory load-file-name) + "Store the directory dired-sidebar.el was loaded from.") + +(defvar dired-sidebar-icons-dir (format "%sicons/" dired-sidebar-basedir) + "Store the icons directory of `dired-sidebar'.") + +(defvar dired-sidebar-alist '() + "An alist that maps from frame to currently opened `dired-sidebar' buffer.") + +(defvar-local dired-sidebar-stale-buffer-timer nil + "Timer used for setting `dired-sidebar-check-for-stale-buffer-p'. + +This is buffer local.") + +(defvar-local dired-sidebar-follow-file-timer nil + "Timer used when `dired-sidebar-should-follow-file' is true.") + +(defvar-local dired-sidebar-check-for-stale-buffer-p nil + "Whether to check if buffer is stale. + +When this is true `dired-sidebar-buffer-stale-p' +will check if buffer is stale through `auto-revert-mode'.") + +;; Mode + +(defmacro dired-sidebar-with-no-dedication (&rest body) + "Run BODY after undedicating window." + (declare (debug (&rest form))) + `(progn + (let ((window (get-buffer-window (current-buffer)))) + (set-window-dedicated-p window nil) + ,@body + (set-window-dedicated-p window t)))) + +(defvar dired-sidebar-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "TAB") 'dired-sidebar-subtree-toggle) + (define-key map [tab] 'dired-sidebar-subtree-toggle) + (define-key map (kbd "C-m") 'dired-sidebar-find-file) + (define-key map (kbd "RET") 'dired-sidebar-find-file) + (define-key map (kbd "") 'dired-sidebar-find-file) + (define-key map "^" 'dired-sidebar-up-directory) + (define-key map "-" 'dired-sidebar-up-directory) + (define-key map (kbd "C-o") 'dired-sidebar-find-file-alt) + (define-key map [mouse-2] 'dired-sidebar-mouse-subtree-cycle-or-find-file) + map) + "Keymap used for symbol `dired-sidebar-mode'.") + +(define-derived-mode dired-sidebar-mode dired-mode + "Dired-sidebar" + "A major mode that puts `dired' in a sidebar." + :group 'dired-sidebar + + ;; Hack for https://github.com/jojojames/dired-sidebar/issues/18. + ;; Would be open to a better fix... + ;; `dired-remember-hidden' in Emacs 25 (terminal?) seems to throw + ;; an error upon calling `goto-char'. + (when (<= emacs-major-version 25) + (defun dired-sidebar-remember-hidden-hack (f &rest args) + "Return nil for `dired-remember-hidden'. + +Works around marker pointing to wrong buffer in Emacs 25." + (if (eq major-mode 'dired-sidebar-mode) + nil + (apply f args))) + (advice-remove 'dired-remember-hidden 'dired-sidebar-remember-hidden-hack) + (advice-add 'dired-remember-hidden :around 'dired-sidebar-remember-hidden-hack)) + + ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32392 + (when dired-sidebar-use-wdired-integration + (advice-remove 'wdired-change-to-dired-mode + 'dired-sidebar-wdired-change-to-dired-mode-advice) + (advice-remove 'wdired-change-to-wdired-mode + 'dired-sidebar-wdired-change-to-wdired-mode-advice) + + (advice-add 'wdired-change-to-dired-mode + :around 'dired-sidebar-wdired-change-to-dired-mode-advice) + (advice-add 'wdired-change-to-wdired-mode + :around 'dired-sidebar-wdired-change-to-wdired-mode-advice)) + + (setq window-size-fixed 'width) + + ;; Match backgrounds. + (setq-local dired-subtree-use-backgrounds nil) + + ;; `dired-subtree''s line prefix is determined by `dired-sidebar'. + (setq-local dired-subtree-line-prefix dired-sidebar-subtree-line-prefix) + + ;; https://github.com/jojojames/dired-sidebar/issues/7 + ;; Symlinks are displayed incorrectly when these three things happen. + ;; 1. `dired-hide-details-mode' is on. + ;; 2. `dired-subtree' toggles a symlink folder via `dired-subtree-toggle'. + ;; 3. `dired-hide-details-hide-symlink-targets' is set to true. + ;; Since we use both 1 & 2, disable 3 to avoid the issue. + ;; This needs to be set to nil before `dired-hide-details-mode' is called. + (setq-local dired-hide-details-hide-symlink-targets nil) + + ;; Use `dired-sidebar-revert' instead that wraps `dired-revert'. + (setq-local revert-buffer-function 'dired-sidebar-revert) + + ;; We don't want extra details in the sidebar. + (dired-hide-details-mode) + + (when (and dired-sidebar-disable-dired-collapse + (fboundp 'dired-collapse-mode)) + (add-hook 'dired-mode-hook + (lambda () + (when (bound-and-true-p dired-collapse-mode) + (dired-collapse-mode -1))) + :append :local)) + + (when (and + (not dired-sidebar-display-autorevert-messages) + (boundp 'auto-revert-verbose)) + (setq-local auto-revert-verbose nil)) + + (when dired-sidebar-delay-auto-revert-updates + (setq-local buffer-stale-function #'dired-sidebar-buffer-stale-p) + (let ((current-buffer (current-buffer))) + (setq dired-sidebar-stale-buffer-timer + (run-with-idle-timer + dired-sidebar-stale-buffer-time-idle-delay + t (lambda () + ;; Only do a check if `dired-sidebar' buffer is in the foreground. + (when (get-buffer-window current-buffer) + (with-current-buffer current-buffer + (setq dired-sidebar-check-for-stale-buffer-p t)))))) + + (add-hook 'kill-buffer-hook + (lambda () + (when (timerp dired-sidebar-stale-buffer-timer) + (cancel-timer dired-sidebar-stale-buffer-timer))) + nil t))) + + (when dired-sidebar-refresh-on-special-commands + (mapc + (lambda (x) + (if (consp x) + (let ((command (car x)) + (delay (cdr x))) + (advice-add + command + :after + (defalias (intern (format "dired-sidebar-refresh-after-%S" command)) + (function + (lambda (&rest _) + (let ((timer-symbol + (intern + (format + "dired-sidebar-refresh-%S-timer" command)))) + (when (and (boundp timer-symbol) + (timerp (symbol-value timer-symbol))) + (cancel-timer (symbol-value timer-symbol))) + (setf + (symbol-value timer-symbol) + (run-with-idle-timer + delay + nil + #'dired-sidebar-refresh-buffer)))))))) + (advice-add x :after #'dired-sidebar-refresh-buffer))) + dired-sidebar-special-refresh-commands)) + + (when dired-sidebar-toggle-hidden-commands + (mapc + (lambda (x) + (advice-add x :around #'dired-sidebar-advice-hide-temporarily)) + dired-sidebar-toggle-hidden-commands)) + + (when dired-sidebar-use-custom-font + (dired-sidebar-set-font)) + + (when dired-sidebar-use-custom-modeline + (dired-sidebar-set-mode-line)) + + (when dired-sidebar-refresh-on-projectile-switch + (add-hook 'projectile-after-switch-project-hook + #'dired-sidebar-follow-file)) + + (when dired-sidebar-should-follow-file + (setq dired-sidebar-follow-file-timer + (run-with-idle-timer + dired-sidebar-follow-file-idle-delay + t #'dired-sidebar-follow-file))) + + ;; This comment is taken from `dired-readin'. + ;; Begin --- Copied comment from dired.el. + ;; Must first make alist buffer local and set it to nil because + ;; dired-build-subdir-alist will call dired-clear-alist first + ;; End --- Copied comment from dired.el. + (setq-local dired-subdir-alist nil) + (dired-build-subdir-alist) + + (dired-unadvertise (dired-current-directory)) + (dired-sidebar-update-buffer-name) + (dired-sidebar-update-state (current-buffer)) + + ;; Move setting theme until the end after `dired-sidebar' has set up + ;; its directory structure. + ;; https://github.com/jojojames/dired-sidebar/issues/29 + (unless (file-remote-p default-directory) + (cond + ((dired-sidebar-using-tui-p) + (dired-sidebar-setup-tui)) + ((and (eq dired-sidebar-theme 'icons) + (display-graphic-p) + (or + (fboundp 'all-the-icons-dired-mode) + (autoloadp (symbol-function 'all-the-icons-dired-mode)))) + (with-no-warnings + (all-the-icons-dired-mode))) + (:default :no-theme)))) + +;; User Interface + +;;;###autoload +(defun dired-sidebar-toggle-sidebar (&optional dir) + "Toggle the project explorer window. +Optional argument DIR Use DIR as sidebar root if available. + +With universal argument, use current directory." + (interactive) + (if (dired-sidebar-showing-sidebar-p) + (dired-sidebar-hide-sidebar) + (let* ((old-buffer (dired-sidebar-buffer (selected-frame))) + (file-to-show (dired-sidebar-get-file-to-show)) + (dir-to-show (or dir + (when current-prefix-arg + (expand-file-name default-directory)) + (dired-sidebar-get-dir-to-show))) + (sidebar-buffer (dired-sidebar-get-or-create-buffer dir-to-show))) + (dired-sidebar-show-sidebar sidebar-buffer) + (when (and dired-sidebar-one-instance-p old-buffer (not (eq sidebar-buffer old-buffer))) + (kill-buffer old-buffer)) + (if (and dired-sidebar-follow-file-at-point-on-toggle-open + file-to-show) + (if dired-sidebar-pop-to-sidebar-on-toggle-open + (dired-sidebar-point-at-file file-to-show dir-to-show) + (with-selected-window (selected-window) + (dired-sidebar-point-at-file file-to-show dir-to-show))) + (when dired-sidebar-pop-to-sidebar-on-toggle-open + (pop-to-buffer (dired-sidebar-buffer))))))) + +(defun dired-sidebar-point-at-file (name root) + "Try to point at NAME from sidebar. + +Keep `dired' pointed at ROOT while cycling directories until +NAME is found in ROOT path. + +This is dependent on `dired-subtree-cycle'." + (let ((sidebar (dired-sidebar-buffer))) + (pop-to-buffer sidebar) + (when (and name + ;; Checking for a private method. *shrug* + (fboundp 'dired-subtree--is-expanded-p)) + (pop-to-buffer sidebar) + (goto-char 0) + (let* ((path root) + ;; Imagine root is /root/var/ and name is + ;; /root/var/a/b/c. + ;; This will return a list of '\("a" "b" "c"\). + (dirs (when (cadr (split-string name root)) + (split-string (cadr (split-string name root)) "/")))) + (dolist (dir dirs) + (let ((path-regex (concat "^.*[[:space:]]" (regexp-quote dir)))) + (setq path (concat path dir)) + (if (file-regular-p path) + ;; Try to use `dired-goto-file' to go to the correct + ;; file. If that fails, just search for the text. + (let ((default-directory (file-name-directory path))) + (unless (dired-goto-file path) + (condition-case nil + ;; It's hard to get this right so just using a + ;; heuristic will get 90% of the way there. + ;; Making sure there's a space in front of the name + ;; skips matches that contains the name as a + ;; substring which is probably good enough... + (re-search-forward path-regex) + ;; Sometimes `dired' gets out of sync with the file. + ;; Refresh the buffer and try the search again. + ;; One way to reproduce this: + ;; 1. Open file A as buffer B. + ;; 2. Delete file A in `dired'. + ;; 3. Hide `dired-sidebar'. + ;; 4. Save buffer B. + ;; 5. Re-open `dired-sidebar'. + (error + (revert-buffer) + (re-search-forward path-regex nil :no-error))))) + (re-search-forward path-regex) + ;; Check if subtree has already been expanded. + ;; Basically, we're using `dired-subtree-cycle' more + ;; like dired-subtree-expand. + (when (not (dired-subtree--is-expanded-p)) + ;; This will probably throw an error when trying to expand + ;; directories that have been collapsed by `dired-collapse'. + (dired-subtree-cycle)) + (setq path (concat path "/")))))) + (when dired-sidebar-recenter-cursor-on-follow-file + (recenter nil)) + (dired-sidebar-redisplay-icons)))) + +;;;###autoload +(defun dired-sidebar-toggle-with-current-directory () + "Like `dired-sidebar-toggle-sidebar' but use current-directory." + (interactive) + (let ((current-prefix-arg '(4))) ; C-u + (call-interactively #'dired-sidebar-toggle-sidebar))) + +;;;###autoload +(defun dired-sidebar-show-sidebar (&optional b) + "Show sidebar displaying buffer B." + (interactive) + (let ((buffer (or b + ;; Only expect this to be hit when called interactively. + (dired-sidebar-get-or-create-buffer + (dired-sidebar-get-dir-to-show))))) + (display-buffer-in-side-window buffer dired-sidebar-display-alist) + (let ((window (get-buffer-window buffer))) + (when dired-sidebar-no-delete-other-windows + (set-window-parameter window 'no-delete-other-windows t)) + (set-window-dedicated-p window t) + (with-selected-window window + (let ((window-size-fixed)) + (dired-sidebar-set-width dired-sidebar-width)))) + (with-current-buffer buffer + (if (eq major-mode 'dired-sidebar-mode) + (dired-build-subdir-alist) + (dired-sidebar-mode))) + (dired-sidebar-update-state buffer))) + +;;;###autoload +(defun dired-sidebar-hide-sidebar () + "Hide the sidebar in the selected frame." + (interactive) + (dired-sidebar-when-let* ((buffer (dired-sidebar-buffer))) + (delete-window (get-buffer-window buffer)) + (dired-sidebar-update-state nil))) + +;;;###autoload +(defun dired-sidebar-jump-to-sidebar () + "Jump to `dired-sidebar' buffer if it is showing. + +If it's not showing, act as `dired-sidebar-toggle-sidebar'." + (interactive) + (if (dired-sidebar-showing-sidebar-p) + (select-window + (get-buffer-window (dired-sidebar-buffer (selected-frame)))) + (call-interactively #'dired-sidebar-toggle-sidebar))) + +(defun dired-sidebar-find-file (&optional dir) + "Wrapper over `dired-find-file'. +Optional argument DIR Fine file using DIR of available. + +With prefix argument, use `dired-sidebar-alternate-select-window-function' for +window selection." + (interactive) + (let ((find-file-run-dired t) + (dired-file-name (or dir (dired-get-file-for-visit))) + (select-with-alt-window-function current-prefix-arg)) + (if (and (file-directory-p dired-file-name) + ;; For "." open a full-blown dired buffer, since the directory is + ;; already open in the sidebar. + (not (string= (file-name-nondirectory dired-file-name) + "."))) + (dired-sidebar-with-no-dedication + (let ((buf-name (dired-sidebar-buffer-name + dired-file-name))) + (if (dired-sidebar-buffer-exists-p buf-name) + (progn + (switch-to-buffer buf-name) + (dired-sidebar-update-state (current-buffer))) + (if (and dired-sidebar-one-instance-p (file-directory-p dired-file-name)) + (find-alternate-file dired-file-name) + ;; Copied from `dired-find-file'. + (find-file dired-file-name)) + (dired-sidebar-mode) + (dired-sidebar-update-state (current-buffer))))) + ;; Select the sidebar window so that `next-window' is consistent + ;; in picking the window next to the sidebar. + ;; This is useful for when `dired-sidebar-find-file' is called + ;; from a buffer that is not already in the sidebar buffer. + ;; e.g. A mouse click event. + (switch-to-buffer (dired-sidebar-buffer)) + (select-window + (if select-with-alt-window-function + (funcall dired-sidebar-alternate-select-window-function) + (if dired-sidebar-open-file-in-most-recently-used-window + (get-mru-window nil nil t) + (next-window)))) + (find-file dired-file-name) + (when dired-sidebar-close-sidebar-on-file-open + (dired-sidebar-hide-sidebar))))) + +(defun dired-sidebar-find-file-alt () + "Like `dired-sidebar-find-file' but select window with alterate method. + +Select alternate window using `dired-sidebar-alternate-select-window-function'." + (interactive) + (let ((current-prefix-arg '(4))) ; C-u + (call-interactively 'dired-sidebar-find-file))) + +(defun dired-sidebar-up-directory () + "Wrapper over `dired-up-directory'." + (interactive) + (dired-sidebar-with-no-dedication + ;; If `dired-subtree' is used, `dired-current-directory' is redefined. + ;; So move point to the top of the buffer to get the actual directory and + ;; not the one at point. + (when dired-sidebar-skip-subtree-parent + (goto-char (point-min))) + (let* ((dir (dired-current-directory)) + (up (file-name-directory (directory-file-name dir))) + (up-name (dired-sidebar-buffer-name up))) + (if (dired-sidebar-buffer-exists-p up-name) + (progn + (switch-to-buffer up-name) + (dired-sidebar-update-state (current-buffer))) + (if dired-sidebar-one-instance-p + (find-alternate-file "..") + (dired-up-directory)) + (dired-sidebar-mode) + (dired-sidebar-update-state (current-buffer))) + (let ((default-directory up)) + (dired-goto-file dir))))) + +(defun dired-sidebar-mouse-subtree-cycle-or-find-file (event) + "Handle a mouse click EVENT in `dired-sidebar'. + +For directories, if `dired-sidebar-cycle-subtree-on-click' is true, +cycle the directory. + +Otherwise, behaves the same as if user clicked on a file. + +For files, use `dired-sidebar-find-file'. + +This uses the same code as `dired-mouse-find-file-other-window' to find +the relevant file-directory clicked on by the mouse." + (interactive "e") + (let (window pos file) + (save-excursion + (setq window (posn-window (event-end event)) + pos (posn-point (event-end event))) + (if (not (windowp window)) + (error "No file chosen")) + (set-buffer (window-buffer window)) + (goto-char pos) + (setq file (dired-get-file-for-visit))) + ;; There's a flicker doing this but it doesn't seem like + ;; `dired-subtree-cycle' works without first selecting the window. + (with-selected-window window + (if (and dired-sidebar-cycle-subtree-on-click + (file-directory-p file) + (not (string-suffix-p "." file))) + (dired-subtree-cycle) + (dired-sidebar-find-file file))))) + +;; Helpers + +(defun dired-sidebar-buffer-exists-p (buffer-name) + "Check if a `dired-sidebar' buffer exists for BUFFER-NAME." + (get-buffer buffer-name)) + +(defun dired-sidebar-sidebar-root () + "Return directory using `projectile', `project' or current directory." + (if (featurep 'projectile) + (condition-case nil + (if (fboundp 'projectile-project-root) + (or (projectile-project-root) default-directory) + default-directory) + (error default-directory)) + ;; Use `project' if `projectile' is not loaded yet. + ;; `projectile' is a big package and takes a while to load so it's better + ;; to defer loading it as long as possible (until the user chooses). + (dired-sidebar-if-let* ((project (project-current))) + (cdr project) + default-directory))) + +(defun dired-sidebar-buffer-name (dir) + "Return name of `dired-sidebar' buffer given DIR." + (let ((b (cond + ((string-suffix-p ".." dir) + ;; ~/.emacs.d/elpa/.. -> ~/.emacs.d/ + (file-name-directory (substring dir 0 (- (length dir) 3)))) + ((not (string-suffix-p "/" dir)) + (concat dir "/")) + (:default + dir)))) + (concat ":" (abbreviate-file-name b)))) + +(defun dired-sidebar-get-or-create-buffer (root) + "Get or create a `dired-sidebar' buffer matching ROOT." + (interactive) + (let ((name (dired-sidebar-buffer-name root))) + (dired-sidebar-if-let* ((existing-buffer (get-buffer name))) + existing-buffer + (let ((buffer (dired-noselect root))) + ;; When opening a sidebar while in a dired buffer that matches + ;; the sidebar's root directory. + (if (eq (current-buffer) buffer) + ;; https://github.com/Fuco1/dired-hacks/issues/102 + (if (member 'dired-collapse-mode dired-mode-hook) + (progn + (remove-hook 'dired-mode-hook 'dired-collapse-mode) + (let ((clone (clone-buffer))) + (add-hook 'dired-mode-hook 'dired-collapse-mode) + clone)) + (clone-buffer)) + ;; Rename the buffer generated by `dired-noselect'. + (when (not (string-equal (buffer-name buffer) name)) + (with-current-buffer buffer + (rename-buffer name))) + buffer))))) + +(defun dired-sidebar-set-font () + "Customize font in `dired-sidebar'. + +Set font to a variable width (proportional) in the current buffer." + (interactive) + (setq-local buffer-face-mode-face dired-sidebar-face) + (buffer-face-mode)) + +(defun dired-sidebar-set-mode-line () + "Customize modeline in `dired-sidebar'." + (setq mode-line-format dired-sidebar-mode-line-format)) + +(defun dired-sidebar-set-width (width) + "Set the width of the buffer to WIDTH when it is created." + ;; Copied from `treemacs--set-width' as well as `neotree'. + (unless (one-window-p) + (let ((window-size-fixed) + (w (max width window-min-width))) + (cond + ((> (window-width) w) + (shrink-window-horizontally (- (window-width) w))) + ((< (window-width) w) + (enlarge-window-horizontally (- w (window-width)))))))) + +(defun dired-sidebar-update-buffer-name () + "Change buffer name to avoid collision with regular `dired' buffers." + (rename-buffer + (dired-sidebar-buffer-name (dired-current-directory)))) + +(defun dired-sidebar-update-state (buffer &optional f) + "Update current state with BUFFER for sidebar in F or selected frame." + (let ((frame (or f (selected-frame)))) + (if (assq frame dired-sidebar-alist) + (setcdr (assq frame dired-sidebar-alist) buffer) + (push `(,frame . ,buffer) dired-sidebar-alist)))) + +(defun dired-sidebar-showing-sidebar-p (&optional f) + "Whether F or selected frame is showing a sidebar. + +Check if F or selected frame contains a sidebar and return +corresponding buffer if buffer has a window attached to it. + +Return buffer if so." + (dired-sidebar-when-let* ((buffer (dired-sidebar-buffer f))) + (get-buffer-window buffer))) + +(defun dired-sidebar-buffer (&optional f) + "Return the current sidebar buffer in F or selected frame. + +This can return nil if the buffer has been killed." + (let* ((frame (or f (selected-frame))) + (buffer (alist-get frame dired-sidebar-alist))) + ;; The buffer can be killed for a variety of reasons. + ;; This side effect is kind of messy but it's the simplest place + ;; to put the clean up code for `dired-sidebar-alist'. + (if (buffer-live-p buffer) + buffer + ;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Association-Lists.html + ;; Documentation for `assq-delete-all'. + ;; What kind of API is this?? :() + ;; Why does it only modify 'often' and not 'always'? ¯\_(ツ)_/¯ + ;; It returns the shortened alist, and often modifies the original list + ;; structure of alist. + ;; For correct results, use the return value of assq-delete-all rather + ;; than looking at the saved value of alist. + (setq dired-sidebar-alist + (assq-delete-all frame dired-sidebar-alist)) + nil))) + +(defun dired-sidebar-switch-to-dir (dir) + "Update buffer with DIR as root." + (when (dired-sidebar-showing-sidebar-p) + (let ((buffer (dired-sidebar-get-or-create-buffer dir))) + (dired-sidebar-show-sidebar buffer)))) + +(defun dired-sidebar-buffer-stale-p (&optional noconfirm) + "Wrapper over `dired-buffer-stale-p'. + +Check if buffer is stale only if `dired-sidebar-stale-buffer-time-idle-delay' + +has elapsed. + +Optional argument NOCONFIRM Pass NOCONFIRM on to `dired-buffer-stale-p'." + (when dired-sidebar-check-for-stale-buffer-p + (setq dired-sidebar-check-for-stale-buffer-p nil) + (dired-buffer-stale-p noconfirm))) + +(defun dired-sidebar-refresh-buffer (&rest _) + "Refresh sidebar buffer." + (dired-sidebar-when-let* ((sidebar (dired-sidebar-buffer))) + (with-current-buffer sidebar + (let ((auto-revert-verbose nil)) + (ignore auto-revert-verbose) ;; Make byte compiler happy. + (revert-buffer))))) + +(defun dired-sidebar-follow-file () + "Follow new file. + +The root of the sidebar will be determined by `dired-sidebar-get-dir-to-show' +and the file followed is will be determined by `dired-sidebar-get-file-to-show', + +both accounting for the currently selected window." + (when (dired-sidebar-showing-sidebar-p) + ;; Wrap in `with-selected-window' because we don't want to pop to + ;; the sidebar buffer. + ;; We also need to pick the correct selected-window so that + ;; `dired-sidebar-get-dir-to-show' can get the correct root to change to. + (with-selected-window (selected-window) + (let ((root (dired-sidebar-get-dir-to-show))) + (dired-sidebar-switch-to-dir root) + (when dired-sidebar-follow-file-at-point-on-toggle-open + (dired-sidebar-when-let* ((file (dired-sidebar-get-file-to-show))) + (dired-sidebar-point-at-file file root))))))) + +(defun dired-sidebar-default-alternate-select-window () + "Default function for `dired-sidebar-alternate-select-window-function'." + (if (fboundp 'aw-select) + (aw-select "Select Window") + (next-window))) + +(defun dired-sidebar-get-dir-to-show () + "Return the directory `dired-sidebar' should open to." + (expand-file-name + (cond + ((and (derived-mode-p 'magit-mode) + dired-sidebar-use-magit-integration + (fboundp 'magit-toplevel)) + (magit-toplevel)) + ((and (eq major-mode 'term-mode) + dired-sidebar-use-term-integration) + (dired-sidebar-term-get-pwd)) + ((and (eq major-mode 'dired-mode)) + default-directory) + ((and (eq major-mode 'ibuffer-mode) + (fboundp 'ibuffer-current-buffer) + (ibuffer-current-buffer)) + (let ((buffer-at-point (ibuffer-current-buffer))) + (if (fboundp 'ibuffer-projectile-root) + (dired-sidebar-if-let* ((ibuffer-projectile-root + (ibuffer-projectile-root buffer-at-point))) + (cdr ibuffer-projectile-root) + (with-current-buffer buffer-at-point + default-directory)) + (with-current-buffer buffer-at-point + default-directory)))) + (:default + (dired-sidebar-sidebar-root))))) + +(defun dired-sidebar-get-file-to-show () + "Return the file `dired-sidebar' should open to. + +This may return nil if there's no suitable file to show." + (cond + ((and dired-sidebar-use-magit-integration + (derived-mode-p 'magit-mode) + (fboundp 'magit-file-at-point) + (magit-file-at-point)) + (expand-file-name (magit-file-at-point))) + ((and (eq major-mode 'dired-mode)) + ;; Not sure if `dired-get-filename' is more appropriate. + (condition-case nil + (dired-get-file-for-visit) + (error nil))) + ((and (eq major-mode 'ibuffer-mode) + (fboundp 'ibuffer-current-buffer)) + (let ((bf-name (buffer-file-name (ibuffer-current-buffer)))) + (and bf-name (file-exists-p bf-name) bf-name))) + (:default + (and buffer-file-name (file-exists-p buffer-file-name) buffer-file-name)))) + +(defun dired-sidebar-term-get-pwd () + "Get current directory of `term-mode'. + +This is somewhat experimental/hacky." + (interactive) + (condition-case nil + (progn + (forward-paragraph) + (when (fboundp 'term-previous-prompt) + (term-previous-prompt 1)) + (when (fboundp 'term-simple-send) + (term-simple-send (get-buffer-process (current-buffer)) "pwd")) + (sleep-for 0 50) + (forward-line 1) + (let ((result (string-trim (thing-at-point 'line)))) + (kill-whole-line) + (forward-line -1) + (kill-whole-line) + result)) + (error + default-directory))) + +(defun dired-sidebar-subtree-toggle () + "Wrapper over `dired-subtree-toggle' that accounts for `all-the-icons-dired'." + (interactive) + (dired-subtree-toggle) + (dired-sidebar-redisplay-icons)) + +(defun dired-sidebar-redisplay-icons () + "Redisplay icon themes unless over TRAMP." + (unless (file-remote-p default-directory) + (when (and (eq dired-sidebar-theme 'icons) + (fboundp 'all-the-icons-dired--refresh)) + ;; Refresh `all-the-icons-dired'. + (dired-sidebar-revert) + (all-the-icons-dired--refresh)) + (when (dired-sidebar-using-tui-p) + (dired-sidebar-tui-update-with-delay)))) + +(defun dired-sidebar-advice-hide-temporarily (f &rest args) + "A function meant to be used with advice to temporarily hide itself. + +This function hides the sidebar before executing F and then reshows itself after." + (if (not (dired-sidebar-showing-sidebar-p)) + (apply f args) + (let ((sidebar (dired-sidebar-buffer))) + (dired-sidebar-hide-sidebar) + (apply f args) + (dired-sidebar-show-sidebar sidebar)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Text User Interface ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar-local dired-sidebar-tui-dired-displayed nil + "Flags whether icons have been added.") + +(defun dired-sidebar-tui-dired-reset (&optional _arg _noconfirm) + "Function used as advice when redisplaying buffer." + (setq-local dired-sidebar-tui-dired-displayed nil)) + +(defun dired-sidebar-tui-dired-display () + "Display the icons of files in a dired buffer." + (interactive) + (when (or t (and (not dired-sidebar-tui-dired-displayed) dired-subdir-alist)) + (setq-local dired-sidebar-tui-dired-displayed t) + (let ((inhibit-read-only t) + (collapsible-icon (if (eq dired-sidebar-theme 'nerd) "▾" "-")) + (expandable-icon (if (eq dired-sidebar-theme 'nerd) "▸" "+"))) + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (when (dired-move-to-filename nil) + (dired-move-to-filename) + (let ((file (dired-get-filename 'verbatim t))) + (unless (member file '("." "..")) + (let ((filename (dired-get-filename nil t))) + (if (eq dired-sidebar-theme 'vscode) + (progn + (require 'vscode-icon) + (when (fboundp 'vscode-icon-for-file) + (insert-image + (vscode-icon-for-file filename) " ")) + (insert " ")) + (if (file-directory-p filename) + (if (dired-subtree--is-expanded-p) + (insert (concat collapsible-icon " ")) + (insert (concat expandable-icon " "))) + (insert ""))))))) + (forward-line 1)))))) + +(defun dired-sidebar-tui-update-with-delay (&rest _) + "Update tui interface after a delay." + (run-with-idle-timer + dired-sidebar-tui-update-delay nil + #'dired-sidebar-tui-update)) + +(defun dired-sidebar-tui-update () + "Workhorse function to update tui interface." + (dired-sidebar-when-let* ((buffer (dired-sidebar-buffer))) + (with-current-buffer buffer + (dired-sidebar-revert) + (when dired-sidebar-recenter-cursor-on-tui-update + (recenter))))) + +(defun dired-sidebar-revert (&rest _) + "Wrapper around `dired-revert' but saves window position." + (dired-sidebar-when-let* ((window (get-buffer-window + (dired-sidebar-buffer)))) + (with-selected-window window + (let ((old-window-start (window-start))) + (when (dired-sidebar-using-tui-p) + (dired-sidebar-tui-reset-in-sidebar)) + (dired-revert) + (set-window-start window old-window-start))))) + +(defun dired-sidebar-tui-reset-in-sidebar (&rest _) + "Runs `dired-sidebar-tui-dired-reset' in current `dired-sidebar' buffer." + (dired-sidebar-when-let* ((buffer (dired-sidebar-buffer))) + (with-current-buffer buffer + (dired-sidebar-tui-dired-reset)))) + +(defun dired-sidebar-setup-tui () + "Sets up text user interface for `dired-sidebar'. + +This is used in place of `all-the-icons' to add directory indicators. + +e.g. + and -." + (add-hook 'dired-after-readin-hook + 'dired-sidebar-tui-dired-display :append :local) + (setq-local dired-subtree-line-prefix " ") + (dired-build-subdir-alist) + (dired-sidebar-revert)) + +(defun dired-sidebar-using-tui-p () + "Return t if `dired-sidebar-theme' is using tui code path." + (or + (eq dired-sidebar-theme 'ascii) + (eq dired-sidebar-theme 'nerd) + (eq dired-sidebar-theme 'vscode))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Text User Interface ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; `wdired' Hack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32392 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; `wdired' Hack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar-local dired-sidebar-wdired-tracking-major-mode nil + "Track current `major-mode' when toggling to `wdired'.") + +(defun dired-sidebar-wdired-change-to-dired-mode-advice (f &rest args) + "Advice for `wdired-change-to-dired-mode'." + (if (eq dired-sidebar-wdired-tracking-major-mode 'dired-sidebar-mode) + (dired-sidebar-wdired-change-to-dired-mode) + (apply f args))) + +(defun dired-sidebar-wdired-change-to-dired-mode () + "Change the mode back to dired-sidebar. + +This is an exact copy of `wdired-change-to-dired-mode' but changes the +`major-mode' to `dired-sidebar-mode' instead of `dired-mode'." + (let ((inhibit-read-only t)) + (remove-text-properties + (point-min) (point-max) + '(front-sticky nil rear-nonsticky nil read-only nil keymap nil))) + (use-local-map dired-mode-map) + (force-mode-line-update) + (setq buffer-read-only t) + (setq major-mode 'dired-sidebar-mode) + (setq mode-name "Dired-sidebar") + (dired-advertise) + (remove-hook 'kill-buffer-hook 'wdired-check-kill-buffer t) + (set (make-local-variable 'revert-buffer-function) 'dired-sidebar-revert)) + +(defun dired-sidebar-wdired-change-to-wdired-mode-advice (f &rest args) + "Forward to `wdired-change-to-wdired-mode'. + +`wdired' expected the `major-mode' to be `dired-mode' first. + +Track the current `major-mode' and revert to that upon exiting `wdired'." + (setq dired-sidebar-wdired-tracking-major-mode major-mode) + (if (eq major-mode 'dired-mode) + (apply f args) + (let ((major-mode 'dired-mode)) + (apply f args)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; `wdired' Hack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(provide 'dired-sidebar) +;;; dired-sidebar.el ends here diff --git a/.vimrc-gentoo b/.vimrc-gentoo index e0d019f..dcd5ae1 100644 --- a/.vimrc-gentoo +++ b/.vimrc-gentoo @@ -14,11 +14,13 @@ au BufWritePre * let &bex = '@' . strftime("%F.%H:%M") " plugins call plug#begin('~/.vim/plugged') Plug 'preservim/nerdtree' -Plug 'vimwiki/vimwiki' +"Plug 'vimwiki/vimwiki' +Plug 'y0rune/vimwiki' Plug 'nmante/vim-latex-live-preview' Plug 'lervag/vimtex' Plug 'junegunn/goyo.vim' Plug 'jceb/vim-orgmode' +Plug 'tpope/vim-speeddating' Plug 'prettier/vim-prettier', { 'do': 'npm install --force' } Plug 'iamcco/markdown-preview.nvim', { 'do': 'npm install --force' } " Problem with vim-prettier @@ -30,10 +32,10 @@ call plug#end() " Vim wiki let wiki = {} let wiki.path = '~/git/notes/' -let wiki.path_html = '~/vimwiki_html/' +let wiki.path_html = '~/git/notes/www/' let wiki.syntax = 'default' let wiki.ext = '.wiki' -let wiki.text_ignore_newline = 0 +let text_ignore_newline = 0 let g:vimwiki_list = [wiki] let g:vimwiki_list_ignore_newline = 1 let g:vimwiki_table_mappings = 0 @@ -141,3 +143,4 @@ au BufRead,BufNewFile *.md setlocal textwidth=80 " Auto add notes git au BufRead,BufNewFile *.wiki setlocal textwidth=80 autocmd BufWritePost *.wiki execute '!git add % && git commit -m "Auto-commit: saved %" && git push' | redraw! + autocmd BufWritePost *.wiki :VimwikiAll2HTML