emacs-prelude/core/prelude-core.el

413 lines
14 KiB
EmacsLisp
Raw Normal View History

2013-03-07 09:57:33 +02:00
;;; prelude-core.el --- Emacs Prelude: Core Prelude functions.
2011-10-08 23:05:06 +03:00
;;
2013-01-02 13:13:59 +02:00
;; Copyright © 2011-2013 Bozhidar Batsov
2011-10-08 23:05:06 +03:00
;;
;; Author: Bozhidar Batsov <bozhidar@batsov.com>
2013-03-07 09:57:33 +02:00
;; URL: https://github.com/bbatsov/prelude
2011-10-08 23:05:06 +03:00
;; Version: 1.0.0
;; Keywords: convenience
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Here are the definitions of most of the functions added by Prelude.
;;; License:
;; 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 GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Code:
(require 'thingatpt)
2013-04-25 15:13:52 +03:00
(require 'dash)
2011-10-08 23:05:06 +03:00
(defun prelude-open-with (arg)
"Open visited file in default external program.
With a prefix ARG always prompt for command to use."
(interactive "P")
2011-10-08 23:05:06 +03:00
(when buffer-file-name
(shell-command (concat
(cond
((and (not arg) (eq system-type 'darwin)) "open")
((and (not arg) (member system-type '(gnu gnu/linux gnu/kfreebsd))) "xdg-open")
(t (read-shell-command "Open current file with: ")))
2011-10-08 23:05:06 +03:00
" "
2013-05-09 18:23:07 +03:00
(shell-quote-argument buffer-file-name)))))
2011-10-08 23:05:06 +03:00
(defun prelude-buffer-mode (buffer-or-name)
2013-03-07 09:57:33 +02:00
"Retrieve the `major-mode' of BUFFER-OR-NAME."
2013-04-05 14:43:15 +03:00
(with-current-buffer buffer-or-name
major-mode))
2011-10-08 23:05:06 +03:00
(defun prelude-visit-term-buffer ()
2013-03-07 09:57:33 +02:00
"Create or visit a terminal buffer."
2011-10-08 23:05:06 +03:00
(interactive)
2013-04-29 17:12:40 +03:00
(prelude-start-or-switch-to (lambda ()
(ansi-term (getenv "SHELL")))
"*ansi-term*"))
2011-10-08 23:05:06 +03:00
2011-10-10 17:29:52 +03:00
(defun prelude-google ()
"Googles a query or region if any."
(interactive)
(browse-url
(concat
"http://www.google.com/search?ie=utf-8&oe=utf-8&q="
(url-hexify-string (if mark-active
(buffer-substring (region-beginning) (region-end))
(read-string "Google: "))))))
2011-10-10 17:29:52 +03:00
2013-03-07 13:09:13 +02:00
(defun prelude-indent-rigidly-and-copy-to-clipboard (begin end arg)
"Indent region between BEGIN and END by ARG columns and copy to clipboard."
(interactive "r\nP")
(let ((arg (or arg 4))
(buffer (current-buffer)))
2011-10-08 23:05:06 +03:00
(with-temp-buffer
(insert-buffer-substring-no-properties buffer begin end)
2013-03-07 13:09:13 +02:00
(indent-rigidly (point-min) (point-max) arg)
2011-10-08 23:05:06 +03:00
(clipboard-kill-ring-save (point-min) (point-max)))))
(defun prelude-smart-open-line-above ()
"Insert an empty line above the current line.
2013-04-03 12:14:04 +03:00
Position the cursor at it's beginning, according to the current mode."
(interactive)
2013-04-03 12:14:04 +03:00
(forward-line -1)
(prelude-smart-open-line))
2013-03-27 12:13:24 +02:00
(defun prelude-smart-open-line ()
2013-03-07 13:09:13 +02:00
"Insert an empty line after the current line.
Position the cursor at its beginning, according to the current mode."
2011-10-08 23:05:06 +03:00
(interactive)
(move-end-of-line nil)
2013-03-27 12:13:24 +02:00
(newline-and-indent))
2011-10-08 23:05:06 +03:00
(defun prelude-move-line-up ()
"Move the current line up."
2011-10-08 23:05:06 +03:00
(interactive)
(transpose-lines 1)
2013-04-02 12:14:10 +03:00
(forward-line -2)
(indent-according-to-mode))
2011-10-08 23:05:06 +03:00
(defun prelude-move-line-down ()
"Move the current line down."
2011-10-08 23:05:06 +03:00
(interactive)
(forward-line 1)
2011-10-08 23:05:06 +03:00
(transpose-lines 1)
2013-04-02 12:14:10 +03:00
(forward-line -1)
(indent-according-to-mode))
2011-10-08 23:05:06 +03:00
2013-04-09 15:18:08 +03:00
(defun prelude-kill-whole-line (&optional arg)
"A simple wrapper around command `kill-whole-line' that respects indentation.
Passes ARG to command `kill-whole-line' when provided."
(interactive "P")
(kill-whole-line arg)
(back-to-indentation))
(defun prelude-move-beginning-of-line (arg)
"Move point back to indentation of beginning of line.
Move point to the first non-whitespace character on this line.
If point is already there, move to the beginning of the line.
Effectively toggle between the first non-whitespace character and
the beginning of the line.
If ARG is not nil or 1, move forward ARG - 1 lines first. If
point reaches the beginning or end of the buffer, stop there."
(interactive "^p")
(setq arg (or arg 1))
;; Move lines first
(when (/= arg 1)
(let ((line-move-visual nil))
(forward-line (1- arg))))
(let ((orig-point (point)))
(back-to-indentation)
(when (= orig-point (point))
(move-beginning-of-line 1))))
(global-set-key [remap move-beginning-of-line]
'prelude-move-beginning-of-line)
2011-10-08 23:05:06 +03:00
(defun prelude-indent-buffer ()
2013-03-07 09:57:33 +02:00
"Indent the currently visited buffer."
2011-10-08 23:05:06 +03:00
(interactive)
(indent-region (point-min) (point-max)))
(defun prelude-indent-region-or-buffer ()
2013-03-07 09:57:33 +02:00
"Indent a region if selected, otherwise the whole buffer."
2011-10-08 23:05:06 +03:00
(interactive)
(save-excursion
(if (region-active-p)
(progn
(indent-region (region-beginning) (region-end))
(message "Indented selected region."))
(progn
2011-10-10 17:50:23 +03:00
(prelude-indent-buffer)
2011-10-08 23:05:06 +03:00
(message "Indented buffer.")))))
2013-03-28 12:43:45 +02:00
(defun prelude-indent-defun ()
"Indent the current defun."
(interactive)
(save-excursion
(mark-defun)
(indent-region (region-beginning) (region-end))))
2011-10-08 23:05:06 +03:00
(defun prelude-annotate-todo ()
"Put fringe marker on TODO: lines in the curent buffer."
(interactive)
(save-excursion
(goto-char (point-min))
(while (re-search-forward "TODO:" nil t)
(let ((overlay (make-overlay (- (point) 5) (point))))
(overlay-put overlay
'before-string
(propertize (format "A")
'display '(left-fringe right-triangle)))))))
(defun prelude-copy-file-name-to-clipboard ()
"Copy the current buffer file name to the clipboard."
2011-10-08 23:05:06 +03:00
(interactive)
(let ((filename (if (equal major-mode 'dired-mode)
default-directory
(buffer-file-name))))
(when filename
(kill-new filename)
(message "Copied buffer file name '%s' to the clipboard." filename))))
2011-10-08 23:05:06 +03:00
(defun prelude-duplicate-current-line-or-region (arg)
"Duplicates the current line or region ARG times.
2013-03-07 09:57:33 +02:00
If there's no region, the current line will be duplicated. However, if
2011-10-08 23:05:06 +03:00
there's a region, all lines that region covers will be duplicated."
(interactive "p")
(let (beg end (origin (point)))
(if (and mark-active (> (point) (mark)))
(exchange-point-and-mark))
(setq beg (line-beginning-position))
(if mark-active
(exchange-point-and-mark))
(setq end (line-end-position))
(let ((region (buffer-substring-no-properties beg end)))
2012-12-15 20:42:19 +02:00
(-dotimes arg
(lambda (n)
2012-12-15 20:42:19 +02:00
(goto-char end)
(newline)
(insert region)
(setq end (point))))
2011-10-08 23:05:06 +03:00
(goto-char (+ origin (* (length region) arg) arg)))))
(defun prelude-rename-file-and-buffer ()
"Renames current buffer and file it is visiting."
(interactive)
(let ((filename (buffer-file-name)))
2011-10-08 23:05:06 +03:00
(if (not (and filename (file-exists-p filename)))
(message "Buffer is not visiting a file!")
2011-10-08 23:05:06 +03:00
(let ((new-name (read-file-name "New name: " filename)))
(cond
((vc-backend filename) (vc-rename-file filename new-name))
(t
(rename-file filename new-name t)
(set-visited-file-name new-name t t)))))))
2011-10-08 23:05:06 +03:00
(defun prelude-delete-file-and-buffer ()
2013-03-07 09:57:33 +02:00
"Kill the current buffer and deletes the file it is visiting."
2011-10-08 23:05:06 +03:00
(interactive)
(let ((filename (buffer-file-name)))
(when filename
2013-04-03 12:53:37 +03:00
(if (vc-backend filename)
(vc-delete-file filename)
(progn
(delete-file filename)
(message "Deleted file %s" filename)
(kill-buffer))))))
2011-10-08 23:05:06 +03:00
(defun prelude-view-url ()
"Open a new buffer containing the contents of URL."
(interactive)
(let* ((default (thing-at-point-url-at-point))
(url (read-from-minibuffer "URL: " default)))
(switch-to-buffer (url-retrieve-synchronously url))
(rename-buffer url t)
2013-04-05 14:43:15 +03:00
(cond ((search-forward "<?xml" nil t) (nxml-mode))
2011-10-08 23:05:06 +03:00
((search-forward "<html" nil t) (html-mode)))))
(defun prelude-untabify-buffer ()
2013-03-07 09:57:33 +02:00
"Remove all tabs from the current buffer."
2011-10-08 23:05:06 +03:00
(interactive)
(untabify (point-min) (point-max)))
(defun prelude-cleanup-buffer ()
"Perform a bunch of operations on the whitespace content of a buffer."
(interactive)
(prelude-indent-buffer)
(prelude-untabify-buffer)
(whitespace-cleanup))
2011-10-08 23:05:06 +03:00
(defun prelude-eval-and-replace ()
"Replace the preceding sexp with its value."
(interactive)
(backward-kill-sexp)
(condition-case nil
(prin1 (eval (read (current-kill 0)))
(current-buffer))
(error (message "Invalid expression")
(insert (current-kill 0)))))
(defun prelude-recompile-init ()
"Byte-compile all your dotfiles again."
(interactive)
2012-04-17 17:47:05 +03:00
(byte-recompile-directory prelude-dir 0))
2011-10-08 23:05:06 +03:00
(defun prelude-sudo-edit (&optional arg)
2013-04-21 10:01:43 +03:00
"Edit currently visited file as root.
With a prefix ARG prompt for a file to visit.
Will also prompt for a file to visit if current
buffer is not visiting a file."
(interactive "P")
2011-10-08 23:05:06 +03:00
(if (or arg (not buffer-file-name))
2013-04-21 10:01:43 +03:00
(find-file (concat "/sudo:root@localhost:"
(ido-read-file-name "Find file(as root): ")))
(find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name))))
(defadvice ido-find-file (after find-file-sudo activate)
"Find file as root if necessary."
2013-04-28 22:18:38 +03:00
(unless (or (equal major-mode 'dired-mode)
2013-04-28 22:49:01 +03:00
(and (buffer-file-name)
(file-writable-p buffer-file-name)))
2011-10-08 23:05:06 +03:00
(find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name))))
2013-04-29 07:59:51 +03:00
(defun prelude-start-or-switch-to (function buffer-name)
"Invoke FUNCTION if there is no buffer with BUFFER-NAME.
Otherwise switch to the buffer named BUFFER-NAME. Don't clobber
the current buffer."
(if (not (get-buffer buffer-name))
(progn
(split-window-sensibly (selected-window))
(other-window 1)
(funcall function))
(switch-to-buffer-other-window buffer-name)))
2011-10-08 23:05:06 +03:00
(defun prelude-insert-date ()
2013-03-07 09:57:33 +02:00
"Insert a timestamp according to locale's date and time format."
2011-10-08 23:05:06 +03:00
(interactive)
(insert (format-time-string "%c" (current-time))))
(defun prelude-conditionally-enable-paredit-mode ()
2013-03-07 09:57:33 +02:00
"Enable `paredit-mode' in the minibuffer, during `eval-expression'."
2011-10-08 23:05:06 +03:00
(if (eq this-command 'eval-expression)
(paredit-mode 1)))
(add-hook 'minibuffer-setup-hook 'prelude-conditionally-enable-paredit-mode)
(defun prelude-recentf-ido-find-file ()
"Find a recent file using ido."
(interactive)
(let ((file (ido-completing-read "Choose recent file: "
(-map 'abbreviate-file-name recentf-list)
nil t)))
2011-10-08 23:05:06 +03:00
(when file
(find-file file))))
(defun prelude-swap-windows ()
"If you have 2 windows, it swaps them."
(interactive)
2011-10-10 17:50:23 +03:00
(if (/= (count-windows) 2)
(message "You need exactly 2 windows to do this.")
2012-12-17 17:43:35 +02:00
(let* ((w1 (car (window-list)))
(w2 (cadr (window-list)))
2011-10-10 17:50:23 +03:00
(b1 (window-buffer w1))
(b2 (window-buffer w2))
(s1 (window-start w1))
(s2 (window-start w2)))
(set-window-buffer w1 b2)
(set-window-buffer w2 b1)
(set-window-start w1 s2)
(set-window-start w2 s1)))
2011-10-08 23:05:06 +03:00
(other-window 1))
2013-04-24 11:24:30 +03:00
(defun prelude-switch-to-previous-buffer ()
"Switch to previously open buffer.
Repeated invocations toggle between the two most recently open buffers."
(interactive)
(switch-to-buffer (other-buffer (current-buffer) 1)))
(defun prelude-kill-other-buffers ()
2013-03-07 09:57:33 +02:00
"Kill all buffers but the current one.
Doesn't mess with special buffers."
(interactive)
2012-12-15 20:42:19 +02:00
(-each
(->> (buffer-list)
(-filter #'buffer-file-name)
(--remove (eql (current-buffer) it)))
#'kill-buffer))
(defun prelude-create-scratch-buffer ()
"Create a new scratch buffer."
(interactive)
(progn
(switch-to-buffer
(get-buffer-create (generate-new-buffer-name "*scratch*")))
(emacs-lisp-mode)))
2012-05-07 18:04:03 +03:00
(defvar prelude-tips
'("Press <C-c o> to open a file with external program."
"Press <C-c p f> to navigate a project's files with ido."
"Press <C-c h> to navigate a project in Helm."
"Press <C-c g> to search in Google."
"Press <C-c r> to rename the current buffer and file it's visiting."
"Press <C-c t> to open a terminal in Emacs."
2012-05-07 18:06:32 +03:00
"Explore the Prelude menu to find out about some of Prelude extensions to Emacs."
"Access the official Emacs manual by pressing <C-h r>."
"Visit WikEmacs at http://wikemacs.org to find out even more about Emacs."))
2012-05-07 18:04:03 +03:00
(defun prelude-tip-of-the-day ()
2013-03-07 09:57:33 +02:00
"Display a random entry from `prelude-tips'."
2012-05-07 18:04:03 +03:00
(interactive)
(unless (window-minibuffer-p)
;; pick a new random seed
(random t)
(message
(concat "Prelude tip: " (nth (random (length prelude-tips)) prelude-tips)))))
2012-05-07 18:04:03 +03:00
(defun prelude-eval-after-init (form)
"Add `(lambda () FORM)' to `after-init-hook'.
If Emacs has already finished initialization, also eval FORM immediately."
(let ((func (list 'lambda nil form)))
(add-hook 'after-init-hook func)
(when after-init-time
(eval form))))
(defun prelude-exchange-point-and-mark ()
"Identical to `exchange-point-and-mark' but will not activate the region."
(interactive)
(exchange-point-and-mark)
(deactivate-mark nil))
2013-03-11 20:13:06 +02:00
(defun prelude-update ()
"Update Prelude to its latest version."
(interactive)
2013-05-23 11:29:22 +03:00
(when (y-or-n-p "Do you want to update Prelude? ")
2013-03-11 20:13:06 +02:00
(message "Updating Prelude...")
(cd prelude-dir)
(shell-command "git pull")
(message "Update finished. Restart Emacs to complete the process.")))
2011-10-08 23:05:06 +03:00
(provide 'prelude-core)
;;; prelude-core.el ends here