1502 lines
58 KiB
EmacsLisp
1502 lines
58 KiB
EmacsLisp
;;; funcs.el --- Spacemacs Defaults Layer functions File
|
||
;;
|
||
;; Copyright (c) 2012-2018 Sylvain Benner & Contributors
|
||
;;
|
||
;; Author: Sylvain Benner <sylvain.benner@gmail.com>
|
||
;; URL: https://github.com/syl20bnr/spacemacs
|
||
;;
|
||
;; This file is not part of GNU Emacs.
|
||
;;
|
||
;;; License: GPLv3
|
||
|
||
(require 'cl-lib)
|
||
|
||
(defun spacemacs//run-local-vars-mode-hook ()
|
||
"Run a hook for the major-mode after the local variables have been processed."
|
||
(run-hooks (intern (format "%S-local-vars-hook" major-mode))))
|
||
|
||
(defun spacemacs/split-and-new-line ()
|
||
"Split a quoted string or s-expression and insert a new line with
|
||
auto-indent."
|
||
(interactive)
|
||
(sp-split-sexp 1)
|
||
(sp-newline))
|
||
|
||
(defun spacemacs/push-mark-and-goto-beginning-of-line ()
|
||
"Push a mark at current location and go to the beginning of the line."
|
||
(interactive)
|
||
(push-mark (point))
|
||
(evil-beginning-of-line))
|
||
|
||
(defun spacemacs/push-mark-and-goto-end-of-line ()
|
||
"Push a mark at current location and go to the end of the line."
|
||
(interactive)
|
||
(push-mark (point))
|
||
(evil-end-of-line))
|
||
|
||
(defun spacemacs/evil-insert-line-above (count)
|
||
"Insert one or several lines above the current point's line without changing
|
||
the current state and point position."
|
||
(interactive "p")
|
||
(dotimes (_ count) (save-excursion (evil-insert-newline-above))))
|
||
|
||
(defun spacemacs/evil-insert-line-below (count)
|
||
"Insert one or several lines below the current point's line without changing
|
||
the current state and point position."
|
||
(interactive "p")
|
||
(dotimes (_ count) (save-excursion (evil-insert-newline-below))))
|
||
|
||
(defun spacemacs/evil-goto-next-line-and-indent (&optional count)
|
||
"Match the current lines indentation to the next line.
|
||
A COUNT argument matches the indentation to the next COUNT lines."
|
||
(interactive "p")
|
||
(let ((counter (or count 1)))
|
||
(while (> counter 0)
|
||
(join-line 1)
|
||
(newline-and-indent)
|
||
(setq counter (1- counter)))))
|
||
|
||
;; from Prelude
|
||
;; TODO: dispatch these in the layers
|
||
(defcustom spacemacs-indent-sensitive-modes
|
||
'(asm-mode
|
||
coffee-mode
|
||
elm-mode
|
||
haml-mode
|
||
haskell-mode
|
||
slim-mode
|
||
makefile-mode
|
||
makefile-bsdmake-mode
|
||
makefile-gmake-mode
|
||
makefile-imake-mode
|
||
python-mode
|
||
yaml-mode)
|
||
"Modes for which auto-indenting is suppressed."
|
||
:type 'list
|
||
:group 'spacemacs)
|
||
|
||
(defcustom spacemacs-yank-indent-modes '(latex-mode)
|
||
"Modes in which to indent regions that are yanked (or yank-popped).
|
||
Only modes that don't derive from `prog-mode' should be listed here."
|
||
:type 'list
|
||
:group 'spacemacs)
|
||
|
||
(defcustom spacemacs-yank-indent-threshold 1000
|
||
"Threshold (# chars) over which indentation does not automatically occur."
|
||
:type 'number
|
||
:group 'spacemacs)
|
||
|
||
(defcustom spacemacs-large-file-modes-list
|
||
'(archive-mode tar-mode jka-compr git-commit-mode image-mode
|
||
doc-view-mode doc-view-mode-maybe ebrowse-tree-mode
|
||
pdf-view-mode)
|
||
"Major modes which `spacemacs/check-large-file' will not be
|
||
automatically applied to."
|
||
:group 'spacemacs
|
||
:type '(list symbol))
|
||
|
||
|
||
;; ido-mode remaps some commands to ido counterparts. We want default Emacs key
|
||
;; bindings (those under C-x) to use ido, but we want to use the original
|
||
;; commands in Spacemacs key bindings (those under M-m or SPC) so that they use
|
||
;; `read-file-name-function', `completing-read-function',
|
||
;; `completion-in-region-function', etc. configured by Helm or Ivy etc. The
|
||
;; following aliases allow us to bind Spacemacs keys to the original commands.
|
||
(defalias 'spacemacs/find-file-other-frame 'find-file-other-frame)
|
||
(defalias 'spacemacs/dired 'dired)
|
||
(defalias 'spacemacs/dired-other-frame 'dired-other-frame)
|
||
(defalias 'spacemacs/switch-to-buffer-other-frame 'switch-to-buffer-other-frame)
|
||
(defalias 'spacemacs/insert-file 'insert-file)
|
||
(defalias 'spacemacs/display-buffer-other-frame 'display-buffer-other-frame)
|
||
(defalias 'spacemacs/find-file-and-replace-buffer 'find-alternate-file)
|
||
|
||
(defun spacemacs/indent-region-or-buffer ()
|
||
"Indent a region if selected, otherwise the whole buffer."
|
||
(interactive)
|
||
(save-excursion
|
||
(if (region-active-p)
|
||
(progn
|
||
(indent-region (region-beginning) (region-end))
|
||
(message "Indented selected region."))
|
||
(progn
|
||
(evil-indent (point-min) (point-max))
|
||
(message "Indented buffer.")))
|
||
(whitespace-cleanup)))
|
||
|
||
;; from https://gist.github.com/3402786
|
||
(defun spacemacs/toggle-maximize-buffer ()
|
||
"Maximize buffer"
|
||
(interactive)
|
||
(save-excursion
|
||
(if (and (= 1 (length (window-list)))
|
||
(assoc ?_ register-alist))
|
||
(jump-to-register ?_)
|
||
(progn
|
||
(window-configuration-to-register ?_)
|
||
(delete-other-windows)))))
|
||
|
||
;; https://tsdh.wordpress.com/2007/03/28/deleting-windows-vertically-or-horizontally/
|
||
(defun spacemacs/maximize-horizontally ()
|
||
"Delete all windows to the left and right of the current window."
|
||
(interactive)
|
||
(require 'windmove)
|
||
(save-excursion
|
||
(while (condition-case nil (windmove-left) (error nil))
|
||
(delete-window))
|
||
(while (condition-case nil (windmove-right) (error nil))
|
||
(delete-window))))
|
||
|
||
(defun spacemacs/maximize-vertically ()
|
||
"Delete all windows above and below the current window."
|
||
(interactive)
|
||
(require 'windmove)
|
||
(save-excursion
|
||
(while (condition-case nil (windmove-up) (error nil))
|
||
(delete-window))
|
||
(while (condition-case nil (windmove-down) (error nil))
|
||
(delete-window))))
|
||
|
||
(defun spacemacs/useful-buffer-p (buffer)
|
||
"Determines if a buffer is useful."
|
||
(let ((buf-name (buffer-name buffer)))
|
||
(or (with-current-buffer buffer
|
||
(derived-mode-p 'comint-mode))
|
||
(cl-loop for useful-regexp in spacemacs-useful-buffers-regexp
|
||
thereis (string-match-p useful-regexp buf-name))
|
||
(cl-loop for useless-regexp in spacemacs-useless-buffers-regexp
|
||
never (string-match-p useless-regexp buf-name)))))
|
||
|
||
(defun spacemacs/useless-buffer-p (buffer)
|
||
"Determines if a buffer is useless."
|
||
(not (spacemacs/useful-buffer-p buffer)))
|
||
|
||
|
||
(defun spacemacs/swap-windows (window1 window2)
|
||
"Swap two windows.
|
||
WINDOW1 and WINDOW2 must be valid windows. They may contain child
|
||
windows."
|
||
(let ((state1 (window-state-get window1))
|
||
(state2 (window-state-get window2)))
|
||
;; to put state into dedicated windows, we must undedicate them first (not
|
||
;; needed with Emacs 25.1)
|
||
(dolist (win (list window1 window2))
|
||
(if (window-live-p win)
|
||
(set-window-dedicated-p win nil)
|
||
;; win has sub-windows, undedicate all of them
|
||
(walk-window-subtree (lambda (leaf-window)
|
||
(set-window-dedicated-p leaf-window nil))
|
||
win)))
|
||
(window-state-put state1 window2)
|
||
(window-state-put state2 window1)))
|
||
|
||
;; from @bmag
|
||
(defun spacemacs/window-layout-toggle ()
|
||
"Toggle between horizontal and vertical layout of two windows."
|
||
(interactive)
|
||
(if (= (count-windows) 2)
|
||
(let* ((window-tree (car (window-tree)))
|
||
(current-split-vertical-p (car window-tree))
|
||
(first-window (nth 2 window-tree))
|
||
(second-window (nth 3 window-tree))
|
||
(second-window-state (window-state-get second-window))
|
||
(splitter (if current-split-vertical-p
|
||
#'split-window-horizontally
|
||
#'split-window-vertically)))
|
||
(delete-other-windows first-window)
|
||
;; `window-state-put' also re-selects the window if needed, so we don't
|
||
;; need to call `select-window'
|
||
(window-state-put second-window-state (funcall splitter)))
|
||
(error "Can't toggle window layout when the number of windows isn't two.")))
|
||
|
||
;; originally from magnars and modified by ffevotte for dedicated windows
|
||
;; support, it has quite diverged by now
|
||
(defun spacemacs/rotate-windows-forward (count)
|
||
"Rotate each window forwards.
|
||
A negative prefix argument rotates each window backwards.
|
||
Dedicated (locked) windows are left untouched."
|
||
(interactive "p")
|
||
(let* ((non-dedicated-windows (cl-remove-if 'window-dedicated-p (window-list)))
|
||
(states (mapcar #'window-state-get non-dedicated-windows))
|
||
(num-windows (length non-dedicated-windows))
|
||
(step (+ num-windows count)))
|
||
(if (< num-windows 2)
|
||
(error "You can't rotate a single window!")
|
||
(dotimes (i num-windows)
|
||
(window-state-put
|
||
(elt states i)
|
||
(elt non-dedicated-windows (% (+ step i) num-windows)))))))
|
||
|
||
(defun spacemacs/rotate-windows-backward (count)
|
||
"Rotate each window backwards.
|
||
Dedicated (locked) windows are left untouched."
|
||
(interactive "p")
|
||
(spacemacs/rotate-windows-forward (* -1 count)))
|
||
|
||
(defun spacemacs/move-buffer-to-window (windownum follow-focus-p)
|
||
"Moves a buffer to a window, using the spacemacs numbering. follow-focus-p
|
||
controls whether focus moves to new window (with buffer), or stays on
|
||
current"
|
||
(interactive)
|
||
(let ((b (current-buffer))
|
||
(w1 (selected-window))
|
||
(w2 (winum-get-window-by-number windownum)))
|
||
(unless (eq w1 w2)
|
||
(set-window-buffer w2 b)
|
||
(switch-to-prev-buffer)
|
||
(unrecord-window-buffer w1 b)))
|
||
(when follow-focus-p (select-window (winum-get-window-by-number windownum))))
|
||
|
||
(defun spacemacs/swap-buffers-to-window (windownum follow-focus-p)
|
||
"Swaps visible buffers between active window and selected window.
|
||
follow-focus-p controls whether focus moves to new window (with buffer), or
|
||
stays on current"
|
||
(interactive)
|
||
(let* ((b1 (current-buffer))
|
||
(w1 (selected-window))
|
||
(w2 (winum-get-window-by-number windownum))
|
||
(b2 (window-buffer w2)))
|
||
(unless (eq w1 w2)
|
||
(set-window-buffer w1 b2)
|
||
(set-window-buffer w2 b1)
|
||
(unrecord-window-buffer w1 b1)
|
||
(unrecord-window-buffer w2 b2)))
|
||
(when follow-focus-p (winum-select-window-by-number windownum)))
|
||
|
||
(dotimes (i 9)
|
||
(let ((n (+ i 1)))
|
||
(eval `(defun ,(intern (format "buffer-to-window-%s" n)) (&optional arg)
|
||
,(format "Move buffer to the window with number %i." n)
|
||
(interactive "P")
|
||
(if arg
|
||
(spacemacs/swap-buffers-to-window ,n t)
|
||
(spacemacs/move-buffer-to-window ,n t))))
|
||
(eval `(defun ,(intern (format "move-buffer-window-no-follow-%s" n)) ()
|
||
(interactive)
|
||
(spacemacs/move-buffer-to-window ,n nil)))
|
||
(eval `(defun ,(intern (format "swap-buffer-window-no-follow-%s" n)) ()
|
||
(interactive)
|
||
(spacemacs/swap-buffers-to-window ,n nil)))
|
||
))
|
||
|
||
(defun spacemacs/rename-file (filename &optional new-filename)
|
||
"Rename FILENAME to NEW-FILENAME.
|
||
|
||
When NEW-FILENAME is not specified, asks user for a new name.
|
||
|
||
Also renames associated buffers (if any exists), invalidates
|
||
projectile cache and updates recentf list."
|
||
(interactive "f")
|
||
(when (and filename (file-exists-p filename))
|
||
(let* ((is-dir (file-directory-p filename))
|
||
(short-name
|
||
(if is-dir
|
||
(file-name-base (directory-file-name filename))
|
||
(file-name-nondirectory filename)))
|
||
(new-filename
|
||
(if new-filename new-filename
|
||
(read-file-name
|
||
(format "Rename %s to: " short-name)))))
|
||
|
||
;; Rename filename to new-filename and error if new-filename already
|
||
;; exists. `dired-rename-file' handles renaming of directories and files.
|
||
;; It updates the name of all associated buffers.
|
||
(dired-rename-file filename new-filename nil)
|
||
|
||
;; Update recentf list.
|
||
(when (fboundp 'recentf-add-file)
|
||
(seq-map
|
||
(lambda (fp)
|
||
(recentf-add-file
|
||
(concat new-filename (string-remove-prefix filename fp)))
|
||
(recentf-remove-if-non-kept fp))
|
||
(seq-filter
|
||
(lambda (fp)
|
||
(string-prefix-p filename fp))
|
||
recentf-list)))
|
||
|
||
;; Invalidate projectile cache.
|
||
(when (and (configuration-layer/package-used-p 'projectile)
|
||
(projectile-project-p))
|
||
(call-interactively #'projectile-invalidate-cache))
|
||
|
||
;; Inform user about tremendous success.
|
||
(message "%s '%s' successfully renamed to '%s'"
|
||
(if is-dir "Directory" "File")
|
||
short-name
|
||
(file-name-nondirectory new-filename)))))
|
||
|
||
;; from magnars
|
||
(defun spacemacs/rename-current-buffer-file (&optional arg)
|
||
"Rename the current buffer and the file it is visiting.
|
||
If the buffer isn't visiting a file, ask if it should
|
||
be saved to a file, or just renamed.
|
||
|
||
If called without a prefix argument, the prompt is
|
||
initialized with the current directory instead of filename."
|
||
(interactive "P")
|
||
(let* ((name (buffer-name))
|
||
(filename (buffer-file-name)))
|
||
(if (and filename (file-exists-p filename))
|
||
;; the buffer is visiting a file
|
||
(let* ((dir (file-name-directory filename))
|
||
(new-name (read-file-name "New name: " (if arg dir filename))))
|
||
(cond ((get-buffer new-name)
|
||
(error "A buffer named '%s' already exists!" new-name))
|
||
(t
|
||
(let ((dir (file-name-directory new-name)))
|
||
(when (and (not (file-exists-p dir))
|
||
(yes-or-no-p
|
||
(format "Create directory '%s'?" dir)))
|
||
(make-directory dir t)))
|
||
(rename-file filename new-name 1)
|
||
(rename-buffer new-name)
|
||
(set-visited-file-name new-name)
|
||
(set-buffer-modified-p nil)
|
||
(when (fboundp 'recentf-add-file)
|
||
(recentf-add-file new-name)
|
||
(recentf-remove-if-non-kept filename))
|
||
(when (and (configuration-layer/package-used-p 'projectile)
|
||
(projectile-project-p))
|
||
(call-interactively #'projectile-invalidate-cache))
|
||
(message "File '%s' successfully renamed to '%s'"
|
||
name (file-name-nondirectory new-name)))))
|
||
;; the buffer is not visiting a file
|
||
(let ((key))
|
||
(while (not (memq key '(?s ?r)))
|
||
(setq key (read-key (propertize
|
||
(format
|
||
(concat "Buffer '%s' is not visiting a file: "
|
||
"[s]ave to file or [r]ename buffer?")
|
||
name)
|
||
'face 'minibuffer-prompt)))
|
||
(cond ((eq key ?s) ; save to file
|
||
;; this allows for saving a new empty (unmodified) buffer
|
||
(unless (buffer-modified-p) (set-buffer-modified-p t))
|
||
(save-buffer))
|
||
((eq key ?r) ; rename buffer
|
||
(let ((new-name (read-string "New buffer name: ")))
|
||
(while (get-buffer new-name)
|
||
;; ask to rename again, if the new buffer name exists
|
||
(if (yes-or-no-p
|
||
(format (concat "A buffer named '%s' already exists: "
|
||
"Rename again?")
|
||
new-name))
|
||
(setq new-name (read-string "New buffer name: "))
|
||
(keyboard-quit)))
|
||
(rename-buffer new-name)
|
||
(message "Buffer '%s' successfully renamed to '%s'"
|
||
name new-name)))
|
||
;; ?\a = C-g, ?\e = Esc and C-[
|
||
((memq key '(?\a ?\e)) (keyboard-quit))))))))
|
||
|
||
(defun spacemacs/delete-file (filename &optional ask-user)
|
||
"Remove specified file or directory.
|
||
|
||
Also kills associated buffer (if any exists) and invalidates
|
||
projectile cache when it's possible.
|
||
|
||
When ASK-USER is non-nil, user will be asked to confirm file
|
||
removal."
|
||
(interactive "f")
|
||
(when (and filename (file-exists-p filename))
|
||
(let ((buffer (find-buffer-visiting filename)))
|
||
(when buffer
|
||
(kill-buffer buffer)))
|
||
(when (or (not ask-user)
|
||
(yes-or-no-p "Are you sure you want to delete this file? "))
|
||
(delete-file filename)
|
||
(when (and (configuration-layer/package-used-p 'projectile)
|
||
(projectile-project-p))
|
||
(call-interactively #'projectile-invalidate-cache)))))
|
||
|
||
(defun spacemacs/delete-file-confirm (filename)
|
||
"Remove specified file or directory after users approval.
|
||
|
||
FILENAME is deleted using `spacemacs/delete-file' function.."
|
||
(interactive "f")
|
||
(funcall-interactively #'spacemacs/delete-file filename t))
|
||
|
||
;; from magnars
|
||
(defun spacemacs/delete-current-buffer-file ()
|
||
"Removes file connected to current buffer and kills buffer."
|
||
(interactive)
|
||
(let ((filename (buffer-file-name))
|
||
(buffer (current-buffer))
|
||
(name (buffer-name)))
|
||
(if (not (and filename (file-exists-p filename)))
|
||
(ido-kill-buffer)
|
||
(if (yes-or-no-p
|
||
(format "Are you sure you want to delete this file: '%s'?" name))
|
||
(progn
|
||
(delete-file filename t)
|
||
(kill-buffer buffer)
|
||
(when (and (configuration-layer/package-used-p 'projectile)
|
||
(projectile-project-p))
|
||
(call-interactively #'projectile-invalidate-cache))
|
||
(message "File deleted: '%s'" filename))
|
||
(message "Canceled: File deletion")))))
|
||
|
||
;; from magnars
|
||
(defun spacemacs/sudo-edit (&optional arg)
|
||
(interactive "P")
|
||
(require 'tramp)
|
||
(let ((fname (if (or arg (not buffer-file-name))
|
||
(read-file-name "File: ")
|
||
buffer-file-name)))
|
||
(find-file
|
||
(if (not (tramp-tramp-file-p fname))
|
||
(concat "/sudo:root@localhost:" fname)
|
||
(with-parsed-tramp-file-name fname parsed
|
||
(when (equal parsed-user "root")
|
||
(error "Already root!"))
|
||
(let* ((new-hop (tramp-make-tramp-file-name parsed-method
|
||
parsed-user
|
||
parsed-host
|
||
nil
|
||
parsed-hop
|
||
))
|
||
(new-hop (substring new-hop 1 -1))
|
||
(new-hop (concat new-hop "|"))
|
||
(new-fname (tramp-make-tramp-file-name "sudo"
|
||
"root"
|
||
parsed-host
|
||
parsed-localname
|
||
new-hop)))
|
||
new-fname))))))
|
||
|
||
;; check when opening large files - literal file open
|
||
(defun spacemacs/check-large-file ()
|
||
(let* ((filename (buffer-file-name))
|
||
(size (nth 7 (file-attributes filename))))
|
||
(when (and
|
||
(not (memq major-mode spacemacs-large-file-modes-list))
|
||
size (> size (* 1024 1024 dotspacemacs-large-file-size))
|
||
(y-or-n-p (format (concat "%s is a large file, open literally to "
|
||
"avoid performance issues?")
|
||
filename)))
|
||
(setq buffer-read-only t)
|
||
(buffer-disable-undo)
|
||
(fundamental-mode))))
|
||
|
||
(defun spacemacs/delete-window (&optional arg)
|
||
"Delete the current window.
|
||
If the universal prefix argument is used then kill the buffer too."
|
||
(interactive "P")
|
||
(if (equal '(4) arg)
|
||
(kill-buffer-and-window)
|
||
(delete-window)))
|
||
|
||
;; our own implementation of kill-this-buffer from menu-bar.el
|
||
(defun spacemacs/kill-this-buffer (&optional arg)
|
||
"Kill the current buffer.
|
||
If the universal prefix argument is used then kill also the window."
|
||
(interactive "P")
|
||
(if (window-minibuffer-p)
|
||
(abort-recursive-edit)
|
||
(if (equal '(4) arg)
|
||
(kill-buffer-and-window)
|
||
(kill-buffer))))
|
||
|
||
;; found at http://emacswiki.org/emacs/KillingBuffers
|
||
(defun spacemacs/kill-other-buffers (&optional arg)
|
||
"Kill all other buffers.
|
||
If the universal prefix argument is used then will the windows too."
|
||
(interactive "P")
|
||
(when (yes-or-no-p (format "Killing all buffers except \"%s\"? "
|
||
(buffer-name)))
|
||
(mapc 'kill-buffer (delq (current-buffer) (buffer-list)))
|
||
(when (equal '(4) arg) (delete-other-windows))
|
||
(message "Buffers deleted!")))
|
||
|
||
;; from http://dfan.org/blog/2009/02/19/emacs-dedicated-windows/
|
||
(defun spacemacs/toggle-current-window-dedication ()
|
||
"Toggle dedication state of a window. Commands that change the buffer that a
|
||
window is displaying will not typically change the buffer displayed by
|
||
a dedicated window."
|
||
(interactive)
|
||
(let* ((window (selected-window))
|
||
(dedicated (window-dedicated-p window)))
|
||
(set-window-dedicated-p window (not dedicated))
|
||
(message "Window %sdedicated to %s"
|
||
(if dedicated "no longer " "")
|
||
(buffer-name))))
|
||
|
||
|
||
;; Copy file path
|
||
|
||
(defun spacemacs--directory-path ()
|
||
"Retrieve the directory path of the current buffer.
|
||
|
||
If the buffer is not visiting a file, use the `list-buffers-directory'
|
||
variable as a fallback to display the directory, useful in buffers like the
|
||
ones created by `magit' and `dired'.
|
||
|
||
Returns:
|
||
- A string containing the directory path in case of success.
|
||
- `nil' in case the current buffer does not have a directory."
|
||
(when-let (directory-name (if-let (file-name (buffer-file-name))
|
||
(file-name-directory file-name)
|
||
list-buffers-directory))
|
||
(file-truename directory-name)))
|
||
|
||
(defun spacemacs--file-path ()
|
||
"Retrieve the file path of the current buffer.
|
||
|
||
Returns:
|
||
- A string containing the file path in case of success.
|
||
- `nil' in case the current buffer does not have a directory."
|
||
(when-let (file-path (buffer-file-name))
|
||
(file-truename file-path)))
|
||
|
||
(defun spacemacs--file-path-with-line ()
|
||
"Retrieve the file path of the current buffer, including line number.
|
||
|
||
Returns:
|
||
- A string containing the file path in case of success.
|
||
- `nil' in case the current buffer does not have a directory."
|
||
(when-let (file-path (spacemacs--file-path))
|
||
(concat file-path ":" (number-to-string (line-number-at-pos)))))
|
||
|
||
(defun spacemacs--file-path-with-line-column ()
|
||
"Retrieve the file path of the current buffer, including line and column number.
|
||
|
||
Returns:
|
||
- A string containing the file path in case of success.
|
||
- `nil' in case the current buffer does not have a directory."
|
||
(when-let (file-path (spacemacs--file-path-with-line))
|
||
(concat
|
||
file-path
|
||
":"
|
||
(number-to-string (if (and
|
||
;; Emacs 26 introduced this variable.
|
||
;; Remove this check once 26 becomes the minimum version.
|
||
(boundp column-number-indicator-zero-based)
|
||
(not column-number-indicator-zero-based))
|
||
(1+ (current-column))
|
||
(current-column))))))
|
||
|
||
(defun spacemacs/copy-directory-path ()
|
||
"Copy and show the directory path of the current buffer.
|
||
|
||
If the buffer is not visiting a file, use the `list-buffers-directory'
|
||
variable as a fallback to display the directory, useful in buffers like the
|
||
ones created by `magit' and `dired'."
|
||
(interactive)
|
||
(if-let (directory-path (spacemacs--directory-path))
|
||
(message "%s" (kill-new directory-path))
|
||
(message "WARNING: Current buffer does not have a directory!")))
|
||
|
||
(defun spacemacs/copy-file-path ()
|
||
"Copy and show the file path of the current buffer."
|
||
(interactive)
|
||
(if-let (file-path (spacemacs--file-path))
|
||
(message "%s" (kill-new file-path))
|
||
(message "WARNING: Current buffer is not attached to a file!")))
|
||
|
||
(defun spacemacs/copy-file-name ()
|
||
"Copy and show the file name of the current buffer."
|
||
(interactive)
|
||
(if-let (file-name (file-name-nondirectory (spacemacs--file-path)))
|
||
(message "%s" (kill-new file-name))
|
||
(message "WARNING: Current buffer is not attached to a file!")))
|
||
|
||
(defun spacemacs/copy-file-name-base ()
|
||
"Copy and show the file name without its final extension of the current
|
||
buffer."
|
||
(interactive)
|
||
(if-let (file-name (file-name-base (spacemacs--file-path)))
|
||
(message "%s" (kill-new file-name))
|
||
(message "WARNING: Current buffer is not attached to a file!")))
|
||
|
||
(defun spacemacs/copy-file-path-with-line ()
|
||
"Copy and show the file path of the current buffer, including line number."
|
||
(interactive)
|
||
(if-let (file-path (spacemacs--file-path-with-line))
|
||
(message "%s" (kill-new file-path))
|
||
(message "WARNING: Current buffer is not attached to a file!")))
|
||
|
||
(defun spacemacs/copy-file-path-with-line-column ()
|
||
"Copy and show the file path of the current buffer, including line and column number.
|
||
|
||
This function respects the value of the `column-number-indicator-zero-based'
|
||
variable."
|
||
(interactive)
|
||
(if-let (file-path (spacemacs--file-path-with-line-column))
|
||
(message "%s" (kill-new file-path))
|
||
(message "WARNING: Current buffer is not attached to a file!")))
|
||
|
||
|
||
|
||
;; adapted from bozhidar
|
||
;; http://emacsredux.com/blog/2013/05/18/instant-access-to-init-dot-el/
|
||
(defun spacemacs/find-user-init-file ()
|
||
"Edit the `user-init-file', in the current window."
|
||
(interactive)
|
||
(find-file-existing user-init-file))
|
||
|
||
(defun spacemacs/find-dotfile ()
|
||
"Edit the `dotfile', in the current window."
|
||
(interactive)
|
||
(find-file-existing (dotspacemacs/location)))
|
||
|
||
(defun spacemacs/ediff-dotfile-and-template ()
|
||
"ediff the current `dotfile' with the template"
|
||
(interactive)
|
||
(ediff-files (dotspacemacs/location)
|
||
(concat dotspacemacs-template-directory ".spacemacs.template")))
|
||
|
||
(defun spacemacs/new-empty-buffer (&optional split)
|
||
"Create a new buffer called untitled(<n>).
|
||
A SPLIT argument with the value: `left',
|
||
`below', `above' or `right', opens the new
|
||
buffer in a split window."
|
||
(interactive)
|
||
(let ((newbuf (generate-new-buffer "untitled")))
|
||
(case split
|
||
('left (split-window-horizontally))
|
||
('below (spacemacs/split-window-vertically-and-switch))
|
||
('above (split-window-vertically))
|
||
('right (spacemacs/split-window-horizontally-and-switch))
|
||
('frame (select-frame (make-frame))))
|
||
;; Prompt to save on `save-some-buffers' with positive PRED
|
||
(with-current-buffer newbuf
|
||
(setq-local buffer-offer-save t))
|
||
;; pass non-nil force-same-window to prevent `switch-to-buffer' from
|
||
;; displaying buffer in another window
|
||
(switch-to-buffer newbuf nil 'force-same-window)))
|
||
|
||
(defun spacemacs/new-empty-buffer-left ()
|
||
"Create a new buffer called untitled(<n>),
|
||
in a split window to the left."
|
||
(interactive)
|
||
(spacemacs/new-empty-buffer 'left))
|
||
|
||
(defun spacemacs/new-empty-buffer-below ()
|
||
"Create a new buffer called untitled(<n>),
|
||
in a split window below."
|
||
(interactive)
|
||
(spacemacs/new-empty-buffer 'below))
|
||
|
||
(defun spacemacs/new-empty-buffer-above ()
|
||
"Create a new buffer called untitled(<n>),
|
||
in a split window above."
|
||
(interactive)
|
||
(spacemacs/new-empty-buffer 'above))
|
||
|
||
(defun spacemacs/new-empty-buffer-right ()
|
||
"Create a new buffer called untitled(<n>),
|
||
in a split window to the right."
|
||
(interactive)
|
||
(spacemacs/new-empty-buffer 'right))
|
||
|
||
(defun spacemacs/new-empty-buffer-new-frame ()
|
||
"Create a new buffer called untitled(<n>),
|
||
in a new frame."
|
||
(interactive)
|
||
(spacemacs/new-empty-buffer 'frame))
|
||
|
||
;; from https://gist.github.com/timcharper/493269
|
||
(defun spacemacs/split-window-vertically-and-switch ()
|
||
(interactive)
|
||
(split-window-vertically)
|
||
(other-window 1))
|
||
|
||
(defun spacemacs/split-window-horizontally-and-switch ()
|
||
(interactive)
|
||
(split-window-horizontally)
|
||
(other-window 1))
|
||
|
||
|
||
;; Window Split
|
||
|
||
(defun spacemacs--window-split-splittable-windows ()
|
||
(seq-remove
|
||
(lambda (window)
|
||
;; TODO: find a way to identify unsplittable side windows reliably!
|
||
nil)
|
||
(spacemacs--window-split-non-ignored-windows)))
|
||
|
||
(defun spacemacs--window-split-non-ignored-windows ()
|
||
"Determines the list of windows to be deleted."
|
||
(seq-filter
|
||
(lambda (window)
|
||
(let* ((name (buffer-name (window-buffer window)))
|
||
(prefixes-matching
|
||
(seq-filter
|
||
(lambda (prefix) (string-prefix-p prefix name))
|
||
spacemacs-window-split-ignore-prefixes)))
|
||
(not prefixes-matching)))
|
||
(window-list (selected-frame))))
|
||
|
||
(defun spacemacs/window-split-default-delete ()
|
||
"Deletes other windows, except a list of excluded ones."
|
||
(if spacemacs-window-split-ignore-prefixes
|
||
(let* ((deletable (spacemacs--window-split-non-ignored-windows))
|
||
(splittable (spacemacs--window-split-splittable-windows)))
|
||
(when splittable
|
||
(let* ((selected (car splittable))
|
||
(to-delete (delq selected deletable)))
|
||
(select-window selected)
|
||
(dolist (window to-delete) (delete-window window)))))
|
||
(delete-other-windows)))
|
||
|
||
(defvar spacemacs-window-split-ignore-prefixes nil
|
||
"Prefixes for windows that are not deleted when changing split layout.
|
||
|
||
You can add an entry here by using the following:
|
||
(add-to-list 'spacemacs-window-split-ignore-prefixes \"Buffer prefix\")")
|
||
|
||
(defvar spacemacs-window-split-delete-function 'spacemacs/window-split-default-delete
|
||
"Function used to delete other windows when changing layout.
|
||
|
||
Used as a callback by the following functions:
|
||
- spacemacs/window-split-grid
|
||
- spacemacs/window-split-triple-columns
|
||
- spacemacs/window-split-double-columns
|
||
- spacemacs/window-split-single-column
|
||
|
||
Possible values:
|
||
- 'spacemacs/window-split-default-delete (default)
|
||
- 'delete-other-windows
|
||
- 'treemacs-delete-other-windows (when using the treemacs package)
|
||
- a lambda: (lambda () (delete-other-windows))
|
||
- a custom function:
|
||
(defun my-delete-other-windows () (delete-other-windows))
|
||
(setq spacemacs-window-split-delete-function 'my-delete-other-windows)")
|
||
|
||
(defun spacemacs/window-split-grid (&optional purge)
|
||
"Set the layout to a 2x2 grid.
|
||
|
||
Uses the funcion defined in `spacemacs-window-split-delete-function' as a means to
|
||
remove windows.
|
||
|
||
When called with a prefix argument, it uses `delete-other-windows' as a means
|
||
to remove windows, regardless of the value in `spacemacs-window-split-delete-function'."
|
||
(interactive "P")
|
||
(if purge
|
||
(let ((ignore-window-parameters t))
|
||
(delete-other-windows))
|
||
(funcall spacemacs-window-split-delete-function))
|
||
(if (spacemacs--window-split-splittable-windows)
|
||
(let* ((previous-files (seq-filter #'buffer-file-name
|
||
(delq (current-buffer) (buffer-list))))
|
||
(second (split-window-below))
|
||
(third (split-window-right))
|
||
(fourth (split-window second nil 'right)))
|
||
(set-window-buffer third (or (car previous-files) "*scratch*"))
|
||
(set-window-buffer second (or (cadr previous-files) "*scratch*"))
|
||
(set-window-buffer fourth (or (caddr previous-files) "*scratch*"))
|
||
(balance-windows))
|
||
(message "There are no main windows available to split!")))
|
||
|
||
(defun spacemacs/window-split-triple-columns (&optional purge)
|
||
"Set the layout to triple columns.
|
||
|
||
Uses the funcion defined in `spacemacs-window-split-delete-function' as a means to
|
||
remove windows.
|
||
|
||
When called with a prefix argument, it uses `delete-other-windows' as a means
|
||
to remove windows, regardless of the value in `spacemacs-window-split-delete-function'."
|
||
(interactive "P")
|
||
(if purge
|
||
(let ((ignore-window-parameters t))
|
||
(delete-other-windows))
|
||
(funcall spacemacs-window-split-delete-function))
|
||
(if (spacemacs--window-split-splittable-windows)
|
||
(let* ((previous-files (seq-filter #'buffer-file-name
|
||
(delq (current-buffer) (buffer-list))))
|
||
(second (split-window-right))
|
||
(third (split-window second nil 'right)))
|
||
(set-window-buffer second (or (car previous-files) "*scratch*"))
|
||
(set-window-buffer third (or (cadr previous-files) "*scratch*"))
|
||
(balance-windows))
|
||
(message "There are no main windows available to split!")))
|
||
|
||
(defun spacemacs/window-split-double-columns (&optional purge)
|
||
"Set the layout to double columns.
|
||
|
||
Uses the funcion defined in `spacemacs-window-split-delete-function' as a means to
|
||
remove windows.
|
||
|
||
When called with a prefix argument, it uses `delete-other-windows' as a means
|
||
to remove windows, regardless of the value in `spacemacs-window-split-delete-function'."
|
||
(interactive "P")
|
||
(if purge
|
||
(let ((ignore-window-parameters t))
|
||
(delete-other-windows))
|
||
(funcall spacemacs-window-split-delete-function))
|
||
(if (spacemacs--window-split-splittable-windows)
|
||
(let* ((previous-files (seq-filter #'buffer-file-name
|
||
(delq (current-buffer) (buffer-list)))))
|
||
(set-window-buffer (split-window-right) (or (car previous-files) "*scratch*"))
|
||
(balance-windows))
|
||
(message "There are no main windows available to split!")))
|
||
|
||
(defun spacemacs/window-split-single-column (&optional purge)
|
||
"Set the layout to single column.
|
||
|
||
Uses the funcion defined in `spacemacs-window-split-delete-function' as a means to
|
||
remove windows.
|
||
|
||
When called with a prefix argument, it uses `delete-other-windows' as a means
|
||
to remove windows, regardless of the value in `spacemacs-window-split-delete-function'."
|
||
(interactive "P")
|
||
(if purge
|
||
(let ((ignore-window-parameters t))
|
||
(delete-other-windows))
|
||
(funcall spacemacs-window-split-delete-function))
|
||
(balance-windows))
|
||
|
||
|
||
|
||
(defun spacemacs/insert-line-above-no-indent (count)
|
||
"Insert a new line above with no indentation."
|
||
(interactive "p")
|
||
(let ((p (+ (point) count)))
|
||
(save-excursion
|
||
(if (eq (line-number-at-pos) 1)
|
||
(evil-move-beginning-of-line)
|
||
(progn
|
||
(evil-previous-line)
|
||
(evil-move-end-of-line)))
|
||
(while (> count 0)
|
||
(insert "\n")
|
||
(setq count (1- count))))
|
||
(goto-char p)))
|
||
|
||
(defun spacemacs/insert-line-below-no-indent (count)
|
||
"Insert a new line below with no indentation."
|
||
(interactive "p")
|
||
(save-excursion
|
||
(evil-move-end-of-line)
|
||
(while (> count 0)
|
||
(insert "\n")
|
||
(setq count (1- count)))))
|
||
|
||
;; see https://github.com/gempesaw/dotemacs/blob/emacs/dg-elisp/dg-defun.el
|
||
(defun spacemacs/rudekill-matching-buffers (regexp &optional internal-too)
|
||
"Kill - WITHOUT ASKING - buffers whose name matches the specified REGEXP. See
|
||
the `kill-matching-buffers` for grateful killing. The optional 2nd argument
|
||
indicates whether to kill internal buffers too.
|
||
|
||
Returns the count of killed buffers."
|
||
(let* ((buffers (remove-if-not
|
||
(lambda (buffer)
|
||
(let ((name (buffer-name buffer)))
|
||
(and name (not (string-equal name ""))
|
||
(or internal-too (/= (aref name 0) ?\s))
|
||
(string-match regexp name))))
|
||
(buffer-list))))
|
||
(mapc 'kill-buffer buffers)
|
||
(length buffers)))
|
||
|
||
(defun spacemacs/kill-matching-buffers-rudely (regexp &optional internal-too)
|
||
"Kill - WITHOUT ASKING - buffers whose name matches the specified REGEXP. See
|
||
the `kill-matching-buffers` for grateful killing. The optional 2nd argument
|
||
indicates whether to kill internal buffers too.
|
||
|
||
Returns a message with the count of killed buffers."
|
||
(interactive "sKill buffers matching this regular expression: \nP")
|
||
(message
|
||
(format "%d buffer(s) killed."
|
||
(spacemacs/rudekill-matching-buffers regexp internal-too))))
|
||
|
||
;; advise to prevent server from closing
|
||
|
||
(defvar spacemacs-really-kill-emacs nil
|
||
"prevent window manager close from closing instance.")
|
||
|
||
(defun spacemacs//persistent-server-running-p ()
|
||
"Requires spacemacs-really-kill-emacs to be toggled and
|
||
dotspacemacs-persistent-server to be t"
|
||
(and (fboundp 'server-running-p)
|
||
(server-running-p)
|
||
dotspacemacs-persistent-server))
|
||
|
||
(defadvice kill-emacs (around spacemacs-really-exit activate)
|
||
"Only kill emacs if a prefix is set"
|
||
(if (and (not spacemacs-really-kill-emacs)
|
||
(spacemacs//persistent-server-running-p))
|
||
(spacemacs/frame-killer)
|
||
ad-do-it))
|
||
|
||
(defadvice save-buffers-kill-emacs (around spacemacs-really-exit activate)
|
||
"Only kill emacs if a prefix is set"
|
||
(if (and (not spacemacs-really-kill-emacs)
|
||
(spacemacs//persistent-server-running-p))
|
||
(spacemacs/frame-killer)
|
||
ad-do-it))
|
||
|
||
(defun spacemacs/save-buffers-kill-emacs ()
|
||
"Save all changed buffers and exit Spacemacs"
|
||
(interactive)
|
||
(setq spacemacs-really-kill-emacs t)
|
||
(save-buffers-kill-emacs))
|
||
|
||
(defun spacemacs/kill-emacs ()
|
||
"Lose all changes and exit Spacemacs"
|
||
(interactive)
|
||
(setq spacemacs-really-kill-emacs t)
|
||
(kill-emacs))
|
||
|
||
(defun spacemacs/prompt-kill-emacs ()
|
||
"Prompt to save changed buffers and exit Spacemacs"
|
||
(interactive)
|
||
(setq spacemacs-really-kill-emacs t)
|
||
(save-some-buffers nil t)
|
||
(kill-emacs))
|
||
|
||
(defun spacemacs/frame-killer ()
|
||
"Kill server buffer and hide the main Emacs window"
|
||
(interactive)
|
||
(condition-case nil
|
||
(delete-frame nil 1)
|
||
(error
|
||
(make-frame-invisible nil 1))))
|
||
|
||
(defun spacemacs/toggle-frame-fullscreen ()
|
||
"Respect the `dotspacemacs-fullscreen-use-non-native' variable when
|
||
toggling fullscreen."
|
||
(interactive)
|
||
(if dotspacemacs-fullscreen-use-non-native
|
||
(spacemacs/toggle-frame-fullscreen-non-native)
|
||
(toggle-frame-fullscreen)))
|
||
|
||
(defun spacemacs/toggle-fullscreen ()
|
||
"Toggle full screen on X11 and Carbon"
|
||
(interactive)
|
||
(cond
|
||
((eq window-system 'x)
|
||
(set-frame-parameter nil 'fullscreen
|
||
(when (not (frame-parameter nil 'fullscreen))
|
||
'fullboth)))
|
||
((eq window-system 'mac)
|
||
(set-frame-parameter
|
||
nil 'fullscreen
|
||
(when (not (frame-parameter nil 'fullscreen)) 'fullscreen)))))
|
||
|
||
(defun spacemacs/toggle-frame-fullscreen-non-native ()
|
||
"Toggle full screen non-natively. Uses the `fullboth' frame paramerter
|
||
rather than `fullscreen'. Useful to fullscreen on OSX w/o animations."
|
||
(interactive)
|
||
(modify-frame-parameters
|
||
nil
|
||
`((maximized
|
||
. ,(unless (memq (frame-parameter nil 'fullscreen) '(fullscreen fullboth))
|
||
(frame-parameter nil 'fullscreen)))
|
||
(fullscreen
|
||
. ,(if (memq (frame-parameter nil 'fullscreen) '(fullscreen fullboth))
|
||
(if (eq (frame-parameter nil 'maximized) 'maximized)
|
||
'maximized)
|
||
'fullboth)))))
|
||
|
||
(defun spacemacs/safe-revert-buffer ()
|
||
"Prompt before reverting the file."
|
||
(interactive)
|
||
(revert-buffer nil nil))
|
||
|
||
(defun spacemacs/safe-erase-buffer ()
|
||
"Prompt before erasing the content of the file."
|
||
(interactive)
|
||
(if (y-or-n-p (format "Erase content of buffer %s ? " (current-buffer)))
|
||
(erase-buffer)))
|
||
|
||
(defun spacemacs//find-ert-test-buffer (ert-test)
|
||
"Return the buffer where ERT-TEST is defined."
|
||
(save-excursion
|
||
(car (find-definition-noselect (ert-test-name ert-test) 'ert-deftest))))
|
||
|
||
(defun spacemacs/ert-run-tests-buffer ()
|
||
"Run all the tests in the current buffer."
|
||
(interactive)
|
||
(save-buffer)
|
||
(load-file (buffer-file-name))
|
||
(let ((cbuf (current-buffer)))
|
||
(ert '(satisfies (lambda (test)
|
||
(eq cbuf (spacemacs//find-ert-test-buffer test)))))))
|
||
|
||
(defun spacemacs//open-in-external-app (file-path)
|
||
"Open `file-path' in external application."
|
||
(cond
|
||
((spacemacs/system-is-mswindows) (w32-shell-execute "open" (replace-regexp-in-string "/" "\\\\" file-path)))
|
||
((spacemacs/system-is-mac) (shell-command (format "open \"%s\"" file-path)))
|
||
((spacemacs/system-is-linux) (let ((process-connection-type nil))
|
||
(start-process "" nil "xdg-open" file-path)))))
|
||
|
||
(defun spacemacs/open-file-or-directory-in-external-app (arg)
|
||
"Open current file in external application.
|
||
If the universal prefix argument is used then open the folder
|
||
containing the current file by the default explorer."
|
||
(interactive "P")
|
||
(if arg
|
||
(spacemacs//open-in-external-app (expand-file-name default-directory))
|
||
(let ((file-path (if (derived-mode-p 'dired-mode)
|
||
(dired-get-file-for-visit)
|
||
buffer-file-name)))
|
||
(if file-path
|
||
(spacemacs//open-in-external-app file-path)
|
||
(message "No file associated to this buffer.")))))
|
||
|
||
(defun spacemacs/switch-to-minibuffer-window ()
|
||
"switch to minibuffer window (if active)"
|
||
(interactive)
|
||
(when (active-minibuffer-window)
|
||
(select-window (active-minibuffer-window))))
|
||
|
||
;; http://stackoverflow.com/a/10216338/4869
|
||
(defun spacemacs/copy-whole-buffer-to-clipboard ()
|
||
"Copy entire buffer to clipboard"
|
||
(interactive)
|
||
(clipboard-kill-ring-save (point-min) (point-max)))
|
||
|
||
(defun spacemacs/copy-clipboard-to-whole-buffer ()
|
||
"Copy clipboard and replace buffer"
|
||
(interactive)
|
||
(delete-region (point-min) (point-max))
|
||
(clipboard-yank)
|
||
(deactivate-mark))
|
||
|
||
;; BEGIN align functions
|
||
|
||
;; modified function from http://emacswiki.org/emacs/AlignCommands
|
||
(defun spacemacs/align-repeat (start end regexp &optional justify-right after)
|
||
"Repeat alignment with respect to the given regular expression.
|
||
If JUSTIFY-RIGHT is non nil justify to the right instead of the
|
||
left. If AFTER is non-nil, add whitespace to the left instead of
|
||
the right."
|
||
(interactive "r\nsAlign regexp: ")
|
||
(let* ((ws-regexp (if (string-empty-p regexp)
|
||
"\\(\\s-+\\)"
|
||
"\\(\\s-*\\)"))
|
||
(complete-regexp (if after
|
||
(concat regexp ws-regexp)
|
||
(concat ws-regexp regexp)))
|
||
(group (if justify-right -1 1)))
|
||
|
||
(unless (use-region-p)
|
||
(save-excursion
|
||
(while (and
|
||
(string-match-p complete-regexp (thing-at-point 'line))
|
||
(= 0 (forward-line -1)))
|
||
(setq start (point-at-bol))))
|
||
(save-excursion
|
||
(while (and
|
||
(string-match-p complete-regexp (thing-at-point 'line))
|
||
(= 0 (forward-line 1)))
|
||
(setq end (point-at-eol)))))
|
||
|
||
(align-regexp start end complete-regexp group 1 t)))
|
||
|
||
;; Modified answer from http://emacs.stackexchange.com/questions/47/align-vertical-columns-of-numbers-on-the-decimal-point
|
||
(defun spacemacs/align-repeat-decimal (start end)
|
||
"Align a table of numbers on decimal points and dollar signs (both optional)"
|
||
(interactive "r")
|
||
(require 'align)
|
||
(align-region start end nil
|
||
'((nil (regexp . "\\([\t ]*\\)\\$?\\([\t ]+[0-9]+\\)\\.?")
|
||
(repeat . t)
|
||
(group 1 2)
|
||
(spacing 1 1)
|
||
(justify nil t)))
|
||
nil))
|
||
|
||
(defmacro spacemacs|create-align-repeat-x (name regexp &optional justify-right default-after)
|
||
(let ((new-func (intern (concat "spacemacs/align-repeat-" name))))
|
||
`(defun ,new-func (start end switch)
|
||
(interactive "r\nP")
|
||
(let ((after (not (eq (if switch t nil) (if ,default-after t nil)))))
|
||
(spacemacs/align-repeat start end ,regexp ,justify-right after)))))
|
||
|
||
(spacemacs|create-align-repeat-x "comma" "," nil t)
|
||
(spacemacs|create-align-repeat-x "semicolon" ";" nil t)
|
||
(spacemacs|create-align-repeat-x "colon" ":" nil t)
|
||
(spacemacs|create-align-repeat-x "equal" "=")
|
||
(spacemacs|create-align-repeat-x "math-oper" "[+\\-*/]")
|
||
(spacemacs|create-align-repeat-x "percent" "%")
|
||
(spacemacs|create-align-repeat-x "ampersand" "&")
|
||
(spacemacs|create-align-repeat-x "bar" "|")
|
||
(spacemacs|create-align-repeat-x "left-paren" "(")
|
||
(spacemacs|create-align-repeat-x "right-paren" ")" t)
|
||
(spacemacs|create-align-repeat-x "left-curly-brace" "{")
|
||
(spacemacs|create-align-repeat-x "right-curly-brace" "}" t)
|
||
(spacemacs|create-align-repeat-x "left-square-brace" "\\[")
|
||
(spacemacs|create-align-repeat-x "right-square-brace" "\\]" t)
|
||
(spacemacs|create-align-repeat-x "backslash" "\\\\")
|
||
|
||
;; END align functions
|
||
|
||
(defun spacemacs/dos2unix ()
|
||
"Converts the current buffer to UNIX file format."
|
||
(interactive)
|
||
(set-buffer-file-coding-system 'undecided-unix nil))
|
||
|
||
(defun spacemacs/unix2dos ()
|
||
"Converts the current buffer to DOS file format."
|
||
(interactive)
|
||
(set-buffer-file-coding-system 'undecided-dos nil))
|
||
|
||
(defun spacemacs/copy-file ()
|
||
"Write the file under new name."
|
||
(interactive)
|
||
(call-interactively 'write-file))
|
||
|
||
;; from https://www.emacswiki.org/emacs/CopyingWholeLines
|
||
(defun spacemacs/duplicate-line-or-region (&optional n)
|
||
"Duplicate current line, or region if active.
|
||
With argument N, make N copies.
|
||
With negative N, comment out original line and use the absolute value."
|
||
(interactive "*p")
|
||
(let ((use-region (use-region-p)))
|
||
(save-excursion
|
||
(let ((text (if use-region ; Get region if active, otherwise line
|
||
(buffer-substring (region-beginning) (region-end))
|
||
(prog1 (thing-at-point 'line)
|
||
(end-of-line)
|
||
(if (< 0 (forward-line 1)) ; Go to beginning of next line, or make a new one
|
||
(newline))))))
|
||
(dotimes (i (abs (or n 1))) ; Insert N times, or once if not specified
|
||
(insert text))))
|
||
(if use-region nil ; Only if we're working with a line (not a region)
|
||
(let ((pos (- (point) (line-beginning-position)))) ; Save column
|
||
(if (> 0 n) ; Comment out original with negative arg
|
||
(comment-region (line-beginning-position) (line-end-position)))
|
||
(forward-line 1)
|
||
(forward-char pos)))))
|
||
|
||
(defun spacemacs/uniquify-lines ()
|
||
"Remove duplicate adjacent lines in a region or the current buffer"
|
||
(interactive)
|
||
(save-excursion
|
||
(save-restriction
|
||
(let* ((region-active (or (region-active-p) (evil-visual-state-p)))
|
||
(beg (if region-active (region-beginning) (point-min)))
|
||
(end (if region-active (region-end) (point-max))))
|
||
(goto-char beg)
|
||
(while (re-search-forward "^\\(.*\n\\)\\1+" end t)
|
||
(replace-match "\\1"))))))
|
||
|
||
(defun spacemacs/sort-lines (&optional reverse)
|
||
"Sort lines in a region or the current buffer.
|
||
A non-nil argument sorts in reverse order."
|
||
(interactive "P")
|
||
(let* ((region-active (or (region-active-p) (evil-visual-state-p)))
|
||
(beg (if region-active (region-beginning) (point-min)))
|
||
(end (if region-active (region-end) (point-max))))
|
||
(sort-lines reverse beg end)))
|
||
|
||
(defun spacemacs/sort-lines-reverse ()
|
||
"Sort lines in reverse order, in a region or the current buffer."
|
||
(interactive)
|
||
(spacemacs/sort-lines -1))
|
||
|
||
(defun spacemacs/sort-lines-by-column (&optional reverse)
|
||
"Sort lines by the selected column,
|
||
using a visual block/rectangle selection.
|
||
A non-nil argument sorts in REVERSE order."
|
||
(interactive "P")
|
||
(if (and
|
||
;; is there an active selection
|
||
(or (region-active-p) (evil-visual-state-p))
|
||
;; is it a block or rectangle selection
|
||
(or (eq evil-visual-selection 'block) (eq rectangle-mark-mode t))
|
||
;; is the selection height 2 or more lines
|
||
(>= (1+ (- (line-number-at-pos (region-end))
|
||
(line-number-at-pos (region-beginning)))) 2))
|
||
(sort-columns reverse (region-beginning) (region-end))
|
||
(error "Sorting by column requires a block/rect selection on 2 or more lines.")))
|
||
|
||
(defun spacemacs/sort-lines-by-column-reverse ()
|
||
"Sort lines by the selected column in reverse order,
|
||
using a visual block/rectangle selection."
|
||
(interactive)
|
||
(spacemacs/sort-lines-by-column -1))
|
||
|
||
;; BEGIN linum mouse helpers
|
||
|
||
(defvar spacemacs-linum-mdown-line nil
|
||
"Define persistent variable for linum selection")
|
||
|
||
(defun spacemacs//line-at-click ()
|
||
"Determine the visual line at click"
|
||
(save-excursion
|
||
(let ((click-y (cddr (mouse-position)))
|
||
(debug-on-error t)
|
||
(line-move-visual t))
|
||
(goto-char (window-start))
|
||
(next-line (1- click-y))
|
||
(1+ (line-number-at-pos))
|
||
)))
|
||
|
||
(defun spacemacs/md-select-linum (event)
|
||
"Set point as spacemacs-linum-mdown-line"
|
||
(interactive "e")
|
||
(mouse-select-window event)
|
||
(goto-line (spacemacs//line-at-click))
|
||
(set-mark (point))
|
||
(setq spacemacs-linum-mdown-line
|
||
(line-number-at-pos)))
|
||
|
||
(defun spacemacs/mu-select-linum ()
|
||
"Select code block between point and spacemacs-linum-mdown-line"
|
||
(interactive)
|
||
(when spacemacs-linum-mdown-line
|
||
(let (mu-line)
|
||
(setq mu-line (spacemacs//line-at-click))
|
||
(goto-line (max spacemacs-linum-mdown-line mu-line))
|
||
(set-mark (line-end-position))
|
||
(goto-line (min spacemacs-linum-mdown-line mu-line))
|
||
(setq spacemacs-linum-mdown-line nil))))
|
||
|
||
(defun spacemacs/select-current-block ()
|
||
"Select the current block of text between blank lines."
|
||
(interactive)
|
||
(let (p1)
|
||
(when (re-search-backward "\n[ \t]*\n" nil "move")
|
||
(re-search-forward "\n[ \t]*\n"))
|
||
(setq p1 (point))
|
||
(if (re-search-forward "\n[ \t]*\n" nil "move")
|
||
(re-search-backward "\n[ \t]*\n"))
|
||
(set-mark p1)))
|
||
|
||
;; END linum mouse helpers
|
||
|
||
;; from http://www.emacswiki.org/emacs/WordCount
|
||
(defun spacemacs/count-words-analysis (start end)
|
||
"Count how many times each word is used in the region.
|
||
Punctuation is ignored."
|
||
(interactive "r")
|
||
(let (words
|
||
alist_words_compare
|
||
(formatted "")
|
||
(overview (call-interactively 'count-words)))
|
||
(save-excursion
|
||
(goto-char start)
|
||
(while (re-search-forward "\\w+" end t)
|
||
(let* ((word (intern (match-string 0)))
|
||
(cell (assq word words)))
|
||
(if cell
|
||
(setcdr cell (1+ (cdr cell)))
|
||
(setq words (cons (cons word 1) words))))))
|
||
(defun alist_words_compare (a b)
|
||
"Compare elements from an associative list of words count.
|
||
Compare them on count first,and in case of tie sort them alphabetically."
|
||
(let ((a_key (car a))
|
||
(a_val (cdr a))
|
||
(b_key (car b))
|
||
(b_val (cdr b)))
|
||
(if (eq a_val b_val)
|
||
(string-lessp a_key b_key)
|
||
(> a_val b_val))))
|
||
(setq words (cl-sort words 'alist_words_compare))
|
||
(while words
|
||
(let* ((word (pop words))
|
||
(name (car word))
|
||
(count (cdr word)))
|
||
(setq formatted (concat formatted (format "[%s: %d], " name count)))))
|
||
(when (interactive-p)
|
||
(if (> (length formatted) 2)
|
||
(message (format "%s\nWord count: %s"
|
||
overview
|
||
(substring formatted 0 -2)))
|
||
(message "No words.")))
|
||
words))
|
||
|
||
;; indent on paste
|
||
;; from Prelude: https://github.com/bbatsov/prelude
|
||
(defun spacemacs/yank-advised-indent-function (beg end)
|
||
"Do indentation, as long as the region isn't too large."
|
||
(if (<= (- end beg) spacemacs-yank-indent-threshold)
|
||
(indent-region beg end nil)))
|
||
|
||
(defun spacemacs//yank-indent-region (yank-func &rest args)
|
||
"If current mode is not one of spacemacs-indent-sensitive-modes
|
||
indent yanked text (with universal arg don't indent)."
|
||
(evil-start-undo-step)
|
||
(let ((prefix (car args))
|
||
(enable (and (not (member major-mode spacemacs-indent-sensitive-modes))
|
||
(or (derived-mode-p 'prog-mode)
|
||
(member major-mode spacemacs-yank-indent-modes)))))
|
||
(when (and enable (equal '(4) prefix))
|
||
(setq args (cdr args)))
|
||
(apply yank-func args)
|
||
(when (and enable (not (equal '(4) prefix)))
|
||
(let ((transient-mark-mode nil)
|
||
(save-undo buffer-undo-list))
|
||
(spacemacs/yank-advised-indent-function (region-beginning)
|
||
(region-end)))))
|
||
(evil-end-undo-step))
|
||
|
||
(dolist (func '(yank yank-pop evil-paste-before evil-paste-after))
|
||
(advice-add func :around #'spacemacs//yank-indent-region))
|
||
|
||
;; find file functions in split
|
||
(defun spacemacs//display-in-split (buffer alist)
|
||
"Split selected window and display BUFFER in the new window.
|
||
BUFFER and ALIST have the same form as in `display-buffer'. If ALIST contains
|
||
a split-side entry, its value must be usable as the SIDE argument for
|
||
`split-window'."
|
||
(let ((window (split-window nil nil (cdr (assq 'split-side alist)))))
|
||
(window--display-buffer buffer window 'window alist)
|
||
window))
|
||
|
||
(defun spacemacs/find-file-vsplit (file)
|
||
"find file in vertical split"
|
||
(interactive "FFind file (vsplit): ")
|
||
(let ((buffer (find-file-noselect file)))
|
||
(pop-to-buffer buffer '(spacemacs//display-in-split (split-side . right)))))
|
||
|
||
(defun spacemacs/find-file-split (file)
|
||
"find file in horizontal split"
|
||
(interactive "FFind file (split): ")
|
||
(let ((buffer (find-file-noselect file)))
|
||
(pop-to-buffer buffer '(spacemacs//display-in-split (split-side . below)))))
|
||
|
||
(defun spacemacs/switch-to-help-buffer ()
|
||
"Open or select the `*Help*' buffer, if it exists."
|
||
(interactive)
|
||
(if (get-buffer "*Help*")
|
||
(switch-to-buffer (help-buffer))
|
||
(message "No previous Help buffer found")))
|
||
|
||
(defun spacemacs/switch-to-scratch-buffer (&optional arg)
|
||
"Switch to the `*scratch*' buffer, creating it first if needed.
|
||
if prefix argument ARG is given, switch to it in an other, possibly new window."
|
||
(interactive "P")
|
||
(let ((exists (get-buffer "*scratch*")))
|
||
(if arg
|
||
(switch-to-buffer-other-window (get-buffer-create "*scratch*"))
|
||
(switch-to-buffer (get-buffer-create "*scratch*")))
|
||
(when (and (not exists)
|
||
(not (eq major-mode dotspacemacs-scratch-mode))
|
||
(fboundp dotspacemacs-scratch-mode))
|
||
(funcall dotspacemacs-scratch-mode))))
|
||
|
||
(defvar spacemacs--killed-buffer-list nil
|
||
"List of recently killed buffers.")
|
||
|
||
(defun spacemacs//add-buffer-to-killed-list ()
|
||
"If buffer is associated with a file name, add that file
|
||
to the `killed-buffer-list' when killing the buffer."
|
||
(when buffer-file-name
|
||
(push buffer-file-name spacemacs--killed-buffer-list)))
|
||
|
||
(defun spacemacs/reopen-killed-buffer ()
|
||
"Reopen the most recently killed file buffer, if one exists."
|
||
(interactive)
|
||
(when spacemacs--killed-buffer-list
|
||
(find-file (pop spacemacs--killed-buffer-list))))
|
||
|
||
(defun spacemacs/switch-to-messages-buffer (&optional arg)
|
||
"Switch to the `*Messages*' buffer.
|
||
if prefix argument ARG is given, switch to it in an other, possibly new window."
|
||
(interactive "P")
|
||
(with-current-buffer (messages-buffer)
|
||
(goto-char (point-max))
|
||
(if arg
|
||
(switch-to-buffer-other-window (current-buffer))
|
||
(switch-to-buffer (current-buffer)))))
|
||
|
||
(defun spacemacs/close-compilation-window ()
|
||
"Close the window containing the '*compilation*' buffer."
|
||
(interactive)
|
||
(when compilation-last-buffer
|
||
(delete-windows-on compilation-last-buffer)))
|
||
|
||
|
||
;; Line number
|
||
|
||
(defun spacemacs/no-linum (&rest ignore)
|
||
"Disable linum if current buffer."
|
||
(when (or 'linum-mode global-linum-mode)
|
||
(linum-mode 0)))
|
||
|
||
(defun spacemacs/enable-line-numbers-p ()
|
||
"Return non-nil if line numbers should be enabled for current buffer.
|
||
Decision is based on `dotspacemacs-line-numbers'."
|
||
(and dotspacemacs-line-numbers
|
||
(spacemacs//linum-curent-buffer-is-not-too-big)
|
||
(or (spacemacs//linum-backward-compabitility)
|
||
(and (listp dotspacemacs-line-numbers)
|
||
(spacemacs//linum-enabled-for-current-major-mode)))))
|
||
|
||
(defun spacemacs/relative-line-numbers-p ()
|
||
"Return non-nil if line numbers should be relative.
|
||
Decision is based on `dotspacemacs-line-numbers'."
|
||
(or (eq dotspacemacs-line-numbers 'relative)
|
||
(and (listp dotspacemacs-line-numbers)
|
||
(car (spacemacs/mplist-get-values dotspacemacs-line-numbers :relative)))))
|
||
|
||
(defun spacemacs//linum-on (origfunc &rest args)
|
||
"Advice function to improve `linum-on' function."
|
||
(when (spacemacs/enable-line-numbers-p)
|
||
(apply origfunc args)))
|
||
|
||
(defun spacemacs//linum-update-window-scale-fix (win)
|
||
"Fix linum for scaled text in the window WIN."
|
||
(when (display-multi-font-p)
|
||
(unless (boundp 'text-scale-mode-step)
|
||
(setq window-initial-margins (window-margins win)))
|
||
(set-window-margins win
|
||
(ceiling (* (if (boundp 'text-scale-mode-step)
|
||
(expt text-scale-mode-step
|
||
text-scale-mode-amount)
|
||
1)
|
||
(or (car (if (boundp 'window-initial-margins)
|
||
window-initial-margins
|
||
(window-margins win)))
|
||
1))))))
|
||
|
||
(defun spacemacs//linum-backward-compabitility ()
|
||
"Return non-nil if `dotspacemacs-line-numbers' has an old format and if
|
||
`linum' should be enabled."
|
||
(and dotspacemacs-line-numbers
|
||
(not (listp dotspacemacs-line-numbers))
|
||
(or (eq dotspacemacs-line-numbers t)
|
||
(eq dotspacemacs-line-numbers 'relative))
|
||
(derived-mode-p 'prog-mode 'text-mode)))
|
||
|
||
(defun spacemacs//linum-curent-buffer-is-not-too-big ()
|
||
"Return non-nil if buffer size is not too big."
|
||
(not (and (listp dotspacemacs-line-numbers)
|
||
(spacemacs/mplist-get-values dotspacemacs-line-numbers :size-limit-kb)
|
||
(> (buffer-size)
|
||
(* 1000 (car (spacemacs/mplist-get-values dotspacemacs-line-numbers
|
||
:size-limit-kb)))))))
|
||
|
||
;; see tests in tests/layers/+distribution/spacemacs-base/line-numbers-utest.el
|
||
;; for the different possible cases
|
||
(defun spacemacs//linum-enabled-for-current-major-mode ()
|
||
"Return non-nil if line number is enabled for current major-mode."
|
||
(let* ((disabled-for-modes (spacemacs/mplist-get-values dotspacemacs-line-numbers
|
||
:disabled-for-modes))
|
||
(user-enabled-for-modes (spacemacs/mplist-get-values dotspacemacs-line-numbers
|
||
:enabled-for-modes))
|
||
;; default `enabled-for-modes' to '(prog-mode text-mode), because it is
|
||
;; a more sensible default than enabling in all buffers - including
|
||
;; Magit buffers, terminal buffers, etc. But don't include prog-mode or
|
||
;; text-mode if they're explicitly disabled by user
|
||
(enabled-for-modes (or user-enabled-for-modes
|
||
(seq-difference '(prog-mode text-mode)
|
||
disabled-for-modes
|
||
#'eq)))
|
||
(enabled-for-parent (or (and (equal enabled-for-modes '(all)) 'all)
|
||
(apply #'derived-mode-p enabled-for-modes)))
|
||
(disabled-for-parent (apply #'derived-mode-p disabled-for-modes)))
|
||
(or
|
||
;; special case 'all: enable for any mode that isn't specifically disabled
|
||
(and (eq enabled-for-parent 'all) (not disabled-for-parent))
|
||
;; current mode or a parent is in :enabled-for-modes, and there isn't a
|
||
;; more specific parent (or the mode itself) in :disabled-for-modes
|
||
(and enabled-for-parent
|
||
(or (not disabled-for-parent)
|
||
;; handles the case where current major-mode has a parent both in
|
||
;; :enabled-for-modes and in :disabled-for-modes. Return non-nil
|
||
;; if enabled-for-parent is the more specific parent (IOW derives
|
||
;; from disabled-for-parent)
|
||
(spacemacs/derived-mode-p enabled-for-parent disabled-for-parent)))
|
||
;; current mode (or parent) not explicitly disabled
|
||
(and (null user-enabled-for-modes)
|
||
enabled-for-parent ; mode is one of default allowed modes
|
||
disabled-for-modes
|
||
(not disabled-for-parent)))))
|
||
|