From 0a7a80603df17c0ec47a6374366c982bb25b5a2f Mon Sep 17 00:00:00 2001 From: TLINDEN Date: Sat, 14 May 2016 19:14:05 +0200 Subject: [PATCH] initial --- README.md | 136 ++++++++++++++++++++ viking-mode.el | 342 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 478 insertions(+) create mode 100644 viking-mode.el diff --git a/README.md b/README.md index de78ef2..8b42400 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,138 @@ # viking-mode Kill first, ask later - an emacs mode for killing things quickly + +# Introduction + +Viking minor mode enables you to delete things at point with one key +stroke at once. More and more will be deleted if you repeat the key +stroke. + +The default key binding is C-d, but you may also bind it to C-k or +whatever you wish. + +If you press C-d the first time, the word at point will be deleted. +If you press it again, the remainder of the line from point will be +deleted. If pressed again, the whole line, then the paragraph and +finally the whole buffer will be deleted. + +Like: + + [keep pressing ctrl] C-d - word + C-d C-d - line remainder + C-d C-d C-d - line + C-d C-d C-d C-d - paragraph + C-d C-d C-d C-d C-d - buffer + +However, this only works when pressing the key in a row. If you do +something else in between, it starts from scratch (i.e. delete word). + +# Install + +To use, save viking-mode.el to a directory in your load-path. + +Add something like this to your config: + + (require 'viking-mode) + (add-hook 'text-mode-hook 'turn-on-viking-mode) + +or load it manually + + M-x viking-mode + +However, it's also possible to enable viking-mode globally: + + (viking-global-mode) + +# Customize + +By default viking-mode doesn't really delete things, everything +remains available for yanking in the kill ring. However, you may turn +it into berserk mode by setting 'viking-really-delete to t: + + (setq viking-really-delete t) + +In case you don't like the default key binding cascade, you may also +use separate bindings for each kill function, e.g.: + + (define-key viking-mode-map (kbd "C-d") nil) turn C-d into a prefix-key + (define-key viking-mode-map (kbd "C-d w") 'vk/kill-word) + (define-key viking-mode-map (kbd "C-d r") 'vk/kill-line-from-point) + (define-key viking-mode-map (kbd "C-d l") 'vk/kill-line) + (define-key viking-mode-map (kbd "C-d p") 'vk/kill-paragraph) + (define-key viking-mode-map (kbd "C-d a") 'vk/kill-buffer) + +Besides, the primary function of viking-mode is vk/last-key-repeats, +which returns the number of times the last key have been pressed. +This can be used for other things as well, for example: + + (defun goto-begin() + (interactive) + (let* ((key-times (vk/last-key-repeats))) + (cond + ((eq key-times 3) + (if mark-active + (goto-char (point-min)) + (beginning-of-buffer))) + ((eq key-times 2) + (if mark-active () (push-mark)) + (move-to-window-line 0)) + ((eq key-times 1) + (beginning-of-line))))) + + (defun goto-end () + (interactive) + (let* ((key-times (vk/last-key-repeats))) + (cond + ((eq key-times 3) + (if mark-active + (goto-char (point-max)) + (end-of-buffer))) + ((eq key-times 2) + (if mark-active () (push-mark)) + (move-to-window-line -1) + (end-of-line)) + ((eq key-times 1) + (end-of-line))))) + + (global-set-key (kbd "") 'goto-begin) + (global-set-key (kbd "") 'goto-end) + +When you put this into your .emacs config, then you can do: + + hit once: goto beginning of current line + repeat: goto beginning of current window + repeat: goto beginning of current buffer + +(and the same with in the other direction) + + +# Meta + +Copyright (C) 2016, T.v.Dein + +This file is NOT part of Emacs. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + + Version: 0.01 + Author: T.v.Dein + Maintainer: T.v.Dein + Created: May 2016 + Keywords: kill delete + Homepage: http://www.daemon.de/Viking + Repository: https://github.com/tlinden/viking-mode + License: GNU General Public License >= 2 + Distribution: This file is not part of Emacs diff --git a/viking-mode.el b/viking-mode.el new file mode 100644 index 0000000..32d35a7 --- /dev/null +++ b/viking-mode.el @@ -0,0 +1,342 @@ +;;; viking-mode.el - kill first, ask later + +;; Copyright (C) 2016, T.v.Dein + +;; This file is NOT part of Emacs. + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 2 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, write to the Free Software +;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +;; USA + +;; Version: 0.01 +;; Author: T.v.Dein +;; Maintainer: T.v.Dein +;; Created: May 2016 +;; Keywords: kill delete +;; Homepage: http://www.daemon.de/Viking +;; Repository: https://github.com/tlinden/viking-mode +;; License: GNU General Public License >= 2 +;; Distribution: This file is not part of Emacs + +;;; Commentary + +;; Viking minor mode enables you to delete things at point with one +;; key stroke at once. More and more will be deleted if you repeat the +;; key stroke. + +;; The default key binding is C-d, but you may also bind it to C-k or +;; whatever you wish. + +;; If you press C-d the first time, the word at point will be deleted. +;; If you press it again, the remainder of the line from point will be +;; deleted. If pressed again, the whole line, then the paragraph and +;; finally the whole buffer will be deleted. + +;; Like: +;; [keep pressing ctrl] C-d - word +;; C-d C-d - line remainder +;; C-d C-d C-d - line +;; C-d C-d C-d C-d - paragraph +;; C-d C-d C-d C-d C-d - buffer + +;; However, this only works when pressing the key in a row. If you do +;; something else in between, it starts from scratch (i.e. delete +;; word). + +;; To use, save viking-mode.el to a directory in your load-path. + +;; Add something like this to your config: + +;; (require 'viking-mode) +;; (add-hook 'text-mode-hook 'turn-on-viking-mode) + +;; or load it manually + +;; M-x viking-mode + +;; However, it's also possible to enable viking-mode globally: + +;; (viking-global-mode) + +;; By default viking-mode doesn't really delete things, everything +;; remains available for yanking in the kill ring. However, you may +;; turn it into berserk mode by setting 'viking-really-delete to t: + +;; (setq viking-really-delete t) + +;; In case you don't like the default key binding cascade, you may +;; also use separate bindings for each kill function, e.g.: + +;; (define-key viking-mode-map (kbd "C-d") nil) ;; turn C-d into a prefix-key +;; (define-key viking-mode-map (kbd "C-d w") 'vk/kill-word) +;; (define-key viking-mode-map (kbd "C-d r") 'vk/kill-line-from-point) +;; (define-key viking-mode-map (kbd "C-d l") 'vk/kill-line) +;; (define-key viking-mode-map (kbd "C-d p") 'vk/kill-paragraph) +;; (define-key viking-mode-map (kbd "C-d a") 'vk/kill-buffer) + +;; Besides, the primary function of viking-mode is vk/last-key-repeats, +;; which returns the number of times the last key have been pressed. +;; This can be used for other things as well, for example: + +;; (defun goto-begin() +;; (interactive) +;; (let* ((key-times (vk/last-key-repeats))) +;; (cond +;; ((eq key-times 3) +;; (if mark-active +;; (goto-char (point-min)) +;; (beginning-of-buffer))) +;; ((eq key-times 2) +;; (if mark-active () (push-mark)) +;; (move-to-window-line 0)) +;; ((eq key-times 1) +;; (beginning-of-line))))) + +;; (defun goto-end () +;; (interactive) +;; (let* ((key-times (vk/last-key-repeats))) +;; (cond +;; ((eq key-times 3) +;; (if mark-active +;; (goto-char (point-max)) +;; (end-of-buffer))) +;; ((eq key-times 2) +;; (if mark-active () (push-mark)) +;; (move-to-window-line -1) +;; (end-of-line)) +;; ((eq key-times 1) +;; (end-of-line))))) + +;; (global-set-key (kbd "") 'goto-begin) +;; (global-set-key (kbd "") 'goto-end) + +;; When you put this into your .emacs config, then you can do: +;; hit once: goto beginning of current line +;; repeat: goto beginning of current window +;; repeat: goto beginning of current buffer +;; (and the same with in the other direction) + + + + + +;;; Code +;;;; Customizable variables +;;;;; Fonts + +(defface viking-blink + '((t :inherit highlight)) + "Level 1." + :group 'viking-mode) + +;;;;; Modify behavior + +(defcustom viking-blink-time 0.2 + "How long should 'vk/blink-region highligh a region, +in seconds, specify milliseconds like this: 0.1" + :group 'viking-mode) + +(defcustom viking-really-delete nil + "If set to t, really delete killed things. That is, +nothing will remain available via kill-ring once deleted. +The default is nil: keep deleted things in the kill-ring." + :group 'viking-mode) + + + +;;;; Functions +;;;;; Utilities + +;; internal utility functions required by the kill functions + +(defun vk/blink-region(begin end) + "Blink a region shortly. It does this by highlighting the +region between 'begin and 'end using 'font-lock-viking-face +for 'viking-blink-time seconds." + (interactive) + (let* ((rh (make-overlay begin end))) + (progn + (overlay-put rh 'face 'viking-blink) + (sit-for viking-blink-time t) + (delete-overlay rh) + ))) + +(defun vk/get-point (symbol &optional arg) + "Returns the point by evaluating 'symbol, which +should be a point-moving function." + (funcall symbol arg) + (point) + ) + +(defun vk/last-key-repeats () + "Returns how many times the last key has been pressed as integer." + ;; FIXME: should be possible with just counting '(recent-keys) + (interactive) + (let* ((keys (recent-keys)) + (len (length keys)) + (key1 (if (> len 0) (elt keys (- len 1)) nil)) + (key2 (if (> len 1) (elt keys (- len 2)) nil)) + (key3 (if (> len 2) (elt keys (- len 3)) nil)) + (key4 (if (> len 3) (elt keys (- len 4)) nil)) + (key5 (if (> len 4) (elt keys (- len 5)) nil)) + (key-equal-1 (equal key1 key2)) + (key-equal-2 (and key-equal-1 (equal key2 key3))) + (key-equal-3 (and key-equal-2 (equal key3 key4))) + (key-equal-4 (and key-equal-3 (equal key4 key5)))) + (cond (key-equal-4 5) + (key-equal-3 4) + (key-equal-2 3) + (key-equal-1 2) + (t 1) + ))) + +;;;;; kill/delete wrappers + +(defun vk/kill-region (beg end) + "Deletes or kills a region depending on 'viking-really-delete." + (message "vkkill") + (if viking-really-delete + (delete-region beg end) + (kill-region beg end))) + +(defun vk/really-delete-line () ;; based on code by xah + "kill-line without copy" + (interactive) + (delete-region + (point) + (save-excursion (move-end-of-line 1) (point))) + (delete-char 1) + ) + +;;;;; Interactive kill functions + +(defun vk/kill-word-at-point () + "Kill word at point." + (interactive) + (let + ((beg (vk/get-point 'backward-word 1)) + (end (vk/get-point 'forward-word 1))) + (progn + (vk/blink-region beg end) + (vk/kill-region beg end) + (message "word at point deleted")))) + + +(defun vk/kill-word-right () + "Kill word to the right" + (interactive) + (let + ((beg (point)) + (end (vk/get-point 'forward-word 1))) + (vk/blink-region beg end) + (vk/kill-region beg end) + (message "word right of point deleted"))) + +(defun vk/kill-word () + "Kill word" + (interactive) + (if (eq (point) (line-beginning-position)) + (vk/kill-word-right) + (vk/kill-word-at-point) + )) + +(defun vk/kill-line () + "Kill line at point including newline." + (interactive) + (vk/blink-region (vk/get-point 'beginning-of-line 1) (vk/get-point 'end-of-line 1)) + (move-beginning-of-line 1) + (let ((k kill-whole-line)) + (progn + (setq kill-whole-line t) ;; temp enable + (if viking-really-delete + (vk/really-delete-line) + (kill-line) + ) + (setq kill-whole-line k) + )) + (message "line at point deleted")) + +(defun vk/kill-line-from-point () + "Kill line from point to the right without newline." + (interactive) + (let ((beg (point)) + (end (vk/get-point 'end-of-line 1))) + (progn + (vk/blink-region beg end) + (vk/kill-region beg end) + (message "line from point deleted")))) + +(defun vk/kill-paragraph () + "Kill paragraph at point." + (interactive) + (let + ((beg (vk/get-point 'backward-paragraph 1)) + (end (vk/get-point 'forward-paragraph 1))) + (progn + (vk/blink-region beg end) + (vk/kill-region beg end) + (message "paragraph at point deleted")))) + +(defun vk/kill-buffer () + "Kill the whole buffer." + (interactive) + (let ((beg (point-min)) + (end (point-max))) + (progn + (vk/blink-region beg end) + (vk/kill-region beg end) + (message "buffer deleted")))) + +;;;;; Primary key binding function + +(defun vk/kill-thing-at-point() + "Delete thing at point. Checks how many times the +calling key has been pressed and runs the appropriate +kill function then." + (interactive) + (let* ((key-times (vk/last-key-repeats))) + (cond + ((eq key-times 5) (vk/kill-buffer)) + ((eq key-times 4) (vk/kill-paragraph)) + ((eq key-times 3) (vk/kill-line)) + ((eq key-times 2) (vk/kill-line-from-point)) + ((eq key-times 1) (vk/kill-word)) + ) + )) + +;;;; Interface +;;;###autoload + +;; the minor mode, can be enabled by major mode via hook or manually +(define-minor-mode viking-mode "kill first, ask later" + :lighter " V" + :group 'viking-mode + :keymap (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-d") 'vk/kill-thing-at-point) + map)) + +;; just in case someone wants to use it globally +(define-global-minor-mode viking-global-mode + viking-mode + (lambda () (viking-mode t) + )) + +;; be nice and provide a toggle +(defun turn-on-viking-mode () + "turn viking-mode on" + (interactive) + (viking-mode 1)) + +;; un-*ing-believable: I'm done *g* +(provide 'viking-mode)