Created
March 21, 2014 17:41
-
-
Save dhaley/9691569 to your computer and use it in GitHub Desktop.
helm-swoop-info error
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil) | |
1-(nil) | |
(forward-line (1- $line)) | |
helm-swoop--goto-line(nil) | |
(progn (helm-swoop--goto-line $num) (save-current-buffer (set-buffer helm-swoop-target-buffer) (delete-overlay helm-swoop-line-overlay) (helm-swoop--target-line-overlay-move)) (helm-swoop--recenter)) | |
(progn (select-window (car save-selected-window--state) (quote norecord)) (progn (helm-swoop--goto-line $num) (save-current-buffer (set-buffer helm-swoop-target-buffer) (delete-overlay helm-swoop-line-overlay) (helm-swoop--target-line-overlay-move)) (helm-swoop--recenter))) | |
(unwind-protect (progn (select-window (car save-selected-window--state) (quote norecord)) (progn (helm-swoop--goto-line $num) (save-current-buffer (set-buffer helm-swoop-target-buffer) (delete-overlay helm-swoop-line-overlay) (helm-swoop--target-line-overlay-move)) (helm-swoop--recenter))) (internal--after-with-selected-window save-selected-window--state)) | |
(save-current-buffer (unwind-protect (progn (select-window (car save-selected-window--state) (quote norecord)) (progn (helm-swoop--goto-line $num) (save-current-buffer (set-buffer helm-swoop-target-buffer) (delete-overlay helm-swoop-line-overlay) (helm-swoop--target-line-overlay-move)) (helm-swoop--recenter))) (internal--after-with-selected-window save-selected-window--state))) | |
(let ((save-selected-window--state (internal--before-with-selected-window helm-swoop-synchronizing-window))) (save-current-buffer (unwind-protect (progn (select-window (car save-selected-window--state) (quote norecord)) (progn (helm-swoop--goto-line $num) (save-current-buffer (set-buffer helm-swoop-target-buffer) (delete-overlay helm-swoop-line-overlay) (helm-swoop--target-line-overlay-move)) (helm-swoop--recenter))) (internal--after-with-selected-window save-selected-window--state)))) | |
(let* (($key (helm-swoop--get-string-at-line)) ($num (if (string-match "^[0-9]+" $key) (progn (string-to-number (match-string 0 $key)))))) (let ((save-selected-window--state (internal--before-with-selected-window helm-swoop-synchronizing-window))) (save-current-buffer (unwind-protect (progn (select-window (car save-selected-window--state) (quote norecord)) (progn (helm-swoop--goto-line $num) (save-current-buffer (set-buffer helm-swoop-target-buffer) (delete-overlay helm-swoop-line-overlay) (helm-swoop--target-line-overlay-move)) (helm-swoop--recenter))) (internal--after-with-selected-window save-selected-window--state)))) (setq helm-swoop-last-line-info (cons helm-swoop-target-buffer $num))) | |
(progn (select-window (car save-selected-window--state) (quote norecord)) (let* (($key (helm-swoop--get-string-at-line)) ($num (if (string-match "^[0-9]+" $key) (progn (string-to-number (match-string 0 $key)))))) (let ((save-selected-window--state (internal--before-with-selected-window helm-swoop-synchronizing-window))) (save-current-buffer (unwind-protect (progn (select-window (car save-selected-window--state) (quote norecord)) (progn (helm-swoop--goto-line $num) (save-current-buffer ... ... ...) (helm-swoop--recenter))) (internal--after-with-selected-window save-selected-window--state)))) (setq helm-swoop-last-line-info (cons helm-swoop-target-buffer $num)))) | |
(unwind-protect (progn (select-window (car save-selected-window--state) (quote norecord)) (let* (($key (helm-swoop--get-string-at-line)) ($num (if (string-match "^[0-9]+" $key) (progn (string-to-number ...))))) (let ((save-selected-window--state (internal--before-with-selected-window helm-swoop-synchronizing-window))) (save-current-buffer (unwind-protect (progn (select-window ... ...) (progn ... ... ...)) (internal--after-with-selected-window save-selected-window--state)))) (setq helm-swoop-last-line-info (cons helm-swoop-target-buffer $num)))) (internal--after-with-selected-window save-selected-window--state)) | |
(save-current-buffer (unwind-protect (progn (select-window (car save-selected-window--state) (quote norecord)) (let* (($key (helm-swoop--get-string-at-line)) ($num (if (string-match "^[0-9]+" $key) (progn ...)))) (let ((save-selected-window--state (internal--before-with-selected-window helm-swoop-synchronizing-window))) (save-current-buffer (unwind-protect (progn ... ...) (internal--after-with-selected-window save-selected-window--state)))) (setq helm-swoop-last-line-info (cons helm-swoop-target-buffer $num)))) (internal--after-with-selected-window save-selected-window--state))) | |
(let ((save-selected-window--state (internal--before-with-selected-window (helm-window)))) (save-current-buffer (unwind-protect (progn (select-window (car save-selected-window--state) (quote norecord)) (let* (($key (helm-swoop--get-string-at-line)) ($num (if ... ...))) (let ((save-selected-window--state ...)) (save-current-buffer (unwind-protect ... ...))) (setq helm-swoop-last-line-info (cons helm-swoop-target-buffer $num)))) (internal--after-with-selected-window save-selected-window--state)))) | |
helm-swoop--move-line-action() | |
helm-next-line(1) | |
call-interactively(helm-next-line nil nil) | |
read-from-minibuffer("Swoop: " #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) (keymap (C-M-left . paren-backward-sexp) (C-M-right . paren-forward-sexp) (94 . helm-swoop-caret-match) (menu-bar keymap (help-menu keymap (describe keymap (describe-mode . helm-help)))) (help keymap (109 . helm-help)) (f1 keymap (109 . helm-help)) (8 keymap (109 . helm-help) (104 . undefined) (8 . undefined) (4 . helm-debug-output)) (20 . helm-toggle-resplit-and-swap-windows) (C-tab . undefined) (triple-mouse-3 . ignore) (double-mouse-3 . ignore) (mouse-3 . ignore) (drag-mouse-3 . ignore) (down-mouse-3 . ignore) (triple-mouse-2 . ignore) (double-mouse-2 . ignore) (mouse-2 . ignore) (drag-mouse-2 . ignore) (down-mouse-2 . ignore) (triple-mouse-1 . ignore) (double-mouse-1 . ignore) (mouse-1 . ignore) (drag-mouse-1 . ignore) (down-mouse-1 . ignore) (67108897 . helm-toggle-suspend-update) (3 keymap (21 . helm-force-update) (6 . helm-follow-mode) (11 . helm-kill-selection-and-quit) (25 . helm-yank-selection) (4 . helm-delete-current-selection) (45 . helm-swap-windows)) (67108987 . helm-enlarge-window) (67108989 . helm-narrow-window) (19 . undefined) (18 . undefined) (23 . helm-yank-text-at-point) (24 keymap (2 . helm-resume-list-buffers-after-quit) (98 . helm-resume-previous-session-after-quit) (6 . helm-quit-and-find-file)) (11 . helm-delete-minibuffer-contents) (67108896 . helm-toggle-visible-mark) (0 . helm-toggle-visible-mark) (C-M-up . helm-scroll-other-window-down) (C-M-down . helm-scroll-other-window) (M-prior . helm-scroll-other-window-down) (M-next . helm-scroll-other-window) (12 . helm-recenter-top-bottom-other-window) (15 . helm-next-source) (10 . helm-select-3rd-action) (5 . helm-select-2nd-action-or-end-of-line) (26 . helm-execute-persistent-action) (9 . helm-select-action) (13 . helm-exit-minibuffer) (left . helm-previous-source) (right . helm-next-source) (7 . helm-keyboard-quit) ...) nil nil nil t) | |
helm-read-pattern-maybe("Swoop: " #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) "^368 " nil nil nil nil) | |
#[0 "\312\211\211\211\305\206\n (()*+,\313\314-\"\315\316\317\320\321\322!\323\"\324\325%DC\216\326\303\301\307\300$\210\327(!\210\330\331!\210\315\332DC\216\333\302\301\304\303\306\307\310\205P \3109\205P \310&\210*\210)?\205r \334\335\316\317\336\321\322\311!\337\"\340\325%\"\210\341\342\335\"\210\343 \330\344\345\346\347\"P!\210-\207" [((name . "git-gutter+.el") (init lambda nil (unless helm-swoop-cache (with-current-buffer (helm-candidate-buffer (quote local)) (insert "1 ;;; git-gutter+.el --- Manage Git hunks straight from the buffer\n\n3 ;; Copyright (C) 2013 by Syohei YOSHIDA and contributors\n\n5 ;; Author: Syohei YOSHIDA <syohex@gmail.com> and contributors\n6 ;; URL: https://github.com/nonsequitur/git-gutter-plus\n7 ;; Version: 0.1\n\n9 ;; This program is free software; you can redistribute it and/or modify\n10 ;; it under the terms of the GNU General Public License as published by\n11 ;; the Free Software Foundation, either version 3 of the License, or\n12 ;; (at your option) any later version.\n\n14 ;; This program is distributed in the hope that it will be useful,\n15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n17 ;; GNU General Public License for more details.\n\n19 ;; You should have received a copy of the GNU General Public License\n20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n22 ;; Package-Requires: ((git-commit-mode \"0.14\"))\n\n24 ;;; Commentary:\n25 ;;\n26 ;; View, stage and revert Git changes straight from the buffer.\n\n28 ;;; Code:\n\n30 (eval-when-compile\n31 (require 'cl))\n\n33 (require 'tramp)\n34 (require 'log-edit)\n35 (require 'git-commit-mode)\n\n37 (defgroup git-gutter+ nil\n38 \"Manage Git hunks straight from the buffer\"\n39 :prefix \"git-gutter+-\"\n40 :group 'vc)\n\n42 (defcustom git-gutter+-window-width nil\n43 \"Character width of the gutter margin. Set this variable if the automatically\n44 calculated width looks wrong. (This can happen with some special characters.)\"\n45 :type 'integer\n46 :group 'git-gutter+)\n\n48 (defcustom git-gutter+-git-executable \"git\"\n49 \"The path of the Git executable.\"\n50 :type 'string\n51 :group 'git-gutter+)\n\n53 (defcustom git-gutter+-diff-options nil\n54 \"List of strings containing extra arguments to 'git diff'\"\n55 :type 'list\n56 :group 'git-gutter+)\n\n58 (defcustom git-gutter+-separator-sign nil\n59 \"Separator sign\"\n60 :type 'string\n61 :group 'git-gutter+)\n\n63 (defcustom git-gutter+-modified-sign \"=\"\n64 \"Modified sign\"\n65 :type 'string\n66 :group 'git-gutter+)\n\n68 (defcustom git-gutter+-added-sign \"+\"\n69 \"Added sign\"\n70 :type 'string\n71 :group 'git-gutter+)\n\n73 (defcustom git-gutter+-deleted-sign \"-\"\n74 \"Deleted sign\"\n75 :type 'string\n76 :group 'git-gutter+)\n\n78 (defcustom git-gutter+-unchanged-sign nil\n79 \"Unchanged sign\"\n80 :type 'string\n81 :group 'git-gutter+)\n\n83 (defcustom git-gutter+-hide-gutter nil\n84 \"Hide gutter if there are no changes\"\n85 :type 'boolean\n86 :group 'git-gutter+)\n\n88 (defcustom git-gutter+-lighter \" GitGutter\"\n89 \"Minor mode lighter in mode-line\"\n90 :type 'string\n91 :group 'git-gutter+)\n\n93 (defface git-gutter+-separator\n94 '((t (:foreground \"cyan\" :weight bold)))\n95 \"Face of the separator\"\n96 :group 'git-gutter+)\n\n98 (defface git-gutter+-modified\n99 '((t (:foreground \"magenta\" :weight bold)))\n100 \"Face for modified lines\"\n101 :group 'git-gutter+)\n\n103 (defface git-gutter+-added\n104 '((t (:foreground \"green\" :weight bold)))\n105 \"Face for added lines\"\n106 :group 'git-gutter+)\n\n108 (defface git-gutter+-deleted\n109 '((t (:foreground \"red\" :weight bold)))\n110 \"Face for deleted lines\"\n111 :group 'git-gutter+)\n\n113 (defface git-gutter+-unchanged\n114 '((t (:background \"yellow\")))\n115 \"Face for unchanged lines\"\n116 :group 'git-gutter+)\n\n118 (defcustom git-gutter+-disabled-modes nil\n119 \"A list of modes for which `global-git-gutter+-mode' should be disabled.\"\n120 :type '(repeat symbol)\n121 :group 'git-gutter+)\n\n123 (defvar git-gutter+-mode-map\n124 (make-sparse-keymap))\n\n126 (defvar git-gutter+-view-diff-function nil\n127 \"Function to call for displaying diffs\")\n\n129 (defvar git-gutter+-clear-function nil\n130 \"Function to call for clearing the diff display\")\n\n132 (defvar git-gutter+-window-config-change-function nil\n133 \"Function to call when the buffer's local window configuration has changed\")\n\n135 (defvar git-gutter+-diffinfos nil)\n136 (defvar git-gutter+-diff-header nil)\n137 (make-variable-buffer-local 'git-gutter+-diffinfos)\n138 (make-variable-buffer-local 'git-gutter+-diff-header)\n\n140 (defvar git-gutter+-popup-buffer \"*git-gutter+-diff*\")\n141 (defvar git-gutter+-buffers-to-reenable nil)\n\n143 (defconst git-gutter+-hunk-header-regex\n144 ;; The same as diff-hunk-header-re-unified\n145 \"^@@ -\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? \\\\+\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? @@\")\n\n147 (defalias 'git-gutter+-popup-hunk 'git-gutter+-show-hunk)\n148 (defalias 'git-gutter+-revert-hunk 'git-gutter+-revert-hunks)\n\n150 (defmacro git-gutter+-awhen (test &rest body)\n151 \"Anaphoric when.\"\n152 (declare (indent 1))\n153 `(let ((it ,test))\n154 (when it ,@body)))\n\n156 (defun git-gutter+-enable-default-display-mode ()\n157 (setq git-gutter+-view-diff-function 'git-gutter+-view-diff-infos\n158 git-gutter+-clear-function 'git-gutter+-clear-diff-infos\n159 git-gutter+-window-config-change-function 'git-gutter+-show-gutter))\n\n161 (unless git-gutter+-view-diff-function\n162 (git-gutter+-enable-default-display-mode))\n\n164 (defun git-gutter+-call-git (args &optional file)\n165 (if (and file (file-remote-p file))\n166 (apply #'process-file git-gutter+-git-executable nil t nil args)\n167 (apply #'call-process git-gutter+-git-executable nil t nil args)))\n\n169 (defun git-gutter+-in-git-repository-p (file)\n170 (with-temp-buffer\n171 (let ((args '(\"rev-parse\" \"--is-inside-work-tree\")))\n172 (when (zerop (git-gutter+-call-git args file))\n173 (goto-char (point-min))\n174 (string= \"true\" (buffer-substring-no-properties\n175 (point) (line-end-position)))))))\n\n177 (defun git-gutter+-root-directory (file)\n178 (with-temp-buffer\n179 (let* ((args '(\"rev-parse\" \"--show-toplevel\"))\n180 (ret (git-gutter+-call-git args file)))\n181 (when (zerop ret)\n182 (goto-char (point-min))\n183 (let ((root (buffer-substring-no-properties (point) (line-end-position))))\n184 (unless (string= root \"\")\n185 (file-name-as-directory root)))))))\n\n187 (defsubst git-gutter+-diff-args (file)\n188 (delq nil (list \"--no-pager\" \"diff\" \"--no-color\" \"--no-ext-diff\" \"-U0\"\n189 git-gutter+-diff-options file)))\n\n191 (defun git-gutter+-diff (curfile)\n192 (let ((args (git-gutter+-diff-args curfile))\n193 (file (buffer-file-name))) ;; for tramp\n194 (with-temp-buffer\n195 (when (zerop (git-gutter+-call-git args file))\n196 (goto-char (point-min))\n197 (let ((diff-header (git-gutter+-get-diff-header))\n198 (diffinfos (git-gutter+-get-diffinfos)))\n199 (list diff-header diffinfos))))))\n\n201 (defun git-gutter+-get-diff-header ()\n202 (save-excursion\n203 (if (re-search-forward git-gutter+-hunk-header-regex nil t)\n204 (buffer-substring (point-min) (match-beginning 0)))))\n\n206 (defsubst git-gutter+-make-diffinfo (type content start end)\n207 (list :type type :content content :start-line start :end-line end))\n\n209 (defun git-gutter+-get-diffinfos ()\n210 (loop while (re-search-forward git-gutter+-hunk-header-regex nil t)\n211 ;; Hunk header format:\n212 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n213 for del-len = (string-to-number (or (match-string 2) \"1\"))\n214 for add-line = (string-to-number (match-string 3))\n215 for add-len = (string-to-number (or (match-string 4) \"1\"))\n216 for type = (cond ((zerop del-len) 'added)\n217 ((zerop add-len) 'deleted)\n218 (t 'modified))\n219 for start-line = (if (eq type 'deleted)\n220 (1+ add-line)\n221 add-line)\n222 for end-line = (if (eq type 'deleted)\n223 start-line\n224 (1- (+ add-line add-len)))\n225 for content = (git-gutter+-diff-content)\n226 collect\n227 (git-gutter+-make-diffinfo type content start-line end-line)))\n\n229 (defun git-gutter+-diff-content ()\n230 (save-excursion\n231 (goto-char (line-beginning-position)) ; Move to beginning of hunk header\n232 (let ((hunk-start (point)))\n233 ;; Move to end of hunk\n234 (forward-line 1)\n235 (if (re-search-forward \"^@@\" nil t)\n236 (backward-char 3) ;; exclude \"\\n@@\"\n237 (goto-char (1- (point-max)))) ; Skip trailing newline\n238 (buffer-substring hunk-start (point)))))\n\n240 (defun git-gutter+-line-to-pos (line)\n241 (save-excursion\n242 (goto-char (point-min))\n243 (forward-line (1- line))\n244 (point)))\n\n246 (defun git-gutter+-before-string (sign)\n247 (let* ((sep-sign git-gutter+-separator-sign)\n248 (sep (when sep-sign\n249 (propertize sep-sign 'face 'git-gutter+-separator)))\n250 (gutter-sep (concat sign sep)))\n251 (propertize \" \" 'display `((margin left-margin) ,gutter-sep))))\n\n253 (defsubst git-gutter+-select-face (type)\n254 (case type\n255 (added 'git-gutter+-added)\n256 (modified 'git-gutter+-modified)\n257 (deleted 'git-gutter+-deleted)))\n\n259 (defsubst git-gutter+-select-sign (type)\n260 (case type\n261 (added git-gutter+-added-sign)\n262 (modified git-gutter+-modified-sign)\n263 (deleted git-gutter+-deleted-sign)))\n\n265 (defun git-gutter+-propertized-sign (type)\n266 (let ((sign (git-gutter+-select-sign type))\n267 (face (git-gutter+-select-face type)))\n268 (propertize sign 'face face)))\n\n270 (defun git-gutter+-view-region (sign start-line end-line)\n271 (let ((beg (git-gutter+-line-to-pos start-line)))\n272 (goto-char beg)\n273 (while (and (<= (line-number-at-pos) end-line) (not (eobp)))\n274 (git-gutter+-view-at-pos sign (point))\n275 (forward-line 1))))\n\n277 (defun git-gutter+-view-at-pos (sign pos)\n278 (let ((ov (make-overlay pos pos)))\n279 (overlay-put ov 'before-string (git-gutter+-before-string sign))\n280 (overlay-put ov 'git-gutter+ t)))\n\n282 (defun git-gutter+-view-diff-info (diffinfo)\n283 (let* ((start-line (plist-get diffinfo :start-line))\n284 (end-line (plist-get diffinfo :end-line))\n285 (type (plist-get diffinfo :type))\n286 (sign (git-gutter+-propertized-sign type)))\n287 (case type\n288 ((modified added) (git-gutter+-view-region sign start-line end-line))\n289 (deleted (git-gutter+-view-at-pos\n290 sign (git-gutter+-line-to-pos start-line))))))\n\n292 (defun git-gutter+-sign-width (sign)\n293 (loop for s across sign\n294 sum (char-width s)))\n\n296 (defun git-gutter+-longest-sign-width ()\n297 (let ((signs (list git-gutter+-modified-sign\n298 git-gutter+-added-sign\n299 git-gutter+-deleted-sign)))\n300 (when git-gutter+-unchanged-sign\n301 (add-to-list 'signs git-gutter+-unchanged-sign))\n302 (+ (apply 'max (mapcar 'git-gutter+-sign-width signs))\n303 (git-gutter+-sign-width git-gutter+-separator-sign))))\n\n305 (defun git-gutter+-view-for-unchanged ()\n306 (save-excursion\n307 (let ((sign (if git-gutter+-unchanged-sign\n308 (propertize git-gutter+-unchanged-sign\n309 'face 'git-gutter+-unchanged)\n310 \" \")))\n311 (goto-char (point-min))\n312 (while (not (eobp))\n313 (git-gutter+-view-at-pos sign (point))\n314 (forward-line 1)))))\n\n316 (defun git-gutter+-set-window-margin (width)\n317 (let ((curwin (get-buffer-window)))\n318 (set-window-margins curwin width (cdr (window-margins curwin)))))\n\n320 (defsubst git-gutter+-file-buffer-p ()\n321 (and (buffer-file-name)\n322 default-directory\n323 (file-directory-p default-directory)))\n\n325 ;;;###autoload\n326 (define-minor-mode git-gutter+-mode\n327 \"Git-Gutter mode\"\n328 :group 'git-gutter+\n329 :init-value nil\n330 :global nil\n331 :lighter git-gutter+-lighter\n332 (if git-gutter+-mode\n333 (if (and (git-gutter+-file-buffer-p)\n334 (git-gutter+-in-git-repository-p (buffer-file-name)))\n335 (progn\n336 (git-gutter+-add-local-hooks)\n337 (git-gutter+-refresh))\n338 (if (called-interactively-p 'any)\n339 (message \"No Git repo for current buffer\"))\n340 (git-gutter+-mode -1))\n341 (git-gutter+-remove-local-hooks)\n342 (git-gutter+-clear)))\n\n344 (defun git-gutter+-add-local-hooks ()\n345 (add-hook 'after-save-hook 'git-gutter+-refresh nil t)\n346 ;; Turn off `git-gutter+-mode' while reverting to prevent any redundant calls to\n347 ;; `git-gutter+-refresh'.\n348 (add-hook 'before-revert-hook 'git-gutter+-turn-off nil t)\n349 (add-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change nil t)\n350 (if git-gutter+-window-config-change-function\n351 (add-hook 'window-configuration-change-hook\n352 git-gutter+-window-config-change-function nil t)))\n\n354 (defun git-gutter+-remove-local-hooks ()\n355 (remove-hook 'after-save-hook 'git-gutter+-refresh t)\n356 (remove-hook 'before-revert-hook 'git-gutter+-turn-off t)\n357 (remove-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change t)\n358 (if git-gutter+-window-config-change-function\n359 (remove-hook 'window-configuration-change-hook\n360 git-gutter+-window-config-change-function t)))\n\n362 (defmacro git-gutter+-in-all-buffers (&rest body)\n363 `(dolist (buf (buffer-list))\n364 (with-current-buffer buf\n365 ,@body)))\n\n367 ;; When `define-globalized-minor-mode' is used to define `global-git-gutter+-mode',\n368 ;; `git-gutter+-mode' and thus `git-gutter+-refresh' get run twice when a new file\n369 ;; is opened. (First for `fundamental-mode', then for the file-specific mode.)\n370 ;; The following definition of `global-git-gutter+-mode' avoids any redundant calls to\n371 ;; `git-gutter+-refresh'.\n\n373 ;;;###autoload\n374 (define-minor-mode global-git-gutter+-mode ()\n375 \"Global Git-Gutter mode\"\n376 :group 'git-gutter+\n377 :init-value nil\n378 :global t\n379 (if global-git-gutter+-mode\n380 (progn\n381 (add-hook 'find-file-hook 'git-gutter+-turn-on)\n382 (add-hook 'after-revert-hook 'git-gutter+-turn-on)\n383 (add-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n384 (git-gutter+-in-all-buffers (git-gutter+-turn-on)))\n385 (remove-hook 'find-file-hook 'git-gutter+-turn-on)\n386 (remove-hook 'after-revert-hook 'git-gutter+-turn-on)\n387 (remove-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n388 (git-gutter+-in-all-buffers (git-gutter+-turn-off))))\n\n390 (defun git-gutter+-turn-on ()\n391 (when (and (buffer-file-name)\n392 (not (memq major-mode git-gutter+-disabled-modes))\n393 (not git-gutter+-mode))\n394 (git-gutter+-mode t)))\n\n396 (defun git-gutter+-turn-off ()\n397 (if git-gutter+-mode (git-gutter+-mode -1)))\n\n399 (defun git-gutter+-reenable-after-major-mode-change ()\n400 (if global-git-gutter+-mode\n401 (add-to-list 'git-gutter+-buffers-to-reenable (current-buffer))))\n\n403 (defun git-gutter+-reenable-buffers ()\n404 (dolist (buf git-gutter+-buffers-to-reenable)\n405 (with-current-buffer buf\n406 (git-gutter+-turn-on)))\n407 (setq git-gutter+-buffers-to-reenable nil))\n\n409 (defsubst git-gutter+-show-gutter-p (diffinfos)\n410 (if git-gutter+-hide-gutter\n411 (or diffinfos git-gutter+-unchanged-sign)\n412 (or global-git-gutter+-mode git-gutter+-unchanged-sign diffinfos)))\n\n414 (defun git-gutter+-show-gutter (&optional diffinfos)\n415 (when (git-gutter+-show-gutter-p (or diffinfos git-gutter+-diffinfos))\n416 (let ((win-width (or git-gutter+-window-width\n417 (git-gutter+-longest-sign-width))))\n418 (git-gutter+-set-window-margin win-width))))\n\n420 (defun git-gutter+-view-diff-infos (diffinfos)\n421 (when (or git-gutter+-unchanged-sign\n422 git-gutter+-separator-sign)\n423 (git-gutter+-view-for-unchanged))\n424 (when diffinfos\n425 (save-excursion\n426 (mapc 'git-gutter+-view-diff-info diffinfos)))\n427 (git-gutter+-show-gutter diffinfos))\n\n429 (defsubst git-gutter+-reset-window-margin-p ()\n430 (or git-gutter+-hide-gutter\n431 (not global-git-gutter+-mode)))\n\n433 (defun git-gutter+-clear-diff-infos ()\n434 (when (git-gutter+-reset-window-margin-p)\n435 (git-gutter+-set-window-margin 0))\n436 (remove-overlays (point-min) (point-max) 'git-gutter+ t))\n\n438 (defun git-gutter+-process-diff (curfile)\n439 (destructuring-bind\n440 (diff-header diffinfos) (git-gutter+-diff curfile)\n441 (setq git-gutter+-diff-header diff-header\n442 git-gutter+-diffinfos diffinfos)\n443 (save-restriction\n444 (widen)\n445 (funcall git-gutter+-view-diff-function diffinfos))))\n\n447 (defun git-gutter+-search-near-diff-index (diffinfos is-reverse)\n448 (loop with current-line = (line-number-at-pos)\n449 with cmp-fn = (if is-reverse '> '<)\n450 for diffinfo in (if is-reverse (reverse diffinfos) diffinfos)\n451 for index = 0 then (1+ index)\n452 for start-line = (plist-get diffinfo :start-line)\n453 when (funcall cmp-fn current-line start-line)\n454 return (if is-reverse\n455 (1- (- (length diffinfos) index))\n456 index)))\n\n458 (defun git-gutter+-diffinfo-at-point ()\n459 (save-restriction\n460 (widen)\n461 (loop with current-line = (line-number-at-pos)\n462 for diffinfo in git-gutter+-diffinfos\n463 for start = (plist-get diffinfo :start-line)\n464 for end = (or (plist-get diffinfo :end-line) (1+ start))\n465 when (and (>= current-line start) (<= current-line end))\n466 return diffinfo)))\n\n468 (defun git-gutter+-collect-deleted-line (str)\n469 (with-temp-buffer\n470 (insert str)\n471 (goto-char (point-min))\n472 (loop while (re-search-forward \"^-\\\\(.*?\\\\)$\" nil t)\n473 collect (match-string 1) into deleted-lines\n474 finally return deleted-lines)))\n\n476 (defun git-gutter+-delete-added-lines (start-line end-line)\n477 (forward-line (1- start-line))\n478 (let ((start-point (point)))\n479 (forward-line (1+ (- end-line start-line)))\n480 (delete-region start-point (point))))\n\n482 (defun git-gutter+-insert-deleted-lines (content)\n483 (dolist (line (git-gutter+-collect-deleted-line content))\n484 (insert (concat line \"\\n\"))))\n\n486 (defun git-gutter+-do-revert-hunk (diffinfo)\n487 (save-excursion\n488 (save-restriction\n489 (widen)\n490 (goto-char (point-min))\n491 (let ((start-line (plist-get diffinfo :start-line))\n492 (end-line (plist-get diffinfo :end-line))\n493 (content (plist-get diffinfo :content)))\n494 (case (plist-get diffinfo :type)\n495 (added (git-gutter+-delete-added-lines start-line end-line))\n496 (deleted (forward-line (1- start-line))\n497 (git-gutter+-insert-deleted-lines content))\n498 (modified (git-gutter+-delete-added-lines start-line end-line)\n499 (git-gutter+-insert-deleted-lines content)))))))\n\n501 (defun git-gutter+-revert-hunks ()\n502 \"Revert hunk at point. If region is active, revert all hunks within the region.\"\n503 (interactive)\n504 (let* ((diffinfos (git-gutter+-selected-diffinfos))\n505 (one-diffinfo-p (= 1 (length diffinfos))))\n506 (save-window-excursion\n507 (if one-diffinfo-p (git-gutter+-show-hunk (car diffinfos)))\n508 (when (and diffinfos\n509 (yes-or-no-p (if one-diffinfo-p\n510 \"Revert hunk?\"\n511 (format \"Revert %d hunks?\" (length diffinfos)))))\n512 ;; Revert diffinfos in reverse so that earlier hunks don't invalidate the\n513 ;; line number information of the later hunks.\n514 (dolist (diffinfo (nreverse diffinfos))\n515 (git-gutter+-do-revert-hunk diffinfo))\n516 (save-buffer))\n517 (if one-diffinfo-p\n518 (git-gutter+-awhen (get-buffer git-gutter+-popup-buffer)\n519 (kill-buffer it))))))\n\n521 (defun git-gutter+-show-hunk (&optional diffinfo)\n522 \"Show hunk at point in another window\"\n523 (interactive)\n524 (git-gutter+-awhen (or diffinfo\n525 (git-gutter+-diffinfo-at-point))\n526 (save-selected-window\n527 (with-current-buffer (get-buffer-create git-gutter+-popup-buffer)\n528 (setq buffer-read-only nil)\n529 (erase-buffer)\n530 (insert (plist-get it :content))\n531 (insert \"\\n\")\n532 (goto-char (point-min))\n533 (diff-mode)\n534 (view-mode)\n535 (pop-to-buffer (current-buffer))))))\n\n537 (defun git-gutter+-next-hunk (arg)\n538 \"Move to next diff hunk\"\n539 (interactive \"p\")\n540 (if (not git-gutter+-diffinfos)\n541 (message \"No changes in buffer\")\n542 (save-restriction\n543 (widen)\n544 (let* ((is-reverse (< arg 0))\n545 (diffinfos git-gutter+-diffinfos)\n546 (len (length diffinfos))\n547 (index (git-gutter+-search-near-diff-index diffinfos is-reverse))\n548 (real-index (if index\n549 (let ((next (if is-reverse (1+ index) (1- index))))\n550 (mod (+ arg next) len))\n551 (if is-reverse (1- (length diffinfos)) 0)))\n552 (diffinfo (nth real-index diffinfos)))\n553 (goto-char (point-min))\n554 (forward-line (1- (plist-get diffinfo :start-line)))\n555 (when (buffer-live-p (get-buffer git-gutter+-popup-buffer))\n556 (save-window-excursion\n557 (git-gutter+-show-hunk)))))))\n\n559 (defun git-gutter+-previous-hunk (arg)\n560 \"Move to previous diff hunk\"\n561 (interactive \"p\")\n562 (git-gutter+-next-hunk (- arg)))\n\n564 (defun git-gutter+-remote-default-directory (dir file)\n565 (let* ((vec (tramp-dissect-file-name file))\n566 (method (aref vec 0))\n567 (user (aref vec 1))\n568 (host (aref vec 2)))\n569 (format \"/%s:%s%s:%s\" method (if user (concat user \"@\") \"\") host dir)))\n\n571 (defun git-gutter+-remote-file-path (dir file)\n572 (let ((file (aref (tramp-dissect-file-name file) 3)))\n573 (replace-regexp-in-string (concat \"\\\\`\" dir) \"\" file)))\n\n575 (defun git-gutter+-local-file-path (file)\n576 (if (eq system-type 'windows-nt)\n577 ;; Cygwin can't handle Windows absolute paths\n578 (file-relative-name file default-directory)\n579 file))\n\n581 (defun git-gutter+-refresh ()\n582 (git-gutter+-clear)\n583 (let ((file (buffer-file-name)))\n584 (when (and file (file-exists-p file))\n585 (if (file-remote-p file)\n586 (let* ((repo-root (git-gutter+-root-directory file))\n587 (default-directory (git-gutter+-remote-default-directory repo-root file)))\n588 (git-gutter+-process-diff (git-gutter+-remote-file-path repo-root file)))\n589 (git-gutter+-process-diff (git-gutter+-local-file-path file))))))\n\n591 (defun git-gutter+-clear ()\n592 (save-restriction\n593 (widen)\n594 (funcall git-gutter+-clear-function))\n595 (setq git-gutter+-diffinfos nil))\n\n\n598 ;;; Staging\n\n600 (defun git-gutter+-stage-hunks ()\n601 \"Stage hunk at point. If region is active, stage all hunk lines within the region.\"\n602 (interactive)\n603 (let* ((line-range (if (use-region-p)\n604 (cons (line-number-at-pos (region-beginning))\n605 (line-number-at-pos (region-end)))))\n606 (diffinfos (git-gutter+-selected-diffinfos line-range)))\n607 (when diffinfos\n608 (let ((error-msg (git-gutter+-stage-diffinfos diffinfos line-range)))\n609 (if error-msg\n610 (message \"Error staging hunks:\\n%s\" error-msg))\n611 (git-gutter+-refresh)))))\n\n613 (defun git-gutter+-selected-diffinfos (&optional line-range)\n614 (unless line-range\n615 (setq line-range (if (use-region-p)\n616 (cons (line-number-at-pos (region-beginning))\n617 (line-number-at-pos (region-end))))))\n618 (if line-range\n619 (git-gutter+-diffinfos-between-lines line-range)\n620 (git-gutter+-awhen (git-gutter+-diffinfo-at-point)\n621 (list it))))\n\n623 (defsubst git-gutter+-diffinfo-between-lines-p (diffinfo start-line end-line)\n624 (let ((diff-start (plist-get diffinfo :start-line))\n625 (diff-end (plist-get diffinfo :end-line)))\n626 (and (<= start-line diff-end)\n627 (<= diff-start end-line))))\n\n629 (defun git-gutter+-diffinfos-between-lines (line-range)\n630 (save-restriction\n631 (widen)\n632 (let ((start-line (car line-range))\n633 (end-line (cdr line-range)))\n634 (delq nil\n635 (mapcar (lambda (diffinfo)\n636 (if (git-gutter+-diffinfo-between-lines-p\n637 diffinfo start-line end-line)\n638 diffinfo))\n639 git-gutter+-diffinfos)))))\n\n641 (defun git-gutter+-stage-diffinfos (diffinfos line-range)\n642 (let ((header git-gutter+-diff-header))\n643 (with-temp-buffer\n644 (insert header)\n645 ;; Insert hunks in reverse so that earlier hunks don't invalidate the line\n646 ;; number information of the later hunks.\n647 (dolist (diffinfo (nreverse diffinfos))\n648 (git-gutter+-insert-diffinfo diffinfo line-range)\n649 (goto-char (point-max)))\n650 (git-gutter+-call-git-on-current-buffer\n651 '(\"apply\" \"--unidiff-zero\" \"--cached\" \"-\")))))\n\n653 (defun git-gutter+-insert-diffinfo (diffinfo line-range)\n654 (let ((content (plist-get diffinfo :content))\n655 (type (plist-get diffinfo :type)))\n656 (if (not line-range)\n657 (git-gutter+-insert-hunk content type)\n658 (let ((diff-start-line (plist-get diffinfo :start-line))\n659 (diff-end-line (plist-get diffinfo :end-line))\n660 (start-line (car line-range))\n661 (end-line (cdr line-range)))\n662 (git-gutter+-insert-hunk content type\n663 (1+ (- start-line diff-start-line))\n664 (1+ (- end-line diff-start-line)))))))\n\n666 (defun git-gutter+-call-git-on-current-buffer (args)\n667 \"Sends the current buffer contents to Git and replaces them with Git's output.\n\n669 RETURNS nil if Git ran successfully. Returns an error description otherwise.\"\n670 (unless (zerop (apply #'call-process-region (point-min) (point-max)\n671 git-gutter+-git-executable t t nil args))\n672 (buffer-string)))\n\n674 (defsubst git-gutter+-read-hunk-header (hunk)\n675 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n676 (string-match git-gutter+-hunk-header-regex hunk)\n677 (list (string-to-number (match-string 1 hunk))\n678 (string-to-number (or (match-string 2 hunk) \"1\"))\n679 (string-to-number (match-string 3 hunk))\n680 (string-to-number (or (match-string 4 hunk) \"1\"))))\n\n682 (defun git-gutter+-insert-hunk (hunk type &optional start end)\n683 \"If START and END are provided, only insert addition (+) lines between\n684 START and END (inclusive). START and END are both line numbers starting with 1.\"\n685 (destructuring-bind\n686 (del-line del-len add-line add-len) (git-gutter+-read-hunk-header hunk)\n687 (let* ((start (max 1 (or start 1)))\n688 (end (min add-len (or end add-len)))\n689 (insert-all-p (or (eq type :deleted)\n690 (and (= start 1) (= end add-len))))\n691 (num-lines-selected (if insert-all-p\n692 add-len\n693 (1+ (- end start)))))\n694 ;; When the user selected the last lines of a hunk with type `modified' (but\n695 ;; not the complete hunk), then don't insert any deletion (-) lines from that\n696 ;; hunk.\n697 (if (and (eq type 'modified)\n698 (> start 1) (= end add-len))\n699 (setq type 'modified-trailing))\n\n701 (save-excursion\n702 (insert hunk \"\\n\"))\n\n704 (git-gutter+-delete-hunk-header)\n\n706 (if (not insert-all-p)\n707 (git-gutter+-modify-hunk type num-lines-selected del-len start))\n\n709 (let ((hunk-header (git-gutter+-make-hunk-header type num-lines-selected\n710 del-line del-len add-line)))\n711 (insert hunk-header \"\\n\")))))\n\n713 (defun git-gutter+-delete-hunk-header ()\n714 (let ((hunk-start (point)))\n715 (forward-line 1)\n716 (delete-region hunk-start (point))))\n\n718 (defun git-gutter+-modify-hunk (type num-lines-selected del-len start)\n719 \"Remove all addition (+) lines from hunk that aren't selected.\n720 If TYPE is not `modified', also remove all deletion (-) lines.\"\n721 (let ((first-line-selected (+ del-len (1- start)))\n722 selected-lines)\n723 (save-excursion\n724 (forward-line first-line-selected)\n725 (let ((selection-start (point)))\n726 (forward-line num-lines-selected)\n727 (setq selected-lines (buffer-substring selection-start (point)))))\n728 (save-excursion\n729 (if (eq type 'modified) (forward-line del-len)) ; skip over deletion (-) lines\n730 (delete-region (point) (point-max))\n731 (insert selected-lines))))\n\n733 (defun git-gutter+-make-hunk-header (type num-lines-selected del-line del-len add-line)\n734 (let ((add-len num-lines-selected))\n735 (case type\n736 (added (setq add-line (1+ del-line)))\n737 (modified-trailing (setq add-line (+ del-line del-len)\n738 del-line (1- add-line)\n739 del-len 0))\n740 (t (setq add-line del-line)))\n741 (format \"@@ -%d,%d +%d,%d @@\"\n742 del-line del-len\n743 add-line add-len)))\n\n\n746 ;;; Committing\n747 ;; This section draws heavily from old Magit source code.\n\n749 (defvar git-gutter+-pre-commit-window-config nil)\n750 (defvar git-gutter+-commit-origin-buffer nil\n751 \"Buffer that started the commit\")\n\n753 (defconst git-gutter+-commit-buffer-name \"*Commit Message*\")\n754 (defconst git-gutter+-staged-changes-buffer-name \"*Staged Changes*\")\n\n756 ;;;###autoload\n757 (defun git-gutter+-commit ()\n758 \"Commit staged changes. If nothing is staged, ask to stage the current buffer.\"\n759 (interactive)\n\n761 (when (and (not (git-gutter+-anything-staged-p))\n762 git-gutter+-diffinfos\n763 (y-or-n-p \"Nothing staged. Stage current buffer? \"))\n764 (git-gutter+-stage-whole-buffer))\n\n766 (let ((file (buffer-file-name))\n767 (dir default-directory))\n768 (git-gutter+-save-window-config-if-needed)\n769 (setq git-gutter+-commit-origin-buffer (current-buffer))\n770 (git-gutter+-open-commit-edit-buffer dir)\n771 (git-gutter+-show-staged-changes file dir)))\n\n773 (defun git-gutter+-stage-and-commit ()\n774 (interactive)\n775 (git-gutter+-stage-hunks)\n776 (git-gutter+-commit))\n\n778 (defun git-gutter+-save-window-config-if-needed ()\n779 ;; Only save the window config if the temporary buffers that get popped-up by\n780 ;; git-gutter+ are not already visible.\n781 ;; In this way, `git-gutter+-commit' can be called twice in a row without\n782 ;; losing the original window config.\n783 (when (not (and git-gutter+-pre-commit-window-config\n784 (get-buffer-window git-gutter+-commit-buffer-name)\n785 (get-buffer-window git-gutter+-staged-changes-buffer-name)))\n786 (setq git-gutter+-pre-commit-window-config (current-window-configuration))))\n\n788 (defun git-gutter+-open-commit-edit-buffer (dir)\n789 \"Opens a buffer for composing the commit message\"\n790 (pop-to-buffer (get-buffer-create git-gutter+-commit-buffer-name))\n791 (setq default-directory dir)\n792 (git-gutter+-commit-mode)\n793 (message \"Type C-c C-c to commit (C-c C-k to cancel).\"))\n\n795 (defsubst git-gutter+-pop-to-staged-changes-buffer ()\n796 (let* ((buf (get-buffer-create git-gutter+-staged-changes-buffer-name))\n797 (window (get-buffer-window buf)))\n798 (if window\n799 ;; Buffer is already visible\n800 (select-window window)\n801 (if (<= (length (window-list)) 2)\n802 (split-window))\n803 (pop-to-buffer buf))))\n\n805 (defun git-gutter+-show-staged-changes (file dir)\n806 (save-selected-window\n807 (git-gutter+-pop-to-staged-changes-buffer)\n808 (setq buffer-read-only nil)\n809 (erase-buffer)\n810 (let ((default-directory dir))\n811 (git-gutter+-call-git '(\"diff\" \"--staged\") file))\n812 (goto-char (point-min))\n813 (diff-mode)\n814 (view-mode)))\n\n816 (defsubst git-gutter+-abort-commit-when-no-changes (allow-empty amend)\n817 (unless (or amend\n818 allow-empty\n819 (git-gutter+-anything-staged-p))\n820 (error\n821 \"Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?\"\n822 (key-description (car (where-is-internal\n823 'git-gutter+-commit-toggle-amending)))\n824 (key-description (car (where-is-internal\n825 'git-gutter+-commit-toggle-allow-empty))))))\n\n827 (defsubst git-gutter+-buffer-is-whitespace ()\n828 (save-excursion\n829 (goto-char (point-min))\n830 (looking-at-p \"[ \\t\\n]*\\\\'\")))\n\n832 (defun git-gutter+-publish-commit ()\n833 \"Publish commit\"\n834 (interactive)\n835 (let* ((fields (git-gutter+-commit-get-fields))\n836 (amend (equal \"yes\" (git-gutter+-commit-get-field 'amend fields)))\n837 (allow-empty (equal \"yes\" (git-gutter+-commit-get-field 'allow-empty fields)))\n838 (author (git-gutter+-commit-get-field 'author fields))\n839 (date (git-gutter+-commit-get-field 'date fields)))\n\n841 (git-gutter+-abort-commit-when-no-changes allow-empty amend)\n\n843 (git-gutter+-push-to-comment-ring (buffer-string))\n\n845 (git-gutter+-commit-set-fields nil) ; Delete message header\n\n847 (when (git-gutter+-buffer-is-whitespace)\n848 (erase-buffer)\n849 (insert \"(Empty description)\"))\n\n851 (let ((error-msg (git-gutter+-call-git-on-current-buffer\n852 (append '(\"--no-pager\" \"commit\" \"-F\" \"-\")\n853 (if amend '(\"--amend\"))\n854 (if allow-empty '(\"--allow-empty\"))\n855 (if author (list (concat \"--author=\" author)))\n856 (if date (list (concat \"--date=\" date)))))))\n857 (if error-msg\n858 (progn\n859 (message \"Commit error:\\n%s\" error-msg)\n860 (erase-buffer)\n861 (insert (ring-ref log-edit-comment-ring 0))) ; Reinsert commit message\n862 (message \"Commit successful.\")\n863 (git-gutter+-close-commit-edit-buffer)\n864 (git-gutter+-update-vc-modeline)))))\n\n866 (defun git-gutter+-close-commit-edit-buffer ()\n867 \"Abort edits and discard commit message being composed.\"\n868 (interactive)\n869 (kill-buffer)\n870 (set-window-configuration git-gutter+-pre-commit-window-config))\n\n872 (defun git-gutter+-update-vc-modeline ()\n873 (when (buffer-live-p git-gutter+-commit-origin-buffer)\n874 (with-current-buffer git-gutter+-commit-origin-buffer\n875 ;; Updating the modeline has no effect if the buffer still has\n876 ;; changes - it will remain in the 'modified' state. So skip it then.\n877 (unless git-gutter+-diffinfos\n878 (ignore-errors (vc-find-file-hook))))))\n\n880 (defun git-gutter+-stage-whole-buffer ()\n881 (save-excursion\n882 (mark-whole-buffer)\n883 (git-gutter+-stage-hunks)))\n\n885 (defun git-gutter+-anything-staged-p ()\n886 \"Return t if the current repo has staged changes\"\n887 (not (zerop (git-gutter+-call-git '(\"diff\" \"--quiet\" \"--cached\")))))\n\n889 (defun git-gutter+-commit-toggle-amending ()\n890 \"Toggle whether this will be an amendment to the previous commit.\n891 \\(i.e., whether commit is run via 'git commit --amend')\"\n892 (interactive)\n893 ;; Remove the newline that 'git-commit-mode' adds to a new commit\n894 ;; message buffer by default. This prevents an ugly visual\n895 ;; gap between the commit message header and the previous commit\n896 ;; message.\n897 (when (git-gutter+-buffer-is-whitespace)\n898 (erase-buffer))\n\n900 (let ((amend-was-already-set (git-gutter+-commit-get-field 'amend)))\n901 (git-gutter+-commit-toggle-field 'amend t)\n902 (unless amend-was-already-set\n903 ;; Insert previous commit message\n904 (goto-char (point-max))\n905 (unless (zerop (current-column))\n906 (insert \"\\n\"))\n907 (insert (git-gutter+-get-last-commit-msg)\n908 \"\\n\"))))\n\n910 (defun git-gutter+-commit-toggle-allow-empty ()\n911 \"Toggle whether this commit is allowed to be empty.\n912 \\(i.e., whether commit is run via 'git commit --allow-empty')\"\n913 (interactive)\n914 (git-gutter+-commit-toggle-field 'allow-empty t))\n\n916 (defun git-gutter+-format-author (author email)\n917 (format \"%s <%s>\" author email))\n\n919 (defun git-gutter+-commit-toggle-author ()\n920 \"Toggle whether this commit should have a user-defined author.\"\n921 (interactive)\n922 (git-gutter+-commit-toggle-input\n923 'author (git-gutter+-format-author\n924 (or (git-gutter+-get-cfg \"user\" \"name\") \"Author Name\")\n925 (or (git-gutter+-get-cfg \"user\" \"email\") \"author@email\"))))\n\n927 (defun git-gutter+-commit-toggle-date ()\n928 \"Toggle whether this commit should have a user-defined date.\"\n929 (interactive)\n930 (git-gutter+-commit-toggle-input 'date\n931 ;; ISO 8601\n932 (format-time-string \"%Y-%m-%dT%T%z\" (current-time))))\n\n934 (defun git-gutter+-push-to-comment-ring (comment)\n935 (when (or (ring-empty-p log-edit-comment-ring)\n936 (not (equal comment (ring-ref log-edit-comment-ring 0))))\n937 (ring-insert log-edit-comment-ring comment)))\n\n939 (defun git-gutter+-get-last-commit-msg ()\n940 (git-gutter+-git-output '(\"log\" \"--max-count=1\" \"--pretty=format:%s%n%n%b\" \"HEAD\")))\n\n942 (defun git-gutter+-get-cfg (&rest keys)\n943 (git-gutter+-git-output (list \"config\" (mapconcat 'identity keys \".\"))))\n\n945 (defun git-gutter+-git-output (args)\n946 (with-temp-buffer\n947 (git-gutter+-call-git args)\n948 ;; Delete trailing newlines\n949 (goto-char (point-min))\n950 (if (re-search-forward \"\\n+\\\\'\" nil t)\n951 (replace-match \"\"))\n952 (buffer-string)))\n\n\n955 ;;; Commit message header\n\n957 (defconst git-gutter+-commit-header-end \"-- End of commit options header --\\n\")\n\n959 (defun git-gutter+-commit-get-field (name &optional fields)\n960 (cdr (assq name (or fields (git-gutter+-commit-get-fields)))))\n\n962 (defun git-gutter+-commit-set-field (name value)\n963 (let* ((fields (git-gutter+-commit-get-fields))\n964 (cell (assq name fields)))\n965 (cond (cell\n966 (if value\n967 (rplacd cell value)\n968 (setq fields (delq cell fields))))\n969 (t\n970 (if value\n971 (setq fields (append fields (list (cons name value)))))))\n972 (git-gutter+-commit-set-fields fields)))\n\n974 (defun git-gutter+-commit-toggle-field (name default)\n975 \"Toggle the commit header field named NAME.\n976 If it's currently unset, set it to DEFAULT (t or nil).\"\n977 (let* ((fields (git-gutter+-commit-get-fields))\n978 (cell (assq name fields)))\n979 (if cell\n980 (rplacd cell (if (equal (cdr cell) \"yes\") \"no\" \"yes\"))\n981 (setq fields (acons name (if default \"yes\" \"no\") fields)))\n982 (git-gutter+-commit-set-fields fields)))\n\n984 (defun git-gutter+-commit-toggle-input (name default)\n985 \"Toggle the commit header input named NAME.\n986 If it's currently unset, set it to DEFAULT (a string). If it is\n987 set remove it.\"\n988 (let* ((fields (git-gutter+-commit-get-fields))\n989 (cell (assq name fields)))\n990 (if cell\n991 (setq fields (assq-delete-all name fields))\n992 (setq fields (acons name default fields)))\n993 (git-gutter+-commit-set-fields fields)))\n\n995 (defun git-gutter+-commit-get-fields ()\n996 (let (result)\n997 (goto-char (point-min))\n998 (while (looking-at \"^\\\\([A-Za-z0-9-_]+\\\\): *\\\\(.+\\\\)?$\")\n999 (let ((name (intern (downcase (match-string 1))))\n1000 (value (read (or (match-string 2) \"nil\"))))\n1001 (push (cons name value) result))\n1002 (forward-line))\n1003 (if (looking-at (regexp-quote git-gutter+-commit-header-end))\n1004 (nreverse result))))\n\n1006 (defun git-gutter+-commit-set-fields (fields)\n1007 (goto-char (point-min))\n1008 ;; Delete commit header\n1009 (if (search-forward-regexp (format \"^\\\\(?:[A-Za-z0-9-_]+:.*\\n\\\\)*%s\"\n1010 (regexp-quote git-gutter+-commit-header-end))\n1011 nil t)\n1012 (delete-region (match-beginning 0) (match-end 0)))\n1013 (goto-char (point-min))\n1014 (when fields\n1015 (dolist (field fields)\n1016 (insert (capitalize (symbol-name (car field))) \": \"\n1017 (prin1-to-string (cdr field)) \"\\n\"))\n1018 (insert git-gutter+-commit-header-end)))\n\n\n1021 ;;; git-gutter+-commit-mode\n1022 ;; Like git-commit-mode, but adds keybindings to git-gutter+ commands and\n1023 ;; highlighting support for the commit message header.\n\n1025 (define-derived-mode git-gutter+-commit-mode git-commit-mode \"Git-Gutter-Commit\"\n1026 (setq font-lock-defaults (list (git-gutter+-commit-font-lock-keywords) t)))\n\n1028 (setq git-gutter+-commit-mode-map\n1029 (let ((map (copy-keymap git-commit-mode-map)))\n1030 (define-key map (kbd \"C-c C-c\") 'git-gutter+-publish-commit)\n1031 (define-key map (kbd \"C-c C-k\") 'git-gutter+-close-commit-edit-buffer)\n1032 (define-key map (kbd \"C-c C-a\") 'git-gutter+-commit-toggle-amending)\n1033 (define-key map (kbd \"C-c C-e\") 'git-gutter+-commit-toggle-allow-empty)\n1034 (define-key map (kbd \"C-c C-u\") 'git-gutter+-commit-toggle-author)\n1035 (define-key map (kbd \"C-c C-d\") 'git-gutter+-commit-toggle-date)\n1036 (define-key map (kbd \"C-c C-b\") 'git-commit-ack)\n1037 (define-key map (kbd \"M-p\") 'log-edit-previous-comment)\n1038 (define-key map (kbd \"M-n\") 'log-edit-next-comment)\n1039 map))\n\n1041 (defface git-gutter+-commit-header-face\n1042 '((t :inherit font-lock-comment-face))\n1043 \"Highlights the commit message header\"\n1044 :group 'git-gutter+-faces)\n\n1046 (defconst git-gutter+-commit-header-regex\n1047 (concat \"\\\\(?:.\\\\|\\n\\\\)*?\" (regexp-quote git-gutter+-commit-header-end)))\n\n1049 (defconst git-gutter+-skip-commit-header-regex\n1050 (concat \"\\\\`\\\\(?:\" git-gutter+-commit-header-regex \"\\\\)?\"))\n\n1052 ;; Modify git-commit-summary-regexp to ignore the commit header\n1053 (defadvice git-commit-summary-regexp\n1054 (after ignore-git-gutter+-commit-header activate compile)\n1055 (if (eq major-mode 'git-gutter+-commit-mode)\n1056 (setq ad-return-value\n1057 (concat git-gutter+-skip-commit-header-regex\n1058 (substring ; Remove leading \"\\\\`\"\n1059 ad-return-value 2)))))\n\n1061 (defun git-gutter+-commit-font-lock-keywords ()\n1062 \"Like `git-commit-mode-font-lock-keywords' but with commit header highlighting\"\n1063 `((,(concat \"\\\\`\" git-gutter+-commit-header-regex) . 'git-gutter+-commit-header-face)\n1064 ,@(git-commit-mode-font-lock-keywords)))\n\n\n1067 ;;; Magit synchronization\n1068 ;; Force Magit to refresh git-gutter+ when updating the VC mode line.\n\n1070 (defvar git-gutter+-orig-vc-find-file-hook)\n\n1072 (defvar git-gutter+-vc-find-file-hook-with-refresh\n1073 (lambda ()\n1074 (funcall git-gutter+-orig-vc-find-file-hook)\n1075 (if git-gutter+-mode (git-gutter+-refresh))))\n\n1077 (defadvice magit-update-vc-modeline (around refresh-git-gutter+ compile activate)\n1078 ;; `magit-update-vc-modeline' calls `vc-find-file-hook' (a function!) on each\n1079 ;; buffer in the repo. Temporarily rebind it to `vc-find-file-hook-with-refresh',\n1080 ;; which calls git-gutter+-refresh after updating the VC mode line.\n1081 ;;\n1082 ;; Using `flet' would have been much simpler, but it's deprecated since 24.3.\n1083 (setq git-gutter+-orig-vc-find-file-hook (symbol-function 'vc-find-file-hook))\n1084 (fset 'vc-find-file-hook git-gutter+-vc-find-file-hook-with-refresh)\n1085 (unwind-protect\n1086 ad-do-it\n1087 (fset 'vc-find-file-hook git-gutter+-orig-vc-find-file-hook)))\n\n1089 (provide 'git-gutter+)\n\n1091 ;;; git-gutter+.el ends here\n")) (setq helm-swoop-cache t))) (candidates-in-buffer) (get-line . buffer-substring-no-properties) (keymap keymap (C-M-left . backward-sexp) (C-M-right . forward-sexp) (27 keymap (105 . helm-multi-swoop-all-from-helm-swoop)) (3 keymap (5 . helm-swoop-edit)) keymap (C-M-left . paren-backward-sexp) (C-M-right . paren-forward-sexp) (94 . helm-swoop-caret-match) (menu-bar keymap (help-menu keymap (describe keymap (describe-mode . helm-help)))) (help keymap (109 . helm-help)) (f1 keymap (109 . helm-help)) (8 keymap (109 . helm-help) (104 . undefined) (8 . undefined) (4 . helm-debug-output)) (20 . helm-toggle-resplit-and-swap-windows) (C-tab . undefined) (triple-mouse-3 . ignore) (double-mouse-3 . ignore) (mouse-3 . ignore) (drag-mouse-3 . ignore) (down-mouse-3 . ignore) (triple-mouse-2 . ignore) (double-mouse-2 . ignore) (mouse-2 . ignore) (drag-mouse-2 . ignore) (down-mouse-2 . ignore) (triple-mouse-1 . ignore) (double-mouse-1 . ignore) (mouse-1 . ignore) (drag-mouse-1 . ignore) (down-mouse-1 . ignore) (67108897 . helm-toggle-suspend-update) (3 keymap (21 . helm-force-update) (6 . helm-follow-mode) (11 . helm-kill-selection-and-quit) (25 . helm-yank-selection) (4 . helm-delete-current-selection) (45 . helm-swap-windows)) (67108987 . helm-enlarge-window) (67108989 . helm-narrow-window) (19 . undefined) (18 . undefined) (23 . helm-yank-text-at-point) (24 keymap (2 . helm-resume-list-buffers-after-quit) (98 . helm-resume-previous-session-after-quit) (6 . helm-quit-and-find-file)) (11 . helm-delete-minibuffer-contents) (67108896 . helm-toggle-visible-mark) (0 . helm-toggle-visible-mark) (C-M-up . helm-scroll-other-window-down) (C-M-down . helm-scroll-other-window) (M-prior . helm-scroll-other-window-down) (M-next . helm-scroll-other-window) (12 . helm-recenter-top-bottom-other-window) (15 . helm-next-source) (10 . helm-select-3rd-action) (5 . helm-select-2nd-action-or-end-of-line) ...) (header-line . "[C-c C-e] Edit mode, [M-i] apply all buffers") (action lambda ($line) (helm-swoop--goto-line (when (string-match "^[0-9]+" $line) (string-to-number (match-string 0 $line)))) (when (re-search-forward (mapconcat (quote identity) (split-string helm-pattern " ") "\\|") nil t) (goto-char (match-beginning 0))) (helm-swoop--recenter)) (migemo)) #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) "Swoop: " nil "^368 " "*Helm Swoop*" nil nil nil nil nil mapcar #[257 "\211JB\207" [] 3 "\n\n(fn V)"] funcall make-byte-code 0 "\300\301\211:\203 @\262\211A\262@\262\211L\210A\262\202 \266\302\303!\207" vconcat vector [nil helm-log "restore variables"] 5 "\n\n(fn)" helm-initialize helm-display-buffer helm-log "show prompt" #[0 "\300 \207" [helm-cleanup] 1 "\n\n(fn)"] helm-read-pattern-maybe defalias helm-hook51420 "\302\303DC\216\300\211)\207" [overriding-local-map funcall #[0 "\300\301\302\"\207" [remove-hook helm-cleanup-hook helm-hook51420] 3 "\n\n(fn)"]] 2 add-hook helm-cleanup-hook helm-execute-selection-action "[End session] " make-string 41 45 helm-buffer helm-quit helm-in-persistent-action helm-current-source helm-source-name helm-restored-variables] 9 "\n\n(fn)"]() | |
funcall(#[0 "\312\211\211\211\305\206\n (()*+,\313\314-\"\315\316\317\320\321\322!\323\"\324\325%DC\216\326\303\301\307\300$\210\327(!\210\330\331!\210\315\332DC\216\333\302\301\304\303\306\307\310\205P \3109\205P \310&\210*\210)?\205r \334\335\316\317\336\321\322\311!\337\"\340\325%\"\210\341\342\335\"\210\343 \330\344\345\346\347\"P!\210-\207" [((name . "git-gutter+.el") (init lambda nil (unless helm-swoop-cache (with-current-buffer (helm-candidate-buffer (quote local)) (insert "1 ;;; git-gutter+.el --- Manage Git hunks straight from the buffer\n\n3 ;; Copyright (C) 2013 by Syohei YOSHIDA and contributors\n\n5 ;; Author: Syohei YOSHIDA <syohex@gmail.com> and contributors\n6 ;; URL: https://github.com/nonsequitur/git-gutter-plus\n7 ;; Version: 0.1\n\n9 ;; This program is free software; you can redistribute it and/or modify\n10 ;; it under the terms of the GNU General Public License as published by\n11 ;; the Free Software Foundation, either version 3 of the License, or\n12 ;; (at your option) any later version.\n\n14 ;; This program is distributed in the hope that it will be useful,\n15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n17 ;; GNU General Public License for more details.\n\n19 ;; You should have received a copy of the GNU General Public License\n20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n22 ;; Package-Requires: ((git-commit-mode \"0.14\"))\n\n24 ;;; Commentary:\n25 ;;\n26 ;; View, stage and revert Git changes straight from the buffer.\n\n28 ;;; Code:\n\n30 (eval-when-compile\n31 (require 'cl))\n\n33 (require 'tramp)\n34 (require 'log-edit)\n35 (require 'git-commit-mode)\n\n37 (defgroup git-gutter+ nil\n38 \"Manage Git hunks straight from the buffer\"\n39 :prefix \"git-gutter+-\"\n40 :group 'vc)\n\n42 (defcustom git-gutter+-window-width nil\n43 \"Character width of the gutter margin. Set this variable if the automatically\n44 calculated width looks wrong. (This can happen with some special characters.)\"\n45 :type 'integer\n46 :group 'git-gutter+)\n\n48 (defcustom git-gutter+-git-executable \"git\"\n49 \"The path of the Git executable.\"\n50 :type 'string\n51 :group 'git-gutter+)\n\n53 (defcustom git-gutter+-diff-options nil\n54 \"List of strings containing extra arguments to 'git diff'\"\n55 :type 'list\n56 :group 'git-gutter+)\n\n58 (defcustom git-gutter+-separator-sign nil\n59 \"Separator sign\"\n60 :type 'string\n61 :group 'git-gutter+)\n\n63 (defcustom git-gutter+-modified-sign \"=\"\n64 \"Modified sign\"\n65 :type 'string\n66 :group 'git-gutter+)\n\n68 (defcustom git-gutter+-added-sign \"+\"\n69 \"Added sign\"\n70 :type 'string\n71 :group 'git-gutter+)\n\n73 (defcustom git-gutter+-deleted-sign \"-\"\n74 \"Deleted sign\"\n75 :type 'string\n76 :group 'git-gutter+)\n\n78 (defcustom git-gutter+-unchanged-sign nil\n79 \"Unchanged sign\"\n80 :type 'string\n81 :group 'git-gutter+)\n\n83 (defcustom git-gutter+-hide-gutter nil\n84 \"Hide gutter if there are no changes\"\n85 :type 'boolean\n86 :group 'git-gutter+)\n\n88 (defcustom git-gutter+-lighter \" GitGutter\"\n89 \"Minor mode lighter in mode-line\"\n90 :type 'string\n91 :group 'git-gutter+)\n\n93 (defface git-gutter+-separator\n94 '((t (:foreground \"cyan\" :weight bold)))\n95 \"Face of the separator\"\n96 :group 'git-gutter+)\n\n98 (defface git-gutter+-modified\n99 '((t (:foreground \"magenta\" :weight bold)))\n100 \"Face for modified lines\"\n101 :group 'git-gutter+)\n\n103 (defface git-gutter+-added\n104 '((t (:foreground \"green\" :weight bold)))\n105 \"Face for added lines\"\n106 :group 'git-gutter+)\n\n108 (defface git-gutter+-deleted\n109 '((t (:foreground \"red\" :weight bold)))\n110 \"Face for deleted lines\"\n111 :group 'git-gutter+)\n\n113 (defface git-gutter+-unchanged\n114 '((t (:background \"yellow\")))\n115 \"Face for unchanged lines\"\n116 :group 'git-gutter+)\n\n118 (defcustom git-gutter+-disabled-modes nil\n119 \"A list of modes for which `global-git-gutter+-mode' should be disabled.\"\n120 :type '(repeat symbol)\n121 :group 'git-gutter+)\n\n123 (defvar git-gutter+-mode-map\n124 (make-sparse-keymap))\n\n126 (defvar git-gutter+-view-diff-function nil\n127 \"Function to call for displaying diffs\")\n\n129 (defvar git-gutter+-clear-function nil\n130 \"Function to call for clearing the diff display\")\n\n132 (defvar git-gutter+-window-config-change-function nil\n133 \"Function to call when the buffer's local window configuration has changed\")\n\n135 (defvar git-gutter+-diffinfos nil)\n136 (defvar git-gutter+-diff-header nil)\n137 (make-variable-buffer-local 'git-gutter+-diffinfos)\n138 (make-variable-buffer-local 'git-gutter+-diff-header)\n\n140 (defvar git-gutter+-popup-buffer \"*git-gutter+-diff*\")\n141 (defvar git-gutter+-buffers-to-reenable nil)\n\n143 (defconst git-gutter+-hunk-header-regex\n144 ;; The same as diff-hunk-header-re-unified\n145 \"^@@ -\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? \\\\+\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? @@\")\n\n147 (defalias 'git-gutter+-popup-hunk 'git-gutter+-show-hunk)\n148 (defalias 'git-gutter+-revert-hunk 'git-gutter+-revert-hunks)\n\n150 (defmacro git-gutter+-awhen (test &rest body)\n151 \"Anaphoric when.\"\n152 (declare (indent 1))\n153 `(let ((it ,test))\n154 (when it ,@body)))\n\n156 (defun git-gutter+-enable-default-display-mode ()\n157 (setq git-gutter+-view-diff-function 'git-gutter+-view-diff-infos\n158 git-gutter+-clear-function 'git-gutter+-clear-diff-infos\n159 git-gutter+-window-config-change-function 'git-gutter+-show-gutter))\n\n161 (unless git-gutter+-view-diff-function\n162 (git-gutter+-enable-default-display-mode))\n\n164 (defun git-gutter+-call-git (args &optional file)\n165 (if (and file (file-remote-p file))\n166 (apply #'process-file git-gutter+-git-executable nil t nil args)\n167 (apply #'call-process git-gutter+-git-executable nil t nil args)))\n\n169 (defun git-gutter+-in-git-repository-p (file)\n170 (with-temp-buffer\n171 (let ((args '(\"rev-parse\" \"--is-inside-work-tree\")))\n172 (when (zerop (git-gutter+-call-git args file))\n173 (goto-char (point-min))\n174 (string= \"true\" (buffer-substring-no-properties\n175 (point) (line-end-position)))))))\n\n177 (defun git-gutter+-root-directory (file)\n178 (with-temp-buffer\n179 (let* ((args '(\"rev-parse\" \"--show-toplevel\"))\n180 (ret (git-gutter+-call-git args file)))\n181 (when (zerop ret)\n182 (goto-char (point-min))\n183 (let ((root (buffer-substring-no-properties (point) (line-end-position))))\n184 (unless (string= root \"\")\n185 (file-name-as-directory root)))))))\n\n187 (defsubst git-gutter+-diff-args (file)\n188 (delq nil (list \"--no-pager\" \"diff\" \"--no-color\" \"--no-ext-diff\" \"-U0\"\n189 git-gutter+-diff-options file)))\n\n191 (defun git-gutter+-diff (curfile)\n192 (let ((args (git-gutter+-diff-args curfile))\n193 (file (buffer-file-name))) ;; for tramp\n194 (with-temp-buffer\n195 (when (zerop (git-gutter+-call-git args file))\n196 (goto-char (point-min))\n197 (let ((diff-header (git-gutter+-get-diff-header))\n198 (diffinfos (git-gutter+-get-diffinfos)))\n199 (list diff-header diffinfos))))))\n\n201 (defun git-gutter+-get-diff-header ()\n202 (save-excursion\n203 (if (re-search-forward git-gutter+-hunk-header-regex nil t)\n204 (buffer-substring (point-min) (match-beginning 0)))))\n\n206 (defsubst git-gutter+-make-diffinfo (type content start end)\n207 (list :type type :content content :start-line start :end-line end))\n\n209 (defun git-gutter+-get-diffinfos ()\n210 (loop while (re-search-forward git-gutter+-hunk-header-regex nil t)\n211 ;; Hunk header format:\n212 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n213 for del-len = (string-to-number (or (match-string 2) \"1\"))\n214 for add-line = (string-to-number (match-string 3))\n215 for add-len = (string-to-number (or (match-string 4) \"1\"))\n216 for type = (cond ((zerop del-len) 'added)\n217 ((zerop add-len) 'deleted)\n218 (t 'modified))\n219 for start-line = (if (eq type 'deleted)\n220 (1+ add-line)\n221 add-line)\n222 for end-line = (if (eq type 'deleted)\n223 start-line\n224 (1- (+ add-line add-len)))\n225 for content = (git-gutter+-diff-content)\n226 collect\n227 (git-gutter+-make-diffinfo type content start-line end-line)))\n\n229 (defun git-gutter+-diff-content ()\n230 (save-excursion\n231 (goto-char (line-beginning-position)) ; Move to beginning of hunk header\n232 (let ((hunk-start (point)))\n233 ;; Move to end of hunk\n234 (forward-line 1)\n235 (if (re-search-forward \"^@@\" nil t)\n236 (backward-char 3) ;; exclude \"\\n@@\"\n237 (goto-char (1- (point-max)))) ; Skip trailing newline\n238 (buffer-substring hunk-start (point)))))\n\n240 (defun git-gutter+-line-to-pos (line)\n241 (save-excursion\n242 (goto-char (point-min))\n243 (forward-line (1- line))\n244 (point)))\n\n246 (defun git-gutter+-before-string (sign)\n247 (let* ((sep-sign git-gutter+-separator-sign)\n248 (sep (when sep-sign\n249 (propertize sep-sign 'face 'git-gutter+-separator)))\n250 (gutter-sep (concat sign sep)))\n251 (propertize \" \" 'display `((margin left-margin) ,gutter-sep))))\n\n253 (defsubst git-gutter+-select-face (type)\n254 (case type\n255 (added 'git-gutter+-added)\n256 (modified 'git-gutter+-modified)\n257 (deleted 'git-gutter+-deleted)))\n\n259 (defsubst git-gutter+-select-sign (type)\n260 (case type\n261 (added git-gutter+-added-sign)\n262 (modified git-gutter+-modified-sign)\n263 (deleted git-gutter+-deleted-sign)))\n\n265 (defun git-gutter+-propertized-sign (type)\n266 (let ((sign (git-gutter+-select-sign type))\n267 (face (git-gutter+-select-face type)))\n268 (propertize sign 'face face)))\n\n270 (defun git-gutter+-view-region (sign start-line end-line)\n271 (let ((beg (git-gutter+-line-to-pos start-line)))\n272 (goto-char beg)\n273 (while (and (<= (line-number-at-pos) end-line) (not (eobp)))\n274 (git-gutter+-view-at-pos sign (point))\n275 (forward-line 1))))\n\n277 (defun git-gutter+-view-at-pos (sign pos)\n278 (let ((ov (make-overlay pos pos)))\n279 (overlay-put ov 'before-string (git-gutter+-before-string sign))\n280 (overlay-put ov 'git-gutter+ t)))\n\n282 (defun git-gutter+-view-diff-info (diffinfo)\n283 (let* ((start-line (plist-get diffinfo :start-line))\n284 (end-line (plist-get diffinfo :end-line))\n285 (type (plist-get diffinfo :type))\n286 (sign (git-gutter+-propertized-sign type)))\n287 (case type\n288 ((modified added) (git-gutter+-view-region sign start-line end-line))\n289 (deleted (git-gutter+-view-at-pos\n290 sign (git-gutter+-line-to-pos start-line))))))\n\n292 (defun git-gutter+-sign-width (sign)\n293 (loop for s across sign\n294 sum (char-width s)))\n\n296 (defun git-gutter+-longest-sign-width ()\n297 (let ((signs (list git-gutter+-modified-sign\n298 git-gutter+-added-sign\n299 git-gutter+-deleted-sign)))\n300 (when git-gutter+-unchanged-sign\n301 (add-to-list 'signs git-gutter+-unchanged-sign))\n302 (+ (apply 'max (mapcar 'git-gutter+-sign-width signs))\n303 (git-gutter+-sign-width git-gutter+-separator-sign))))\n\n305 (defun git-gutter+-view-for-unchanged ()\n306 (save-excursion\n307 (let ((sign (if git-gutter+-unchanged-sign\n308 (propertize git-gutter+-unchanged-sign\n309 'face 'git-gutter+-unchanged)\n310 \" \")))\n311 (goto-char (point-min))\n312 (while (not (eobp))\n313 (git-gutter+-view-at-pos sign (point))\n314 (forward-line 1)))))\n\n316 (defun git-gutter+-set-window-margin (width)\n317 (let ((curwin (get-buffer-window)))\n318 (set-window-margins curwin width (cdr (window-margins curwin)))))\n\n320 (defsubst git-gutter+-file-buffer-p ()\n321 (and (buffer-file-name)\n322 default-directory\n323 (file-directory-p default-directory)))\n\n325 ;;;###autoload\n326 (define-minor-mode git-gutter+-mode\n327 \"Git-Gutter mode\"\n328 :group 'git-gutter+\n329 :init-value nil\n330 :global nil\n331 :lighter git-gutter+-lighter\n332 (if git-gutter+-mode\n333 (if (and (git-gutter+-file-buffer-p)\n334 (git-gutter+-in-git-repository-p (buffer-file-name)))\n335 (progn\n336 (git-gutter+-add-local-hooks)\n337 (git-gutter+-refresh))\n338 (if (called-interactively-p 'any)\n339 (message \"No Git repo for current buffer\"))\n340 (git-gutter+-mode -1))\n341 (git-gutter+-remove-local-hooks)\n342 (git-gutter+-clear)))\n\n344 (defun git-gutter+-add-local-hooks ()\n345 (add-hook 'after-save-hook 'git-gutter+-refresh nil t)\n346 ;; Turn off `git-gutter+-mode' while reverting to prevent any redundant calls to\n347 ;; `git-gutter+-refresh'.\n348 (add-hook 'before-revert-hook 'git-gutter+-turn-off nil t)\n349 (add-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change nil t)\n350 (if git-gutter+-window-config-change-function\n351 (add-hook 'window-configuration-change-hook\n352 git-gutter+-window-config-change-function nil t)))\n\n354 (defun git-gutter+-remove-local-hooks ()\n355 (remove-hook 'after-save-hook 'git-gutter+-refresh t)\n356 (remove-hook 'before-revert-hook 'git-gutter+-turn-off t)\n357 (remove-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change t)\n358 (if git-gutter+-window-config-change-function\n359 (remove-hook 'window-configuration-change-hook\n360 git-gutter+-window-config-change-function t)))\n\n362 (defmacro git-gutter+-in-all-buffers (&rest body)\n363 `(dolist (buf (buffer-list))\n364 (with-current-buffer buf\n365 ,@body)))\n\n367 ;; When `define-globalized-minor-mode' is used to define `global-git-gutter+-mode',\n368 ;; `git-gutter+-mode' and thus `git-gutter+-refresh' get run twice when a new file\n369 ;; is opened. (First for `fundamental-mode', then for the file-specific mode.)\n370 ;; The following definition of `global-git-gutter+-mode' avoids any redundant calls to\n371 ;; `git-gutter+-refresh'.\n\n373 ;;;###autoload\n374 (define-minor-mode global-git-gutter+-mode ()\n375 \"Global Git-Gutter mode\"\n376 :group 'git-gutter+\n377 :init-value nil\n378 :global t\n379 (if global-git-gutter+-mode\n380 (progn\n381 (add-hook 'find-file-hook 'git-gutter+-turn-on)\n382 (add-hook 'after-revert-hook 'git-gutter+-turn-on)\n383 (add-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n384 (git-gutter+-in-all-buffers (git-gutter+-turn-on)))\n385 (remove-hook 'find-file-hook 'git-gutter+-turn-on)\n386 (remove-hook 'after-revert-hook 'git-gutter+-turn-on)\n387 (remove-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n388 (git-gutter+-in-all-buffers (git-gutter+-turn-off))))\n\n390 (defun git-gutter+-turn-on ()\n391 (when (and (buffer-file-name)\n392 (not (memq major-mode git-gutter+-disabled-modes))\n393 (not git-gutter+-mode))\n394 (git-gutter+-mode t)))\n\n396 (defun git-gutter+-turn-off ()\n397 (if git-gutter+-mode (git-gutter+-mode -1)))\n\n399 (defun git-gutter+-reenable-after-major-mode-change ()\n400 (if global-git-gutter+-mode\n401 (add-to-list 'git-gutter+-buffers-to-reenable (current-buffer))))\n\n403 (defun git-gutter+-reenable-buffers ()\n404 (dolist (buf git-gutter+-buffers-to-reenable)\n405 (with-current-buffer buf\n406 (git-gutter+-turn-on)))\n407 (setq git-gutter+-buffers-to-reenable nil))\n\n409 (defsubst git-gutter+-show-gutter-p (diffinfos)\n410 (if git-gutter+-hide-gutter\n411 (or diffinfos git-gutter+-unchanged-sign)\n412 (or global-git-gutter+-mode git-gutter+-unchanged-sign diffinfos)))\n\n414 (defun git-gutter+-show-gutter (&optional diffinfos)\n415 (when (git-gutter+-show-gutter-p (or diffinfos git-gutter+-diffinfos))\n416 (let ((win-width (or git-gutter+-window-width\n417 (git-gutter+-longest-sign-width))))\n418 (git-gutter+-set-window-margin win-width))))\n\n420 (defun git-gutter+-view-diff-infos (diffinfos)\n421 (when (or git-gutter+-unchanged-sign\n422 git-gutter+-separator-sign)\n423 (git-gutter+-view-for-unchanged))\n424 (when diffinfos\n425 (save-excursion\n426 (mapc 'git-gutter+-view-diff-info diffinfos)))\n427 (git-gutter+-show-gutter diffinfos))\n\n429 (defsubst git-gutter+-reset-window-margin-p ()\n430 (or git-gutter+-hide-gutter\n431 (not global-git-gutter+-mode)))\n\n433 (defun git-gutter+-clear-diff-infos ()\n434 (when (git-gutter+-reset-window-margin-p)\n435 (git-gutter+-set-window-margin 0))\n436 (remove-overlays (point-min) (point-max) 'git-gutter+ t))\n\n438 (defun git-gutter+-process-diff (curfile)\n439 (destructuring-bind\n440 (diff-header diffinfos) (git-gutter+-diff curfile)\n441 (setq git-gutter+-diff-header diff-header\n442 git-gutter+-diffinfos diffinfos)\n443 (save-restriction\n444 (widen)\n445 (funcall git-gutter+-view-diff-function diffinfos))))\n\n447 (defun git-gutter+-search-near-diff-index (diffinfos is-reverse)\n448 (loop with current-line = (line-number-at-pos)\n449 with cmp-fn = (if is-reverse '> '<)\n450 for diffinfo in (if is-reverse (reverse diffinfos) diffinfos)\n451 for index = 0 then (1+ index)\n452 for start-line = (plist-get diffinfo :start-line)\n453 when (funcall cmp-fn current-line start-line)\n454 return (if is-reverse\n455 (1- (- (length diffinfos) index))\n456 index)))\n\n458 (defun git-gutter+-diffinfo-at-point ()\n459 (save-restriction\n460 (widen)\n461 (loop with current-line = (line-number-at-pos)\n462 for diffinfo in git-gutter+-diffinfos\n463 for start = (plist-get diffinfo :start-line)\n464 for end = (or (plist-get diffinfo :end-line) (1+ start))\n465 when (and (>= current-line start) (<= current-line end))\n466 return diffinfo)))\n\n468 (defun git-gutter+-collect-deleted-line (str)\n469 (with-temp-buffer\n470 (insert str)\n471 (goto-char (point-min))\n472 (loop while (re-search-forward \"^-\\\\(.*?\\\\)$\" nil t)\n473 collect (match-string 1) into deleted-lines\n474 finally return deleted-lines)))\n\n476 (defun git-gutter+-delete-added-lines (start-line end-line)\n477 (forward-line (1- start-line))\n478 (let ((start-point (point)))\n479 (forward-line (1+ (- end-line start-line)))\n480 (delete-region start-point (point))))\n\n482 (defun git-gutter+-insert-deleted-lines (content)\n483 (dolist (line (git-gutter+-collect-deleted-line content))\n484 (insert (concat line \"\\n\"))))\n\n486 (defun git-gutter+-do-revert-hunk (diffinfo)\n487 (save-excursion\n488 (save-restriction\n489 (widen)\n490 (goto-char (point-min))\n491 (let ((start-line (plist-get diffinfo :start-line))\n492 (end-line (plist-get diffinfo :end-line))\n493 (content (plist-get diffinfo :content)))\n494 (case (plist-get diffinfo :type)\n495 (added (git-gutter+-delete-added-lines start-line end-line))\n496 (deleted (forward-line (1- start-line))\n497 (git-gutter+-insert-deleted-lines content))\n498 (modified (git-gutter+-delete-added-lines start-line end-line)\n499 (git-gutter+-insert-deleted-lines content)))))))\n\n501 (defun git-gutter+-revert-hunks ()\n502 \"Revert hunk at point. If region is active, revert all hunks within the region.\"\n503 (interactive)\n504 (let* ((diffinfos (git-gutter+-selected-diffinfos))\n505 (one-diffinfo-p (= 1 (length diffinfos))))\n506 (save-window-excursion\n507 (if one-diffinfo-p (git-gutter+-show-hunk (car diffinfos)))\n508 (when (and diffinfos\n509 (yes-or-no-p (if one-diffinfo-p\n510 \"Revert hunk?\"\n511 (format \"Revert %d hunks?\" (length diffinfos)))))\n512 ;; Revert diffinfos in reverse so that earlier hunks don't invalidate the\n513 ;; line number information of the later hunks.\n514 (dolist (diffinfo (nreverse diffinfos))\n515 (git-gutter+-do-revert-hunk diffinfo))\n516 (save-buffer))\n517 (if one-diffinfo-p\n518 (git-gutter+-awhen (get-buffer git-gutter+-popup-buffer)\n519 (kill-buffer it))))))\n\n521 (defun git-gutter+-show-hunk (&optional diffinfo)\n522 \"Show hunk at point in another window\"\n523 (interactive)\n524 (git-gutter+-awhen (or diffinfo\n525 (git-gutter+-diffinfo-at-point))\n526 (save-selected-window\n527 (with-current-buffer (get-buffer-create git-gutter+-popup-buffer)\n528 (setq buffer-read-only nil)\n529 (erase-buffer)\n530 (insert (plist-get it :content))\n531 (insert \"\\n\")\n532 (goto-char (point-min))\n533 (diff-mode)\n534 (view-mode)\n535 (pop-to-buffer (current-buffer))))))\n\n537 (defun git-gutter+-next-hunk (arg)\n538 \"Move to next diff hunk\"\n539 (interactive \"p\")\n540 (if (not git-gutter+-diffinfos)\n541 (message \"No changes in buffer\")\n542 (save-restriction\n543 (widen)\n544 (let* ((is-reverse (< arg 0))\n545 (diffinfos git-gutter+-diffinfos)\n546 (len (length diffinfos))\n547 (index (git-gutter+-search-near-diff-index diffinfos is-reverse))\n548 (real-index (if index\n549 (let ((next (if is-reverse (1+ index) (1- index))))\n550 (mod (+ arg next) len))\n551 (if is-reverse (1- (length diffinfos)) 0)))\n552 (diffinfo (nth real-index diffinfos)))\n553 (goto-char (point-min))\n554 (forward-line (1- (plist-get diffinfo :start-line)))\n555 (when (buffer-live-p (get-buffer git-gutter+-popup-buffer))\n556 (save-window-excursion\n557 (git-gutter+-show-hunk)))))))\n\n559 (defun git-gutter+-previous-hunk (arg)\n560 \"Move to previous diff hunk\"\n561 (interactive \"p\")\n562 (git-gutter+-next-hunk (- arg)))\n\n564 (defun git-gutter+-remote-default-directory (dir file)\n565 (let* ((vec (tramp-dissect-file-name file))\n566 (method (aref vec 0))\n567 (user (aref vec 1))\n568 (host (aref vec 2)))\n569 (format \"/%s:%s%s:%s\" method (if user (concat user \"@\") \"\") host dir)))\n\n571 (defun git-gutter+-remote-file-path (dir file)\n572 (let ((file (aref (tramp-dissect-file-name file) 3)))\n573 (replace-regexp-in-string (concat \"\\\\`\" dir) \"\" file)))\n\n575 (defun git-gutter+-local-file-path (file)\n576 (if (eq system-type 'windows-nt)\n577 ;; Cygwin can't handle Windows absolute paths\n578 (file-relative-name file default-directory)\n579 file))\n\n581 (defun git-gutter+-refresh ()\n582 (git-gutter+-clear)\n583 (let ((file (buffer-file-name)))\n584 (when (and file (file-exists-p file))\n585 (if (file-remote-p file)\n586 (let* ((repo-root (git-gutter+-root-directory file))\n587 (default-directory (git-gutter+-remote-default-directory repo-root file)))\n588 (git-gutter+-process-diff (git-gutter+-remote-file-path repo-root file)))\n589 (git-gutter+-process-diff (git-gutter+-local-file-path file))))))\n\n591 (defun git-gutter+-clear ()\n592 (save-restriction\n593 (widen)\n594 (funcall git-gutter+-clear-function))\n595 (setq git-gutter+-diffinfos nil))\n\n\n598 ;;; Staging\n\n600 (defun git-gutter+-stage-hunks ()\n601 \"Stage hunk at point. If region is active, stage all hunk lines within the region.\"\n602 (interactive)\n603 (let* ((line-range (if (use-region-p)\n604 (cons (line-number-at-pos (region-beginning))\n605 (line-number-at-pos (region-end)))))\n606 (diffinfos (git-gutter+-selected-diffinfos line-range)))\n607 (when diffinfos\n608 (let ((error-msg (git-gutter+-stage-diffinfos diffinfos line-range)))\n609 (if error-msg\n610 (message \"Error staging hunks:\\n%s\" error-msg))\n611 (git-gutter+-refresh)))))\n\n613 (defun git-gutter+-selected-diffinfos (&optional line-range)\n614 (unless line-range\n615 (setq line-range (if (use-region-p)\n616 (cons (line-number-at-pos (region-beginning))\n617 (line-number-at-pos (region-end))))))\n618 (if line-range\n619 (git-gutter+-diffinfos-between-lines line-range)\n620 (git-gutter+-awhen (git-gutter+-diffinfo-at-point)\n621 (list it))))\n\n623 (defsubst git-gutter+-diffinfo-between-lines-p (diffinfo start-line end-line)\n624 (let ((diff-start (plist-get diffinfo :start-line))\n625 (diff-end (plist-get diffinfo :end-line)))\n626 (and (<= start-line diff-end)\n627 (<= diff-start end-line))))\n\n629 (defun git-gutter+-diffinfos-between-lines (line-range)\n630 (save-restriction\n631 (widen)\n632 (let ((start-line (car line-range))\n633 (end-line (cdr line-range)))\n634 (delq nil\n635 (mapcar (lambda (diffinfo)\n636 (if (git-gutter+-diffinfo-between-lines-p\n637 diffinfo start-line end-line)\n638 diffinfo))\n639 git-gutter+-diffinfos)))))\n\n641 (defun git-gutter+-stage-diffinfos (diffinfos line-range)\n642 (let ((header git-gutter+-diff-header))\n643 (with-temp-buffer\n644 (insert header)\n645 ;; Insert hunks in reverse so that earlier hunks don't invalidate the line\n646 ;; number information of the later hunks.\n647 (dolist (diffinfo (nreverse diffinfos))\n648 (git-gutter+-insert-diffinfo diffinfo line-range)\n649 (goto-char (point-max)))\n650 (git-gutter+-call-git-on-current-buffer\n651 '(\"apply\" \"--unidiff-zero\" \"--cached\" \"-\")))))\n\n653 (defun git-gutter+-insert-diffinfo (diffinfo line-range)\n654 (let ((content (plist-get diffinfo :content))\n655 (type (plist-get diffinfo :type)))\n656 (if (not line-range)\n657 (git-gutter+-insert-hunk content type)\n658 (let ((diff-start-line (plist-get diffinfo :start-line))\n659 (diff-end-line (plist-get diffinfo :end-line))\n660 (start-line (car line-range))\n661 (end-line (cdr line-range)))\n662 (git-gutter+-insert-hunk content type\n663 (1+ (- start-line diff-start-line))\n664 (1+ (- end-line diff-start-line)))))))\n\n666 (defun git-gutter+-call-git-on-current-buffer (args)\n667 \"Sends the current buffer contents to Git and replaces them with Git's output.\n\n669 RETURNS nil if Git ran successfully. Returns an error description otherwise.\"\n670 (unless (zerop (apply #'call-process-region (point-min) (point-max)\n671 git-gutter+-git-executable t t nil args))\n672 (buffer-string)))\n\n674 (defsubst git-gutter+-read-hunk-header (hunk)\n675 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n676 (string-match git-gutter+-hunk-header-regex hunk)\n677 (list (string-to-number (match-string 1 hunk))\n678 (string-to-number (or (match-string 2 hunk) \"1\"))\n679 (string-to-number (match-string 3 hunk))\n680 (string-to-number (or (match-string 4 hunk) \"1\"))))\n\n682 (defun git-gutter+-insert-hunk (hunk type &optional start end)\n683 \"If START and END are provided, only insert addition (+) lines between\n684 START and END (inclusive). START and END are both line numbers starting with 1.\"\n685 (destructuring-bind\n686 (del-line del-len add-line add-len) (git-gutter+-read-hunk-header hunk)\n687 (let* ((start (max 1 (or start 1)))\n688 (end (min add-len (or end add-len)))\n689 (insert-all-p (or (eq type :deleted)\n690 (and (= start 1) (= end add-len))))\n691 (num-lines-selected (if insert-all-p\n692 add-len\n693 (1+ (- end start)))))\n694 ;; When the user selected the last lines of a hunk with type `modified' (but\n695 ;; not the complete hunk), then don't insert any deletion (-) lines from that\n696 ;; hunk.\n697 (if (and (eq type 'modified)\n698 (> start 1) (= end add-len))\n699 (setq type 'modified-trailing))\n\n701 (save-excursion\n702 (insert hunk \"\\n\"))\n\n704 (git-gutter+-delete-hunk-header)\n\n706 (if (not insert-all-p)\n707 (git-gutter+-modify-hunk type num-lines-selected del-len start))\n\n709 (let ((hunk-header (git-gutter+-make-hunk-header type num-lines-selected\n710 del-line del-len add-line)))\n711 (insert hunk-header \"\\n\")))))\n\n713 (defun git-gutter+-delete-hunk-header ()\n714 (let ((hunk-start (point)))\n715 (forward-line 1)\n716 (delete-region hunk-start (point))))\n\n718 (defun git-gutter+-modify-hunk (type num-lines-selected del-len start)\n719 \"Remove all addition (+) lines from hunk that aren't selected.\n720 If TYPE is not `modified', also remove all deletion (-) lines.\"\n721 (let ((first-line-selected (+ del-len (1- start)))\n722 selected-lines)\n723 (save-excursion\n724 (forward-line first-line-selected)\n725 (let ((selection-start (point)))\n726 (forward-line num-lines-selected)\n727 (setq selected-lines (buffer-substring selection-start (point)))))\n728 (save-excursion\n729 (if (eq type 'modified) (forward-line del-len)) ; skip over deletion (-) lines\n730 (delete-region (point) (point-max))\n731 (insert selected-lines))))\n\n733 (defun git-gutter+-make-hunk-header (type num-lines-selected del-line del-len add-line)\n734 (let ((add-len num-lines-selected))\n735 (case type\n736 (added (setq add-line (1+ del-line)))\n737 (modified-trailing (setq add-line (+ del-line del-len)\n738 del-line (1- add-line)\n739 del-len 0))\n740 (t (setq add-line del-line)))\n741 (format \"@@ -%d,%d +%d,%d @@\"\n742 del-line del-len\n743 add-line add-len)))\n\n\n746 ;;; Committing\n747 ;; This section draws heavily from old Magit source code.\n\n749 (defvar git-gutter+-pre-commit-window-config nil)\n750 (defvar git-gutter+-commit-origin-buffer nil\n751 \"Buffer that started the commit\")\n\n753 (defconst git-gutter+-commit-buffer-name \"*Commit Message*\")\n754 (defconst git-gutter+-staged-changes-buffer-name \"*Staged Changes*\")\n\n756 ;;;###autoload\n757 (defun git-gutter+-commit ()\n758 \"Commit staged changes. If nothing is staged, ask to stage the current buffer.\"\n759 (interactive)\n\n761 (when (and (not (git-gutter+-anything-staged-p))\n762 git-gutter+-diffinfos\n763 (y-or-n-p \"Nothing staged. Stage current buffer? \"))\n764 (git-gutter+-stage-whole-buffer))\n\n766 (let ((file (buffer-file-name))\n767 (dir default-directory))\n768 (git-gutter+-save-window-config-if-needed)\n769 (setq git-gutter+-commit-origin-buffer (current-buffer))\n770 (git-gutter+-open-commit-edit-buffer dir)\n771 (git-gutter+-show-staged-changes file dir)))\n\n773 (defun git-gutter+-stage-and-commit ()\n774 (interactive)\n775 (git-gutter+-stage-hunks)\n776 (git-gutter+-commit))\n\n778 (defun git-gutter+-save-window-config-if-needed ()\n779 ;; Only save the window config if the temporary buffers that get popped-up by\n780 ;; git-gutter+ are not already visible.\n781 ;; In this way, `git-gutter+-commit' can be called twice in a row without\n782 ;; losing the original window config.\n783 (when (not (and git-gutter+-pre-commit-window-config\n784 (get-buffer-window git-gutter+-commit-buffer-name)\n785 (get-buffer-window git-gutter+-staged-changes-buffer-name)))\n786 (setq git-gutter+-pre-commit-window-config (current-window-configuration))))\n\n788 (defun git-gutter+-open-commit-edit-buffer (dir)\n789 \"Opens a buffer for composing the commit message\"\n790 (pop-to-buffer (get-buffer-create git-gutter+-commit-buffer-name))\n791 (setq default-directory dir)\n792 (git-gutter+-commit-mode)\n793 (message \"Type C-c C-c to commit (C-c C-k to cancel).\"))\n\n795 (defsubst git-gutter+-pop-to-staged-changes-buffer ()\n796 (let* ((buf (get-buffer-create git-gutter+-staged-changes-buffer-name))\n797 (window (get-buffer-window buf)))\n798 (if window\n799 ;; Buffer is already visible\n800 (select-window window)\n801 (if (<= (length (window-list)) 2)\n802 (split-window))\n803 (pop-to-buffer buf))))\n\n805 (defun git-gutter+-show-staged-changes (file dir)\n806 (save-selected-window\n807 (git-gutter+-pop-to-staged-changes-buffer)\n808 (setq buffer-read-only nil)\n809 (erase-buffer)\n810 (let ((default-directory dir))\n811 (git-gutter+-call-git '(\"diff\" \"--staged\") file))\n812 (goto-char (point-min))\n813 (diff-mode)\n814 (view-mode)))\n\n816 (defsubst git-gutter+-abort-commit-when-no-changes (allow-empty amend)\n817 (unless (or amend\n818 allow-empty\n819 (git-gutter+-anything-staged-p))\n820 (error\n821 \"Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?\"\n822 (key-description (car (where-is-internal\n823 'git-gutter+-commit-toggle-amending)))\n824 (key-description (car (where-is-internal\n825 'git-gutter+-commit-toggle-allow-empty))))))\n\n827 (defsubst git-gutter+-buffer-is-whitespace ()\n828 (save-excursion\n829 (goto-char (point-min))\n830 (looking-at-p \"[ \\t\\n]*\\\\'\")))\n\n832 (defun git-gutter+-publish-commit ()\n833 \"Publish commit\"\n834 (interactive)\n835 (let* ((fields (git-gutter+-commit-get-fields))\n836 (amend (equal \"yes\" (git-gutter+-commit-get-field 'amend fields)))\n837 (allow-empty (equal \"yes\" (git-gutter+-commit-get-field 'allow-empty fields)))\n838 (author (git-gutter+-commit-get-field 'author fields))\n839 (date (git-gutter+-commit-get-field 'date fields)))\n\n841 (git-gutter+-abort-commit-when-no-changes allow-empty amend)\n\n843 (git-gutter+-push-to-comment-ring (buffer-string))\n\n845 (git-gutter+-commit-set-fields nil) ; Delete message header\n\n847 (when (git-gutter+-buffer-is-whitespace)\n848 (erase-buffer)\n849 (insert \"(Empty description)\"))\n\n851 (let ((error-msg (git-gutter+-call-git-on-current-buffer\n852 (append '(\"--no-pager\" \"commit\" \"-F\" \"-\")\n853 (if amend '(\"--amend\"))\n854 (if allow-empty '(\"--allow-empty\"))\n855 (if author (list (concat \"--author=\" author)))\n856 (if date (list (concat \"--date=\" date)))))))\n857 (if error-msg\n858 (progn\n859 (message \"Commit error:\\n%s\" error-msg)\n860 (erase-buffer)\n861 (insert (ring-ref log-edit-comment-ring 0))) ; Reinsert commit message\n862 (message \"Commit successful.\")\n863 (git-gutter+-close-commit-edit-buffer)\n864 (git-gutter+-update-vc-modeline)))))\n\n866 (defun git-gutter+-close-commit-edit-buffer ()\n867 \"Abort edits and discard commit message being composed.\"\n868 (interactive)\n869 (kill-buffer)\n870 (set-window-configuration git-gutter+-pre-commit-window-config))\n\n872 (defun git-gutter+-update-vc-modeline ()\n873 (when (buffer-live-p git-gutter+-commit-origin-buffer)\n874 (with-current-buffer git-gutter+-commit-origin-buffer\n875 ;; Updating the modeline has no effect if the buffer still has\n876 ;; changes - it will remain in the 'modified' state. So skip it then.\n877 (unless git-gutter+-diffinfos\n878 (ignore-errors (vc-find-file-hook))))))\n\n880 (defun git-gutter+-stage-whole-buffer ()\n881 (save-excursion\n882 (mark-whole-buffer)\n883 (git-gutter+-stage-hunks)))\n\n885 (defun git-gutter+-anything-staged-p ()\n886 \"Return t if the current repo has staged changes\"\n887 (not (zerop (git-gutter+-call-git '(\"diff\" \"--quiet\" \"--cached\")))))\n\n889 (defun git-gutter+-commit-toggle-amending ()\n890 \"Toggle whether this will be an amendment to the previous commit.\n891 \\(i.e., whether commit is run via 'git commit --amend')\"\n892 (interactive)\n893 ;; Remove the newline that 'git-commit-mode' adds to a new commit\n894 ;; message buffer by default. This prevents an ugly visual\n895 ;; gap between the commit message header and the previous commit\n896 ;; message.\n897 (when (git-gutter+-buffer-is-whitespace)\n898 (erase-buffer))\n\n900 (let ((amend-was-already-set (git-gutter+-commit-get-field 'amend)))\n901 (git-gutter+-commit-toggle-field 'amend t)\n902 (unless amend-was-already-set\n903 ;; Insert previous commit message\n904 (goto-char (point-max))\n905 (unless (zerop (current-column))\n906 (insert \"\\n\"))\n907 (insert (git-gutter+-get-last-commit-msg)\n908 \"\\n\"))))\n\n910 (defun git-gutter+-commit-toggle-allow-empty ()\n911 \"Toggle whether this commit is allowed to be empty.\n912 \\(i.e., whether commit is run via 'git commit --allow-empty')\"\n913 (interactive)\n914 (git-gutter+-commit-toggle-field 'allow-empty t))\n\n916 (defun git-gutter+-format-author (author email)\n917 (format \"%s <%s>\" author email))\n\n919 (defun git-gutter+-commit-toggle-author ()\n920 \"Toggle whether this commit should have a user-defined author.\"\n921 (interactive)\n922 (git-gutter+-commit-toggle-input\n923 'author (git-gutter+-format-author\n924 (or (git-gutter+-get-cfg \"user\" \"name\") \"Author Name\")\n925 (or (git-gutter+-get-cfg \"user\" \"email\") \"author@email\"))))\n\n927 (defun git-gutter+-commit-toggle-date ()\n928 \"Toggle whether this commit should have a user-defined date.\"\n929 (interactive)\n930 (git-gutter+-commit-toggle-input 'date\n931 ;; ISO 8601\n932 (format-time-string \"%Y-%m-%dT%T%z\" (current-time))))\n\n934 (defun git-gutter+-push-to-comment-ring (comment)\n935 (when (or (ring-empty-p log-edit-comment-ring)\n936 (not (equal comment (ring-ref log-edit-comment-ring 0))))\n937 (ring-insert log-edit-comment-ring comment)))\n\n939 (defun git-gutter+-get-last-commit-msg ()\n940 (git-gutter+-git-output '(\"log\" \"--max-count=1\" \"--pretty=format:%s%n%n%b\" \"HEAD\")))\n\n942 (defun git-gutter+-get-cfg (&rest keys)\n943 (git-gutter+-git-output (list \"config\" (mapconcat 'identity keys \".\"))))\n\n945 (defun git-gutter+-git-output (args)\n946 (with-temp-buffer\n947 (git-gutter+-call-git args)\n948 ;; Delete trailing newlines\n949 (goto-char (point-min))\n950 (if (re-search-forward \"\\n+\\\\'\" nil t)\n951 (replace-match \"\"))\n952 (buffer-string)))\n\n\n955 ;;; Commit message header\n\n957 (defconst git-gutter+-commit-header-end \"-- End of commit options header --\\n\")\n\n959 (defun git-gutter+-commit-get-field (name &optional fields)\n960 (cdr (assq name (or fields (git-gutter+-commit-get-fields)))))\n\n962 (defun git-gutter+-commit-set-field (name value)\n963 (let* ((fields (git-gutter+-commit-get-fields))\n964 (cell (assq name fields)))\n965 (cond (cell\n966 (if value\n967 (rplacd cell value)\n968 (setq fields (delq cell fields))))\n969 (t\n970 (if value\n971 (setq fields (append fields (list (cons name value)))))))\n972 (git-gutter+-commit-set-fields fields)))\n\n974 (defun git-gutter+-commit-toggle-field (name default)\n975 \"Toggle the commit header field named NAME.\n976 If it's currently unset, set it to DEFAULT (t or nil).\"\n977 (let* ((fields (git-gutter+-commit-get-fields))\n978 (cell (assq name fields)))\n979 (if cell\n980 (rplacd cell (if (equal (cdr cell) \"yes\") \"no\" \"yes\"))\n981 (setq fields (acons name (if default \"yes\" \"no\") fields)))\n982 (git-gutter+-commit-set-fields fields)))\n\n984 (defun git-gutter+-commit-toggle-input (name default)\n985 \"Toggle the commit header input named NAME.\n986 If it's currently unset, set it to DEFAULT (a string). If it is\n987 set remove it.\"\n988 (let* ((fields (git-gutter+-commit-get-fields))\n989 (cell (assq name fields)))\n990 (if cell\n991 (setq fields (assq-delete-all name fields))\n992 (setq fields (acons name default fields)))\n993 (git-gutter+-commit-set-fields fields)))\n\n995 (defun git-gutter+-commit-get-fields ()\n996 (let (result)\n997 (goto-char (point-min))\n998 (while (looking-at \"^\\\\([A-Za-z0-9-_]+\\\\): *\\\\(.+\\\\)?$\")\n999 (let ((name (intern (downcase (match-string 1))))\n1000 (value (read (or (match-string 2) \"nil\"))))\n1001 (push (cons name value) result))\n1002 (forward-line))\n1003 (if (looking-at (regexp-quote git-gutter+-commit-header-end))\n1004 (nreverse result))))\n\n1006 (defun git-gutter+-commit-set-fields (fields)\n1007 (goto-char (point-min))\n1008 ;; Delete commit header\n1009 (if (search-forward-regexp (format \"^\\\\(?:[A-Za-z0-9-_]+:.*\\n\\\\)*%s\"\n1010 (regexp-quote git-gutter+-commit-header-end))\n1011 nil t)\n1012 (delete-region (match-beginning 0) (match-end 0)))\n1013 (goto-char (point-min))\n1014 (when fields\n1015 (dolist (field fields)\n1016 (insert (capitalize (symbol-name (car field))) \": \"\n1017 (prin1-to-string (cdr field)) \"\\n\"))\n1018 (insert git-gutter+-commit-header-end)))\n\n\n1021 ;;; git-gutter+-commit-mode\n1022 ;; Like git-commit-mode, but adds keybindings to git-gutter+ commands and\n1023 ;; highlighting support for the commit message header.\n\n1025 (define-derived-mode git-gutter+-commit-mode git-commit-mode \"Git-Gutter-Commit\"\n1026 (setq font-lock-defaults (list (git-gutter+-commit-font-lock-keywords) t)))\n\n1028 (setq git-gutter+-commit-mode-map\n1029 (let ((map (copy-keymap git-commit-mode-map)))\n1030 (define-key map (kbd \"C-c C-c\") 'git-gutter+-publish-commit)\n1031 (define-key map (kbd \"C-c C-k\") 'git-gutter+-close-commit-edit-buffer)\n1032 (define-key map (kbd \"C-c C-a\") 'git-gutter+-commit-toggle-amending)\n1033 (define-key map (kbd \"C-c C-e\") 'git-gutter+-commit-toggle-allow-empty)\n1034 (define-key map (kbd \"C-c C-u\") 'git-gutter+-commit-toggle-author)\n1035 (define-key map (kbd \"C-c C-d\") 'git-gutter+-commit-toggle-date)\n1036 (define-key map (kbd \"C-c C-b\") 'git-commit-ack)\n1037 (define-key map (kbd \"M-p\") 'log-edit-previous-comment)\n1038 (define-key map (kbd \"M-n\") 'log-edit-next-comment)\n1039 map))\n\n1041 (defface git-gutter+-commit-header-face\n1042 '((t :inherit font-lock-comment-face))\n1043 \"Highlights the commit message header\"\n1044 :group 'git-gutter+-faces)\n\n1046 (defconst git-gutter+-commit-header-regex\n1047 (concat \"\\\\(?:.\\\\|\\n\\\\)*?\" (regexp-quote git-gutter+-commit-header-end)))\n\n1049 (defconst git-gutter+-skip-commit-header-regex\n1050 (concat \"\\\\`\\\\(?:\" git-gutter+-commit-header-regex \"\\\\)?\"))\n\n1052 ;; Modify git-commit-summary-regexp to ignore the commit header\n1053 (defadvice git-commit-summary-regexp\n1054 (after ignore-git-gutter+-commit-header activate compile)\n1055 (if (eq major-mode 'git-gutter+-commit-mode)\n1056 (setq ad-return-value\n1057 (concat git-gutter+-skip-commit-header-regex\n1058 (substring ; Remove leading \"\\\\`\"\n1059 ad-return-value 2)))))\n\n1061 (defun git-gutter+-commit-font-lock-keywords ()\n1062 \"Like `git-commit-mode-font-lock-keywords' but with commit header highlighting\"\n1063 `((,(concat \"\\\\`\" git-gutter+-commit-header-regex) . 'git-gutter+-commit-header-face)\n1064 ,@(git-commit-mode-font-lock-keywords)))\n\n\n1067 ;;; Magit synchronization\n1068 ;; Force Magit to refresh git-gutter+ when updating the VC mode line.\n\n1070 (defvar git-gutter+-orig-vc-find-file-hook)\n\n1072 (defvar git-gutter+-vc-find-file-hook-with-refresh\n1073 (lambda ()\n1074 (funcall git-gutter+-orig-vc-find-file-hook)\n1075 (if git-gutter+-mode (git-gutter+-refresh))))\n\n1077 (defadvice magit-update-vc-modeline (around refresh-git-gutter+ compile activate)\n1078 ;; `magit-update-vc-modeline' calls `vc-find-file-hook' (a function!) on each\n1079 ;; buffer in the repo. Temporarily rebind it to `vc-find-file-hook-with-refresh',\n1080 ;; which calls git-gutter+-refresh after updating the VC mode line.\n1081 ;;\n1082 ;; Using `flet' would have been much simpler, but it's deprecated since 24.3.\n1083 (setq git-gutter+-orig-vc-find-file-hook (symbol-function 'vc-find-file-hook))\n1084 (fset 'vc-find-file-hook git-gutter+-vc-find-file-hook-with-refresh)\n1085 (unwind-protect\n1086 ad-do-it\n1087 (fset 'vc-find-file-hook git-gutter+-orig-vc-find-file-hook)))\n\n1089 (provide 'git-gutter+)\n\n1091 ;;; git-gutter+.el ends here\n")) (setq helm-swoop-cache t))) (candidates-in-buffer) (get-line . buffer-substring-no-properties) (keymap keymap (C-M-left . backward-sexp) (C-M-right . forward-sexp) (27 keymap (105 . helm-multi-swoop-all-from-helm-swoop)) (3 keymap (5 . helm-swoop-edit)) keymap (C-M-left . paren-backward-sexp) (C-M-right . paren-forward-sexp) (94 . helm-swoop-caret-match) (menu-bar keymap (help-menu keymap (describe keymap (describe-mode . helm-help)))) (help keymap (109 . helm-help)) (f1 keymap (109 . helm-help)) (8 keymap (109 . helm-help) (104 . undefined) (8 . undefined) (4 . helm-debug-output)) (20 . helm-toggle-resplit-and-swap-windows) (C-tab . undefined) (triple-mouse-3 . ignore) (double-mouse-3 . ignore) (mouse-3 . ignore) (drag-mouse-3 . ignore) (down-mouse-3 . ignore) (triple-mouse-2 . ignore) (double-mouse-2 . ignore) (mouse-2 . ignore) (drag-mouse-2 . ignore) (down-mouse-2 . ignore) (triple-mouse-1 . ignore) (double-mouse-1 . ignore) (mouse-1 . ignore) (drag-mouse-1 . ignore) (down-mouse-1 . ignore) (67108897 . helm-toggle-suspend-update) (3 keymap (21 . helm-force-update) (6 . helm-follow-mode) (11 . helm-kill-selection-and-quit) (25 . helm-yank-selection) (4 . helm-delete-current-selection) (45 . helm-swap-windows)) (67108987 . helm-enlarge-window) (67108989 . helm-narrow-window) (19 . undefined) (18 . undefined) (23 . helm-yank-text-at-point) (24 keymap (2 . helm-resume-list-buffers-after-quit) (98 . helm-resume-previous-session-after-quit) (6 . helm-quit-and-find-file)) (11 . helm-delete-minibuffer-contents) (67108896 . helm-toggle-visible-mark) (0 . helm-toggle-visible-mark) (C-M-up . helm-scroll-other-window-down) (C-M-down . helm-scroll-other-window) (M-prior . helm-scroll-other-window-down) (M-next . helm-scroll-other-window) (12 . helm-recenter-top-bottom-other-window) (15 . helm-next-source) (10 . helm-select-3rd-action) (5 . helm-select-2nd-action-or-end-of-line) ...) (header-line . "[C-c C-e] Edit mode, [M-i] apply all buffers") (action lambda ($line) (helm-swoop--goto-line (when (string-match "^[0-9]+" $line) (string-to-number (match-string 0 $line)))) (when (re-search-forward (mapconcat (quote identity) (split-string helm-pattern " ") "\\|") nil t) (goto-char (match-beginning 0))) (helm-swoop--recenter)) (migemo)) #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) "Swoop: " nil "^368 " "*Helm Swoop*" nil nil nil nil nil mapcar #[257 "\211JB\207" [] 3 "\n\n(fn V)"] funcall make-byte-code 0 "\300\301\211:\203 @\262\211A\262@\262\211L\210A\262\202 \266\302\303!\207" vconcat vector [nil helm-log "restore variables"] 5 "\n\n(fn)" helm-initialize helm-display-buffer helm-log "show prompt" #[0 "\300 \207" [helm-cleanup] 1 "\n\n(fn)"] helm-read-pattern-maybe defalias helm-hook51420 "\302\303DC\216\300\211)\207" [overriding-local-map funcall #[0 "\300\301\302\"\207" [remove-hook helm-cleanup-hook helm-hook51420] 3 "\n\n(fn)"]] 2 add-hook helm-cleanup-hook helm-execute-selection-action "[End session] " make-string 41 45 helm-buffer helm-quit helm-in-persistent-action helm-current-source helm-source-name helm-restored-variables] 9 "\n\n(fn)"]) | |
#[0 "\311\312\313\314\315\"P!\210\"\316\211#\206: \317\300!\320\316\320:\2038 @\262$>\211\262?\211\262\2038 A\262\202 \266\203#%\320\262&\320\262\203Q \321\322!\210\323\324\325\326\327\330 \"\331\"\332\333%DC\216\334\323\324\325\335\327\330\300\301\302\303\304\305\306\307\310&\n\336\"\337\333%D\340\323\341\334EDC\217,\207" [((name . "git-gutter+.el") (init lambda nil (unless helm-swoop-cache (with-current-buffer (helm-candidate-buffer (quote local)) (insert "1 ;;; git-gutter+.el --- Manage Git hunks straight from the buffer\n\n3 ;; Copyright (C) 2013 by Syohei YOSHIDA and contributors\n\n5 ;; Author: Syohei YOSHIDA <syohex@gmail.com> and contributors\n6 ;; URL: https://github.com/nonsequitur/git-gutter-plus\n7 ;; Version: 0.1\n\n9 ;; This program is free software; you can redistribute it and/or modify\n10 ;; it under the terms of the GNU General Public License as published by\n11 ;; the Free Software Foundation, either version 3 of the License, or\n12 ;; (at your option) any later version.\n\n14 ;; This program is distributed in the hope that it will be useful,\n15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n17 ;; GNU General Public License for more details.\n\n19 ;; You should have received a copy of the GNU General Public License\n20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n22 ;; Package-Requires: ((git-commit-mode \"0.14\"))\n\n24 ;;; Commentary:\n25 ;;\n26 ;; View, stage and revert Git changes straight from the buffer.\n\n28 ;;; Code:\n\n30 (eval-when-compile\n31 (require 'cl))\n\n33 (require 'tramp)\n34 (require 'log-edit)\n35 (require 'git-commit-mode)\n\n37 (defgroup git-gutter+ nil\n38 \"Manage Git hunks straight from the buffer\"\n39 :prefix \"git-gutter+-\"\n40 :group 'vc)\n\n42 (defcustom git-gutter+-window-width nil\n43 \"Character width of the gutter margin. Set this variable if the automatically\n44 calculated width looks wrong. (This can happen with some special characters.)\"\n45 :type 'integer\n46 :group 'git-gutter+)\n\n48 (defcustom git-gutter+-git-executable \"git\"\n49 \"The path of the Git executable.\"\n50 :type 'string\n51 :group 'git-gutter+)\n\n53 (defcustom git-gutter+-diff-options nil\n54 \"List of strings containing extra arguments to 'git diff'\"\n55 :type 'list\n56 :group 'git-gutter+)\n\n58 (defcustom git-gutter+-separator-sign nil\n59 \"Separator sign\"\n60 :type 'string\n61 :group 'git-gutter+)\n\n63 (defcustom git-gutter+-modified-sign \"=\"\n64 \"Modified sign\"\n65 :type 'string\n66 :group 'git-gutter+)\n\n68 (defcustom git-gutter+-added-sign \"+\"\n69 \"Added sign\"\n70 :type 'string\n71 :group 'git-gutter+)\n\n73 (defcustom git-gutter+-deleted-sign \"-\"\n74 \"Deleted sign\"\n75 :type 'string\n76 :group 'git-gutter+)\n\n78 (defcustom git-gutter+-unchanged-sign nil\n79 \"Unchanged sign\"\n80 :type 'string\n81 :group 'git-gutter+)\n\n83 (defcustom git-gutter+-hide-gutter nil\n84 \"Hide gutter if there are no changes\"\n85 :type 'boolean\n86 :group 'git-gutter+)\n\n88 (defcustom git-gutter+-lighter \" GitGutter\"\n89 \"Minor mode lighter in mode-line\"\n90 :type 'string\n91 :group 'git-gutter+)\n\n93 (defface git-gutter+-separator\n94 '((t (:foreground \"cyan\" :weight bold)))\n95 \"Face of the separator\"\n96 :group 'git-gutter+)\n\n98 (defface git-gutter+-modified\n99 '((t (:foreground \"magenta\" :weight bold)))\n100 \"Face for modified lines\"\n101 :group 'git-gutter+)\n\n103 (defface git-gutter+-added\n104 '((t (:foreground \"green\" :weight bold)))\n105 \"Face for added lines\"\n106 :group 'git-gutter+)\n\n108 (defface git-gutter+-deleted\n109 '((t (:foreground \"red\" :weight bold)))\n110 \"Face for deleted lines\"\n111 :group 'git-gutter+)\n\n113 (defface git-gutter+-unchanged\n114 '((t (:background \"yellow\")))\n115 \"Face for unchanged lines\"\n116 :group 'git-gutter+)\n\n118 (defcustom git-gutter+-disabled-modes nil\n119 \"A list of modes for which `global-git-gutter+-mode' should be disabled.\"\n120 :type '(repeat symbol)\n121 :group 'git-gutter+)\n\n123 (defvar git-gutter+-mode-map\n124 (make-sparse-keymap))\n\n126 (defvar git-gutter+-view-diff-function nil\n127 \"Function to call for displaying diffs\")\n\n129 (defvar git-gutter+-clear-function nil\n130 \"Function to call for clearing the diff display\")\n\n132 (defvar git-gutter+-window-config-change-function nil\n133 \"Function to call when the buffer's local window configuration has changed\")\n\n135 (defvar git-gutter+-diffinfos nil)\n136 (defvar git-gutter+-diff-header nil)\n137 (make-variable-buffer-local 'git-gutter+-diffinfos)\n138 (make-variable-buffer-local 'git-gutter+-diff-header)\n\n140 (defvar git-gutter+-popup-buffer \"*git-gutter+-diff*\")\n141 (defvar git-gutter+-buffers-to-reenable nil)\n\n143 (defconst git-gutter+-hunk-header-regex\n144 ;; The same as diff-hunk-header-re-unified\n145 \"^@@ -\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? \\\\+\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? @@\")\n\n147 (defalias 'git-gutter+-popup-hunk 'git-gutter+-show-hunk)\n148 (defalias 'git-gutter+-revert-hunk 'git-gutter+-revert-hunks)\n\n150 (defmacro git-gutter+-awhen (test &rest body)\n151 \"Anaphoric when.\"\n152 (declare (indent 1))\n153 `(let ((it ,test))\n154 (when it ,@body)))\n\n156 (defun git-gutter+-enable-default-display-mode ()\n157 (setq git-gutter+-view-diff-function 'git-gutter+-view-diff-infos\n158 git-gutter+-clear-function 'git-gutter+-clear-diff-infos\n159 git-gutter+-window-config-change-function 'git-gutter+-show-gutter))\n\n161 (unless git-gutter+-view-diff-function\n162 (git-gutter+-enable-default-display-mode))\n\n164 (defun git-gutter+-call-git (args &optional file)\n165 (if (and file (file-remote-p file))\n166 (apply #'process-file git-gutter+-git-executable nil t nil args)\n167 (apply #'call-process git-gutter+-git-executable nil t nil args)))\n\n169 (defun git-gutter+-in-git-repository-p (file)\n170 (with-temp-buffer\n171 (let ((args '(\"rev-parse\" \"--is-inside-work-tree\")))\n172 (when (zerop (git-gutter+-call-git args file))\n173 (goto-char (point-min))\n174 (string= \"true\" (buffer-substring-no-properties\n175 (point) (line-end-position)))))))\n\n177 (defun git-gutter+-root-directory (file)\n178 (with-temp-buffer\n179 (let* ((args '(\"rev-parse\" \"--show-toplevel\"))\n180 (ret (git-gutter+-call-git args file)))\n181 (when (zerop ret)\n182 (goto-char (point-min))\n183 (let ((root (buffer-substring-no-properties (point) (line-end-position))))\n184 (unless (string= root \"\")\n185 (file-name-as-directory root)))))))\n\n187 (defsubst git-gutter+-diff-args (file)\n188 (delq nil (list \"--no-pager\" \"diff\" \"--no-color\" \"--no-ext-diff\" \"-U0\"\n189 git-gutter+-diff-options file)))\n\n191 (defun git-gutter+-diff (curfile)\n192 (let ((args (git-gutter+-diff-args curfile))\n193 (file (buffer-file-name))) ;; for tramp\n194 (with-temp-buffer\n195 (when (zerop (git-gutter+-call-git args file))\n196 (goto-char (point-min))\n197 (let ((diff-header (git-gutter+-get-diff-header))\n198 (diffinfos (git-gutter+-get-diffinfos)))\n199 (list diff-header diffinfos))))))\n\n201 (defun git-gutter+-get-diff-header ()\n202 (save-excursion\n203 (if (re-search-forward git-gutter+-hunk-header-regex nil t)\n204 (buffer-substring (point-min) (match-beginning 0)))))\n\n206 (defsubst git-gutter+-make-diffinfo (type content start end)\n207 (list :type type :content content :start-line start :end-line end))\n\n209 (defun git-gutter+-get-diffinfos ()\n210 (loop while (re-search-forward git-gutter+-hunk-header-regex nil t)\n211 ;; Hunk header format:\n212 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n213 for del-len = (string-to-number (or (match-string 2) \"1\"))\n214 for add-line = (string-to-number (match-string 3))\n215 for add-len = (string-to-number (or (match-string 4) \"1\"))\n216 for type = (cond ((zerop del-len) 'added)\n217 ((zerop add-len) 'deleted)\n218 (t 'modified))\n219 for start-line = (if (eq type 'deleted)\n220 (1+ add-line)\n221 add-line)\n222 for end-line = (if (eq type 'deleted)\n223 start-line\n224 (1- (+ add-line add-len)))\n225 for content = (git-gutter+-diff-content)\n226 collect\n227 (git-gutter+-make-diffinfo type content start-line end-line)))\n\n229 (defun git-gutter+-diff-content ()\n230 (save-excursion\n231 (goto-char (line-beginning-position)) ; Move to beginning of hunk header\n232 (let ((hunk-start (point)))\n233 ;; Move to end of hunk\n234 (forward-line 1)\n235 (if (re-search-forward \"^@@\" nil t)\n236 (backward-char 3) ;; exclude \"\\n@@\"\n237 (goto-char (1- (point-max)))) ; Skip trailing newline\n238 (buffer-substring hunk-start (point)))))\n\n240 (defun git-gutter+-line-to-pos (line)\n241 (save-excursion\n242 (goto-char (point-min))\n243 (forward-line (1- line))\n244 (point)))\n\n246 (defun git-gutter+-before-string (sign)\n247 (let* ((sep-sign git-gutter+-separator-sign)\n248 (sep (when sep-sign\n249 (propertize sep-sign 'face 'git-gutter+-separator)))\n250 (gutter-sep (concat sign sep)))\n251 (propertize \" \" 'display `((margin left-margin) ,gutter-sep))))\n\n253 (defsubst git-gutter+-select-face (type)\n254 (case type\n255 (added 'git-gutter+-added)\n256 (modified 'git-gutter+-modified)\n257 (deleted 'git-gutter+-deleted)))\n\n259 (defsubst git-gutter+-select-sign (type)\n260 (case type\n261 (added git-gutter+-added-sign)\n262 (modified git-gutter+-modified-sign)\n263 (deleted git-gutter+-deleted-sign)))\n\n265 (defun git-gutter+-propertized-sign (type)\n266 (let ((sign (git-gutter+-select-sign type))\n267 (face (git-gutter+-select-face type)))\n268 (propertize sign 'face face)))\n\n270 (defun git-gutter+-view-region (sign start-line end-line)\n271 (let ((beg (git-gutter+-line-to-pos start-line)))\n272 (goto-char beg)\n273 (while (and (<= (line-number-at-pos) end-line) (not (eobp)))\n274 (git-gutter+-view-at-pos sign (point))\n275 (forward-line 1))))\n\n277 (defun git-gutter+-view-at-pos (sign pos)\n278 (let ((ov (make-overlay pos pos)))\n279 (overlay-put ov 'before-string (git-gutter+-before-string sign))\n280 (overlay-put ov 'git-gutter+ t)))\n\n282 (defun git-gutter+-view-diff-info (diffinfo)\n283 (let* ((start-line (plist-get diffinfo :start-line))\n284 (end-line (plist-get diffinfo :end-line))\n285 (type (plist-get diffinfo :type))\n286 (sign (git-gutter+-propertized-sign type)))\n287 (case type\n288 ((modified added) (git-gutter+-view-region sign start-line end-line))\n289 (deleted (git-gutter+-view-at-pos\n290 sign (git-gutter+-line-to-pos start-line))))))\n\n292 (defun git-gutter+-sign-width (sign)\n293 (loop for s across sign\n294 sum (char-width s)))\n\n296 (defun git-gutter+-longest-sign-width ()\n297 (let ((signs (list git-gutter+-modified-sign\n298 git-gutter+-added-sign\n299 git-gutter+-deleted-sign)))\n300 (when git-gutter+-unchanged-sign\n301 (add-to-list 'signs git-gutter+-unchanged-sign))\n302 (+ (apply 'max (mapcar 'git-gutter+-sign-width signs))\n303 (git-gutter+-sign-width git-gutter+-separator-sign))))\n\n305 (defun git-gutter+-view-for-unchanged ()\n306 (save-excursion\n307 (let ((sign (if git-gutter+-unchanged-sign\n308 (propertize git-gutter+-unchanged-sign\n309 'face 'git-gutter+-unchanged)\n310 \" \")))\n311 (goto-char (point-min))\n312 (while (not (eobp))\n313 (git-gutter+-view-at-pos sign (point))\n314 (forward-line 1)))))\n\n316 (defun git-gutter+-set-window-margin (width)\n317 (let ((curwin (get-buffer-window)))\n318 (set-window-margins curwin width (cdr (window-margins curwin)))))\n\n320 (defsubst git-gutter+-file-buffer-p ()\n321 (and (buffer-file-name)\n322 default-directory\n323 (file-directory-p default-directory)))\n\n325 ;;;###autoload\n326 (define-minor-mode git-gutter+-mode\n327 \"Git-Gutter mode\"\n328 :group 'git-gutter+\n329 :init-value nil\n330 :global nil\n331 :lighter git-gutter+-lighter\n332 (if git-gutter+-mode\n333 (if (and (git-gutter+-file-buffer-p)\n334 (git-gutter+-in-git-repository-p (buffer-file-name)))\n335 (progn\n336 (git-gutter+-add-local-hooks)\n337 (git-gutter+-refresh))\n338 (if (called-interactively-p 'any)\n339 (message \"No Git repo for current buffer\"))\n340 (git-gutter+-mode -1))\n341 (git-gutter+-remove-local-hooks)\n342 (git-gutter+-clear)))\n\n344 (defun git-gutter+-add-local-hooks ()\n345 (add-hook 'after-save-hook 'git-gutter+-refresh nil t)\n346 ;; Turn off `git-gutter+-mode' while reverting to prevent any redundant calls to\n347 ;; `git-gutter+-refresh'.\n348 (add-hook 'before-revert-hook 'git-gutter+-turn-off nil t)\n349 (add-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change nil t)\n350 (if git-gutter+-window-config-change-function\n351 (add-hook 'window-configuration-change-hook\n352 git-gutter+-window-config-change-function nil t)))\n\n354 (defun git-gutter+-remove-local-hooks ()\n355 (remove-hook 'after-save-hook 'git-gutter+-refresh t)\n356 (remove-hook 'before-revert-hook 'git-gutter+-turn-off t)\n357 (remove-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change t)\n358 (if git-gutter+-window-config-change-function\n359 (remove-hook 'window-configuration-change-hook\n360 git-gutter+-window-config-change-function t)))\n\n362 (defmacro git-gutter+-in-all-buffers (&rest body)\n363 `(dolist (buf (buffer-list))\n364 (with-current-buffer buf\n365 ,@body)))\n\n367 ;; When `define-globalized-minor-mode' is used to define `global-git-gutter+-mode',\n368 ;; `git-gutter+-mode' and thus `git-gutter+-refresh' get run twice when a new file\n369 ;; is opened. (First for `fundamental-mode', then for the file-specific mode.)\n370 ;; The following definition of `global-git-gutter+-mode' avoids any redundant calls to\n371 ;; `git-gutter+-refresh'.\n\n373 ;;;###autoload\n374 (define-minor-mode global-git-gutter+-mode ()\n375 \"Global Git-Gutter mode\"\n376 :group 'git-gutter+\n377 :init-value nil\n378 :global t\n379 (if global-git-gutter+-mode\n380 (progn\n381 (add-hook 'find-file-hook 'git-gutter+-turn-on)\n382 (add-hook 'after-revert-hook 'git-gutter+-turn-on)\n383 (add-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n384 (git-gutter+-in-all-buffers (git-gutter+-turn-on)))\n385 (remove-hook 'find-file-hook 'git-gutter+-turn-on)\n386 (remove-hook 'after-revert-hook 'git-gutter+-turn-on)\n387 (remove-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n388 (git-gutter+-in-all-buffers (git-gutter+-turn-off))))\n\n390 (defun git-gutter+-turn-on ()\n391 (when (and (buffer-file-name)\n392 (not (memq major-mode git-gutter+-disabled-modes))\n393 (not git-gutter+-mode))\n394 (git-gutter+-mode t)))\n\n396 (defun git-gutter+-turn-off ()\n397 (if git-gutter+-mode (git-gutter+-mode -1)))\n\n399 (defun git-gutter+-reenable-after-major-mode-change ()\n400 (if global-git-gutter+-mode\n401 (add-to-list 'git-gutter+-buffers-to-reenable (current-buffer))))\n\n403 (defun git-gutter+-reenable-buffers ()\n404 (dolist (buf git-gutter+-buffers-to-reenable)\n405 (with-current-buffer buf\n406 (git-gutter+-turn-on)))\n407 (setq git-gutter+-buffers-to-reenable nil))\n\n409 (defsubst git-gutter+-show-gutter-p (diffinfos)\n410 (if git-gutter+-hide-gutter\n411 (or diffinfos git-gutter+-unchanged-sign)\n412 (or global-git-gutter+-mode git-gutter+-unchanged-sign diffinfos)))\n\n414 (defun git-gutter+-show-gutter (&optional diffinfos)\n415 (when (git-gutter+-show-gutter-p (or diffinfos git-gutter+-diffinfos))\n416 (let ((win-width (or git-gutter+-window-width\n417 (git-gutter+-longest-sign-width))))\n418 (git-gutter+-set-window-margin win-width))))\n\n420 (defun git-gutter+-view-diff-infos (diffinfos)\n421 (when (or git-gutter+-unchanged-sign\n422 git-gutter+-separator-sign)\n423 (git-gutter+-view-for-unchanged))\n424 (when diffinfos\n425 (save-excursion\n426 (mapc 'git-gutter+-view-diff-info diffinfos)))\n427 (git-gutter+-show-gutter diffinfos))\n\n429 (defsubst git-gutter+-reset-window-margin-p ()\n430 (or git-gutter+-hide-gutter\n431 (not global-git-gutter+-mode)))\n\n433 (defun git-gutter+-clear-diff-infos ()\n434 (when (git-gutter+-reset-window-margin-p)\n435 (git-gutter+-set-window-margin 0))\n436 (remove-overlays (point-min) (point-max) 'git-gutter+ t))\n\n438 (defun git-gutter+-process-diff (curfile)\n439 (destructuring-bind\n440 (diff-header diffinfos) (git-gutter+-diff curfile)\n441 (setq git-gutter+-diff-header diff-header\n442 git-gutter+-diffinfos diffinfos)\n443 (save-restriction\n444 (widen)\n445 (funcall git-gutter+-view-diff-function diffinfos))))\n\n447 (defun git-gutter+-search-near-diff-index (diffinfos is-reverse)\n448 (loop with current-line = (line-number-at-pos)\n449 with cmp-fn = (if is-reverse '> '<)\n450 for diffinfo in (if is-reverse (reverse diffinfos) diffinfos)\n451 for index = 0 then (1+ index)\n452 for start-line = (plist-get diffinfo :start-line)\n453 when (funcall cmp-fn current-line start-line)\n454 return (if is-reverse\n455 (1- (- (length diffinfos) index))\n456 index)))\n\n458 (defun git-gutter+-diffinfo-at-point ()\n459 (save-restriction\n460 (widen)\n461 (loop with current-line = (line-number-at-pos)\n462 for diffinfo in git-gutter+-diffinfos\n463 for start = (plist-get diffinfo :start-line)\n464 for end = (or (plist-get diffinfo :end-line) (1+ start))\n465 when (and (>= current-line start) (<= current-line end))\n466 return diffinfo)))\n\n468 (defun git-gutter+-collect-deleted-line (str)\n469 (with-temp-buffer\n470 (insert str)\n471 (goto-char (point-min))\n472 (loop while (re-search-forward \"^-\\\\(.*?\\\\)$\" nil t)\n473 collect (match-string 1) into deleted-lines\n474 finally return deleted-lines)))\n\n476 (defun git-gutter+-delete-added-lines (start-line end-line)\n477 (forward-line (1- start-line))\n478 (let ((start-point (point)))\n479 (forward-line (1+ (- end-line start-line)))\n480 (delete-region start-point (point))))\n\n482 (defun git-gutter+-insert-deleted-lines (content)\n483 (dolist (line (git-gutter+-collect-deleted-line content))\n484 (insert (concat line \"\\n\"))))\n\n486 (defun git-gutter+-do-revert-hunk (diffinfo)\n487 (save-excursion\n488 (save-restriction\n489 (widen)\n490 (goto-char (point-min))\n491 (let ((start-line (plist-get diffinfo :start-line))\n492 (end-line (plist-get diffinfo :end-line))\n493 (content (plist-get diffinfo :content)))\n494 (case (plist-get diffinfo :type)\n495 (added (git-gutter+-delete-added-lines start-line end-line))\n496 (deleted (forward-line (1- start-line))\n497 (git-gutter+-insert-deleted-lines content))\n498 (modified (git-gutter+-delete-added-lines start-line end-line)\n499 (git-gutter+-insert-deleted-lines content)))))))\n\n501 (defun git-gutter+-revert-hunks ()\n502 \"Revert hunk at point. If region is active, revert all hunks within the region.\"\n503 (interactive)\n504 (let* ((diffinfos (git-gutter+-selected-diffinfos))\n505 (one-diffinfo-p (= 1 (length diffinfos))))\n506 (save-window-excursion\n507 (if one-diffinfo-p (git-gutter+-show-hunk (car diffinfos)))\n508 (when (and diffinfos\n509 (yes-or-no-p (if one-diffinfo-p\n510 \"Revert hunk?\"\n511 (format \"Revert %d hunks?\" (length diffinfos)))))\n512 ;; Revert diffinfos in reverse so that earlier hunks don't invalidate the\n513 ;; line number information of the later hunks.\n514 (dolist (diffinfo (nreverse diffinfos))\n515 (git-gutter+-do-revert-hunk diffinfo))\n516 (save-buffer))\n517 (if one-diffinfo-p\n518 (git-gutter+-awhen (get-buffer git-gutter+-popup-buffer)\n519 (kill-buffer it))))))\n\n521 (defun git-gutter+-show-hunk (&optional diffinfo)\n522 \"Show hunk at point in another window\"\n523 (interactive)\n524 (git-gutter+-awhen (or diffinfo\n525 (git-gutter+-diffinfo-at-point))\n526 (save-selected-window\n527 (with-current-buffer (get-buffer-create git-gutter+-popup-buffer)\n528 (setq buffer-read-only nil)\n529 (erase-buffer)\n530 (insert (plist-get it :content))\n531 (insert \"\\n\")\n532 (goto-char (point-min))\n533 (diff-mode)\n534 (view-mode)\n535 (pop-to-buffer (current-buffer))))))\n\n537 (defun git-gutter+-next-hunk (arg)\n538 \"Move to next diff hunk\"\n539 (interactive \"p\")\n540 (if (not git-gutter+-diffinfos)\n541 (message \"No changes in buffer\")\n542 (save-restriction\n543 (widen)\n544 (let* ((is-reverse (< arg 0))\n545 (diffinfos git-gutter+-diffinfos)\n546 (len (length diffinfos))\n547 (index (git-gutter+-search-near-diff-index diffinfos is-reverse))\n548 (real-index (if index\n549 (let ((next (if is-reverse (1+ index) (1- index))))\n550 (mod (+ arg next) len))\n551 (if is-reverse (1- (length diffinfos)) 0)))\n552 (diffinfo (nth real-index diffinfos)))\n553 (goto-char (point-min))\n554 (forward-line (1- (plist-get diffinfo :start-line)))\n555 (when (buffer-live-p (get-buffer git-gutter+-popup-buffer))\n556 (save-window-excursion\n557 (git-gutter+-show-hunk)))))))\n\n559 (defun git-gutter+-previous-hunk (arg)\n560 \"Move to previous diff hunk\"\n561 (interactive \"p\")\n562 (git-gutter+-next-hunk (- arg)))\n\n564 (defun git-gutter+-remote-default-directory (dir file)\n565 (let* ((vec (tramp-dissect-file-name file))\n566 (method (aref vec 0))\n567 (user (aref vec 1))\n568 (host (aref vec 2)))\n569 (format \"/%s:%s%s:%s\" method (if user (concat user \"@\") \"\") host dir)))\n\n571 (defun git-gutter+-remote-file-path (dir file)\n572 (let ((file (aref (tramp-dissect-file-name file) 3)))\n573 (replace-regexp-in-string (concat \"\\\\`\" dir) \"\" file)))\n\n575 (defun git-gutter+-local-file-path (file)\n576 (if (eq system-type 'windows-nt)\n577 ;; Cygwin can't handle Windows absolute paths\n578 (file-relative-name file default-directory)\n579 file))\n\n581 (defun git-gutter+-refresh ()\n582 (git-gutter+-clear)\n583 (let ((file (buffer-file-name)))\n584 (when (and file (file-exists-p file))\n585 (if (file-remote-p file)\n586 (let* ((repo-root (git-gutter+-root-directory file))\n587 (default-directory (git-gutter+-remote-default-directory repo-root file)))\n588 (git-gutter+-process-diff (git-gutter+-remote-file-path repo-root file)))\n589 (git-gutter+-process-diff (git-gutter+-local-file-path file))))))\n\n591 (defun git-gutter+-clear ()\n592 (save-restriction\n593 (widen)\n594 (funcall git-gutter+-clear-function))\n595 (setq git-gutter+-diffinfos nil))\n\n\n598 ;;; Staging\n\n600 (defun git-gutter+-stage-hunks ()\n601 \"Stage hunk at point. If region is active, stage all hunk lines within the region.\"\n602 (interactive)\n603 (let* ((line-range (if (use-region-p)\n604 (cons (line-number-at-pos (region-beginning))\n605 (line-number-at-pos (region-end)))))\n606 (diffinfos (git-gutter+-selected-diffinfos line-range)))\n607 (when diffinfos\n608 (let ((error-msg (git-gutter+-stage-diffinfos diffinfos line-range)))\n609 (if error-msg\n610 (message \"Error staging hunks:\\n%s\" error-msg))\n611 (git-gutter+-refresh)))))\n\n613 (defun git-gutter+-selected-diffinfos (&optional line-range)\n614 (unless line-range\n615 (setq line-range (if (use-region-p)\n616 (cons (line-number-at-pos (region-beginning))\n617 (line-number-at-pos (region-end))))))\n618 (if line-range\n619 (git-gutter+-diffinfos-between-lines line-range)\n620 (git-gutter+-awhen (git-gutter+-diffinfo-at-point)\n621 (list it))))\n\n623 (defsubst git-gutter+-diffinfo-between-lines-p (diffinfo start-line end-line)\n624 (let ((diff-start (plist-get diffinfo :start-line))\n625 (diff-end (plist-get diffinfo :end-line)))\n626 (and (<= start-line diff-end)\n627 (<= diff-start end-line))))\n\n629 (defun git-gutter+-diffinfos-between-lines (line-range)\n630 (save-restriction\n631 (widen)\n632 (let ((start-line (car line-range))\n633 (end-line (cdr line-range)))\n634 (delq nil\n635 (mapcar (lambda (diffinfo)\n636 (if (git-gutter+-diffinfo-between-lines-p\n637 diffinfo start-line end-line)\n638 diffinfo))\n639 git-gutter+-diffinfos)))))\n\n641 (defun git-gutter+-stage-diffinfos (diffinfos line-range)\n642 (let ((header git-gutter+-diff-header))\n643 (with-temp-buffer\n644 (insert header)\n645 ;; Insert hunks in reverse so that earlier hunks don't invalidate the line\n646 ;; number information of the later hunks.\n647 (dolist (diffinfo (nreverse diffinfos))\n648 (git-gutter+-insert-diffinfo diffinfo line-range)\n649 (goto-char (point-max)))\n650 (git-gutter+-call-git-on-current-buffer\n651 '(\"apply\" \"--unidiff-zero\" \"--cached\" \"-\")))))\n\n653 (defun git-gutter+-insert-diffinfo (diffinfo line-range)\n654 (let ((content (plist-get diffinfo :content))\n655 (type (plist-get diffinfo :type)))\n656 (if (not line-range)\n657 (git-gutter+-insert-hunk content type)\n658 (let ((diff-start-line (plist-get diffinfo :start-line))\n659 (diff-end-line (plist-get diffinfo :end-line))\n660 (start-line (car line-range))\n661 (end-line (cdr line-range)))\n662 (git-gutter+-insert-hunk content type\n663 (1+ (- start-line diff-start-line))\n664 (1+ (- end-line diff-start-line)))))))\n\n666 (defun git-gutter+-call-git-on-current-buffer (args)\n667 \"Sends the current buffer contents to Git and replaces them with Git's output.\n\n669 RETURNS nil if Git ran successfully. Returns an error description otherwise.\"\n670 (unless (zerop (apply #'call-process-region (point-min) (point-max)\n671 git-gutter+-git-executable t t nil args))\n672 (buffer-string)))\n\n674 (defsubst git-gutter+-read-hunk-header (hunk)\n675 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n676 (string-match git-gutter+-hunk-header-regex hunk)\n677 (list (string-to-number (match-string 1 hunk))\n678 (string-to-number (or (match-string 2 hunk) \"1\"))\n679 (string-to-number (match-string 3 hunk))\n680 (string-to-number (or (match-string 4 hunk) \"1\"))))\n\n682 (defun git-gutter+-insert-hunk (hunk type &optional start end)\n683 \"If START and END are provided, only insert addition (+) lines between\n684 START and END (inclusive). START and END are both line numbers starting with 1.\"\n685 (destructuring-bind\n686 (del-line del-len add-line add-len) (git-gutter+-read-hunk-header hunk)\n687 (let* ((start (max 1 (or start 1)))\n688 (end (min add-len (or end add-len)))\n689 (insert-all-p (or (eq type :deleted)\n690 (and (= start 1) (= end add-len))))\n691 (num-lines-selected (if insert-all-p\n692 add-len\n693 (1+ (- end start)))))\n694 ;; When the user selected the last lines of a hunk with type `modified' (but\n695 ;; not the complete hunk), then don't insert any deletion (-) lines from that\n696 ;; hunk.\n697 (if (and (eq type 'modified)\n698 (> start 1) (= end add-len))\n699 (setq type 'modified-trailing))\n\n701 (save-excursion\n702 (insert hunk \"\\n\"))\n\n704 (git-gutter+-delete-hunk-header)\n\n706 (if (not insert-all-p)\n707 (git-gutter+-modify-hunk type num-lines-selected del-len start))\n\n709 (let ((hunk-header (git-gutter+-make-hunk-header type num-lines-selected\n710 del-line del-len add-line)))\n711 (insert hunk-header \"\\n\")))))\n\n713 (defun git-gutter+-delete-hunk-header ()\n714 (let ((hunk-start (point)))\n715 (forward-line 1)\n716 (delete-region hunk-start (point))))\n\n718 (defun git-gutter+-modify-hunk (type num-lines-selected del-len start)\n719 \"Remove all addition (+) lines from hunk that aren't selected.\n720 If TYPE is not `modified', also remove all deletion (-) lines.\"\n721 (let ((first-line-selected (+ del-len (1- start)))\n722 selected-lines)\n723 (save-excursion\n724 (forward-line first-line-selected)\n725 (let ((selection-start (point)))\n726 (forward-line num-lines-selected)\n727 (setq selected-lines (buffer-substring selection-start (point)))))\n728 (save-excursion\n729 (if (eq type 'modified) (forward-line del-len)) ; skip over deletion (-) lines\n730 (delete-region (point) (point-max))\n731 (insert selected-lines))))\n\n733 (defun git-gutter+-make-hunk-header (type num-lines-selected del-line del-len add-line)\n734 (let ((add-len num-lines-selected))\n735 (case type\n736 (added (setq add-line (1+ del-line)))\n737 (modified-trailing (setq add-line (+ del-line del-len)\n738 del-line (1- add-line)\n739 del-len 0))\n740 (t (setq add-line del-line)))\n741 (format \"@@ -%d,%d +%d,%d @@\"\n742 del-line del-len\n743 add-line add-len)))\n\n\n746 ;;; Committing\n747 ;; This section draws heavily from old Magit source code.\n\n749 (defvar git-gutter+-pre-commit-window-config nil)\n750 (defvar git-gutter+-commit-origin-buffer nil\n751 \"Buffer that started the commit\")\n\n753 (defconst git-gutter+-commit-buffer-name \"*Commit Message*\")\n754 (defconst git-gutter+-staged-changes-buffer-name \"*Staged Changes*\")\n\n756 ;;;###autoload\n757 (defun git-gutter+-commit ()\n758 \"Commit staged changes. If nothing is staged, ask to stage the current buffer.\"\n759 (interactive)\n\n761 (when (and (not (git-gutter+-anything-staged-p))\n762 git-gutter+-diffinfos\n763 (y-or-n-p \"Nothing staged. Stage current buffer? \"))\n764 (git-gutter+-stage-whole-buffer))\n\n766 (let ((file (buffer-file-name))\n767 (dir default-directory))\n768 (git-gutter+-save-window-config-if-needed)\n769 (setq git-gutter+-commit-origin-buffer (current-buffer))\n770 (git-gutter+-open-commit-edit-buffer dir)\n771 (git-gutter+-show-staged-changes file dir)))\n\n773 (defun git-gutter+-stage-and-commit ()\n774 (interactive)\n775 (git-gutter+-stage-hunks)\n776 (git-gutter+-commit))\n\n778 (defun git-gutter+-save-window-config-if-needed ()\n779 ;; Only save the window config if the temporary buffers that get popped-up by\n780 ;; git-gutter+ are not already visible.\n781 ;; In this way, `git-gutter+-commit' can be called twice in a row without\n782 ;; losing the original window config.\n783 (when (not (and git-gutter+-pre-commit-window-config\n784 (get-buffer-window git-gutter+-commit-buffer-name)\n785 (get-buffer-window git-gutter+-staged-changes-buffer-name)))\n786 (setq git-gutter+-pre-commit-window-config (current-window-configuration))))\n\n788 (defun git-gutter+-open-commit-edit-buffer (dir)\n789 \"Opens a buffer for composing the commit message\"\n790 (pop-to-buffer (get-buffer-create git-gutter+-commit-buffer-name))\n791 (setq default-directory dir)\n792 (git-gutter+-commit-mode)\n793 (message \"Type C-c C-c to commit (C-c C-k to cancel).\"))\n\n795 (defsubst git-gutter+-pop-to-staged-changes-buffer ()\n796 (let* ((buf (get-buffer-create git-gutter+-staged-changes-buffer-name))\n797 (window (get-buffer-window buf)))\n798 (if window\n799 ;; Buffer is already visible\n800 (select-window window)\n801 (if (<= (length (window-list)) 2)\n802 (split-window))\n803 (pop-to-buffer buf))))\n\n805 (defun git-gutter+-show-staged-changes (file dir)\n806 (save-selected-window\n807 (git-gutter+-pop-to-staged-changes-buffer)\n808 (setq buffer-read-only nil)\n809 (erase-buffer)\n810 (let ((default-directory dir))\n811 (git-gutter+-call-git '(\"diff\" \"--staged\") file))\n812 (goto-char (point-min))\n813 (diff-mode)\n814 (view-mode)))\n\n816 (defsubst git-gutter+-abort-commit-when-no-changes (allow-empty amend)\n817 (unless (or amend\n818 allow-empty\n819 (git-gutter+-anything-staged-p))\n820 (error\n821 \"Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?\"\n822 (key-description (car (where-is-internal\n823 'git-gutter+-commit-toggle-amending)))\n824 (key-description (car (where-is-internal\n825 'git-gutter+-commit-toggle-allow-empty))))))\n\n827 (defsubst git-gutter+-buffer-is-whitespace ()\n828 (save-excursion\n829 (goto-char (point-min))\n830 (looking-at-p \"[ \\t\\n]*\\\\'\")))\n\n832 (defun git-gutter+-publish-commit ()\n833 \"Publish commit\"\n834 (interactive)\n835 (let* ((fields (git-gutter+-commit-get-fields))\n836 (amend (equal \"yes\" (git-gutter+-commit-get-field 'amend fields)))\n837 (allow-empty (equal \"yes\" (git-gutter+-commit-get-field 'allow-empty fields)))\n838 (author (git-gutter+-commit-get-field 'author fields))\n839 (date (git-gutter+-commit-get-field 'date fields)))\n\n841 (git-gutter+-abort-commit-when-no-changes allow-empty amend)\n\n843 (git-gutter+-push-to-comment-ring (buffer-string))\n\n845 (git-gutter+-commit-set-fields nil) ; Delete message header\n\n847 (when (git-gutter+-buffer-is-whitespace)\n848 (erase-buffer)\n849 (insert \"(Empty description)\"))\n\n851 (let ((error-msg (git-gutter+-call-git-on-current-buffer\n852 (append '(\"--no-pager\" \"commit\" \"-F\" \"-\")\n853 (if amend '(\"--amend\"))\n854 (if allow-empty '(\"--allow-empty\"))\n855 (if author (list (concat \"--author=\" author)))\n856 (if date (list (concat \"--date=\" date)))))))\n857 (if error-msg\n858 (progn\n859 (message \"Commit error:\\n%s\" error-msg)\n860 (erase-buffer)\n861 (insert (ring-ref log-edit-comment-ring 0))) ; Reinsert commit message\n862 (message \"Commit successful.\")\n863 (git-gutter+-close-commit-edit-buffer)\n864 (git-gutter+-update-vc-modeline)))))\n\n866 (defun git-gutter+-close-commit-edit-buffer ()\n867 \"Abort edits and discard commit message being composed.\"\n868 (interactive)\n869 (kill-buffer)\n870 (set-window-configuration git-gutter+-pre-commit-window-config))\n\n872 (defun git-gutter+-update-vc-modeline ()\n873 (when (buffer-live-p git-gutter+-commit-origin-buffer)\n874 (with-current-buffer git-gutter+-commit-origin-buffer\n875 ;; Updating the modeline has no effect if the buffer still has\n876 ;; changes - it will remain in the 'modified' state. So skip it then.\n877 (unless git-gutter+-diffinfos\n878 (ignore-errors (vc-find-file-hook))))))\n\n880 (defun git-gutter+-stage-whole-buffer ()\n881 (save-excursion\n882 (mark-whole-buffer)\n883 (git-gutter+-stage-hunks)))\n\n885 (defun git-gutter+-anything-staged-p ()\n886 \"Return t if the current repo has staged changes\"\n887 (not (zerop (git-gutter+-call-git '(\"diff\" \"--quiet\" \"--cached\")))))\n\n889 (defun git-gutter+-commit-toggle-amending ()\n890 \"Toggle whether this will be an amendment to the previous commit.\n891 \\(i.e., whether commit is run via 'git commit --amend')\"\n892 (interactive)\n893 ;; Remove the newline that 'git-commit-mode' adds to a new commit\n894 ;; message buffer by default. This prevents an ugly visual\n895 ;; gap between the commit message header and the previous commit\n896 ;; message.\n897 (when (git-gutter+-buffer-is-whitespace)\n898 (erase-buffer))\n\n900 (let ((amend-was-already-set (git-gutter+-commit-get-field 'amend)))\n901 (git-gutter+-commit-toggle-field 'amend t)\n902 (unless amend-was-already-set\n903 ;; Insert previous commit message\n904 (goto-char (point-max))\n905 (unless (zerop (current-column))\n906 (insert \"\\n\"))\n907 (insert (git-gutter+-get-last-commit-msg)\n908 \"\\n\"))))\n\n910 (defun git-gutter+-commit-toggle-allow-empty ()\n911 \"Toggle whether this commit is allowed to be empty.\n912 \\(i.e., whether commit is run via 'git commit --allow-empty')\"\n913 (interactive)\n914 (git-gutter+-commit-toggle-field 'allow-empty t))\n\n916 (defun git-gutter+-format-author (author email)\n917 (format \"%s <%s>\" author email))\n\n919 (defun git-gutter+-commit-toggle-author ()\n920 \"Toggle whether this commit should have a user-defined author.\"\n921 (interactive)\n922 (git-gutter+-commit-toggle-input\n923 'author (git-gutter+-format-author\n924 (or (git-gutter+-get-cfg \"user\" \"name\") \"Author Name\")\n925 (or (git-gutter+-get-cfg \"user\" \"email\") \"author@email\"))))\n\n927 (defun git-gutter+-commit-toggle-date ()\n928 \"Toggle whether this commit should have a user-defined date.\"\n929 (interactive)\n930 (git-gutter+-commit-toggle-input 'date\n931 ;; ISO 8601\n932 (format-time-string \"%Y-%m-%dT%T%z\" (current-time))))\n\n934 (defun git-gutter+-push-to-comment-ring (comment)\n935 (when (or (ring-empty-p log-edit-comment-ring)\n936 (not (equal comment (ring-ref log-edit-comment-ring 0))))\n937 (ring-insert log-edit-comment-ring comment)))\n\n939 (defun git-gutter+-get-last-commit-msg ()\n940 (git-gutter+-git-output '(\"log\" \"--max-count=1\" \"--pretty=format:%s%n%n%b\" \"HEAD\")))\n\n942 (defun git-gutter+-get-cfg (&rest keys)\n943 (git-gutter+-git-output (list \"config\" (mapconcat 'identity keys \".\"))))\n\n945 (defun git-gutter+-git-output (args)\n946 (with-temp-buffer\n947 (git-gutter+-call-git args)\n948 ;; Delete trailing newlines\n949 (goto-char (point-min))\n950 (if (re-search-forward \"\\n+\\\\'\" nil t)\n951 (replace-match \"\"))\n952 (buffer-string)))\n\n\n955 ;;; Commit message header\n\n957 (defconst git-gutter+-commit-header-end \"-- End of commit options header --\\n\")\n\n959 (defun git-gutter+-commit-get-field (name &optional fields)\n960 (cdr (assq name (or fields (git-gutter+-commit-get-fields)))))\n\n962 (defun git-gutter+-commit-set-field (name value)\n963 (let* ((fields (git-gutter+-commit-get-fields))\n964 (cell (assq name fields)))\n965 (cond (cell\n966 (if value\n967 (rplacd cell value)\n968 (setq fields (delq cell fields))))\n969 (t\n970 (if value\n971 (setq fields (append fields (list (cons name value)))))))\n972 (git-gutter+-commit-set-fields fields)))\n\n974 (defun git-gutter+-commit-toggle-field (name default)\n975 \"Toggle the commit header field named NAME.\n976 If it's currently unset, set it to DEFAULT (t or nil).\"\n977 (let* ((fields (git-gutter+-commit-get-fields))\n978 (cell (assq name fields)))\n979 (if cell\n980 (rplacd cell (if (equal (cdr cell) \"yes\") \"no\" \"yes\"))\n981 (setq fields (acons name (if default \"yes\" \"no\") fields)))\n982 (git-gutter+-commit-set-fields fields)))\n\n984 (defun git-gutter+-commit-toggle-input (name default)\n985 \"Toggle the commit header input named NAME.\n986 If it's currently unset, set it to DEFAULT (a string). If it is\n987 set remove it.\"\n988 (let* ((fields (git-gutter+-commit-get-fields))\n989 (cell (assq name fields)))\n990 (if cell\n991 (setq fields (assq-delete-all name fields))\n992 (setq fields (acons name default fields)))\n993 (git-gutter+-commit-set-fields fields)))\n\n995 (defun git-gutter+-commit-get-fields ()\n996 (let (result)\n997 (goto-char (point-min))\n998 (while (looking-at \"^\\\\([A-Za-z0-9-_]+\\\\): *\\\\(.+\\\\)?$\")\n999 (let ((name (intern (downcase (match-string 1))))\n1000 (value (read (or (match-string 2) \"nil\"))))\n1001 (push (cons name value) result))\n1002 (forward-line))\n1003 (if (looking-at (regexp-quote git-gutter+-commit-header-end))\n1004 (nreverse result))))\n\n1006 (defun git-gutter+-commit-set-fields (fields)\n1007 (goto-char (point-min))\n1008 ;; Delete commit header\n1009 (if (search-forward-regexp (format \"^\\\\(?:[A-Za-z0-9-_]+:.*\\n\\\\)*%s\"\n1010 (regexp-quote git-gutter+-commit-header-end))\n1011 nil t)\n1012 (delete-region (match-beginning 0) (match-end 0)))\n1013 (goto-char (point-min))\n1014 (when fields\n1015 (dolist (field fields)\n1016 (insert (capitalize (symbol-name (car field))) \": \"\n1017 (prin1-to-string (cdr field)) \"\\n\"))\n1018 (insert git-gutter+-commit-header-end)))\n\n\n1021 ;;; git-gutter+-commit-mode\n1022 ;; Like git-commit-mode, but adds keybindings to git-gutter+ commands and\n1023 ;; highlighting support for the commit message header.\n\n1025 (define-derived-mode git-gutter+-commit-mode git-commit-mode \"Git-Gutter-Commit\"\n1026 (setq font-lock-defaults (list (git-gutter+-commit-font-lock-keywords) t)))\n\n1028 (setq git-gutter+-commit-mode-map\n1029 (let ((map (copy-keymap git-commit-mode-map)))\n1030 (define-key map (kbd \"C-c C-c\") 'git-gutter+-publish-commit)\n1031 (define-key map (kbd \"C-c C-k\") 'git-gutter+-close-commit-edit-buffer)\n1032 (define-key map (kbd \"C-c C-a\") 'git-gutter+-commit-toggle-amending)\n1033 (define-key map (kbd \"C-c C-e\") 'git-gutter+-commit-toggle-allow-empty)\n1034 (define-key map (kbd \"C-c C-u\") 'git-gutter+-commit-toggle-author)\n1035 (define-key map (kbd \"C-c C-d\") 'git-gutter+-commit-toggle-date)\n1036 (define-key map (kbd \"C-c C-b\") 'git-commit-ack)\n1037 (define-key map (kbd \"M-p\") 'log-edit-previous-comment)\n1038 (define-key map (kbd \"M-n\") 'log-edit-next-comment)\n1039 map))\n\n1041 (defface git-gutter+-commit-header-face\n1042 '((t :inherit font-lock-comment-face))\n1043 \"Highlights the commit message header\"\n1044 :group 'git-gutter+-faces)\n\n1046 (defconst git-gutter+-commit-header-regex\n1047 (concat \"\\\\(?:.\\\\|\\n\\\\)*?\" (regexp-quote git-gutter+-commit-header-end)))\n\n1049 (defconst git-gutter+-skip-commit-header-regex\n1050 (concat \"\\\\`\\\\(?:\" git-gutter+-commit-header-regex \"\\\\)?\"))\n\n1052 ;; Modify git-commit-summary-regexp to ignore the commit header\n1053 (defadvice git-commit-summary-regexp\n1054 (after ignore-git-gutter+-commit-header activate compile)\n1055 (if (eq major-mode 'git-gutter+-commit-mode)\n1056 (setq ad-return-value\n1057 (concat git-gutter+-skip-commit-header-regex\n1058 (substring ; Remove leading \"\\\\`\"\n1059 ad-return-value 2)))))\n\n1061 (defun git-gutter+-commit-font-lock-keywords ()\n1062 \"Like `git-commit-mode-font-lock-keywords' but with commit header highlighting\"\n1063 `((,(concat \"\\\\`\" git-gutter+-commit-header-regex) . 'git-gutter+-commit-header-face)\n1064 ,@(git-commit-mode-font-lock-keywords)))\n\n\n1067 ;;; Magit synchronization\n1068 ;; Force Magit to refresh git-gutter+ when updating the VC mode line.\n\n1070 (defvar git-gutter+-orig-vc-find-file-hook)\n\n1072 (defvar git-gutter+-vc-find-file-hook-with-refresh\n1073 (lambda ()\n1074 (funcall git-gutter+-orig-vc-find-file-hook)\n1075 (if git-gutter+-mode (git-gutter+-refresh))))\n\n1077 (defadvice magit-update-vc-modeline (around refresh-git-gutter+ compile activate)\n1078 ;; `magit-update-vc-modeline' calls `vc-find-file-hook' (a function!) on each\n1079 ;; buffer in the repo. Temporarily rebind it to `vc-find-file-hook-with-refresh',\n1080 ;; which calls git-gutter+-refresh after updating the VC mode line.\n1081 ;;\n1082 ;; Using `flet' would have been much simpler, but it's deprecated since 24.3.\n1083 (setq git-gutter+-orig-vc-find-file-hook (symbol-function 'vc-find-file-hook))\n1084 (fset 'vc-find-file-hook git-gutter+-vc-find-file-hook-with-refresh)\n1085 (unwind-protect\n1086 ad-do-it\n1087 (fset 'vc-find-file-hook git-gutter+-orig-vc-find-file-hook)))\n\n1089 (provide 'git-gutter+)\n\n1091 ;;; git-gutter+.el ends here\n")) (setq helm-swoop-cache t))) (candidates-in-buffer) (get-line . buffer-substring-no-properties) (keymap keymap (C-M-left . backward-sexp) (C-M-right . forward-sexp) (27 keymap (105 . helm-multi-swoop-all-from-helm-swoop)) (3 keymap (5 . helm-swoop-edit)) keymap (C-M-left . paren-backward-sexp) (C-M-right . paren-forward-sexp) (94 . helm-swoop-caret-match) (menu-bar keymap (help-menu keymap (describe keymap (describe-mode . helm-help)))) (help keymap (109 . helm-help)) (f1 keymap (109 . helm-help)) (8 keymap (109 . helm-help) (104 . undefined) (8 . undefined) (4 . helm-debug-output)) (20 . helm-toggle-resplit-and-swap-windows) (C-tab . undefined) (triple-mouse-3 . ignore) (double-mouse-3 . ignore) (mouse-3 . ignore) (drag-mouse-3 . ignore) (down-mouse-3 . ignore) (triple-mouse-2 . ignore) (double-mouse-2 . ignore) (mouse-2 . ignore) (drag-mouse-2 . ignore) (down-mouse-2 . ignore) (triple-mouse-1 . ignore) (double-mouse-1 . ignore) (mouse-1 . ignore) (drag-mouse-1 . ignore) (down-mouse-1 . ignore) (67108897 . helm-toggle-suspend-update) (3 keymap (21 . helm-force-update) (6 . helm-follow-mode) (11 . helm-kill-selection-and-quit) (25 . helm-yank-selection) (4 . helm-delete-current-selection) (45 . helm-swap-windows)) (67108987 . helm-enlarge-window) (67108989 . helm-narrow-window) (19 . undefined) (18 . undefined) (23 . helm-yank-text-at-point) (24 keymap (2 . helm-resume-list-buffers-after-quit) (98 . helm-resume-previous-session-after-quit) (6 . helm-quit-and-find-file)) (11 . helm-delete-minibuffer-contents) (67108896 . helm-toggle-visible-mark) (0 . helm-toggle-visible-mark) (C-M-up . helm-scroll-other-window-down) (C-M-down . helm-scroll-other-window) (M-prior . helm-scroll-other-window-down) (M-next . helm-scroll-other-window) (12 . helm-recenter-top-bottom-other-window) (15 . helm-next-source) (10 . helm-select-3rd-action) (5 . helm-select-2nd-action-or-end-of-line) ...) (header-line . "[C-c C-e] Edit mode, [M-i] apply all buffers") (action lambda ($line) (helm-swoop--goto-line (when (string-match "^[0-9]+" $line) (string-to-number (match-string 0 $line)))) (when (re-search-forward (mapconcat (quote identity) (split-string helm-pattern " ") "\\|") nil t) (goto-char (match-beginning 0))) (helm-swoop--recenter)) (migemo)) #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) "Swoop: " nil "^368 " "*Helm Swoop*" nil nil nil helm-log "[Start session] " make-string 41 43 t helm-normalize-sources nil cua-mode -1 funcall make-byte-code 0 "\300\305\211\301\203 \306\307!\210\310 \207" vconcat vector [overriding-local-map helm-alive-p helm-in-file-completion-p nil cua-mode 1 helm-log-save-maybe] 3 "\n\n(fn)" err "\312\211\211\211\305\206\n (()*+,\313\314-\"\315\316\317\320\321\322!\323\"\324\325%DC\216\326\303\301\307\300$\210\327(!\210\330\331!\210\315\332DC\216\333\302\301\304\303\306\307\310\205P \3109\205P \310&\210*\210)?\205r \334\335\316\317\336\321\322\311!\337\"\340\325%\"\210\341\342\335\"\210\343 \330\344\345\346\347\"P!\210-\207" [nil mapcar #[257 "\211JB\207" [] 3 "\n\n(fn V)"] funcall make-byte-code 0 "\300\301\211:\203 @\262\211A\262@\262\211L\210A\262\202 \266\302\303!\207" vconcat vector [nil helm-log "restore variables"] 5 "\n\n(fn)" helm-initialize helm-display-buffer helm-log "show prompt" #[0 "\300 \207" [helm-cleanup] 1 "\n\n(fn)"] helm-read-pattern-maybe defalias helm-hook51420 "\302\303DC\216\300\211)\207" [overriding-local-map funcall #[0 "\300\301\302\"\207" [remove-hook helm-cleanup-hook helm-hook51420] 3 "\n\n(fn)"]] 2 add-hook helm-cleanup-hook helm-execute-selection-action "[End session] " make-string 41 45 helm-buffer helm-quit helm-in-persistent-action helm-current-source helm-source-name helm-restored-variables] 9 quit #[257 "\300 \210\301\302\303\304\305\"P!\210\306\207" [helm-restore-position-on-quit helm-log "[End session (quit)] " make-string 34 45 nil] 6 "\n\n(fn V)"] overriding-local-map helm-maybe-use-default-as-input helm-sources-using-default-as-input non-essential cursor-in-echo-area] 21 "\n\n(fn)"]() | |
funcall(#[0 "\311\312\313\314\315\"P!\210\"\316\211#\206: \317\300!\320\316\320:\2038 @\262$>\211\262?\211\262\2038 A\262\202 \266\203#%\320\262&\320\262\203Q \321\322!\210\323\324\325\326\327\330 \"\331\"\332\333%DC\216\334\323\324\325\335\327\330\300\301\302\303\304\305\306\307\310&\n\336\"\337\333%D\340\323\341\334EDC\217,\207" [((name . "git-gutter+.el") (init lambda nil (unless helm-swoop-cache (with-current-buffer (helm-candidate-buffer (quote local)) (insert "1 ;;; git-gutter+.el --- Manage Git hunks straight from the buffer\n\n3 ;; Copyright (C) 2013 by Syohei YOSHIDA and contributors\n\n5 ;; Author: Syohei YOSHIDA <syohex@gmail.com> and contributors\n6 ;; URL: https://github.com/nonsequitur/git-gutter-plus\n7 ;; Version: 0.1\n\n9 ;; This program is free software; you can redistribute it and/or modify\n10 ;; it under the terms of the GNU General Public License as published by\n11 ;; the Free Software Foundation, either version 3 of the License, or\n12 ;; (at your option) any later version.\n\n14 ;; This program is distributed in the hope that it will be useful,\n15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n17 ;; GNU General Public License for more details.\n\n19 ;; You should have received a copy of the GNU General Public License\n20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n22 ;; Package-Requires: ((git-commit-mode \"0.14\"))\n\n24 ;;; Commentary:\n25 ;;\n26 ;; View, stage and revert Git changes straight from the buffer.\n\n28 ;;; Code:\n\n30 (eval-when-compile\n31 (require 'cl))\n\n33 (require 'tramp)\n34 (require 'log-edit)\n35 (require 'git-commit-mode)\n\n37 (defgroup git-gutter+ nil\n38 \"Manage Git hunks straight from the buffer\"\n39 :prefix \"git-gutter+-\"\n40 :group 'vc)\n\n42 (defcustom git-gutter+-window-width nil\n43 \"Character width of the gutter margin. Set this variable if the automatically\n44 calculated width looks wrong. (This can happen with some special characters.)\"\n45 :type 'integer\n46 :group 'git-gutter+)\n\n48 (defcustom git-gutter+-git-executable \"git\"\n49 \"The path of the Git executable.\"\n50 :type 'string\n51 :group 'git-gutter+)\n\n53 (defcustom git-gutter+-diff-options nil\n54 \"List of strings containing extra arguments to 'git diff'\"\n55 :type 'list\n56 :group 'git-gutter+)\n\n58 (defcustom git-gutter+-separator-sign nil\n59 \"Separator sign\"\n60 :type 'string\n61 :group 'git-gutter+)\n\n63 (defcustom git-gutter+-modified-sign \"=\"\n64 \"Modified sign\"\n65 :type 'string\n66 :group 'git-gutter+)\n\n68 (defcustom git-gutter+-added-sign \"+\"\n69 \"Added sign\"\n70 :type 'string\n71 :group 'git-gutter+)\n\n73 (defcustom git-gutter+-deleted-sign \"-\"\n74 \"Deleted sign\"\n75 :type 'string\n76 :group 'git-gutter+)\n\n78 (defcustom git-gutter+-unchanged-sign nil\n79 \"Unchanged sign\"\n80 :type 'string\n81 :group 'git-gutter+)\n\n83 (defcustom git-gutter+-hide-gutter nil\n84 \"Hide gutter if there are no changes\"\n85 :type 'boolean\n86 :group 'git-gutter+)\n\n88 (defcustom git-gutter+-lighter \" GitGutter\"\n89 \"Minor mode lighter in mode-line\"\n90 :type 'string\n91 :group 'git-gutter+)\n\n93 (defface git-gutter+-separator\n94 '((t (:foreground \"cyan\" :weight bold)))\n95 \"Face of the separator\"\n96 :group 'git-gutter+)\n\n98 (defface git-gutter+-modified\n99 '((t (:foreground \"magenta\" :weight bold)))\n100 \"Face for modified lines\"\n101 :group 'git-gutter+)\n\n103 (defface git-gutter+-added\n104 '((t (:foreground \"green\" :weight bold)))\n105 \"Face for added lines\"\n106 :group 'git-gutter+)\n\n108 (defface git-gutter+-deleted\n109 '((t (:foreground \"red\" :weight bold)))\n110 \"Face for deleted lines\"\n111 :group 'git-gutter+)\n\n113 (defface git-gutter+-unchanged\n114 '((t (:background \"yellow\")))\n115 \"Face for unchanged lines\"\n116 :group 'git-gutter+)\n\n118 (defcustom git-gutter+-disabled-modes nil\n119 \"A list of modes for which `global-git-gutter+-mode' should be disabled.\"\n120 :type '(repeat symbol)\n121 :group 'git-gutter+)\n\n123 (defvar git-gutter+-mode-map\n124 (make-sparse-keymap))\n\n126 (defvar git-gutter+-view-diff-function nil\n127 \"Function to call for displaying diffs\")\n\n129 (defvar git-gutter+-clear-function nil\n130 \"Function to call for clearing the diff display\")\n\n132 (defvar git-gutter+-window-config-change-function nil\n133 \"Function to call when the buffer's local window configuration has changed\")\n\n135 (defvar git-gutter+-diffinfos nil)\n136 (defvar git-gutter+-diff-header nil)\n137 (make-variable-buffer-local 'git-gutter+-diffinfos)\n138 (make-variable-buffer-local 'git-gutter+-diff-header)\n\n140 (defvar git-gutter+-popup-buffer \"*git-gutter+-diff*\")\n141 (defvar git-gutter+-buffers-to-reenable nil)\n\n143 (defconst git-gutter+-hunk-header-regex\n144 ;; The same as diff-hunk-header-re-unified\n145 \"^@@ -\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? \\\\+\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? @@\")\n\n147 (defalias 'git-gutter+-popup-hunk 'git-gutter+-show-hunk)\n148 (defalias 'git-gutter+-revert-hunk 'git-gutter+-revert-hunks)\n\n150 (defmacro git-gutter+-awhen (test &rest body)\n151 \"Anaphoric when.\"\n152 (declare (indent 1))\n153 `(let ((it ,test))\n154 (when it ,@body)))\n\n156 (defun git-gutter+-enable-default-display-mode ()\n157 (setq git-gutter+-view-diff-function 'git-gutter+-view-diff-infos\n158 git-gutter+-clear-function 'git-gutter+-clear-diff-infos\n159 git-gutter+-window-config-change-function 'git-gutter+-show-gutter))\n\n161 (unless git-gutter+-view-diff-function\n162 (git-gutter+-enable-default-display-mode))\n\n164 (defun git-gutter+-call-git (args &optional file)\n165 (if (and file (file-remote-p file))\n166 (apply #'process-file git-gutter+-git-executable nil t nil args)\n167 (apply #'call-process git-gutter+-git-executable nil t nil args)))\n\n169 (defun git-gutter+-in-git-repository-p (file)\n170 (with-temp-buffer\n171 (let ((args '(\"rev-parse\" \"--is-inside-work-tree\")))\n172 (when (zerop (git-gutter+-call-git args file))\n173 (goto-char (point-min))\n174 (string= \"true\" (buffer-substring-no-properties\n175 (point) (line-end-position)))))))\n\n177 (defun git-gutter+-root-directory (file)\n178 (with-temp-buffer\n179 (let* ((args '(\"rev-parse\" \"--show-toplevel\"))\n180 (ret (git-gutter+-call-git args file)))\n181 (when (zerop ret)\n182 (goto-char (point-min))\n183 (let ((root (buffer-substring-no-properties (point) (line-end-position))))\n184 (unless (string= root \"\")\n185 (file-name-as-directory root)))))))\n\n187 (defsubst git-gutter+-diff-args (file)\n188 (delq nil (list \"--no-pager\" \"diff\" \"--no-color\" \"--no-ext-diff\" \"-U0\"\n189 git-gutter+-diff-options file)))\n\n191 (defun git-gutter+-diff (curfile)\n192 (let ((args (git-gutter+-diff-args curfile))\n193 (file (buffer-file-name))) ;; for tramp\n194 (with-temp-buffer\n195 (when (zerop (git-gutter+-call-git args file))\n196 (goto-char (point-min))\n197 (let ((diff-header (git-gutter+-get-diff-header))\n198 (diffinfos (git-gutter+-get-diffinfos)))\n199 (list diff-header diffinfos))))))\n\n201 (defun git-gutter+-get-diff-header ()\n202 (save-excursion\n203 (if (re-search-forward git-gutter+-hunk-header-regex nil t)\n204 (buffer-substring (point-min) (match-beginning 0)))))\n\n206 (defsubst git-gutter+-make-diffinfo (type content start end)\n207 (list :type type :content content :start-line start :end-line end))\n\n209 (defun git-gutter+-get-diffinfos ()\n210 (loop while (re-search-forward git-gutter+-hunk-header-regex nil t)\n211 ;; Hunk header format:\n212 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n213 for del-len = (string-to-number (or (match-string 2) \"1\"))\n214 for add-line = (string-to-number (match-string 3))\n215 for add-len = (string-to-number (or (match-string 4) \"1\"))\n216 for type = (cond ((zerop del-len) 'added)\n217 ((zerop add-len) 'deleted)\n218 (t 'modified))\n219 for start-line = (if (eq type 'deleted)\n220 (1+ add-line)\n221 add-line)\n222 for end-line = (if (eq type 'deleted)\n223 start-line\n224 (1- (+ add-line add-len)))\n225 for content = (git-gutter+-diff-content)\n226 collect\n227 (git-gutter+-make-diffinfo type content start-line end-line)))\n\n229 (defun git-gutter+-diff-content ()\n230 (save-excursion\n231 (goto-char (line-beginning-position)) ; Move to beginning of hunk header\n232 (let ((hunk-start (point)))\n233 ;; Move to end of hunk\n234 (forward-line 1)\n235 (if (re-search-forward \"^@@\" nil t)\n236 (backward-char 3) ;; exclude \"\\n@@\"\n237 (goto-char (1- (point-max)))) ; Skip trailing newline\n238 (buffer-substring hunk-start (point)))))\n\n240 (defun git-gutter+-line-to-pos (line)\n241 (save-excursion\n242 (goto-char (point-min))\n243 (forward-line (1- line))\n244 (point)))\n\n246 (defun git-gutter+-before-string (sign)\n247 (let* ((sep-sign git-gutter+-separator-sign)\n248 (sep (when sep-sign\n249 (propertize sep-sign 'face 'git-gutter+-separator)))\n250 (gutter-sep (concat sign sep)))\n251 (propertize \" \" 'display `((margin left-margin) ,gutter-sep))))\n\n253 (defsubst git-gutter+-select-face (type)\n254 (case type\n255 (added 'git-gutter+-added)\n256 (modified 'git-gutter+-modified)\n257 (deleted 'git-gutter+-deleted)))\n\n259 (defsubst git-gutter+-select-sign (type)\n260 (case type\n261 (added git-gutter+-added-sign)\n262 (modified git-gutter+-modified-sign)\n263 (deleted git-gutter+-deleted-sign)))\n\n265 (defun git-gutter+-propertized-sign (type)\n266 (let ((sign (git-gutter+-select-sign type))\n267 (face (git-gutter+-select-face type)))\n268 (propertize sign 'face face)))\n\n270 (defun git-gutter+-view-region (sign start-line end-line)\n271 (let ((beg (git-gutter+-line-to-pos start-line)))\n272 (goto-char beg)\n273 (while (and (<= (line-number-at-pos) end-line) (not (eobp)))\n274 (git-gutter+-view-at-pos sign (point))\n275 (forward-line 1))))\n\n277 (defun git-gutter+-view-at-pos (sign pos)\n278 (let ((ov (make-overlay pos pos)))\n279 (overlay-put ov 'before-string (git-gutter+-before-string sign))\n280 (overlay-put ov 'git-gutter+ t)))\n\n282 (defun git-gutter+-view-diff-info (diffinfo)\n283 (let* ((start-line (plist-get diffinfo :start-line))\n284 (end-line (plist-get diffinfo :end-line))\n285 (type (plist-get diffinfo :type))\n286 (sign (git-gutter+-propertized-sign type)))\n287 (case type\n288 ((modified added) (git-gutter+-view-region sign start-line end-line))\n289 (deleted (git-gutter+-view-at-pos\n290 sign (git-gutter+-line-to-pos start-line))))))\n\n292 (defun git-gutter+-sign-width (sign)\n293 (loop for s across sign\n294 sum (char-width s)))\n\n296 (defun git-gutter+-longest-sign-width ()\n297 (let ((signs (list git-gutter+-modified-sign\n298 git-gutter+-added-sign\n299 git-gutter+-deleted-sign)))\n300 (when git-gutter+-unchanged-sign\n301 (add-to-list 'signs git-gutter+-unchanged-sign))\n302 (+ (apply 'max (mapcar 'git-gutter+-sign-width signs))\n303 (git-gutter+-sign-width git-gutter+-separator-sign))))\n\n305 (defun git-gutter+-view-for-unchanged ()\n306 (save-excursion\n307 (let ((sign (if git-gutter+-unchanged-sign\n308 (propertize git-gutter+-unchanged-sign\n309 'face 'git-gutter+-unchanged)\n310 \" \")))\n311 (goto-char (point-min))\n312 (while (not (eobp))\n313 (git-gutter+-view-at-pos sign (point))\n314 (forward-line 1)))))\n\n316 (defun git-gutter+-set-window-margin (width)\n317 (let ((curwin (get-buffer-window)))\n318 (set-window-margins curwin width (cdr (window-margins curwin)))))\n\n320 (defsubst git-gutter+-file-buffer-p ()\n321 (and (buffer-file-name)\n322 default-directory\n323 (file-directory-p default-directory)))\n\n325 ;;;###autoload\n326 (define-minor-mode git-gutter+-mode\n327 \"Git-Gutter mode\"\n328 :group 'git-gutter+\n329 :init-value nil\n330 :global nil\n331 :lighter git-gutter+-lighter\n332 (if git-gutter+-mode\n333 (if (and (git-gutter+-file-buffer-p)\n334 (git-gutter+-in-git-repository-p (buffer-file-name)))\n335 (progn\n336 (git-gutter+-add-local-hooks)\n337 (git-gutter+-refresh))\n338 (if (called-interactively-p 'any)\n339 (message \"No Git repo for current buffer\"))\n340 (git-gutter+-mode -1))\n341 (git-gutter+-remove-local-hooks)\n342 (git-gutter+-clear)))\n\n344 (defun git-gutter+-add-local-hooks ()\n345 (add-hook 'after-save-hook 'git-gutter+-refresh nil t)\n346 ;; Turn off `git-gutter+-mode' while reverting to prevent any redundant calls to\n347 ;; `git-gutter+-refresh'.\n348 (add-hook 'before-revert-hook 'git-gutter+-turn-off nil t)\n349 (add-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change nil t)\n350 (if git-gutter+-window-config-change-function\n351 (add-hook 'window-configuration-change-hook\n352 git-gutter+-window-config-change-function nil t)))\n\n354 (defun git-gutter+-remove-local-hooks ()\n355 (remove-hook 'after-save-hook 'git-gutter+-refresh t)\n356 (remove-hook 'before-revert-hook 'git-gutter+-turn-off t)\n357 (remove-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change t)\n358 (if git-gutter+-window-config-change-function\n359 (remove-hook 'window-configuration-change-hook\n360 git-gutter+-window-config-change-function t)))\n\n362 (defmacro git-gutter+-in-all-buffers (&rest body)\n363 `(dolist (buf (buffer-list))\n364 (with-current-buffer buf\n365 ,@body)))\n\n367 ;; When `define-globalized-minor-mode' is used to define `global-git-gutter+-mode',\n368 ;; `git-gutter+-mode' and thus `git-gutter+-refresh' get run twice when a new file\n369 ;; is opened. (First for `fundamental-mode', then for the file-specific mode.)\n370 ;; The following definition of `global-git-gutter+-mode' avoids any redundant calls to\n371 ;; `git-gutter+-refresh'.\n\n373 ;;;###autoload\n374 (define-minor-mode global-git-gutter+-mode ()\n375 \"Global Git-Gutter mode\"\n376 :group 'git-gutter+\n377 :init-value nil\n378 :global t\n379 (if global-git-gutter+-mode\n380 (progn\n381 (add-hook 'find-file-hook 'git-gutter+-turn-on)\n382 (add-hook 'after-revert-hook 'git-gutter+-turn-on)\n383 (add-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n384 (git-gutter+-in-all-buffers (git-gutter+-turn-on)))\n385 (remove-hook 'find-file-hook 'git-gutter+-turn-on)\n386 (remove-hook 'after-revert-hook 'git-gutter+-turn-on)\n387 (remove-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n388 (git-gutter+-in-all-buffers (git-gutter+-turn-off))))\n\n390 (defun git-gutter+-turn-on ()\n391 (when (and (buffer-file-name)\n392 (not (memq major-mode git-gutter+-disabled-modes))\n393 (not git-gutter+-mode))\n394 (git-gutter+-mode t)))\n\n396 (defun git-gutter+-turn-off ()\n397 (if git-gutter+-mode (git-gutter+-mode -1)))\n\n399 (defun git-gutter+-reenable-after-major-mode-change ()\n400 (if global-git-gutter+-mode\n401 (add-to-list 'git-gutter+-buffers-to-reenable (current-buffer))))\n\n403 (defun git-gutter+-reenable-buffers ()\n404 (dolist (buf git-gutter+-buffers-to-reenable)\n405 (with-current-buffer buf\n406 (git-gutter+-turn-on)))\n407 (setq git-gutter+-buffers-to-reenable nil))\n\n409 (defsubst git-gutter+-show-gutter-p (diffinfos)\n410 (if git-gutter+-hide-gutter\n411 (or diffinfos git-gutter+-unchanged-sign)\n412 (or global-git-gutter+-mode git-gutter+-unchanged-sign diffinfos)))\n\n414 (defun git-gutter+-show-gutter (&optional diffinfos)\n415 (when (git-gutter+-show-gutter-p (or diffinfos git-gutter+-diffinfos))\n416 (let ((win-width (or git-gutter+-window-width\n417 (git-gutter+-longest-sign-width))))\n418 (git-gutter+-set-window-margin win-width))))\n\n420 (defun git-gutter+-view-diff-infos (diffinfos)\n421 (when (or git-gutter+-unchanged-sign\n422 git-gutter+-separator-sign)\n423 (git-gutter+-view-for-unchanged))\n424 (when diffinfos\n425 (save-excursion\n426 (mapc 'git-gutter+-view-diff-info diffinfos)))\n427 (git-gutter+-show-gutter diffinfos))\n\n429 (defsubst git-gutter+-reset-window-margin-p ()\n430 (or git-gutter+-hide-gutter\n431 (not global-git-gutter+-mode)))\n\n433 (defun git-gutter+-clear-diff-infos ()\n434 (when (git-gutter+-reset-window-margin-p)\n435 (git-gutter+-set-window-margin 0))\n436 (remove-overlays (point-min) (point-max) 'git-gutter+ t))\n\n438 (defun git-gutter+-process-diff (curfile)\n439 (destructuring-bind\n440 (diff-header diffinfos) (git-gutter+-diff curfile)\n441 (setq git-gutter+-diff-header diff-header\n442 git-gutter+-diffinfos diffinfos)\n443 (save-restriction\n444 (widen)\n445 (funcall git-gutter+-view-diff-function diffinfos))))\n\n447 (defun git-gutter+-search-near-diff-index (diffinfos is-reverse)\n448 (loop with current-line = (line-number-at-pos)\n449 with cmp-fn = (if is-reverse '> '<)\n450 for diffinfo in (if is-reverse (reverse diffinfos) diffinfos)\n451 for index = 0 then (1+ index)\n452 for start-line = (plist-get diffinfo :start-line)\n453 when (funcall cmp-fn current-line start-line)\n454 return (if is-reverse\n455 (1- (- (length diffinfos) index))\n456 index)))\n\n458 (defun git-gutter+-diffinfo-at-point ()\n459 (save-restriction\n460 (widen)\n461 (loop with current-line = (line-number-at-pos)\n462 for diffinfo in git-gutter+-diffinfos\n463 for start = (plist-get diffinfo :start-line)\n464 for end = (or (plist-get diffinfo :end-line) (1+ start))\n465 when (and (>= current-line start) (<= current-line end))\n466 return diffinfo)))\n\n468 (defun git-gutter+-collect-deleted-line (str)\n469 (with-temp-buffer\n470 (insert str)\n471 (goto-char (point-min))\n472 (loop while (re-search-forward \"^-\\\\(.*?\\\\)$\" nil t)\n473 collect (match-string 1) into deleted-lines\n474 finally return deleted-lines)))\n\n476 (defun git-gutter+-delete-added-lines (start-line end-line)\n477 (forward-line (1- start-line))\n478 (let ((start-point (point)))\n479 (forward-line (1+ (- end-line start-line)))\n480 (delete-region start-point (point))))\n\n482 (defun git-gutter+-insert-deleted-lines (content)\n483 (dolist (line (git-gutter+-collect-deleted-line content))\n484 (insert (concat line \"\\n\"))))\n\n486 (defun git-gutter+-do-revert-hunk (diffinfo)\n487 (save-excursion\n488 (save-restriction\n489 (widen)\n490 (goto-char (point-min))\n491 (let ((start-line (plist-get diffinfo :start-line))\n492 (end-line (plist-get diffinfo :end-line))\n493 (content (plist-get diffinfo :content)))\n494 (case (plist-get diffinfo :type)\n495 (added (git-gutter+-delete-added-lines start-line end-line))\n496 (deleted (forward-line (1- start-line))\n497 (git-gutter+-insert-deleted-lines content))\n498 (modified (git-gutter+-delete-added-lines start-line end-line)\n499 (git-gutter+-insert-deleted-lines content)))))))\n\n501 (defun git-gutter+-revert-hunks ()\n502 \"Revert hunk at point. If region is active, revert all hunks within the region.\"\n503 (interactive)\n504 (let* ((diffinfos (git-gutter+-selected-diffinfos))\n505 (one-diffinfo-p (= 1 (length diffinfos))))\n506 (save-window-excursion\n507 (if one-diffinfo-p (git-gutter+-show-hunk (car diffinfos)))\n508 (when (and diffinfos\n509 (yes-or-no-p (if one-diffinfo-p\n510 \"Revert hunk?\"\n511 (format \"Revert %d hunks?\" (length diffinfos)))))\n512 ;; Revert diffinfos in reverse so that earlier hunks don't invalidate the\n513 ;; line number information of the later hunks.\n514 (dolist (diffinfo (nreverse diffinfos))\n515 (git-gutter+-do-revert-hunk diffinfo))\n516 (save-buffer))\n517 (if one-diffinfo-p\n518 (git-gutter+-awhen (get-buffer git-gutter+-popup-buffer)\n519 (kill-buffer it))))))\n\n521 (defun git-gutter+-show-hunk (&optional diffinfo)\n522 \"Show hunk at point in another window\"\n523 (interactive)\n524 (git-gutter+-awhen (or diffinfo\n525 (git-gutter+-diffinfo-at-point))\n526 (save-selected-window\n527 (with-current-buffer (get-buffer-create git-gutter+-popup-buffer)\n528 (setq buffer-read-only nil)\n529 (erase-buffer)\n530 (insert (plist-get it :content))\n531 (insert \"\\n\")\n532 (goto-char (point-min))\n533 (diff-mode)\n534 (view-mode)\n535 (pop-to-buffer (current-buffer))))))\n\n537 (defun git-gutter+-next-hunk (arg)\n538 \"Move to next diff hunk\"\n539 (interactive \"p\")\n540 (if (not git-gutter+-diffinfos)\n541 (message \"No changes in buffer\")\n542 (save-restriction\n543 (widen)\n544 (let* ((is-reverse (< arg 0))\n545 (diffinfos git-gutter+-diffinfos)\n546 (len (length diffinfos))\n547 (index (git-gutter+-search-near-diff-index diffinfos is-reverse))\n548 (real-index (if index\n549 (let ((next (if is-reverse (1+ index) (1- index))))\n550 (mod (+ arg next) len))\n551 (if is-reverse (1- (length diffinfos)) 0)))\n552 (diffinfo (nth real-index diffinfos)))\n553 (goto-char (point-min))\n554 (forward-line (1- (plist-get diffinfo :start-line)))\n555 (when (buffer-live-p (get-buffer git-gutter+-popup-buffer))\n556 (save-window-excursion\n557 (git-gutter+-show-hunk)))))))\n\n559 (defun git-gutter+-previous-hunk (arg)\n560 \"Move to previous diff hunk\"\n561 (interactive \"p\")\n562 (git-gutter+-next-hunk (- arg)))\n\n564 (defun git-gutter+-remote-default-directory (dir file)\n565 (let* ((vec (tramp-dissect-file-name file))\n566 (method (aref vec 0))\n567 (user (aref vec 1))\n568 (host (aref vec 2)))\n569 (format \"/%s:%s%s:%s\" method (if user (concat user \"@\") \"\") host dir)))\n\n571 (defun git-gutter+-remote-file-path (dir file)\n572 (let ((file (aref (tramp-dissect-file-name file) 3)))\n573 (replace-regexp-in-string (concat \"\\\\`\" dir) \"\" file)))\n\n575 (defun git-gutter+-local-file-path (file)\n576 (if (eq system-type 'windows-nt)\n577 ;; Cygwin can't handle Windows absolute paths\n578 (file-relative-name file default-directory)\n579 file))\n\n581 (defun git-gutter+-refresh ()\n582 (git-gutter+-clear)\n583 (let ((file (buffer-file-name)))\n584 (when (and file (file-exists-p file))\n585 (if (file-remote-p file)\n586 (let* ((repo-root (git-gutter+-root-directory file))\n587 (default-directory (git-gutter+-remote-default-directory repo-root file)))\n588 (git-gutter+-process-diff (git-gutter+-remote-file-path repo-root file)))\n589 (git-gutter+-process-diff (git-gutter+-local-file-path file))))))\n\n591 (defun git-gutter+-clear ()\n592 (save-restriction\n593 (widen)\n594 (funcall git-gutter+-clear-function))\n595 (setq git-gutter+-diffinfos nil))\n\n\n598 ;;; Staging\n\n600 (defun git-gutter+-stage-hunks ()\n601 \"Stage hunk at point. If region is active, stage all hunk lines within the region.\"\n602 (interactive)\n603 (let* ((line-range (if (use-region-p)\n604 (cons (line-number-at-pos (region-beginning))\n605 (line-number-at-pos (region-end)))))\n606 (diffinfos (git-gutter+-selected-diffinfos line-range)))\n607 (when diffinfos\n608 (let ((error-msg (git-gutter+-stage-diffinfos diffinfos line-range)))\n609 (if error-msg\n610 (message \"Error staging hunks:\\n%s\" error-msg))\n611 (git-gutter+-refresh)))))\n\n613 (defun git-gutter+-selected-diffinfos (&optional line-range)\n614 (unless line-range\n615 (setq line-range (if (use-region-p)\n616 (cons (line-number-at-pos (region-beginning))\n617 (line-number-at-pos (region-end))))))\n618 (if line-range\n619 (git-gutter+-diffinfos-between-lines line-range)\n620 (git-gutter+-awhen (git-gutter+-diffinfo-at-point)\n621 (list it))))\n\n623 (defsubst git-gutter+-diffinfo-between-lines-p (diffinfo start-line end-line)\n624 (let ((diff-start (plist-get diffinfo :start-line))\n625 (diff-end (plist-get diffinfo :end-line)))\n626 (and (<= start-line diff-end)\n627 (<= diff-start end-line))))\n\n629 (defun git-gutter+-diffinfos-between-lines (line-range)\n630 (save-restriction\n631 (widen)\n632 (let ((start-line (car line-range))\n633 (end-line (cdr line-range)))\n634 (delq nil\n635 (mapcar (lambda (diffinfo)\n636 (if (git-gutter+-diffinfo-between-lines-p\n637 diffinfo start-line end-line)\n638 diffinfo))\n639 git-gutter+-diffinfos)))))\n\n641 (defun git-gutter+-stage-diffinfos (diffinfos line-range)\n642 (let ((header git-gutter+-diff-header))\n643 (with-temp-buffer\n644 (insert header)\n645 ;; Insert hunks in reverse so that earlier hunks don't invalidate the line\n646 ;; number information of the later hunks.\n647 (dolist (diffinfo (nreverse diffinfos))\n648 (git-gutter+-insert-diffinfo diffinfo line-range)\n649 (goto-char (point-max)))\n650 (git-gutter+-call-git-on-current-buffer\n651 '(\"apply\" \"--unidiff-zero\" \"--cached\" \"-\")))))\n\n653 (defun git-gutter+-insert-diffinfo (diffinfo line-range)\n654 (let ((content (plist-get diffinfo :content))\n655 (type (plist-get diffinfo :type)))\n656 (if (not line-range)\n657 (git-gutter+-insert-hunk content type)\n658 (let ((diff-start-line (plist-get diffinfo :start-line))\n659 (diff-end-line (plist-get diffinfo :end-line))\n660 (start-line (car line-range))\n661 (end-line (cdr line-range)))\n662 (git-gutter+-insert-hunk content type\n663 (1+ (- start-line diff-start-line))\n664 (1+ (- end-line diff-start-line)))))))\n\n666 (defun git-gutter+-call-git-on-current-buffer (args)\n667 \"Sends the current buffer contents to Git and replaces them with Git's output.\n\n669 RETURNS nil if Git ran successfully. Returns an error description otherwise.\"\n670 (unless (zerop (apply #'call-process-region (point-min) (point-max)\n671 git-gutter+-git-executable t t nil args))\n672 (buffer-string)))\n\n674 (defsubst git-gutter+-read-hunk-header (hunk)\n675 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n676 (string-match git-gutter+-hunk-header-regex hunk)\n677 (list (string-to-number (match-string 1 hunk))\n678 (string-to-number (or (match-string 2 hunk) \"1\"))\n679 (string-to-number (match-string 3 hunk))\n680 (string-to-number (or (match-string 4 hunk) \"1\"))))\n\n682 (defun git-gutter+-insert-hunk (hunk type &optional start end)\n683 \"If START and END are provided, only insert addition (+) lines between\n684 START and END (inclusive). START and END are both line numbers starting with 1.\"\n685 (destructuring-bind\n686 (del-line del-len add-line add-len) (git-gutter+-read-hunk-header hunk)\n687 (let* ((start (max 1 (or start 1)))\n688 (end (min add-len (or end add-len)))\n689 (insert-all-p (or (eq type :deleted)\n690 (and (= start 1) (= end add-len))))\n691 (num-lines-selected (if insert-all-p\n692 add-len\n693 (1+ (- end start)))))\n694 ;; When the user selected the last lines of a hunk with type `modified' (but\n695 ;; not the complete hunk), then don't insert any deletion (-) lines from that\n696 ;; hunk.\n697 (if (and (eq type 'modified)\n698 (> start 1) (= end add-len))\n699 (setq type 'modified-trailing))\n\n701 (save-excursion\n702 (insert hunk \"\\n\"))\n\n704 (git-gutter+-delete-hunk-header)\n\n706 (if (not insert-all-p)\n707 (git-gutter+-modify-hunk type num-lines-selected del-len start))\n\n709 (let ((hunk-header (git-gutter+-make-hunk-header type num-lines-selected\n710 del-line del-len add-line)))\n711 (insert hunk-header \"\\n\")))))\n\n713 (defun git-gutter+-delete-hunk-header ()\n714 (let ((hunk-start (point)))\n715 (forward-line 1)\n716 (delete-region hunk-start (point))))\n\n718 (defun git-gutter+-modify-hunk (type num-lines-selected del-len start)\n719 \"Remove all addition (+) lines from hunk that aren't selected.\n720 If TYPE is not `modified', also remove all deletion (-) lines.\"\n721 (let ((first-line-selected (+ del-len (1- start)))\n722 selected-lines)\n723 (save-excursion\n724 (forward-line first-line-selected)\n725 (let ((selection-start (point)))\n726 (forward-line num-lines-selected)\n727 (setq selected-lines (buffer-substring selection-start (point)))))\n728 (save-excursion\n729 (if (eq type 'modified) (forward-line del-len)) ; skip over deletion (-) lines\n730 (delete-region (point) (point-max))\n731 (insert selected-lines))))\n\n733 (defun git-gutter+-make-hunk-header (type num-lines-selected del-line del-len add-line)\n734 (let ((add-len num-lines-selected))\n735 (case type\n736 (added (setq add-line (1+ del-line)))\n737 (modified-trailing (setq add-line (+ del-line del-len)\n738 del-line (1- add-line)\n739 del-len 0))\n740 (t (setq add-line del-line)))\n741 (format \"@@ -%d,%d +%d,%d @@\"\n742 del-line del-len\n743 add-line add-len)))\n\n\n746 ;;; Committing\n747 ;; This section draws heavily from old Magit source code.\n\n749 (defvar git-gutter+-pre-commit-window-config nil)\n750 (defvar git-gutter+-commit-origin-buffer nil\n751 \"Buffer that started the commit\")\n\n753 (defconst git-gutter+-commit-buffer-name \"*Commit Message*\")\n754 (defconst git-gutter+-staged-changes-buffer-name \"*Staged Changes*\")\n\n756 ;;;###autoload\n757 (defun git-gutter+-commit ()\n758 \"Commit staged changes. If nothing is staged, ask to stage the current buffer.\"\n759 (interactive)\n\n761 (when (and (not (git-gutter+-anything-staged-p))\n762 git-gutter+-diffinfos\n763 (y-or-n-p \"Nothing staged. Stage current buffer? \"))\n764 (git-gutter+-stage-whole-buffer))\n\n766 (let ((file (buffer-file-name))\n767 (dir default-directory))\n768 (git-gutter+-save-window-config-if-needed)\n769 (setq git-gutter+-commit-origin-buffer (current-buffer))\n770 (git-gutter+-open-commit-edit-buffer dir)\n771 (git-gutter+-show-staged-changes file dir)))\n\n773 (defun git-gutter+-stage-and-commit ()\n774 (interactive)\n775 (git-gutter+-stage-hunks)\n776 (git-gutter+-commit))\n\n778 (defun git-gutter+-save-window-config-if-needed ()\n779 ;; Only save the window config if the temporary buffers that get popped-up by\n780 ;; git-gutter+ are not already visible.\n781 ;; In this way, `git-gutter+-commit' can be called twice in a row without\n782 ;; losing the original window config.\n783 (when (not (and git-gutter+-pre-commit-window-config\n784 (get-buffer-window git-gutter+-commit-buffer-name)\n785 (get-buffer-window git-gutter+-staged-changes-buffer-name)))\n786 (setq git-gutter+-pre-commit-window-config (current-window-configuration))))\n\n788 (defun git-gutter+-open-commit-edit-buffer (dir)\n789 \"Opens a buffer for composing the commit message\"\n790 (pop-to-buffer (get-buffer-create git-gutter+-commit-buffer-name))\n791 (setq default-directory dir)\n792 (git-gutter+-commit-mode)\n793 (message \"Type C-c C-c to commit (C-c C-k to cancel).\"))\n\n795 (defsubst git-gutter+-pop-to-staged-changes-buffer ()\n796 (let* ((buf (get-buffer-create git-gutter+-staged-changes-buffer-name))\n797 (window (get-buffer-window buf)))\n798 (if window\n799 ;; Buffer is already visible\n800 (select-window window)\n801 (if (<= (length (window-list)) 2)\n802 (split-window))\n803 (pop-to-buffer buf))))\n\n805 (defun git-gutter+-show-staged-changes (file dir)\n806 (save-selected-window\n807 (git-gutter+-pop-to-staged-changes-buffer)\n808 (setq buffer-read-only nil)\n809 (erase-buffer)\n810 (let ((default-directory dir))\n811 (git-gutter+-call-git '(\"diff\" \"--staged\") file))\n812 (goto-char (point-min))\n813 (diff-mode)\n814 (view-mode)))\n\n816 (defsubst git-gutter+-abort-commit-when-no-changes (allow-empty amend)\n817 (unless (or amend\n818 allow-empty\n819 (git-gutter+-anything-staged-p))\n820 (error\n821 \"Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?\"\n822 (key-description (car (where-is-internal\n823 'git-gutter+-commit-toggle-amending)))\n824 (key-description (car (where-is-internal\n825 'git-gutter+-commit-toggle-allow-empty))))))\n\n827 (defsubst git-gutter+-buffer-is-whitespace ()\n828 (save-excursion\n829 (goto-char (point-min))\n830 (looking-at-p \"[ \\t\\n]*\\\\'\")))\n\n832 (defun git-gutter+-publish-commit ()\n833 \"Publish commit\"\n834 (interactive)\n835 (let* ((fields (git-gutter+-commit-get-fields))\n836 (amend (equal \"yes\" (git-gutter+-commit-get-field 'amend fields)))\n837 (allow-empty (equal \"yes\" (git-gutter+-commit-get-field 'allow-empty fields)))\n838 (author (git-gutter+-commit-get-field 'author fields))\n839 (date (git-gutter+-commit-get-field 'date fields)))\n\n841 (git-gutter+-abort-commit-when-no-changes allow-empty amend)\n\n843 (git-gutter+-push-to-comment-ring (buffer-string))\n\n845 (git-gutter+-commit-set-fields nil) ; Delete message header\n\n847 (when (git-gutter+-buffer-is-whitespace)\n848 (erase-buffer)\n849 (insert \"(Empty description)\"))\n\n851 (let ((error-msg (git-gutter+-call-git-on-current-buffer\n852 (append '(\"--no-pager\" \"commit\" \"-F\" \"-\")\n853 (if amend '(\"--amend\"))\n854 (if allow-empty '(\"--allow-empty\"))\n855 (if author (list (concat \"--author=\" author)))\n856 (if date (list (concat \"--date=\" date)))))))\n857 (if error-msg\n858 (progn\n859 (message \"Commit error:\\n%s\" error-msg)\n860 (erase-buffer)\n861 (insert (ring-ref log-edit-comment-ring 0))) ; Reinsert commit message\n862 (message \"Commit successful.\")\n863 (git-gutter+-close-commit-edit-buffer)\n864 (git-gutter+-update-vc-modeline)))))\n\n866 (defun git-gutter+-close-commit-edit-buffer ()\n867 \"Abort edits and discard commit message being composed.\"\n868 (interactive)\n869 (kill-buffer)\n870 (set-window-configuration git-gutter+-pre-commit-window-config))\n\n872 (defun git-gutter+-update-vc-modeline ()\n873 (when (buffer-live-p git-gutter+-commit-origin-buffer)\n874 (with-current-buffer git-gutter+-commit-origin-buffer\n875 ;; Updating the modeline has no effect if the buffer still has\n876 ;; changes - it will remain in the 'modified' state. So skip it then.\n877 (unless git-gutter+-diffinfos\n878 (ignore-errors (vc-find-file-hook))))))\n\n880 (defun git-gutter+-stage-whole-buffer ()\n881 (save-excursion\n882 (mark-whole-buffer)\n883 (git-gutter+-stage-hunks)))\n\n885 (defun git-gutter+-anything-staged-p ()\n886 \"Return t if the current repo has staged changes\"\n887 (not (zerop (git-gutter+-call-git '(\"diff\" \"--quiet\" \"--cached\")))))\n\n889 (defun git-gutter+-commit-toggle-amending ()\n890 \"Toggle whether this will be an amendment to the previous commit.\n891 \\(i.e., whether commit is run via 'git commit --amend')\"\n892 (interactive)\n893 ;; Remove the newline that 'git-commit-mode' adds to a new commit\n894 ;; message buffer by default. This prevents an ugly visual\n895 ;; gap between the commit message header and the previous commit\n896 ;; message.\n897 (when (git-gutter+-buffer-is-whitespace)\n898 (erase-buffer))\n\n900 (let ((amend-was-already-set (git-gutter+-commit-get-field 'amend)))\n901 (git-gutter+-commit-toggle-field 'amend t)\n902 (unless amend-was-already-set\n903 ;; Insert previous commit message\n904 (goto-char (point-max))\n905 (unless (zerop (current-column))\n906 (insert \"\\n\"))\n907 (insert (git-gutter+-get-last-commit-msg)\n908 \"\\n\"))))\n\n910 (defun git-gutter+-commit-toggle-allow-empty ()\n911 \"Toggle whether this commit is allowed to be empty.\n912 \\(i.e., whether commit is run via 'git commit --allow-empty')\"\n913 (interactive)\n914 (git-gutter+-commit-toggle-field 'allow-empty t))\n\n916 (defun git-gutter+-format-author (author email)\n917 (format \"%s <%s>\" author email))\n\n919 (defun git-gutter+-commit-toggle-author ()\n920 \"Toggle whether this commit should have a user-defined author.\"\n921 (interactive)\n922 (git-gutter+-commit-toggle-input\n923 'author (git-gutter+-format-author\n924 (or (git-gutter+-get-cfg \"user\" \"name\") \"Author Name\")\n925 (or (git-gutter+-get-cfg \"user\" \"email\") \"author@email\"))))\n\n927 (defun git-gutter+-commit-toggle-date ()\n928 \"Toggle whether this commit should have a user-defined date.\"\n929 (interactive)\n930 (git-gutter+-commit-toggle-input 'date\n931 ;; ISO 8601\n932 (format-time-string \"%Y-%m-%dT%T%z\" (current-time))))\n\n934 (defun git-gutter+-push-to-comment-ring (comment)\n935 (when (or (ring-empty-p log-edit-comment-ring)\n936 (not (equal comment (ring-ref log-edit-comment-ring 0))))\n937 (ring-insert log-edit-comment-ring comment)))\n\n939 (defun git-gutter+-get-last-commit-msg ()\n940 (git-gutter+-git-output '(\"log\" \"--max-count=1\" \"--pretty=format:%s%n%n%b\" \"HEAD\")))\n\n942 (defun git-gutter+-get-cfg (&rest keys)\n943 (git-gutter+-git-output (list \"config\" (mapconcat 'identity keys \".\"))))\n\n945 (defun git-gutter+-git-output (args)\n946 (with-temp-buffer\n947 (git-gutter+-call-git args)\n948 ;; Delete trailing newlines\n949 (goto-char (point-min))\n950 (if (re-search-forward \"\\n+\\\\'\" nil t)\n951 (replace-match \"\"))\n952 (buffer-string)))\n\n\n955 ;;; Commit message header\n\n957 (defconst git-gutter+-commit-header-end \"-- End of commit options header --\\n\")\n\n959 (defun git-gutter+-commit-get-field (name &optional fields)\n960 (cdr (assq name (or fields (git-gutter+-commit-get-fields)))))\n\n962 (defun git-gutter+-commit-set-field (name value)\n963 (let* ((fields (git-gutter+-commit-get-fields))\n964 (cell (assq name fields)))\n965 (cond (cell\n966 (if value\n967 (rplacd cell value)\n968 (setq fields (delq cell fields))))\n969 (t\n970 (if value\n971 (setq fields (append fields (list (cons name value)))))))\n972 (git-gutter+-commit-set-fields fields)))\n\n974 (defun git-gutter+-commit-toggle-field (name default)\n975 \"Toggle the commit header field named NAME.\n976 If it's currently unset, set it to DEFAULT (t or nil).\"\n977 (let* ((fields (git-gutter+-commit-get-fields))\n978 (cell (assq name fields)))\n979 (if cell\n980 (rplacd cell (if (equal (cdr cell) \"yes\") \"no\" \"yes\"))\n981 (setq fields (acons name (if default \"yes\" \"no\") fields)))\n982 (git-gutter+-commit-set-fields fields)))\n\n984 (defun git-gutter+-commit-toggle-input (name default)\n985 \"Toggle the commit header input named NAME.\n986 If it's currently unset, set it to DEFAULT (a string). If it is\n987 set remove it.\"\n988 (let* ((fields (git-gutter+-commit-get-fields))\n989 (cell (assq name fields)))\n990 (if cell\n991 (setq fields (assq-delete-all name fields))\n992 (setq fields (acons name default fields)))\n993 (git-gutter+-commit-set-fields fields)))\n\n995 (defun git-gutter+-commit-get-fields ()\n996 (let (result)\n997 (goto-char (point-min))\n998 (while (looking-at \"^\\\\([A-Za-z0-9-_]+\\\\): *\\\\(.+\\\\)?$\")\n999 (let ((name (intern (downcase (match-string 1))))\n1000 (value (read (or (match-string 2) \"nil\"))))\n1001 (push (cons name value) result))\n1002 (forward-line))\n1003 (if (looking-at (regexp-quote git-gutter+-commit-header-end))\n1004 (nreverse result))))\n\n1006 (defun git-gutter+-commit-set-fields (fields)\n1007 (goto-char (point-min))\n1008 ;; Delete commit header\n1009 (if (search-forward-regexp (format \"^\\\\(?:[A-Za-z0-9-_]+:.*\\n\\\\)*%s\"\n1010 (regexp-quote git-gutter+-commit-header-end))\n1011 nil t)\n1012 (delete-region (match-beginning 0) (match-end 0)))\n1013 (goto-char (point-min))\n1014 (when fields\n1015 (dolist (field fields)\n1016 (insert (capitalize (symbol-name (car field))) \": \"\n1017 (prin1-to-string (cdr field)) \"\\n\"))\n1018 (insert git-gutter+-commit-header-end)))\n\n\n1021 ;;; git-gutter+-commit-mode\n1022 ;; Like git-commit-mode, but adds keybindings to git-gutter+ commands and\n1023 ;; highlighting support for the commit message header.\n\n1025 (define-derived-mode git-gutter+-commit-mode git-commit-mode \"Git-Gutter-Commit\"\n1026 (setq font-lock-defaults (list (git-gutter+-commit-font-lock-keywords) t)))\n\n1028 (setq git-gutter+-commit-mode-map\n1029 (let ((map (copy-keymap git-commit-mode-map)))\n1030 (define-key map (kbd \"C-c C-c\") 'git-gutter+-publish-commit)\n1031 (define-key map (kbd \"C-c C-k\") 'git-gutter+-close-commit-edit-buffer)\n1032 (define-key map (kbd \"C-c C-a\") 'git-gutter+-commit-toggle-amending)\n1033 (define-key map (kbd \"C-c C-e\") 'git-gutter+-commit-toggle-allow-empty)\n1034 (define-key map (kbd \"C-c C-u\") 'git-gutter+-commit-toggle-author)\n1035 (define-key map (kbd \"C-c C-d\") 'git-gutter+-commit-toggle-date)\n1036 (define-key map (kbd \"C-c C-b\") 'git-commit-ack)\n1037 (define-key map (kbd \"M-p\") 'log-edit-previous-comment)\n1038 (define-key map (kbd \"M-n\") 'log-edit-next-comment)\n1039 map))\n\n1041 (defface git-gutter+-commit-header-face\n1042 '((t :inherit font-lock-comment-face))\n1043 \"Highlights the commit message header\"\n1044 :group 'git-gutter+-faces)\n\n1046 (defconst git-gutter+-commit-header-regex\n1047 (concat \"\\\\(?:.\\\\|\\n\\\\)*?\" (regexp-quote git-gutter+-commit-header-end)))\n\n1049 (defconst git-gutter+-skip-commit-header-regex\n1050 (concat \"\\\\`\\\\(?:\" git-gutter+-commit-header-regex \"\\\\)?\"))\n\n1052 ;; Modify git-commit-summary-regexp to ignore the commit header\n1053 (defadvice git-commit-summary-regexp\n1054 (after ignore-git-gutter+-commit-header activate compile)\n1055 (if (eq major-mode 'git-gutter+-commit-mode)\n1056 (setq ad-return-value\n1057 (concat git-gutter+-skip-commit-header-regex\n1058 (substring ; Remove leading \"\\\\`\"\n1059 ad-return-value 2)))))\n\n1061 (defun git-gutter+-commit-font-lock-keywords ()\n1062 \"Like `git-commit-mode-font-lock-keywords' but with commit header highlighting\"\n1063 `((,(concat \"\\\\`\" git-gutter+-commit-header-regex) . 'git-gutter+-commit-header-face)\n1064 ,@(git-commit-mode-font-lock-keywords)))\n\n\n1067 ;;; Magit synchronization\n1068 ;; Force Magit to refresh git-gutter+ when updating the VC mode line.\n\n1070 (defvar git-gutter+-orig-vc-find-file-hook)\n\n1072 (defvar git-gutter+-vc-find-file-hook-with-refresh\n1073 (lambda ()\n1074 (funcall git-gutter+-orig-vc-find-file-hook)\n1075 (if git-gutter+-mode (git-gutter+-refresh))))\n\n1077 (defadvice magit-update-vc-modeline (around refresh-git-gutter+ compile activate)\n1078 ;; `magit-update-vc-modeline' calls `vc-find-file-hook' (a function!) on each\n1079 ;; buffer in the repo. Temporarily rebind it to `vc-find-file-hook-with-refresh',\n1080 ;; which calls git-gutter+-refresh after updating the VC mode line.\n1081 ;;\n1082 ;; Using `flet' would have been much simpler, but it's deprecated since 24.3.\n1083 (setq git-gutter+-orig-vc-find-file-hook (symbol-function 'vc-find-file-hook))\n1084 (fset 'vc-find-file-hook git-gutter+-vc-find-file-hook-with-refresh)\n1085 (unwind-protect\n1086 ad-do-it\n1087 (fset 'vc-find-file-hook git-gutter+-orig-vc-find-file-hook)))\n\n1089 (provide 'git-gutter+)\n\n1091 ;;; git-gutter+.el ends here\n")) (setq helm-swoop-cache t))) (candidates-in-buffer) (get-line . buffer-substring-no-properties) (keymap keymap (C-M-left . backward-sexp) (C-M-right . forward-sexp) (27 keymap (105 . helm-multi-swoop-all-from-helm-swoop)) (3 keymap (5 . helm-swoop-edit)) keymap (C-M-left . paren-backward-sexp) (C-M-right . paren-forward-sexp) (94 . helm-swoop-caret-match) (menu-bar keymap (help-menu keymap (describe keymap (describe-mode . helm-help)))) (help keymap (109 . helm-help)) (f1 keymap (109 . helm-help)) (8 keymap (109 . helm-help) (104 . undefined) (8 . undefined) (4 . helm-debug-output)) (20 . helm-toggle-resplit-and-swap-windows) (C-tab . undefined) (triple-mouse-3 . ignore) (double-mouse-3 . ignore) (mouse-3 . ignore) (drag-mouse-3 . ignore) (down-mouse-3 . ignore) (triple-mouse-2 . ignore) (double-mouse-2 . ignore) (mouse-2 . ignore) (drag-mouse-2 . ignore) (down-mouse-2 . ignore) (triple-mouse-1 . ignore) (double-mouse-1 . ignore) (mouse-1 . ignore) (drag-mouse-1 . ignore) (down-mouse-1 . ignore) (67108897 . helm-toggle-suspend-update) (3 keymap (21 . helm-force-update) (6 . helm-follow-mode) (11 . helm-kill-selection-and-quit) (25 . helm-yank-selection) (4 . helm-delete-current-selection) (45 . helm-swap-windows)) (67108987 . helm-enlarge-window) (67108989 . helm-narrow-window) (19 . undefined) (18 . undefined) (23 . helm-yank-text-at-point) (24 keymap (2 . helm-resume-list-buffers-after-quit) (98 . helm-resume-previous-session-after-quit) (6 . helm-quit-and-find-file)) (11 . helm-delete-minibuffer-contents) (67108896 . helm-toggle-visible-mark) (0 . helm-toggle-visible-mark) (C-M-up . helm-scroll-other-window-down) (C-M-down . helm-scroll-other-window) (M-prior . helm-scroll-other-window-down) (M-next . helm-scroll-other-window) (12 . helm-recenter-top-bottom-other-window) (15 . helm-next-source) (10 . helm-select-3rd-action) (5 . helm-select-2nd-action-or-end-of-line) ...) (header-line . "[C-c C-e] Edit mode, [M-i] apply all buffers") (action lambda ($line) (helm-swoop--goto-line (when (string-match "^[0-9]+" $line) (string-to-number (match-string 0 $line)))) (when (re-search-forward (mapconcat (quote identity) (split-string helm-pattern " ") "\\|") nil t) (goto-char (match-beginning 0))) (helm-swoop--recenter)) (migemo)) #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) "Swoop: " nil "^368 " "*Helm Swoop*" nil nil nil helm-log "[Start session] " make-string 41 43 t helm-normalize-sources nil cua-mode -1 funcall make-byte-code 0 "\300\305\211\301\203 \306\307!\210\310 \207" vconcat vector [overriding-local-map helm-alive-p helm-in-file-completion-p nil cua-mode 1 helm-log-save-maybe] 3 "\n\n(fn)" err "\312\211\211\211\305\206\n (()*+,\313\314-\"\315\316\317\320\321\322!\323\"\324\325%DC\216\326\303\301\307\300$\210\327(!\210\330\331!\210\315\332DC\216\333\302\301\304\303\306\307\310\205P \3109\205P \310&\210*\210)?\205r \334\335\316\317\336\321\322\311!\337\"\340\325%\"\210\341\342\335\"\210\343 \330\344\345\346\347\"P!\210-\207" [nil mapcar #[257 "\211JB\207" [] 3 "\n\n(fn V)"] funcall make-byte-code 0 "\300\301\211:\203 @\262\211A\262@\262\211L\210A\262\202 \266\302\303!\207" vconcat vector [nil helm-log "restore variables"] 5 "\n\n(fn)" helm-initialize helm-display-buffer helm-log "show prompt" #[0 "\300 \207" [helm-cleanup] 1 "\n\n(fn)"] helm-read-pattern-maybe defalias helm-hook51420 "\302\303DC\216\300\211)\207" [overriding-local-map funcall #[0 "\300\301\302\"\207" [remove-hook helm-cleanup-hook helm-hook51420] 3 "\n\n(fn)"]] 2 add-hook helm-cleanup-hook helm-execute-selection-action "[End session] " make-string 41 45 helm-buffer helm-quit helm-in-persistent-action helm-current-source helm-source-name helm-restored-variables] 9 quit #[257 "\300 \210\301\302\303\304\305\"P!\210\306\207" [helm-restore-position-on-quit helm-log "[End session (quit)] " make-string 34 45 nil] 6 "\n\n(fn V)"] overriding-local-map helm-maybe-use-default-as-input helm-sources-using-default-as-input non-essential cursor-in-echo-area] 21 "\n\n(fn)"]) | |
helm-internal(((name . "git-gutter+.el") (init lambda nil (unless helm-swoop-cache (with-current-buffer (helm-candidate-buffer (quote local)) (insert "1 ;;; git-gutter+.el --- Manage Git hunks straight from the buffer\n\n3 ;; Copyright (C) 2013 by Syohei YOSHIDA and contributors\n\n5 ;; Author: Syohei YOSHIDA <syohex@gmail.com> and contributors\n6 ;; URL: https://github.com/nonsequitur/git-gutter-plus\n7 ;; Version: 0.1\n\n9 ;; This program is free software; you can redistribute it and/or modify\n10 ;; it under the terms of the GNU General Public License as published by\n11 ;; the Free Software Foundation, either version 3 of the License, or\n12 ;; (at your option) any later version.\n\n14 ;; This program is distributed in the hope that it will be useful,\n15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n17 ;; GNU General Public License for more details.\n\n19 ;; You should have received a copy of the GNU General Public License\n20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n22 ;; Package-Requires: ((git-commit-mode \"0.14\"))\n\n24 ;;; Commentary:\n25 ;;\n26 ;; View, stage and revert Git changes straight from the buffer.\n\n28 ;;; Code:\n\n30 (eval-when-compile\n31 (require 'cl))\n\n33 (require 'tramp)\n34 (require 'log-edit)\n35 (require 'git-commit-mode)\n\n37 (defgroup git-gutter+ nil\n38 \"Manage Git hunks straight from the buffer\"\n39 :prefix \"git-gutter+-\"\n40 :group 'vc)\n\n42 (defcustom git-gutter+-window-width nil\n43 \"Character width of the gutter margin. Set this variable if the automatically\n44 calculated width looks wrong. (This can happen with some special characters.)\"\n45 :type 'integer\n46 :group 'git-gutter+)\n\n48 (defcustom git-gutter+-git-executable \"git\"\n49 \"The path of the Git executable.\"\n50 :type 'string\n51 :group 'git-gutter+)\n\n53 (defcustom git-gutter+-diff-options nil\n54 \"List of strings containing extra arguments to 'git diff'\"\n55 :type 'list\n56 :group 'git-gutter+)\n\n58 (defcustom git-gutter+-separator-sign nil\n59 \"Separator sign\"\n60 :type 'string\n61 :group 'git-gutter+)\n\n63 (defcustom git-gutter+-modified-sign \"=\"\n64 \"Modified sign\"\n65 :type 'string\n66 :group 'git-gutter+)\n\n68 (defcustom git-gutter+-added-sign \"+\"\n69 \"Added sign\"\n70 :type 'string\n71 :group 'git-gutter+)\n\n73 (defcustom git-gutter+-deleted-sign \"-\"\n74 \"Deleted sign\"\n75 :type 'string\n76 :group 'git-gutter+)\n\n78 (defcustom git-gutter+-unchanged-sign nil\n79 \"Unchanged sign\"\n80 :type 'string\n81 :group 'git-gutter+)\n\n83 (defcustom git-gutter+-hide-gutter nil\n84 \"Hide gutter if there are no changes\"\n85 :type 'boolean\n86 :group 'git-gutter+)\n\n88 (defcustom git-gutter+-lighter \" GitGutter\"\n89 \"Minor mode lighter in mode-line\"\n90 :type 'string\n91 :group 'git-gutter+)\n\n93 (defface git-gutter+-separator\n94 '((t (:foreground \"cyan\" :weight bold)))\n95 \"Face of the separator\"\n96 :group 'git-gutter+)\n\n98 (defface git-gutter+-modified\n99 '((t (:foreground \"magenta\" :weight bold)))\n100 \"Face for modified lines\"\n101 :group 'git-gutter+)\n\n103 (defface git-gutter+-added\n104 '((t (:foreground \"green\" :weight bold)))\n105 \"Face for added lines\"\n106 :group 'git-gutter+)\n\n108 (defface git-gutter+-deleted\n109 '((t (:foreground \"red\" :weight bold)))\n110 \"Face for deleted lines\"\n111 :group 'git-gutter+)\n\n113 (defface git-gutter+-unchanged\n114 '((t (:background \"yellow\")))\n115 \"Face for unchanged lines\"\n116 :group 'git-gutter+)\n\n118 (defcustom git-gutter+-disabled-modes nil\n119 \"A list of modes for which `global-git-gutter+-mode' should be disabled.\"\n120 :type '(repeat symbol)\n121 :group 'git-gutter+)\n\n123 (defvar git-gutter+-mode-map\n124 (make-sparse-keymap))\n\n126 (defvar git-gutter+-view-diff-function nil\n127 \"Function to call for displaying diffs\")\n\n129 (defvar git-gutter+-clear-function nil\n130 \"Function to call for clearing the diff display\")\n\n132 (defvar git-gutter+-window-config-change-function nil\n133 \"Function to call when the buffer's local window configuration has changed\")\n\n135 (defvar git-gutter+-diffinfos nil)\n136 (defvar git-gutter+-diff-header nil)\n137 (make-variable-buffer-local 'git-gutter+-diffinfos)\n138 (make-variable-buffer-local 'git-gutter+-diff-header)\n\n140 (defvar git-gutter+-popup-buffer \"*git-gutter+-diff*\")\n141 (defvar git-gutter+-buffers-to-reenable nil)\n\n143 (defconst git-gutter+-hunk-header-regex\n144 ;; The same as diff-hunk-header-re-unified\n145 \"^@@ -\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? \\\\+\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? @@\")\n\n147 (defalias 'git-gutter+-popup-hunk 'git-gutter+-show-hunk)\n148 (defalias 'git-gutter+-revert-hunk 'git-gutter+-revert-hunks)\n\n150 (defmacro git-gutter+-awhen (test &rest body)\n151 \"Anaphoric when.\"\n152 (declare (indent 1))\n153 `(let ((it ,test))\n154 (when it ,@body)))\n\n156 (defun git-gutter+-enable-default-display-mode ()\n157 (setq git-gutter+-view-diff-function 'git-gutter+-view-diff-infos\n158 git-gutter+-clear-function 'git-gutter+-clear-diff-infos\n159 git-gutter+-window-config-change-function 'git-gutter+-show-gutter))\n\n161 (unless git-gutter+-view-diff-function\n162 (git-gutter+-enable-default-display-mode))\n\n164 (defun git-gutter+-call-git (args &optional file)\n165 (if (and file (file-remote-p file))\n166 (apply #'process-file git-gutter+-git-executable nil t nil args)\n167 (apply #'call-process git-gutter+-git-executable nil t nil args)))\n\n169 (defun git-gutter+-in-git-repository-p (file)\n170 (with-temp-buffer\n171 (let ((args '(\"rev-parse\" \"--is-inside-work-tree\")))\n172 (when (zerop (git-gutter+-call-git args file))\n173 (goto-char (point-min))\n174 (string= \"true\" (buffer-substring-no-properties\n175 (point) (line-end-position)))))))\n\n177 (defun git-gutter+-root-directory (file)\n178 (with-temp-buffer\n179 (let* ((args '(\"rev-parse\" \"--show-toplevel\"))\n180 (ret (git-gutter+-call-git args file)))\n181 (when (zerop ret)\n182 (goto-char (point-min))\n183 (let ((root (buffer-substring-no-properties (point) (line-end-position))))\n184 (unless (string= root \"\")\n185 (file-name-as-directory root)))))))\n\n187 (defsubst git-gutter+-diff-args (file)\n188 (delq nil (list \"--no-pager\" \"diff\" \"--no-color\" \"--no-ext-diff\" \"-U0\"\n189 git-gutter+-diff-options file)))\n\n191 (defun git-gutter+-diff (curfile)\n192 (let ((args (git-gutter+-diff-args curfile))\n193 (file (buffer-file-name))) ;; for tramp\n194 (with-temp-buffer\n195 (when (zerop (git-gutter+-call-git args file))\n196 (goto-char (point-min))\n197 (let ((diff-header (git-gutter+-get-diff-header))\n198 (diffinfos (git-gutter+-get-diffinfos)))\n199 (list diff-header diffinfos))))))\n\n201 (defun git-gutter+-get-diff-header ()\n202 (save-excursion\n203 (if (re-search-forward git-gutter+-hunk-header-regex nil t)\n204 (buffer-substring (point-min) (match-beginning 0)))))\n\n206 (defsubst git-gutter+-make-diffinfo (type content start end)\n207 (list :type type :content content :start-line start :end-line end))\n\n209 (defun git-gutter+-get-diffinfos ()\n210 (loop while (re-search-forward git-gutter+-hunk-header-regex nil t)\n211 ;; Hunk header format:\n212 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n213 for del-len = (string-to-number (or (match-string 2) \"1\"))\n214 for add-line = (string-to-number (match-string 3))\n215 for add-len = (string-to-number (or (match-string 4) \"1\"))\n216 for type = (cond ((zerop del-len) 'added)\n217 ((zerop add-len) 'deleted)\n218 (t 'modified))\n219 for start-line = (if (eq type 'deleted)\n220 (1+ add-line)\n221 add-line)\n222 for end-line = (if (eq type 'deleted)\n223 start-line\n224 (1- (+ add-line add-len)))\n225 for content = (git-gutter+-diff-content)\n226 collect\n227 (git-gutter+-make-diffinfo type content start-line end-line)))\n\n229 (defun git-gutter+-diff-content ()\n230 (save-excursion\n231 (goto-char (line-beginning-position)) ; Move to beginning of hunk header\n232 (let ((hunk-start (point)))\n233 ;; Move to end of hunk\n234 (forward-line 1)\n235 (if (re-search-forward \"^@@\" nil t)\n236 (backward-char 3) ;; exclude \"\\n@@\"\n237 (goto-char (1- (point-max)))) ; Skip trailing newline\n238 (buffer-substring hunk-start (point)))))\n\n240 (defun git-gutter+-line-to-pos (line)\n241 (save-excursion\n242 (goto-char (point-min))\n243 (forward-line (1- line))\n244 (point)))\n\n246 (defun git-gutter+-before-string (sign)\n247 (let* ((sep-sign git-gutter+-separator-sign)\n248 (sep (when sep-sign\n249 (propertize sep-sign 'face 'git-gutter+-separator)))\n250 (gutter-sep (concat sign sep)))\n251 (propertize \" \" 'display `((margin left-margin) ,gutter-sep))))\n\n253 (defsubst git-gutter+-select-face (type)\n254 (case type\n255 (added 'git-gutter+-added)\n256 (modified 'git-gutter+-modified)\n257 (deleted 'git-gutter+-deleted)))\n\n259 (defsubst git-gutter+-select-sign (type)\n260 (case type\n261 (added git-gutter+-added-sign)\n262 (modified git-gutter+-modified-sign)\n263 (deleted git-gutter+-deleted-sign)))\n\n265 (defun git-gutter+-propertized-sign (type)\n266 (let ((sign (git-gutter+-select-sign type))\n267 (face (git-gutter+-select-face type)))\n268 (propertize sign 'face face)))\n\n270 (defun git-gutter+-view-region (sign start-line end-line)\n271 (let ((beg (git-gutter+-line-to-pos start-line)))\n272 (goto-char beg)\n273 (while (and (<= (line-number-at-pos) end-line) (not (eobp)))\n274 (git-gutter+-view-at-pos sign (point))\n275 (forward-line 1))))\n\n277 (defun git-gutter+-view-at-pos (sign pos)\n278 (let ((ov (make-overlay pos pos)))\n279 (overlay-put ov 'before-string (git-gutter+-before-string sign))\n280 (overlay-put ov 'git-gutter+ t)))\n\n282 (defun git-gutter+-view-diff-info (diffinfo)\n283 (let* ((start-line (plist-get diffinfo :start-line))\n284 (end-line (plist-get diffinfo :end-line))\n285 (type (plist-get diffinfo :type))\n286 (sign (git-gutter+-propertized-sign type)))\n287 (case type\n288 ((modified added) (git-gutter+-view-region sign start-line end-line))\n289 (deleted (git-gutter+-view-at-pos\n290 sign (git-gutter+-line-to-pos start-line))))))\n\n292 (defun git-gutter+-sign-width (sign)\n293 (loop for s across sign\n294 sum (char-width s)))\n\n296 (defun git-gutter+-longest-sign-width ()\n297 (let ((signs (list git-gutter+-modified-sign\n298 git-gutter+-added-sign\n299 git-gutter+-deleted-sign)))\n300 (when git-gutter+-unchanged-sign\n301 (add-to-list 'signs git-gutter+-unchanged-sign))\n302 (+ (apply 'max (mapcar 'git-gutter+-sign-width signs))\n303 (git-gutter+-sign-width git-gutter+-separator-sign))))\n\n305 (defun git-gutter+-view-for-unchanged ()\n306 (save-excursion\n307 (let ((sign (if git-gutter+-unchanged-sign\n308 (propertize git-gutter+-unchanged-sign\n309 'face 'git-gutter+-unchanged)\n310 \" \")))\n311 (goto-char (point-min))\n312 (while (not (eobp))\n313 (git-gutter+-view-at-pos sign (point))\n314 (forward-line 1)))))\n\n316 (defun git-gutter+-set-window-margin (width)\n317 (let ((curwin (get-buffer-window)))\n318 (set-window-margins curwin width (cdr (window-margins curwin)))))\n\n320 (defsubst git-gutter+-file-buffer-p ()\n321 (and (buffer-file-name)\n322 default-directory\n323 (file-directory-p default-directory)))\n\n325 ;;;###autoload\n326 (define-minor-mode git-gutter+-mode\n327 \"Git-Gutter mode\"\n328 :group 'git-gutter+\n329 :init-value nil\n330 :global nil\n331 :lighter git-gutter+-lighter\n332 (if git-gutter+-mode\n333 (if (and (git-gutter+-file-buffer-p)\n334 (git-gutter+-in-git-repository-p (buffer-file-name)))\n335 (progn\n336 (git-gutter+-add-local-hooks)\n337 (git-gutter+-refresh))\n338 (if (called-interactively-p 'any)\n339 (message \"No Git repo for current buffer\"))\n340 (git-gutter+-mode -1))\n341 (git-gutter+-remove-local-hooks)\n342 (git-gutter+-clear)))\n\n344 (defun git-gutter+-add-local-hooks ()\n345 (add-hook 'after-save-hook 'git-gutter+-refresh nil t)\n346 ;; Turn off `git-gutter+-mode' while reverting to prevent any redundant calls to\n347 ;; `git-gutter+-refresh'.\n348 (add-hook 'before-revert-hook 'git-gutter+-turn-off nil t)\n349 (add-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change nil t)\n350 (if git-gutter+-window-config-change-function\n351 (add-hook 'window-configuration-change-hook\n352 git-gutter+-window-config-change-function nil t)))\n\n354 (defun git-gutter+-remove-local-hooks ()\n355 (remove-hook 'after-save-hook 'git-gutter+-refresh t)\n356 (remove-hook 'before-revert-hook 'git-gutter+-turn-off t)\n357 (remove-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change t)\n358 (if git-gutter+-window-config-change-function\n359 (remove-hook 'window-configuration-change-hook\n360 git-gutter+-window-config-change-function t)))\n\n362 (defmacro git-gutter+-in-all-buffers (&rest body)\n363 `(dolist (buf (buffer-list))\n364 (with-current-buffer buf\n365 ,@body)))\n\n367 ;; When `define-globalized-minor-mode' is used to define `global-git-gutter+-mode',\n368 ;; `git-gutter+-mode' and thus `git-gutter+-refresh' get run twice when a new file\n369 ;; is opened. (First for `fundamental-mode', then for the file-specific mode.)\n370 ;; The following definition of `global-git-gutter+-mode' avoids any redundant calls to\n371 ;; `git-gutter+-refresh'.\n\n373 ;;;###autoload\n374 (define-minor-mode global-git-gutter+-mode ()\n375 \"Global Git-Gutter mode\"\n376 :group 'git-gutter+\n377 :init-value nil\n378 :global t\n379 (if global-git-gutter+-mode\n380 (progn\n381 (add-hook 'find-file-hook 'git-gutter+-turn-on)\n382 (add-hook 'after-revert-hook 'git-gutter+-turn-on)\n383 (add-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n384 (git-gutter+-in-all-buffers (git-gutter+-turn-on)))\n385 (remove-hook 'find-file-hook 'git-gutter+-turn-on)\n386 (remove-hook 'after-revert-hook 'git-gutter+-turn-on)\n387 (remove-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n388 (git-gutter+-in-all-buffers (git-gutter+-turn-off))))\n\n390 (defun git-gutter+-turn-on ()\n391 (when (and (buffer-file-name)\n392 (not (memq major-mode git-gutter+-disabled-modes))\n393 (not git-gutter+-mode))\n394 (git-gutter+-mode t)))\n\n396 (defun git-gutter+-turn-off ()\n397 (if git-gutter+-mode (git-gutter+-mode -1)))\n\n399 (defun git-gutter+-reenable-after-major-mode-change ()\n400 (if global-git-gutter+-mode\n401 (add-to-list 'git-gutter+-buffers-to-reenable (current-buffer))))\n\n403 (defun git-gutter+-reenable-buffers ()\n404 (dolist (buf git-gutter+-buffers-to-reenable)\n405 (with-current-buffer buf\n406 (git-gutter+-turn-on)))\n407 (setq git-gutter+-buffers-to-reenable nil))\n\n409 (defsubst git-gutter+-show-gutter-p (diffinfos)\n410 (if git-gutter+-hide-gutter\n411 (or diffinfos git-gutter+-unchanged-sign)\n412 (or global-git-gutter+-mode git-gutter+-unchanged-sign diffinfos)))\n\n414 (defun git-gutter+-show-gutter (&optional diffinfos)\n415 (when (git-gutter+-show-gutter-p (or diffinfos git-gutter+-diffinfos))\n416 (let ((win-width (or git-gutter+-window-width\n417 (git-gutter+-longest-sign-width))))\n418 (git-gutter+-set-window-margin win-width))))\n\n420 (defun git-gutter+-view-diff-infos (diffinfos)\n421 (when (or git-gutter+-unchanged-sign\n422 git-gutter+-separator-sign)\n423 (git-gutter+-view-for-unchanged))\n424 (when diffinfos\n425 (save-excursion\n426 (mapc 'git-gutter+-view-diff-info diffinfos)))\n427 (git-gutter+-show-gutter diffinfos))\n\n429 (defsubst git-gutter+-reset-window-margin-p ()\n430 (or git-gutter+-hide-gutter\n431 (not global-git-gutter+-mode)))\n\n433 (defun git-gutter+-clear-diff-infos ()\n434 (when (git-gutter+-reset-window-margin-p)\n435 (git-gutter+-set-window-margin 0))\n436 (remove-overlays (point-min) (point-max) 'git-gutter+ t))\n\n438 (defun git-gutter+-process-diff (curfile)\n439 (destructuring-bind\n440 (diff-header diffinfos) (git-gutter+-diff curfile)\n441 (setq git-gutter+-diff-header diff-header\n442 git-gutter+-diffinfos diffinfos)\n443 (save-restriction\n444 (widen)\n445 (funcall git-gutter+-view-diff-function diffinfos))))\n\n447 (defun git-gutter+-search-near-diff-index (diffinfos is-reverse)\n448 (loop with current-line = (line-number-at-pos)\n449 with cmp-fn = (if is-reverse '> '<)\n450 for diffinfo in (if is-reverse (reverse diffinfos) diffinfos)\n451 for index = 0 then (1+ index)\n452 for start-line = (plist-get diffinfo :start-line)\n453 when (funcall cmp-fn current-line start-line)\n454 return (if is-reverse\n455 (1- (- (length diffinfos) index))\n456 index)))\n\n458 (defun git-gutter+-diffinfo-at-point ()\n459 (save-restriction\n460 (widen)\n461 (loop with current-line = (line-number-at-pos)\n462 for diffinfo in git-gutter+-diffinfos\n463 for start = (plist-get diffinfo :start-line)\n464 for end = (or (plist-get diffinfo :end-line) (1+ start))\n465 when (and (>= current-line start) (<= current-line end))\n466 return diffinfo)))\n\n468 (defun git-gutter+-collect-deleted-line (str)\n469 (with-temp-buffer\n470 (insert str)\n471 (goto-char (point-min))\n472 (loop while (re-search-forward \"^-\\\\(.*?\\\\)$\" nil t)\n473 collect (match-string 1) into deleted-lines\n474 finally return deleted-lines)))\n\n476 (defun git-gutter+-delete-added-lines (start-line end-line)\n477 (forward-line (1- start-line))\n478 (let ((start-point (point)))\n479 (forward-line (1+ (- end-line start-line)))\n480 (delete-region start-point (point))))\n\n482 (defun git-gutter+-insert-deleted-lines (content)\n483 (dolist (line (git-gutter+-collect-deleted-line content))\n484 (insert (concat line \"\\n\"))))\n\n486 (defun git-gutter+-do-revert-hunk (diffinfo)\n487 (save-excursion\n488 (save-restriction\n489 (widen)\n490 (goto-char (point-min))\n491 (let ((start-line (plist-get diffinfo :start-line))\n492 (end-line (plist-get diffinfo :end-line))\n493 (content (plist-get diffinfo :content)))\n494 (case (plist-get diffinfo :type)\n495 (added (git-gutter+-delete-added-lines start-line end-line))\n496 (deleted (forward-line (1- start-line))\n497 (git-gutter+-insert-deleted-lines content))\n498 (modified (git-gutter+-delete-added-lines start-line end-line)\n499 (git-gutter+-insert-deleted-lines content)))))))\n\n501 (defun git-gutter+-revert-hunks ()\n502 \"Revert hunk at point. If region is active, revert all hunks within the region.\"\n503 (interactive)\n504 (let* ((diffinfos (git-gutter+-selected-diffinfos))\n505 (one-diffinfo-p (= 1 (length diffinfos))))\n506 (save-window-excursion\n507 (if one-diffinfo-p (git-gutter+-show-hunk (car diffinfos)))\n508 (when (and diffinfos\n509 (yes-or-no-p (if one-diffinfo-p\n510 \"Revert hunk?\"\n511 (format \"Revert %d hunks?\" (length diffinfos)))))\n512 ;; Revert diffinfos in reverse so that earlier hunks don't invalidate the\n513 ;; line number information of the later hunks.\n514 (dolist (diffinfo (nreverse diffinfos))\n515 (git-gutter+-do-revert-hunk diffinfo))\n516 (save-buffer))\n517 (if one-diffinfo-p\n518 (git-gutter+-awhen (get-buffer git-gutter+-popup-buffer)\n519 (kill-buffer it))))))\n\n521 (defun git-gutter+-show-hunk (&optional diffinfo)\n522 \"Show hunk at point in another window\"\n523 (interactive)\n524 (git-gutter+-awhen (or diffinfo\n525 (git-gutter+-diffinfo-at-point))\n526 (save-selected-window\n527 (with-current-buffer (get-buffer-create git-gutter+-popup-buffer)\n528 (setq buffer-read-only nil)\n529 (erase-buffer)\n530 (insert (plist-get it :content))\n531 (insert \"\\n\")\n532 (goto-char (point-min))\n533 (diff-mode)\n534 (view-mode)\n535 (pop-to-buffer (current-buffer))))))\n\n537 (defun git-gutter+-next-hunk (arg)\n538 \"Move to next diff hunk\"\n539 (interactive \"p\")\n540 (if (not git-gutter+-diffinfos)\n541 (message \"No changes in buffer\")\n542 (save-restriction\n543 (widen)\n544 (let* ((is-reverse (< arg 0))\n545 (diffinfos git-gutter+-diffinfos)\n546 (len (length diffinfos))\n547 (index (git-gutter+-search-near-diff-index diffinfos is-reverse))\n548 (real-index (if index\n549 (let ((next (if is-reverse (1+ index) (1- index))))\n550 (mod (+ arg next) len))\n551 (if is-reverse (1- (length diffinfos)) 0)))\n552 (diffinfo (nth real-index diffinfos)))\n553 (goto-char (point-min))\n554 (forward-line (1- (plist-get diffinfo :start-line)))\n555 (when (buffer-live-p (get-buffer git-gutter+-popup-buffer))\n556 (save-window-excursion\n557 (git-gutter+-show-hunk)))))))\n\n559 (defun git-gutter+-previous-hunk (arg)\n560 \"Move to previous diff hunk\"\n561 (interactive \"p\")\n562 (git-gutter+-next-hunk (- arg)))\n\n564 (defun git-gutter+-remote-default-directory (dir file)\n565 (let* ((vec (tramp-dissect-file-name file))\n566 (method (aref vec 0))\n567 (user (aref vec 1))\n568 (host (aref vec 2)))\n569 (format \"/%s:%s%s:%s\" method (if user (concat user \"@\") \"\") host dir)))\n\n571 (defun git-gutter+-remote-file-path (dir file)\n572 (let ((file (aref (tramp-dissect-file-name file) 3)))\n573 (replace-regexp-in-string (concat \"\\\\`\" dir) \"\" file)))\n\n575 (defun git-gutter+-local-file-path (file)\n576 (if (eq system-type 'windows-nt)\n577 ;; Cygwin can't handle Windows absolute paths\n578 (file-relative-name file default-directory)\n579 file))\n\n581 (defun git-gutter+-refresh ()\n582 (git-gutter+-clear)\n583 (let ((file (buffer-file-name)))\n584 (when (and file (file-exists-p file))\n585 (if (file-remote-p file)\n586 (let* ((repo-root (git-gutter+-root-directory file))\n587 (default-directory (git-gutter+-remote-default-directory repo-root file)))\n588 (git-gutter+-process-diff (git-gutter+-remote-file-path repo-root file)))\n589 (git-gutter+-process-diff (git-gutter+-local-file-path file))))))\n\n591 (defun git-gutter+-clear ()\n592 (save-restriction\n593 (widen)\n594 (funcall git-gutter+-clear-function))\n595 (setq git-gutter+-diffinfos nil))\n\n\n598 ;;; Staging\n\n600 (defun git-gutter+-stage-hunks ()\n601 \"Stage hunk at point. If region is active, stage all hunk lines within the region.\"\n602 (interactive)\n603 (let* ((line-range (if (use-region-p)\n604 (cons (line-number-at-pos (region-beginning))\n605 (line-number-at-pos (region-end)))))\n606 (diffinfos (git-gutter+-selected-diffinfos line-range)))\n607 (when diffinfos\n608 (let ((error-msg (git-gutter+-stage-diffinfos diffinfos line-range)))\n609 (if error-msg\n610 (message \"Error staging hunks:\\n%s\" error-msg))\n611 (git-gutter+-refresh)))))\n\n613 (defun git-gutter+-selected-diffinfos (&optional line-range)\n614 (unless line-range\n615 (setq line-range (if (use-region-p)\n616 (cons (line-number-at-pos (region-beginning))\n617 (line-number-at-pos (region-end))))))\n618 (if line-range\n619 (git-gutter+-diffinfos-between-lines line-range)\n620 (git-gutter+-awhen (git-gutter+-diffinfo-at-point)\n621 (list it))))\n\n623 (defsubst git-gutter+-diffinfo-between-lines-p (diffinfo start-line end-line)\n624 (let ((diff-start (plist-get diffinfo :start-line))\n625 (diff-end (plist-get diffinfo :end-line)))\n626 (and (<= start-line diff-end)\n627 (<= diff-start end-line))))\n\n629 (defun git-gutter+-diffinfos-between-lines (line-range)\n630 (save-restriction\n631 (widen)\n632 (let ((start-line (car line-range))\n633 (end-line (cdr line-range)))\n634 (delq nil\n635 (mapcar (lambda (diffinfo)\n636 (if (git-gutter+-diffinfo-between-lines-p\n637 diffinfo start-line end-line)\n638 diffinfo))\n639 git-gutter+-diffinfos)))))\n\n641 (defun git-gutter+-stage-diffinfos (diffinfos line-range)\n642 (let ((header git-gutter+-diff-header))\n643 (with-temp-buffer\n644 (insert header)\n645 ;; Insert hunks in reverse so that earlier hunks don't invalidate the line\n646 ;; number information of the later hunks.\n647 (dolist (diffinfo (nreverse diffinfos))\n648 (git-gutter+-insert-diffinfo diffinfo line-range)\n649 (goto-char (point-max)))\n650 (git-gutter+-call-git-on-current-buffer\n651 '(\"apply\" \"--unidiff-zero\" \"--cached\" \"-\")))))\n\n653 (defun git-gutter+-insert-diffinfo (diffinfo line-range)\n654 (let ((content (plist-get diffinfo :content))\n655 (type (plist-get diffinfo :type)))\n656 (if (not line-range)\n657 (git-gutter+-insert-hunk content type)\n658 (let ((diff-start-line (plist-get diffinfo :start-line))\n659 (diff-end-line (plist-get diffinfo :end-line))\n660 (start-line (car line-range))\n661 (end-line (cdr line-range)))\n662 (git-gutter+-insert-hunk content type\n663 (1+ (- start-line diff-start-line))\n664 (1+ (- end-line diff-start-line)))))))\n\n666 (defun git-gutter+-call-git-on-current-buffer (args)\n667 \"Sends the current buffer contents to Git and replaces them with Git's output.\n\n669 RETURNS nil if Git ran successfully. Returns an error description otherwise.\"\n670 (unless (zerop (apply #'call-process-region (point-min) (point-max)\n671 git-gutter+-git-executable t t nil args))\n672 (buffer-string)))\n\n674 (defsubst git-gutter+-read-hunk-header (hunk)\n675 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n676 (string-match git-gutter+-hunk-header-regex hunk)\n677 (list (string-to-number (match-string 1 hunk))\n678 (string-to-number (or (match-string 2 hunk) \"1\"))\n679 (string-to-number (match-string 3 hunk))\n680 (string-to-number (or (match-string 4 hunk) \"1\"))))\n\n682 (defun git-gutter+-insert-hunk (hunk type &optional start end)\n683 \"If START and END are provided, only insert addition (+) lines between\n684 START and END (inclusive). START and END are both line numbers starting with 1.\"\n685 (destructuring-bind\n686 (del-line del-len add-line add-len) (git-gutter+-read-hunk-header hunk)\n687 (let* ((start (max 1 (or start 1)))\n688 (end (min add-len (or end add-len)))\n689 (insert-all-p (or (eq type :deleted)\n690 (and (= start 1) (= end add-len))))\n691 (num-lines-selected (if insert-all-p\n692 add-len\n693 (1+ (- end start)))))\n694 ;; When the user selected the last lines of a hunk with type `modified' (but\n695 ;; not the complete hunk), then don't insert any deletion (-) lines from that\n696 ;; hunk.\n697 (if (and (eq type 'modified)\n698 (> start 1) (= end add-len))\n699 (setq type 'modified-trailing))\n\n701 (save-excursion\n702 (insert hunk \"\\n\"))\n\n704 (git-gutter+-delete-hunk-header)\n\n706 (if (not insert-all-p)\n707 (git-gutter+-modify-hunk type num-lines-selected del-len start))\n\n709 (let ((hunk-header (git-gutter+-make-hunk-header type num-lines-selected\n710 del-line del-len add-line)))\n711 (insert hunk-header \"\\n\")))))\n\n713 (defun git-gutter+-delete-hunk-header ()\n714 (let ((hunk-start (point)))\n715 (forward-line 1)\n716 (delete-region hunk-start (point))))\n\n718 (defun git-gutter+-modify-hunk (type num-lines-selected del-len start)\n719 \"Remove all addition (+) lines from hunk that aren't selected.\n720 If TYPE is not `modified', also remove all deletion (-) lines.\"\n721 (let ((first-line-selected (+ del-len (1- start)))\n722 selected-lines)\n723 (save-excursion\n724 (forward-line first-line-selected)\n725 (let ((selection-start (point)))\n726 (forward-line num-lines-selected)\n727 (setq selected-lines (buffer-substring selection-start (point)))))\n728 (save-excursion\n729 (if (eq type 'modified) (forward-line del-len)) ; skip over deletion (-) lines\n730 (delete-region (point) (point-max))\n731 (insert selected-lines))))\n\n733 (defun git-gutter+-make-hunk-header (type num-lines-selected del-line del-len add-line)\n734 (let ((add-len num-lines-selected))\n735 (case type\n736 (added (setq add-line (1+ del-line)))\n737 (modified-trailing (setq add-line (+ del-line del-len)\n738 del-line (1- add-line)\n739 del-len 0))\n740 (t (setq add-line del-line)))\n741 (format \"@@ -%d,%d +%d,%d @@\"\n742 del-line del-len\n743 add-line add-len)))\n\n\n746 ;;; Committing\n747 ;; This section draws heavily from old Magit source code.\n\n749 (defvar git-gutter+-pre-commit-window-config nil)\n750 (defvar git-gutter+-commit-origin-buffer nil\n751 \"Buffer that started the commit\")\n\n753 (defconst git-gutter+-commit-buffer-name \"*Commit Message*\")\n754 (defconst git-gutter+-staged-changes-buffer-name \"*Staged Changes*\")\n\n756 ;;;###autoload\n757 (defun git-gutter+-commit ()\n758 \"Commit staged changes. If nothing is staged, ask to stage the current buffer.\"\n759 (interactive)\n\n761 (when (and (not (git-gutter+-anything-staged-p))\n762 git-gutter+-diffinfos\n763 (y-or-n-p \"Nothing staged. Stage current buffer? \"))\n764 (git-gutter+-stage-whole-buffer))\n\n766 (let ((file (buffer-file-name))\n767 (dir default-directory))\n768 (git-gutter+-save-window-config-if-needed)\n769 (setq git-gutter+-commit-origin-buffer (current-buffer))\n770 (git-gutter+-open-commit-edit-buffer dir)\n771 (git-gutter+-show-staged-changes file dir)))\n\n773 (defun git-gutter+-stage-and-commit ()\n774 (interactive)\n775 (git-gutter+-stage-hunks)\n776 (git-gutter+-commit))\n\n778 (defun git-gutter+-save-window-config-if-needed ()\n779 ;; Only save the window config if the temporary buffers that get popped-up by\n780 ;; git-gutter+ are not already visible.\n781 ;; In this way, `git-gutter+-commit' can be called twice in a row without\n782 ;; losing the original window config.\n783 (when (not (and git-gutter+-pre-commit-window-config\n784 (get-buffer-window git-gutter+-commit-buffer-name)\n785 (get-buffer-window git-gutter+-staged-changes-buffer-name)))\n786 (setq git-gutter+-pre-commit-window-config (current-window-configuration))))\n\n788 (defun git-gutter+-open-commit-edit-buffer (dir)\n789 \"Opens a buffer for composing the commit message\"\n790 (pop-to-buffer (get-buffer-create git-gutter+-commit-buffer-name))\n791 (setq default-directory dir)\n792 (git-gutter+-commit-mode)\n793 (message \"Type C-c C-c to commit (C-c C-k to cancel).\"))\n\n795 (defsubst git-gutter+-pop-to-staged-changes-buffer ()\n796 (let* ((buf (get-buffer-create git-gutter+-staged-changes-buffer-name))\n797 (window (get-buffer-window buf)))\n798 (if window\n799 ;; Buffer is already visible\n800 (select-window window)\n801 (if (<= (length (window-list)) 2)\n802 (split-window))\n803 (pop-to-buffer buf))))\n\n805 (defun git-gutter+-show-staged-changes (file dir)\n806 (save-selected-window\n807 (git-gutter+-pop-to-staged-changes-buffer)\n808 (setq buffer-read-only nil)\n809 (erase-buffer)\n810 (let ((default-directory dir))\n811 (git-gutter+-call-git '(\"diff\" \"--staged\") file))\n812 (goto-char (point-min))\n813 (diff-mode)\n814 (view-mode)))\n\n816 (defsubst git-gutter+-abort-commit-when-no-changes (allow-empty amend)\n817 (unless (or amend\n818 allow-empty\n819 (git-gutter+-anything-staged-p))\n820 (error\n821 \"Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?\"\n822 (key-description (car (where-is-internal\n823 'git-gutter+-commit-toggle-amending)))\n824 (key-description (car (where-is-internal\n825 'git-gutter+-commit-toggle-allow-empty))))))\n\n827 (defsubst git-gutter+-buffer-is-whitespace ()\n828 (save-excursion\n829 (goto-char (point-min))\n830 (looking-at-p \"[ \\t\\n]*\\\\'\")))\n\n832 (defun git-gutter+-publish-commit ()\n833 \"Publish commit\"\n834 (interactive)\n835 (let* ((fields (git-gutter+-commit-get-fields))\n836 (amend (equal \"yes\" (git-gutter+-commit-get-field 'amend fields)))\n837 (allow-empty (equal \"yes\" (git-gutter+-commit-get-field 'allow-empty fields)))\n838 (author (git-gutter+-commit-get-field 'author fields))\n839 (date (git-gutter+-commit-get-field 'date fields)))\n\n841 (git-gutter+-abort-commit-when-no-changes allow-empty amend)\n\n843 (git-gutter+-push-to-comment-ring (buffer-string))\n\n845 (git-gutter+-commit-set-fields nil) ; Delete message header\n\n847 (when (git-gutter+-buffer-is-whitespace)\n848 (erase-buffer)\n849 (insert \"(Empty description)\"))\n\n851 (let ((error-msg (git-gutter+-call-git-on-current-buffer\n852 (append '(\"--no-pager\" \"commit\" \"-F\" \"-\")\n853 (if amend '(\"--amend\"))\n854 (if allow-empty '(\"--allow-empty\"))\n855 (if author (list (concat \"--author=\" author)))\n856 (if date (list (concat \"--date=\" date)))))))\n857 (if error-msg\n858 (progn\n859 (message \"Commit error:\\n%s\" error-msg)\n860 (erase-buffer)\n861 (insert (ring-ref log-edit-comment-ring 0))) ; Reinsert commit message\n862 (message \"Commit successful.\")\n863 (git-gutter+-close-commit-edit-buffer)\n864 (git-gutter+-update-vc-modeline)))))\n\n866 (defun git-gutter+-close-commit-edit-buffer ()\n867 \"Abort edits and discard commit message being composed.\"\n868 (interactive)\n869 (kill-buffer)\n870 (set-window-configuration git-gutter+-pre-commit-window-config))\n\n872 (defun git-gutter+-update-vc-modeline ()\n873 (when (buffer-live-p git-gutter+-commit-origin-buffer)\n874 (with-current-buffer git-gutter+-commit-origin-buffer\n875 ;; Updating the modeline has no effect if the buffer still has\n876 ;; changes - it will remain in the 'modified' state. So skip it then.\n877 (unless git-gutter+-diffinfos\n878 (ignore-errors (vc-find-file-hook))))))\n\n880 (defun git-gutter+-stage-whole-buffer ()\n881 (save-excursion\n882 (mark-whole-buffer)\n883 (git-gutter+-stage-hunks)))\n\n885 (defun git-gutter+-anything-staged-p ()\n886 \"Return t if the current repo has staged changes\"\n887 (not (zerop (git-gutter+-call-git '(\"diff\" \"--quiet\" \"--cached\")))))\n\n889 (defun git-gutter+-commit-toggle-amending ()\n890 \"Toggle whether this will be an amendment to the previous commit.\n891 \\(i.e., whether commit is run via 'git commit --amend')\"\n892 (interactive)\n893 ;; Remove the newline that 'git-commit-mode' adds to a new commit\n894 ;; message buffer by default. This prevents an ugly visual\n895 ;; gap between the commit message header and the previous commit\n896 ;; message.\n897 (when (git-gutter+-buffer-is-whitespace)\n898 (erase-buffer))\n\n900 (let ((amend-was-already-set (git-gutter+-commit-get-field 'amend)))\n901 (git-gutter+-commit-toggle-field 'amend t)\n902 (unless amend-was-already-set\n903 ;; Insert previous commit message\n904 (goto-char (point-max))\n905 (unless (zerop (current-column))\n906 (insert \"\\n\"))\n907 (insert (git-gutter+-get-last-commit-msg)\n908 \"\\n\"))))\n\n910 (defun git-gutter+-commit-toggle-allow-empty ()\n911 \"Toggle whether this commit is allowed to be empty.\n912 \\(i.e., whether commit is run via 'git commit --allow-empty')\"\n913 (interactive)\n914 (git-gutter+-commit-toggle-field 'allow-empty t))\n\n916 (defun git-gutter+-format-author (author email)\n917 (format \"%s <%s>\" author email))\n\n919 (defun git-gutter+-commit-toggle-author ()\n920 \"Toggle whether this commit should have a user-defined author.\"\n921 (interactive)\n922 (git-gutter+-commit-toggle-input\n923 'author (git-gutter+-format-author\n924 (or (git-gutter+-get-cfg \"user\" \"name\") \"Author Name\")\n925 (or (git-gutter+-get-cfg \"user\" \"email\") \"author@email\"))))\n\n927 (defun git-gutter+-commit-toggle-date ()\n928 \"Toggle whether this commit should have a user-defined date.\"\n929 (interactive)\n930 (git-gutter+-commit-toggle-input 'date\n931 ;; ISO 8601\n932 (format-time-string \"%Y-%m-%dT%T%z\" (current-time))))\n\n934 (defun git-gutter+-push-to-comment-ring (comment)\n935 (when (or (ring-empty-p log-edit-comment-ring)\n936 (not (equal comment (ring-ref log-edit-comment-ring 0))))\n937 (ring-insert log-edit-comment-ring comment)))\n\n939 (defun git-gutter+-get-last-commit-msg ()\n940 (git-gutter+-git-output '(\"log\" \"--max-count=1\" \"--pretty=format:%s%n%n%b\" \"HEAD\")))\n\n942 (defun git-gutter+-get-cfg (&rest keys)\n943 (git-gutter+-git-output (list \"config\" (mapconcat 'identity keys \".\"))))\n\n945 (defun git-gutter+-git-output (args)\n946 (with-temp-buffer\n947 (git-gutter+-call-git args)\n948 ;; Delete trailing newlines\n949 (goto-char (point-min))\n950 (if (re-search-forward \"\\n+\\\\'\" nil t)\n951 (replace-match \"\"))\n952 (buffer-string)))\n\n\n955 ;;; Commit message header\n\n957 (defconst git-gutter+-commit-header-end \"-- End of commit options header --\\n\")\n\n959 (defun git-gutter+-commit-get-field (name &optional fields)\n960 (cdr (assq name (or fields (git-gutter+-commit-get-fields)))))\n\n962 (defun git-gutter+-commit-set-field (name value)\n963 (let* ((fields (git-gutter+-commit-get-fields))\n964 (cell (assq name fields)))\n965 (cond (cell\n966 (if value\n967 (rplacd cell value)\n968 (setq fields (delq cell fields))))\n969 (t\n970 (if value\n971 (setq fields (append fields (list (cons name value)))))))\n972 (git-gutter+-commit-set-fields fields)))\n\n974 (defun git-gutter+-commit-toggle-field (name default)\n975 \"Toggle the commit header field named NAME.\n976 If it's currently unset, set it to DEFAULT (t or nil).\"\n977 (let* ((fields (git-gutter+-commit-get-fields))\n978 (cell (assq name fields)))\n979 (if cell\n980 (rplacd cell (if (equal (cdr cell) \"yes\") \"no\" \"yes\"))\n981 (setq fields (acons name (if default \"yes\" \"no\") fields)))\n982 (git-gutter+-commit-set-fields fields)))\n\n984 (defun git-gutter+-commit-toggle-input (name default)\n985 \"Toggle the commit header input named NAME.\n986 If it's currently unset, set it to DEFAULT (a string). If it is\n987 set remove it.\"\n988 (let* ((fields (git-gutter+-commit-get-fields))\n989 (cell (assq name fields)))\n990 (if cell\n991 (setq fields (assq-delete-all name fields))\n992 (setq fields (acons name default fields)))\n993 (git-gutter+-commit-set-fields fields)))\n\n995 (defun git-gutter+-commit-get-fields ()\n996 (let (result)\n997 (goto-char (point-min))\n998 (while (looking-at \"^\\\\([A-Za-z0-9-_]+\\\\): *\\\\(.+\\\\)?$\")\n999 (let ((name (intern (downcase (match-string 1))))\n1000 (value (read (or (match-string 2) \"nil\"))))\n1001 (push (cons name value) result))\n1002 (forward-line))\n1003 (if (looking-at (regexp-quote git-gutter+-commit-header-end))\n1004 (nreverse result))))\n\n1006 (defun git-gutter+-commit-set-fields (fields)\n1007 (goto-char (point-min))\n1008 ;; Delete commit header\n1009 (if (search-forward-regexp (format \"^\\\\(?:[A-Za-z0-9-_]+:.*\\n\\\\)*%s\"\n1010 (regexp-quote git-gutter+-commit-header-end))\n1011 nil t)\n1012 (delete-region (match-beginning 0) (match-end 0)))\n1013 (goto-char (point-min))\n1014 (when fields\n1015 (dolist (field fields)\n1016 (insert (capitalize (symbol-name (car field))) \": \"\n1017 (prin1-to-string (cdr field)) \"\\n\"))\n1018 (insert git-gutter+-commit-header-end)))\n\n\n1021 ;;; git-gutter+-commit-mode\n1022 ;; Like git-commit-mode, but adds keybindings to git-gutter+ commands and\n1023 ;; highlighting support for the commit message header.\n\n1025 (define-derived-mode git-gutter+-commit-mode git-commit-mode \"Git-Gutter-Commit\"\n1026 (setq font-lock-defaults (list (git-gutter+-commit-font-lock-keywords) t)))\n\n1028 (setq git-gutter+-commit-mode-map\n1029 (let ((map (copy-keymap git-commit-mode-map)))\n1030 (define-key map (kbd \"C-c C-c\") 'git-gutter+-publish-commit)\n1031 (define-key map (kbd \"C-c C-k\") 'git-gutter+-close-commit-edit-buffer)\n1032 (define-key map (kbd \"C-c C-a\") 'git-gutter+-commit-toggle-amending)\n1033 (define-key map (kbd \"C-c C-e\") 'git-gutter+-commit-toggle-allow-empty)\n1034 (define-key map (kbd \"C-c C-u\") 'git-gutter+-commit-toggle-author)\n1035 (define-key map (kbd \"C-c C-d\") 'git-gutter+-commit-toggle-date)\n1036 (define-key map (kbd \"C-c C-b\") 'git-commit-ack)\n1037 (define-key map (kbd \"M-p\") 'log-edit-previous-comment)\n1038 (define-key map (kbd \"M-n\") 'log-edit-next-comment)\n1039 map))\n\n1041 (defface git-gutter+-commit-header-face\n1042 '((t :inherit font-lock-comment-face))\n1043 \"Highlights the commit message header\"\n1044 :group 'git-gutter+-faces)\n\n1046 (defconst git-gutter+-commit-header-regex\n1047 (concat \"\\\\(?:.\\\\|\\n\\\\)*?\" (regexp-quote git-gutter+-commit-header-end)))\n\n1049 (defconst git-gutter+-skip-commit-header-regex\n1050 (concat \"\\\\`\\\\(?:\" git-gutter+-commit-header-regex \"\\\\)?\"))\n\n1052 ;; Modify git-commit-summary-regexp to ignore the commit header\n1053 (defadvice git-commit-summary-regexp\n1054 (after ignore-git-gutter+-commit-header activate compile)\n1055 (if (eq major-mode 'git-gutter+-commit-mode)\n1056 (setq ad-return-value\n1057 (concat git-gutter+-skip-commit-header-regex\n1058 (substring ; Remove leading \"\\\\`\"\n1059 ad-return-value 2)))))\n\n1061 (defun git-gutter+-commit-font-lock-keywords ()\n1062 \"Like `git-commit-mode-font-lock-keywords' but with commit header highlighting\"\n1063 `((,(concat \"\\\\`\" git-gutter+-commit-header-regex) . 'git-gutter+-commit-header-face)\n1064 ,@(git-commit-mode-font-lock-keywords)))\n\n\n1067 ;;; Magit synchronization\n1068 ;; Force Magit to refresh git-gutter+ when updating the VC mode line.\n\n1070 (defvar git-gutter+-orig-vc-find-file-hook)\n\n1072 (defvar git-gutter+-vc-find-file-hook-with-refresh\n1073 (lambda ()\n1074 (funcall git-gutter+-orig-vc-find-file-hook)\n1075 (if git-gutter+-mode (git-gutter+-refresh))))\n\n1077 (defadvice magit-update-vc-modeline (around refresh-git-gutter+ compile activate)\n1078 ;; `magit-update-vc-modeline' calls `vc-find-file-hook' (a function!) on each\n1079 ;; buffer in the repo. Temporarily rebind it to `vc-find-file-hook-with-refresh',\n1080 ;; which calls git-gutter+-refresh after updating the VC mode line.\n1081 ;;\n1082 ;; Using `flet' would have been much simpler, but it's deprecated since 24.3.\n1083 (setq git-gutter+-orig-vc-find-file-hook (symbol-function 'vc-find-file-hook))\n1084 (fset 'vc-find-file-hook git-gutter+-vc-find-file-hook-with-refresh)\n1085 (unwind-protect\n1086 ad-do-it\n1087 (fset 'vc-find-file-hook git-gutter+-orig-vc-find-file-hook)))\n\n1089 (provide 'git-gutter+)\n\n1091 ;;; git-gutter+.el ends here\n")) (setq helm-swoop-cache t))) (candidates-in-buffer) (get-line . buffer-substring-no-properties) (keymap keymap (C-M-left . backward-sexp) (C-M-right . forward-sexp) (27 keymap (105 . helm-multi-swoop-all-from-helm-swoop)) (3 keymap (5 . helm-swoop-edit)) keymap (C-M-left . paren-backward-sexp) (C-M-right . paren-forward-sexp) (94 . helm-swoop-caret-match) (menu-bar keymap (help-menu keymap (describe keymap (describe-mode . helm-help)))) (help keymap (109 . helm-help)) (f1 keymap (109 . helm-help)) (8 keymap (109 . helm-help) (104 . undefined) (8 . undefined) (4 . helm-debug-output)) (20 . helm-toggle-resplit-and-swap-windows) (C-tab . undefined) (triple-mouse-3 . ignore) (double-mouse-3 . ignore) (mouse-3 . ignore) (drag-mouse-3 . ignore) (down-mouse-3 . ignore) (triple-mouse-2 . ignore) (double-mouse-2 . ignore) (mouse-2 . ignore) (drag-mouse-2 . ignore) (down-mouse-2 . ignore) (triple-mouse-1 . ignore) (double-mouse-1 . ignore) (mouse-1 . ignore) (drag-mouse-1 . ignore) (down-mouse-1 . ignore) (67108897 . helm-toggle-suspend-update) (3 keymap (21 . helm-force-update) (6 . helm-follow-mode) (11 . helm-kill-selection-and-quit) (25 . helm-yank-selection) (4 . helm-delete-current-selection) (45 . helm-swap-windows)) (67108987 . helm-enlarge-window) (67108989 . helm-narrow-window) (19 . undefined) (18 . undefined) (23 . helm-yank-text-at-point) (24 keymap (2 . helm-resume-list-buffers-after-quit) (98 . helm-resume-previous-session-after-quit) (6 . helm-quit-and-find-file)) (11 . helm-delete-minibuffer-contents) (67108896 . helm-toggle-visible-mark) (0 . helm-toggle-visible-mark) (C-M-up . helm-scroll-other-window-down) (C-M-down . helm-scroll-other-window) (M-prior . helm-scroll-other-window-down) (M-next . helm-scroll-other-window) (12 . helm-recenter-top-bottom-other-window) (15 . helm-next-source) (10 . helm-select-3rd-action) (5 . helm-select-2nd-action-or-end-of-line) ...) (header-line . "[C-c C-e] Edit mode, [M-i] apply all buffers") (action lambda ($line) (helm-swoop--goto-line (when (string-match "^[0-9]+" $line) (string-to-number (match-string 0 $line)))) (when (re-search-forward (mapconcat (quote identity) (split-string helm-pattern " ") "\\|") nil t) (goto-char (match-beginning 0))) (helm-swoop--recenter)) (migemo)) #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) "Swoop: " nil "^368 " "*Helm Swoop*" nil nil nil) | |
apply(helm-internal (((name . "git-gutter+.el") (init lambda nil (unless helm-swoop-cache (with-current-buffer (helm-candidate-buffer (quote local)) (insert "1 ;;; git-gutter+.el --- Manage Git hunks straight from the buffer\n\n3 ;; Copyright (C) 2013 by Syohei YOSHIDA and contributors\n\n5 ;; Author: Syohei YOSHIDA <syohex@gmail.com> and contributors\n6 ;; URL: https://github.com/nonsequitur/git-gutter-plus\n7 ;; Version: 0.1\n\n9 ;; This program is free software; you can redistribute it and/or modify\n10 ;; it under the terms of the GNU General Public License as published by\n11 ;; the Free Software Foundation, either version 3 of the License, or\n12 ;; (at your option) any later version.\n\n14 ;; This program is distributed in the hope that it will be useful,\n15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n17 ;; GNU General Public License for more details.\n\n19 ;; You should have received a copy of the GNU General Public License\n20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n22 ;; Package-Requires: ((git-commit-mode \"0.14\"))\n\n24 ;;; Commentary:\n25 ;;\n26 ;; View, stage and revert Git changes straight from the buffer.\n\n28 ;;; Code:\n\n30 (eval-when-compile\n31 (require 'cl))\n\n33 (require 'tramp)\n34 (require 'log-edit)\n35 (require 'git-commit-mode)\n\n37 (defgroup git-gutter+ nil\n38 \"Manage Git hunks straight from the buffer\"\n39 :prefix \"git-gutter+-\"\n40 :group 'vc)\n\n42 (defcustom git-gutter+-window-width nil\n43 \"Character width of the gutter margin. Set this variable if the automatically\n44 calculated width looks wrong. (This can happen with some special characters.)\"\n45 :type 'integer\n46 :group 'git-gutter+)\n\n48 (defcustom git-gutter+-git-executable \"git\"\n49 \"The path of the Git executable.\"\n50 :type 'string\n51 :group 'git-gutter+)\n\n53 (defcustom git-gutter+-diff-options nil\n54 \"List of strings containing extra arguments to 'git diff'\"\n55 :type 'list\n56 :group 'git-gutter+)\n\n58 (defcustom git-gutter+-separator-sign nil\n59 \"Separator sign\"\n60 :type 'string\n61 :group 'git-gutter+)\n\n63 (defcustom git-gutter+-modified-sign \"=\"\n64 \"Modified sign\"\n65 :type 'string\n66 :group 'git-gutter+)\n\n68 (defcustom git-gutter+-added-sign \"+\"\n69 \"Added sign\"\n70 :type 'string\n71 :group 'git-gutter+)\n\n73 (defcustom git-gutter+-deleted-sign \"-\"\n74 \"Deleted sign\"\n75 :type 'string\n76 :group 'git-gutter+)\n\n78 (defcustom git-gutter+-unchanged-sign nil\n79 \"Unchanged sign\"\n80 :type 'string\n81 :group 'git-gutter+)\n\n83 (defcustom git-gutter+-hide-gutter nil\n84 \"Hide gutter if there are no changes\"\n85 :type 'boolean\n86 :group 'git-gutter+)\n\n88 (defcustom git-gutter+-lighter \" GitGutter\"\n89 \"Minor mode lighter in mode-line\"\n90 :type 'string\n91 :group 'git-gutter+)\n\n93 (defface git-gutter+-separator\n94 '((t (:foreground \"cyan\" :weight bold)))\n95 \"Face of the separator\"\n96 :group 'git-gutter+)\n\n98 (defface git-gutter+-modified\n99 '((t (:foreground \"magenta\" :weight bold)))\n100 \"Face for modified lines\"\n101 :group 'git-gutter+)\n\n103 (defface git-gutter+-added\n104 '((t (:foreground \"green\" :weight bold)))\n105 \"Face for added lines\"\n106 :group 'git-gutter+)\n\n108 (defface git-gutter+-deleted\n109 '((t (:foreground \"red\" :weight bold)))\n110 \"Face for deleted lines\"\n111 :group 'git-gutter+)\n\n113 (defface git-gutter+-unchanged\n114 '((t (:background \"yellow\")))\n115 \"Face for unchanged lines\"\n116 :group 'git-gutter+)\n\n118 (defcustom git-gutter+-disabled-modes nil\n119 \"A list of modes for which `global-git-gutter+-mode' should be disabled.\"\n120 :type '(repeat symbol)\n121 :group 'git-gutter+)\n\n123 (defvar git-gutter+-mode-map\n124 (make-sparse-keymap))\n\n126 (defvar git-gutter+-view-diff-function nil\n127 \"Function to call for displaying diffs\")\n\n129 (defvar git-gutter+-clear-function nil\n130 \"Function to call for clearing the diff display\")\n\n132 (defvar git-gutter+-window-config-change-function nil\n133 \"Function to call when the buffer's local window configuration has changed\")\n\n135 (defvar git-gutter+-diffinfos nil)\n136 (defvar git-gutter+-diff-header nil)\n137 (make-variable-buffer-local 'git-gutter+-diffinfos)\n138 (make-variable-buffer-local 'git-gutter+-diff-header)\n\n140 (defvar git-gutter+-popup-buffer \"*git-gutter+-diff*\")\n141 (defvar git-gutter+-buffers-to-reenable nil)\n\n143 (defconst git-gutter+-hunk-header-regex\n144 ;; The same as diff-hunk-header-re-unified\n145 \"^@@ -\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? \\\\+\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? @@\")\n\n147 (defalias 'git-gutter+-popup-hunk 'git-gutter+-show-hunk)\n148 (defalias 'git-gutter+-revert-hunk 'git-gutter+-revert-hunks)\n\n150 (defmacro git-gutter+-awhen (test &rest body)\n151 \"Anaphoric when.\"\n152 (declare (indent 1))\n153 `(let ((it ,test))\n154 (when it ,@body)))\n\n156 (defun git-gutter+-enable-default-display-mode ()\n157 (setq git-gutter+-view-diff-function 'git-gutter+-view-diff-infos\n158 git-gutter+-clear-function 'git-gutter+-clear-diff-infos\n159 git-gutter+-window-config-change-function 'git-gutter+-show-gutter))\n\n161 (unless git-gutter+-view-diff-function\n162 (git-gutter+-enable-default-display-mode))\n\n164 (defun git-gutter+-call-git (args &optional file)\n165 (if (and file (file-remote-p file))\n166 (apply #'process-file git-gutter+-git-executable nil t nil args)\n167 (apply #'call-process git-gutter+-git-executable nil t nil args)))\n\n169 (defun git-gutter+-in-git-repository-p (file)\n170 (with-temp-buffer\n171 (let ((args '(\"rev-parse\" \"--is-inside-work-tree\")))\n172 (when (zerop (git-gutter+-call-git args file))\n173 (goto-char (point-min))\n174 (string= \"true\" (buffer-substring-no-properties\n175 (point) (line-end-position)))))))\n\n177 (defun git-gutter+-root-directory (file)\n178 (with-temp-buffer\n179 (let* ((args '(\"rev-parse\" \"--show-toplevel\"))\n180 (ret (git-gutter+-call-git args file)))\n181 (when (zerop ret)\n182 (goto-char (point-min))\n183 (let ((root (buffer-substring-no-properties (point) (line-end-position))))\n184 (unless (string= root \"\")\n185 (file-name-as-directory root)))))))\n\n187 (defsubst git-gutter+-diff-args (file)\n188 (delq nil (list \"--no-pager\" \"diff\" \"--no-color\" \"--no-ext-diff\" \"-U0\"\n189 git-gutter+-diff-options file)))\n\n191 (defun git-gutter+-diff (curfile)\n192 (let ((args (git-gutter+-diff-args curfile))\n193 (file (buffer-file-name))) ;; for tramp\n194 (with-temp-buffer\n195 (when (zerop (git-gutter+-call-git args file))\n196 (goto-char (point-min))\n197 (let ((diff-header (git-gutter+-get-diff-header))\n198 (diffinfos (git-gutter+-get-diffinfos)))\n199 (list diff-header diffinfos))))))\n\n201 (defun git-gutter+-get-diff-header ()\n202 (save-excursion\n203 (if (re-search-forward git-gutter+-hunk-header-regex nil t)\n204 (buffer-substring (point-min) (match-beginning 0)))))\n\n206 (defsubst git-gutter+-make-diffinfo (type content start end)\n207 (list :type type :content content :start-line start :end-line end))\n\n209 (defun git-gutter+-get-diffinfos ()\n210 (loop while (re-search-forward git-gutter+-hunk-header-regex nil t)\n211 ;; Hunk header format:\n212 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n213 for del-len = (string-to-number (or (match-string 2) \"1\"))\n214 for add-line = (string-to-number (match-string 3))\n215 for add-len = (string-to-number (or (match-string 4) \"1\"))\n216 for type = (cond ((zerop del-len) 'added)\n217 ((zerop add-len) 'deleted)\n218 (t 'modified))\n219 for start-line = (if (eq type 'deleted)\n220 (1+ add-line)\n221 add-line)\n222 for end-line = (if (eq type 'deleted)\n223 start-line\n224 (1- (+ add-line add-len)))\n225 for content = (git-gutter+-diff-content)\n226 collect\n227 (git-gutter+-make-diffinfo type content start-line end-line)))\n\n229 (defun git-gutter+-diff-content ()\n230 (save-excursion\n231 (goto-char (line-beginning-position)) ; Move to beginning of hunk header\n232 (let ((hunk-start (point)))\n233 ;; Move to end of hunk\n234 (forward-line 1)\n235 (if (re-search-forward \"^@@\" nil t)\n236 (backward-char 3) ;; exclude \"\\n@@\"\n237 (goto-char (1- (point-max)))) ; Skip trailing newline\n238 (buffer-substring hunk-start (point)))))\n\n240 (defun git-gutter+-line-to-pos (line)\n241 (save-excursion\n242 (goto-char (point-min))\n243 (forward-line (1- line))\n244 (point)))\n\n246 (defun git-gutter+-before-string (sign)\n247 (let* ((sep-sign git-gutter+-separator-sign)\n248 (sep (when sep-sign\n249 (propertize sep-sign 'face 'git-gutter+-separator)))\n250 (gutter-sep (concat sign sep)))\n251 (propertize \" \" 'display `((margin left-margin) ,gutter-sep))))\n\n253 (defsubst git-gutter+-select-face (type)\n254 (case type\n255 (added 'git-gutter+-added)\n256 (modified 'git-gutter+-modified)\n257 (deleted 'git-gutter+-deleted)))\n\n259 (defsubst git-gutter+-select-sign (type)\n260 (case type\n261 (added git-gutter+-added-sign)\n262 (modified git-gutter+-modified-sign)\n263 (deleted git-gutter+-deleted-sign)))\n\n265 (defun git-gutter+-propertized-sign (type)\n266 (let ((sign (git-gutter+-select-sign type))\n267 (face (git-gutter+-select-face type)))\n268 (propertize sign 'face face)))\n\n270 (defun git-gutter+-view-region (sign start-line end-line)\n271 (let ((beg (git-gutter+-line-to-pos start-line)))\n272 (goto-char beg)\n273 (while (and (<= (line-number-at-pos) end-line) (not (eobp)))\n274 (git-gutter+-view-at-pos sign (point))\n275 (forward-line 1))))\n\n277 (defun git-gutter+-view-at-pos (sign pos)\n278 (let ((ov (make-overlay pos pos)))\n279 (overlay-put ov 'before-string (git-gutter+-before-string sign))\n280 (overlay-put ov 'git-gutter+ t)))\n\n282 (defun git-gutter+-view-diff-info (diffinfo)\n283 (let* ((start-line (plist-get diffinfo :start-line))\n284 (end-line (plist-get diffinfo :end-line))\n285 (type (plist-get diffinfo :type))\n286 (sign (git-gutter+-propertized-sign type)))\n287 (case type\n288 ((modified added) (git-gutter+-view-region sign start-line end-line))\n289 (deleted (git-gutter+-view-at-pos\n290 sign (git-gutter+-line-to-pos start-line))))))\n\n292 (defun git-gutter+-sign-width (sign)\n293 (loop for s across sign\n294 sum (char-width s)))\n\n296 (defun git-gutter+-longest-sign-width ()\n297 (let ((signs (list git-gutter+-modified-sign\n298 git-gutter+-added-sign\n299 git-gutter+-deleted-sign)))\n300 (when git-gutter+-unchanged-sign\n301 (add-to-list 'signs git-gutter+-unchanged-sign))\n302 (+ (apply 'max (mapcar 'git-gutter+-sign-width signs))\n303 (git-gutter+-sign-width git-gutter+-separator-sign))))\n\n305 (defun git-gutter+-view-for-unchanged ()\n306 (save-excursion\n307 (let ((sign (if git-gutter+-unchanged-sign\n308 (propertize git-gutter+-unchanged-sign\n309 'face 'git-gutter+-unchanged)\n310 \" \")))\n311 (goto-char (point-min))\n312 (while (not (eobp))\n313 (git-gutter+-view-at-pos sign (point))\n314 (forward-line 1)))))\n\n316 (defun git-gutter+-set-window-margin (width)\n317 (let ((curwin (get-buffer-window)))\n318 (set-window-margins curwin width (cdr (window-margins curwin)))))\n\n320 (defsubst git-gutter+-file-buffer-p ()\n321 (and (buffer-file-name)\n322 default-directory\n323 (file-directory-p default-directory)))\n\n325 ;;;###autoload\n326 (define-minor-mode git-gutter+-mode\n327 \"Git-Gutter mode\"\n328 :group 'git-gutter+\n329 :init-value nil\n330 :global nil\n331 :lighter git-gutter+-lighter\n332 (if git-gutter+-mode\n333 (if (and (git-gutter+-file-buffer-p)\n334 (git-gutter+-in-git-repository-p (buffer-file-name)))\n335 (progn\n336 (git-gutter+-add-local-hooks)\n337 (git-gutter+-refresh))\n338 (if (called-interactively-p 'any)\n339 (message \"No Git repo for current buffer\"))\n340 (git-gutter+-mode -1))\n341 (git-gutter+-remove-local-hooks)\n342 (git-gutter+-clear)))\n\n344 (defun git-gutter+-add-local-hooks ()\n345 (add-hook 'after-save-hook 'git-gutter+-refresh nil t)\n346 ;; Turn off `git-gutter+-mode' while reverting to prevent any redundant calls to\n347 ;; `git-gutter+-refresh'.\n348 (add-hook 'before-revert-hook 'git-gutter+-turn-off nil t)\n349 (add-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change nil t)\n350 (if git-gutter+-window-config-change-function\n351 (add-hook 'window-configuration-change-hook\n352 git-gutter+-window-config-change-function nil t)))\n\n354 (defun git-gutter+-remove-local-hooks ()\n355 (remove-hook 'after-save-hook 'git-gutter+-refresh t)\n356 (remove-hook 'before-revert-hook 'git-gutter+-turn-off t)\n357 (remove-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change t)\n358 (if git-gutter+-window-config-change-function\n359 (remove-hook 'window-configuration-change-hook\n360 git-gutter+-window-config-change-function t)))\n\n362 (defmacro git-gutter+-in-all-buffers (&rest body)\n363 `(dolist (buf (buffer-list))\n364 (with-current-buffer buf\n365 ,@body)))\n\n367 ;; When `define-globalized-minor-mode' is used to define `global-git-gutter+-mode',\n368 ;; `git-gutter+-mode' and thus `git-gutter+-refresh' get run twice when a new file\n369 ;; is opened. (First for `fundamental-mode', then for the file-specific mode.)\n370 ;; The following definition of `global-git-gutter+-mode' avoids any redundant calls to\n371 ;; `git-gutter+-refresh'.\n\n373 ;;;###autoload\n374 (define-minor-mode global-git-gutter+-mode ()\n375 \"Global Git-Gutter mode\"\n376 :group 'git-gutter+\n377 :init-value nil\n378 :global t\n379 (if global-git-gutter+-mode\n380 (progn\n381 (add-hook 'find-file-hook 'git-gutter+-turn-on)\n382 (add-hook 'after-revert-hook 'git-gutter+-turn-on)\n383 (add-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n384 (git-gutter+-in-all-buffers (git-gutter+-turn-on)))\n385 (remove-hook 'find-file-hook 'git-gutter+-turn-on)\n386 (remove-hook 'after-revert-hook 'git-gutter+-turn-on)\n387 (remove-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n388 (git-gutter+-in-all-buffers (git-gutter+-turn-off))))\n\n390 (defun git-gutter+-turn-on ()\n391 (when (and (buffer-file-name)\n392 (not (memq major-mode git-gutter+-disabled-modes))\n393 (not git-gutter+-mode))\n394 (git-gutter+-mode t)))\n\n396 (defun git-gutter+-turn-off ()\n397 (if git-gutter+-mode (git-gutter+-mode -1)))\n\n399 (defun git-gutter+-reenable-after-major-mode-change ()\n400 (if global-git-gutter+-mode\n401 (add-to-list 'git-gutter+-buffers-to-reenable (current-buffer))))\n\n403 (defun git-gutter+-reenable-buffers ()\n404 (dolist (buf git-gutter+-buffers-to-reenable)\n405 (with-current-buffer buf\n406 (git-gutter+-turn-on)))\n407 (setq git-gutter+-buffers-to-reenable nil))\n\n409 (defsubst git-gutter+-show-gutter-p (diffinfos)\n410 (if git-gutter+-hide-gutter\n411 (or diffinfos git-gutter+-unchanged-sign)\n412 (or global-git-gutter+-mode git-gutter+-unchanged-sign diffinfos)))\n\n414 (defun git-gutter+-show-gutter (&optional diffinfos)\n415 (when (git-gutter+-show-gutter-p (or diffinfos git-gutter+-diffinfos))\n416 (let ((win-width (or git-gutter+-window-width\n417 (git-gutter+-longest-sign-width))))\n418 (git-gutter+-set-window-margin win-width))))\n\n420 (defun git-gutter+-view-diff-infos (diffinfos)\n421 (when (or git-gutter+-unchanged-sign\n422 git-gutter+-separator-sign)\n423 (git-gutter+-view-for-unchanged))\n424 (when diffinfos\n425 (save-excursion\n426 (mapc 'git-gutter+-view-diff-info diffinfos)))\n427 (git-gutter+-show-gutter diffinfos))\n\n429 (defsubst git-gutter+-reset-window-margin-p ()\n430 (or git-gutter+-hide-gutter\n431 (not global-git-gutter+-mode)))\n\n433 (defun git-gutter+-clear-diff-infos ()\n434 (when (git-gutter+-reset-window-margin-p)\n435 (git-gutter+-set-window-margin 0))\n436 (remove-overlays (point-min) (point-max) 'git-gutter+ t))\n\n438 (defun git-gutter+-process-diff (curfile)\n439 (destructuring-bind\n440 (diff-header diffinfos) (git-gutter+-diff curfile)\n441 (setq git-gutter+-diff-header diff-header\n442 git-gutter+-diffinfos diffinfos)\n443 (save-restriction\n444 (widen)\n445 (funcall git-gutter+-view-diff-function diffinfos))))\n\n447 (defun git-gutter+-search-near-diff-index (diffinfos is-reverse)\n448 (loop with current-line = (line-number-at-pos)\n449 with cmp-fn = (if is-reverse '> '<)\n450 for diffinfo in (if is-reverse (reverse diffinfos) diffinfos)\n451 for index = 0 then (1+ index)\n452 for start-line = (plist-get diffinfo :start-line)\n453 when (funcall cmp-fn current-line start-line)\n454 return (if is-reverse\n455 (1- (- (length diffinfos) index))\n456 index)))\n\n458 (defun git-gutter+-diffinfo-at-point ()\n459 (save-restriction\n460 (widen)\n461 (loop with current-line = (line-number-at-pos)\n462 for diffinfo in git-gutter+-diffinfos\n463 for start = (plist-get diffinfo :start-line)\n464 for end = (or (plist-get diffinfo :end-line) (1+ start))\n465 when (and (>= current-line start) (<= current-line end))\n466 return diffinfo)))\n\n468 (defun git-gutter+-collect-deleted-line (str)\n469 (with-temp-buffer\n470 (insert str)\n471 (goto-char (point-min))\n472 (loop while (re-search-forward \"^-\\\\(.*?\\\\)$\" nil t)\n473 collect (match-string 1) into deleted-lines\n474 finally return deleted-lines)))\n\n476 (defun git-gutter+-delete-added-lines (start-line end-line)\n477 (forward-line (1- start-line))\n478 (let ((start-point (point)))\n479 (forward-line (1+ (- end-line start-line)))\n480 (delete-region start-point (point))))\n\n482 (defun git-gutter+-insert-deleted-lines (content)\n483 (dolist (line (git-gutter+-collect-deleted-line content))\n484 (insert (concat line \"\\n\"))))\n\n486 (defun git-gutter+-do-revert-hunk (diffinfo)\n487 (save-excursion\n488 (save-restriction\n489 (widen)\n490 (goto-char (point-min))\n491 (let ((start-line (plist-get diffinfo :start-line))\n492 (end-line (plist-get diffinfo :end-line))\n493 (content (plist-get diffinfo :content)))\n494 (case (plist-get diffinfo :type)\n495 (added (git-gutter+-delete-added-lines start-line end-line))\n496 (deleted (forward-line (1- start-line))\n497 (git-gutter+-insert-deleted-lines content))\n498 (modified (git-gutter+-delete-added-lines start-line end-line)\n499 (git-gutter+-insert-deleted-lines content)))))))\n\n501 (defun git-gutter+-revert-hunks ()\n502 \"Revert hunk at point. If region is active, revert all hunks within the region.\"\n503 (interactive)\n504 (let* ((diffinfos (git-gutter+-selected-diffinfos))\n505 (one-diffinfo-p (= 1 (length diffinfos))))\n506 (save-window-excursion\n507 (if one-diffinfo-p (git-gutter+-show-hunk (car diffinfos)))\n508 (when (and diffinfos\n509 (yes-or-no-p (if one-diffinfo-p\n510 \"Revert hunk?\"\n511 (format \"Revert %d hunks?\" (length diffinfos)))))\n512 ;; Revert diffinfos in reverse so that earlier hunks don't invalidate the\n513 ;; line number information of the later hunks.\n514 (dolist (diffinfo (nreverse diffinfos))\n515 (git-gutter+-do-revert-hunk diffinfo))\n516 (save-buffer))\n517 (if one-diffinfo-p\n518 (git-gutter+-awhen (get-buffer git-gutter+-popup-buffer)\n519 (kill-buffer it))))))\n\n521 (defun git-gutter+-show-hunk (&optional diffinfo)\n522 \"Show hunk at point in another window\"\n523 (interactive)\n524 (git-gutter+-awhen (or diffinfo\n525 (git-gutter+-diffinfo-at-point))\n526 (save-selected-window\n527 (with-current-buffer (get-buffer-create git-gutter+-popup-buffer)\n528 (setq buffer-read-only nil)\n529 (erase-buffer)\n530 (insert (plist-get it :content))\n531 (insert \"\\n\")\n532 (goto-char (point-min))\n533 (diff-mode)\n534 (view-mode)\n535 (pop-to-buffer (current-buffer))))))\n\n537 (defun git-gutter+-next-hunk (arg)\n538 \"Move to next diff hunk\"\n539 (interactive \"p\")\n540 (if (not git-gutter+-diffinfos)\n541 (message \"No changes in buffer\")\n542 (save-restriction\n543 (widen)\n544 (let* ((is-reverse (< arg 0))\n545 (diffinfos git-gutter+-diffinfos)\n546 (len (length diffinfos))\n547 (index (git-gutter+-search-near-diff-index diffinfos is-reverse))\n548 (real-index (if index\n549 (let ((next (if is-reverse (1+ index) (1- index))))\n550 (mod (+ arg next) len))\n551 (if is-reverse (1- (length diffinfos)) 0)))\n552 (diffinfo (nth real-index diffinfos)))\n553 (goto-char (point-min))\n554 (forward-line (1- (plist-get diffinfo :start-line)))\n555 (when (buffer-live-p (get-buffer git-gutter+-popup-buffer))\n556 (save-window-excursion\n557 (git-gutter+-show-hunk)))))))\n\n559 (defun git-gutter+-previous-hunk (arg)\n560 \"Move to previous diff hunk\"\n561 (interactive \"p\")\n562 (git-gutter+-next-hunk (- arg)))\n\n564 (defun git-gutter+-remote-default-directory (dir file)\n565 (let* ((vec (tramp-dissect-file-name file))\n566 (method (aref vec 0))\n567 (user (aref vec 1))\n568 (host (aref vec 2)))\n569 (format \"/%s:%s%s:%s\" method (if user (concat user \"@\") \"\") host dir)))\n\n571 (defun git-gutter+-remote-file-path (dir file)\n572 (let ((file (aref (tramp-dissect-file-name file) 3)))\n573 (replace-regexp-in-string (concat \"\\\\`\" dir) \"\" file)))\n\n575 (defun git-gutter+-local-file-path (file)\n576 (if (eq system-type 'windows-nt)\n577 ;; Cygwin can't handle Windows absolute paths\n578 (file-relative-name file default-directory)\n579 file))\n\n581 (defun git-gutter+-refresh ()\n582 (git-gutter+-clear)\n583 (let ((file (buffer-file-name)))\n584 (when (and file (file-exists-p file))\n585 (if (file-remote-p file)\n586 (let* ((repo-root (git-gutter+-root-directory file))\n587 (default-directory (git-gutter+-remote-default-directory repo-root file)))\n588 (git-gutter+-process-diff (git-gutter+-remote-file-path repo-root file)))\n589 (git-gutter+-process-diff (git-gutter+-local-file-path file))))))\n\n591 (defun git-gutter+-clear ()\n592 (save-restriction\n593 (widen)\n594 (funcall git-gutter+-clear-function))\n595 (setq git-gutter+-diffinfos nil))\n\n\n598 ;;; Staging\n\n600 (defun git-gutter+-stage-hunks ()\n601 \"Stage hunk at point. If region is active, stage all hunk lines within the region.\"\n602 (interactive)\n603 (let* ((line-range (if (use-region-p)\n604 (cons (line-number-at-pos (region-beginning))\n605 (line-number-at-pos (region-end)))))\n606 (diffinfos (git-gutter+-selected-diffinfos line-range)))\n607 (when diffinfos\n608 (let ((error-msg (git-gutter+-stage-diffinfos diffinfos line-range)))\n609 (if error-msg\n610 (message \"Error staging hunks:\\n%s\" error-msg))\n611 (git-gutter+-refresh)))))\n\n613 (defun git-gutter+-selected-diffinfos (&optional line-range)\n614 (unless line-range\n615 (setq line-range (if (use-region-p)\n616 (cons (line-number-at-pos (region-beginning))\n617 (line-number-at-pos (region-end))))))\n618 (if line-range\n619 (git-gutter+-diffinfos-between-lines line-range)\n620 (git-gutter+-awhen (git-gutter+-diffinfo-at-point)\n621 (list it))))\n\n623 (defsubst git-gutter+-diffinfo-between-lines-p (diffinfo start-line end-line)\n624 (let ((diff-start (plist-get diffinfo :start-line))\n625 (diff-end (plist-get diffinfo :end-line)))\n626 (and (<= start-line diff-end)\n627 (<= diff-start end-line))))\n\n629 (defun git-gutter+-diffinfos-between-lines (line-range)\n630 (save-restriction\n631 (widen)\n632 (let ((start-line (car line-range))\n633 (end-line (cdr line-range)))\n634 (delq nil\n635 (mapcar (lambda (diffinfo)\n636 (if (git-gutter+-diffinfo-between-lines-p\n637 diffinfo start-line end-line)\n638 diffinfo))\n639 git-gutter+-diffinfos)))))\n\n641 (defun git-gutter+-stage-diffinfos (diffinfos line-range)\n642 (let ((header git-gutter+-diff-header))\n643 (with-temp-buffer\n644 (insert header)\n645 ;; Insert hunks in reverse so that earlier hunks don't invalidate the line\n646 ;; number information of the later hunks.\n647 (dolist (diffinfo (nreverse diffinfos))\n648 (git-gutter+-insert-diffinfo diffinfo line-range)\n649 (goto-char (point-max)))\n650 (git-gutter+-call-git-on-current-buffer\n651 '(\"apply\" \"--unidiff-zero\" \"--cached\" \"-\")))))\n\n653 (defun git-gutter+-insert-diffinfo (diffinfo line-range)\n654 (let ((content (plist-get diffinfo :content))\n655 (type (plist-get diffinfo :type)))\n656 (if (not line-range)\n657 (git-gutter+-insert-hunk content type)\n658 (let ((diff-start-line (plist-get diffinfo :start-line))\n659 (diff-end-line (plist-get diffinfo :end-line))\n660 (start-line (car line-range))\n661 (end-line (cdr line-range)))\n662 (git-gutter+-insert-hunk content type\n663 (1+ (- start-line diff-start-line))\n664 (1+ (- end-line diff-start-line)))))))\n\n666 (defun git-gutter+-call-git-on-current-buffer (args)\n667 \"Sends the current buffer contents to Git and replaces them with Git's output.\n\n669 RETURNS nil if Git ran successfully. Returns an error description otherwise.\"\n670 (unless (zerop (apply #'call-process-region (point-min) (point-max)\n671 git-gutter+-git-executable t t nil args))\n672 (buffer-string)))\n\n674 (defsubst git-gutter+-read-hunk-header (hunk)\n675 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n676 (string-match git-gutter+-hunk-header-regex hunk)\n677 (list (string-to-number (match-string 1 hunk))\n678 (string-to-number (or (match-string 2 hunk) \"1\"))\n679 (string-to-number (match-string 3 hunk))\n680 (string-to-number (or (match-string 4 hunk) \"1\"))))\n\n682 (defun git-gutter+-insert-hunk (hunk type &optional start end)\n683 \"If START and END are provided, only insert addition (+) lines between\n684 START and END (inclusive). START and END are both line numbers starting with 1.\"\n685 (destructuring-bind\n686 (del-line del-len add-line add-len) (git-gutter+-read-hunk-header hunk)\n687 (let* ((start (max 1 (or start 1)))\n688 (end (min add-len (or end add-len)))\n689 (insert-all-p (or (eq type :deleted)\n690 (and (= start 1) (= end add-len))))\n691 (num-lines-selected (if insert-all-p\n692 add-len\n693 (1+ (- end start)))))\n694 ;; When the user selected the last lines of a hunk with type `modified' (but\n695 ;; not the complete hunk), then don't insert any deletion (-) lines from that\n696 ;; hunk.\n697 (if (and (eq type 'modified)\n698 (> start 1) (= end add-len))\n699 (setq type 'modified-trailing))\n\n701 (save-excursion\n702 (insert hunk \"\\n\"))\n\n704 (git-gutter+-delete-hunk-header)\n\n706 (if (not insert-all-p)\n707 (git-gutter+-modify-hunk type num-lines-selected del-len start))\n\n709 (let ((hunk-header (git-gutter+-make-hunk-header type num-lines-selected\n710 del-line del-len add-line)))\n711 (insert hunk-header \"\\n\")))))\n\n713 (defun git-gutter+-delete-hunk-header ()\n714 (let ((hunk-start (point)))\n715 (forward-line 1)\n716 (delete-region hunk-start (point))))\n\n718 (defun git-gutter+-modify-hunk (type num-lines-selected del-len start)\n719 \"Remove all addition (+) lines from hunk that aren't selected.\n720 If TYPE is not `modified', also remove all deletion (-) lines.\"\n721 (let ((first-line-selected (+ del-len (1- start)))\n722 selected-lines)\n723 (save-excursion\n724 (forward-line first-line-selected)\n725 (let ((selection-start (point)))\n726 (forward-line num-lines-selected)\n727 (setq selected-lines (buffer-substring selection-start (point)))))\n728 (save-excursion\n729 (if (eq type 'modified) (forward-line del-len)) ; skip over deletion (-) lines\n730 (delete-region (point) (point-max))\n731 (insert selected-lines))))\n\n733 (defun git-gutter+-make-hunk-header (type num-lines-selected del-line del-len add-line)\n734 (let ((add-len num-lines-selected))\n735 (case type\n736 (added (setq add-line (1+ del-line)))\n737 (modified-trailing (setq add-line (+ del-line del-len)\n738 del-line (1- add-line)\n739 del-len 0))\n740 (t (setq add-line del-line)))\n741 (format \"@@ -%d,%d +%d,%d @@\"\n742 del-line del-len\n743 add-line add-len)))\n\n\n746 ;;; Committing\n747 ;; This section draws heavily from old Magit source code.\n\n749 (defvar git-gutter+-pre-commit-window-config nil)\n750 (defvar git-gutter+-commit-origin-buffer nil\n751 \"Buffer that started the commit\")\n\n753 (defconst git-gutter+-commit-buffer-name \"*Commit Message*\")\n754 (defconst git-gutter+-staged-changes-buffer-name \"*Staged Changes*\")\n\n756 ;;;###autoload\n757 (defun git-gutter+-commit ()\n758 \"Commit staged changes. If nothing is staged, ask to stage the current buffer.\"\n759 (interactive)\n\n761 (when (and (not (git-gutter+-anything-staged-p))\n762 git-gutter+-diffinfos\n763 (y-or-n-p \"Nothing staged. Stage current buffer? \"))\n764 (git-gutter+-stage-whole-buffer))\n\n766 (let ((file (buffer-file-name))\n767 (dir default-directory))\n768 (git-gutter+-save-window-config-if-needed)\n769 (setq git-gutter+-commit-origin-buffer (current-buffer))\n770 (git-gutter+-open-commit-edit-buffer dir)\n771 (git-gutter+-show-staged-changes file dir)))\n\n773 (defun git-gutter+-stage-and-commit ()\n774 (interactive)\n775 (git-gutter+-stage-hunks)\n776 (git-gutter+-commit))\n\n778 (defun git-gutter+-save-window-config-if-needed ()\n779 ;; Only save the window config if the temporary buffers that get popped-up by\n780 ;; git-gutter+ are not already visible.\n781 ;; In this way, `git-gutter+-commit' can be called twice in a row without\n782 ;; losing the original window config.\n783 (when (not (and git-gutter+-pre-commit-window-config\n784 (get-buffer-window git-gutter+-commit-buffer-name)\n785 (get-buffer-window git-gutter+-staged-changes-buffer-name)))\n786 (setq git-gutter+-pre-commit-window-config (current-window-configuration))))\n\n788 (defun git-gutter+-open-commit-edit-buffer (dir)\n789 \"Opens a buffer for composing the commit message\"\n790 (pop-to-buffer (get-buffer-create git-gutter+-commit-buffer-name))\n791 (setq default-directory dir)\n792 (git-gutter+-commit-mode)\n793 (message \"Type C-c C-c to commit (C-c C-k to cancel).\"))\n\n795 (defsubst git-gutter+-pop-to-staged-changes-buffer ()\n796 (let* ((buf (get-buffer-create git-gutter+-staged-changes-buffer-name))\n797 (window (get-buffer-window buf)))\n798 (if window\n799 ;; Buffer is already visible\n800 (select-window window)\n801 (if (<= (length (window-list)) 2)\n802 (split-window))\n803 (pop-to-buffer buf))))\n\n805 (defun git-gutter+-show-staged-changes (file dir)\n806 (save-selected-window\n807 (git-gutter+-pop-to-staged-changes-buffer)\n808 (setq buffer-read-only nil)\n809 (erase-buffer)\n810 (let ((default-directory dir))\n811 (git-gutter+-call-git '(\"diff\" \"--staged\") file))\n812 (goto-char (point-min))\n813 (diff-mode)\n814 (view-mode)))\n\n816 (defsubst git-gutter+-abort-commit-when-no-changes (allow-empty amend)\n817 (unless (or amend\n818 allow-empty\n819 (git-gutter+-anything-staged-p))\n820 (error\n821 \"Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?\"\n822 (key-description (car (where-is-internal\n823 'git-gutter+-commit-toggle-amending)))\n824 (key-description (car (where-is-internal\n825 'git-gutter+-commit-toggle-allow-empty))))))\n\n827 (defsubst git-gutter+-buffer-is-whitespace ()\n828 (save-excursion\n829 (goto-char (point-min))\n830 (looking-at-p \"[ \\t\\n]*\\\\'\")))\n\n832 (defun git-gutter+-publish-commit ()\n833 \"Publish commit\"\n834 (interactive)\n835 (let* ((fields (git-gutter+-commit-get-fields))\n836 (amend (equal \"yes\" (git-gutter+-commit-get-field 'amend fields)))\n837 (allow-empty (equal \"yes\" (git-gutter+-commit-get-field 'allow-empty fields)))\n838 (author (git-gutter+-commit-get-field 'author fields))\n839 (date (git-gutter+-commit-get-field 'date fields)))\n\n841 (git-gutter+-abort-commit-when-no-changes allow-empty amend)\n\n843 (git-gutter+-push-to-comment-ring (buffer-string))\n\n845 (git-gutter+-commit-set-fields nil) ; Delete message header\n\n847 (when (git-gutter+-buffer-is-whitespace)\n848 (erase-buffer)\n849 (insert \"(Empty description)\"))\n\n851 (let ((error-msg (git-gutter+-call-git-on-current-buffer\n852 (append '(\"--no-pager\" \"commit\" \"-F\" \"-\")\n853 (if amend '(\"--amend\"))\n854 (if allow-empty '(\"--allow-empty\"))\n855 (if author (list (concat \"--author=\" author)))\n856 (if date (list (concat \"--date=\" date)))))))\n857 (if error-msg\n858 (progn\n859 (message \"Commit error:\\n%s\" error-msg)\n860 (erase-buffer)\n861 (insert (ring-ref log-edit-comment-ring 0))) ; Reinsert commit message\n862 (message \"Commit successful.\")\n863 (git-gutter+-close-commit-edit-buffer)\n864 (git-gutter+-update-vc-modeline)))))\n\n866 (defun git-gutter+-close-commit-edit-buffer ()\n867 \"Abort edits and discard commit message being composed.\"\n868 (interactive)\n869 (kill-buffer)\n870 (set-window-configuration git-gutter+-pre-commit-window-config))\n\n872 (defun git-gutter+-update-vc-modeline ()\n873 (when (buffer-live-p git-gutter+-commit-origin-buffer)\n874 (with-current-buffer git-gutter+-commit-origin-buffer\n875 ;; Updating the modeline has no effect if the buffer still has\n876 ;; changes - it will remain in the 'modified' state. So skip it then.\n877 (unless git-gutter+-diffinfos\n878 (ignore-errors (vc-find-file-hook))))))\n\n880 (defun git-gutter+-stage-whole-buffer ()\n881 (save-excursion\n882 (mark-whole-buffer)\n883 (git-gutter+-stage-hunks)))\n\n885 (defun git-gutter+-anything-staged-p ()\n886 \"Return t if the current repo has staged changes\"\n887 (not (zerop (git-gutter+-call-git '(\"diff\" \"--quiet\" \"--cached\")))))\n\n889 (defun git-gutter+-commit-toggle-amending ()\n890 \"Toggle whether this will be an amendment to the previous commit.\n891 \\(i.e., whether commit is run via 'git commit --amend')\"\n892 (interactive)\n893 ;; Remove the newline that 'git-commit-mode' adds to a new commit\n894 ;; message buffer by default. This prevents an ugly visual\n895 ;; gap between the commit message header and the previous commit\n896 ;; message.\n897 (when (git-gutter+-buffer-is-whitespace)\n898 (erase-buffer))\n\n900 (let ((amend-was-already-set (git-gutter+-commit-get-field 'amend)))\n901 (git-gutter+-commit-toggle-field 'amend t)\n902 (unless amend-was-already-set\n903 ;; Insert previous commit message\n904 (goto-char (point-max))\n905 (unless (zerop (current-column))\n906 (insert \"\\n\"))\n907 (insert (git-gutter+-get-last-commit-msg)\n908 \"\\n\"))))\n\n910 (defun git-gutter+-commit-toggle-allow-empty ()\n911 \"Toggle whether this commit is allowed to be empty.\n912 \\(i.e., whether commit is run via 'git commit --allow-empty')\"\n913 (interactive)\n914 (git-gutter+-commit-toggle-field 'allow-empty t))\n\n916 (defun git-gutter+-format-author (author email)\n917 (format \"%s <%s>\" author email))\n\n919 (defun git-gutter+-commit-toggle-author ()\n920 \"Toggle whether this commit should have a user-defined author.\"\n921 (interactive)\n922 (git-gutter+-commit-toggle-input\n923 'author (git-gutter+-format-author\n924 (or (git-gutter+-get-cfg \"user\" \"name\") \"Author Name\")\n925 (or (git-gutter+-get-cfg \"user\" \"email\") \"author@email\"))))\n\n927 (defun git-gutter+-commit-toggle-date ()\n928 \"Toggle whether this commit should have a user-defined date.\"\n929 (interactive)\n930 (git-gutter+-commit-toggle-input 'date\n931 ;; ISO 8601\n932 (format-time-string \"%Y-%m-%dT%T%z\" (current-time))))\n\n934 (defun git-gutter+-push-to-comment-ring (comment)\n935 (when (or (ring-empty-p log-edit-comment-ring)\n936 (not (equal comment (ring-ref log-edit-comment-ring 0))))\n937 (ring-insert log-edit-comment-ring comment)))\n\n939 (defun git-gutter+-get-last-commit-msg ()\n940 (git-gutter+-git-output '(\"log\" \"--max-count=1\" \"--pretty=format:%s%n%n%b\" \"HEAD\")))\n\n942 (defun git-gutter+-get-cfg (&rest keys)\n943 (git-gutter+-git-output (list \"config\" (mapconcat 'identity keys \".\"))))\n\n945 (defun git-gutter+-git-output (args)\n946 (with-temp-buffer\n947 (git-gutter+-call-git args)\n948 ;; Delete trailing newlines\n949 (goto-char (point-min))\n950 (if (re-search-forward \"\\n+\\\\'\" nil t)\n951 (replace-match \"\"))\n952 (buffer-string)))\n\n\n955 ;;; Commit message header\n\n957 (defconst git-gutter+-commit-header-end \"-- End of commit options header --\\n\")\n\n959 (defun git-gutter+-commit-get-field (name &optional fields)\n960 (cdr (assq name (or fields (git-gutter+-commit-get-fields)))))\n\n962 (defun git-gutter+-commit-set-field (name value)\n963 (let* ((fields (git-gutter+-commit-get-fields))\n964 (cell (assq name fields)))\n965 (cond (cell\n966 (if value\n967 (rplacd cell value)\n968 (setq fields (delq cell fields))))\n969 (t\n970 (if value\n971 (setq fields (append fields (list (cons name value)))))))\n972 (git-gutter+-commit-set-fields fields)))\n\n974 (defun git-gutter+-commit-toggle-field (name default)\n975 \"Toggle the commit header field named NAME.\n976 If it's currently unset, set it to DEFAULT (t or nil).\"\n977 (let* ((fields (git-gutter+-commit-get-fields))\n978 (cell (assq name fields)))\n979 (if cell\n980 (rplacd cell (if (equal (cdr cell) \"yes\") \"no\" \"yes\"))\n981 (setq fields (acons name (if default \"yes\" \"no\") fields)))\n982 (git-gutter+-commit-set-fields fields)))\n\n984 (defun git-gutter+-commit-toggle-input (name default)\n985 \"Toggle the commit header input named NAME.\n986 If it's currently unset, set it to DEFAULT (a string). If it is\n987 set remove it.\"\n988 (let* ((fields (git-gutter+-commit-get-fields))\n989 (cell (assq name fields)))\n990 (if cell\n991 (setq fields (assq-delete-all name fields))\n992 (setq fields (acons name default fields)))\n993 (git-gutter+-commit-set-fields fields)))\n\n995 (defun git-gutter+-commit-get-fields ()\n996 (let (result)\n997 (goto-char (point-min))\n998 (while (looking-at \"^\\\\([A-Za-z0-9-_]+\\\\): *\\\\(.+\\\\)?$\")\n999 (let ((name (intern (downcase (match-string 1))))\n1000 (value (read (or (match-string 2) \"nil\"))))\n1001 (push (cons name value) result))\n1002 (forward-line))\n1003 (if (looking-at (regexp-quote git-gutter+-commit-header-end))\n1004 (nreverse result))))\n\n1006 (defun git-gutter+-commit-set-fields (fields)\n1007 (goto-char (point-min))\n1008 ;; Delete commit header\n1009 (if (search-forward-regexp (format \"^\\\\(?:[A-Za-z0-9-_]+:.*\\n\\\\)*%s\"\n1010 (regexp-quote git-gutter+-commit-header-end))\n1011 nil t)\n1012 (delete-region (match-beginning 0) (match-end 0)))\n1013 (goto-char (point-min))\n1014 (when fields\n1015 (dolist (field fields)\n1016 (insert (capitalize (symbol-name (car field))) \": \"\n1017 (prin1-to-string (cdr field)) \"\\n\"))\n1018 (insert git-gutter+-commit-header-end)))\n\n\n1021 ;;; git-gutter+-commit-mode\n1022 ;; Like git-commit-mode, but adds keybindings to git-gutter+ commands and\n1023 ;; highlighting support for the commit message header.\n\n1025 (define-derived-mode git-gutter+-commit-mode git-commit-mode \"Git-Gutter-Commit\"\n1026 (setq font-lock-defaults (list (git-gutter+-commit-font-lock-keywords) t)))\n\n1028 (setq git-gutter+-commit-mode-map\n1029 (let ((map (copy-keymap git-commit-mode-map)))\n1030 (define-key map (kbd \"C-c C-c\") 'git-gutter+-publish-commit)\n1031 (define-key map (kbd \"C-c C-k\") 'git-gutter+-close-commit-edit-buffer)\n1032 (define-key map (kbd \"C-c C-a\") 'git-gutter+-commit-toggle-amending)\n1033 (define-key map (kbd \"C-c C-e\") 'git-gutter+-commit-toggle-allow-empty)\n1034 (define-key map (kbd \"C-c C-u\") 'git-gutter+-commit-toggle-author)\n1035 (define-key map (kbd \"C-c C-d\") 'git-gutter+-commit-toggle-date)\n1036 (define-key map (kbd \"C-c C-b\") 'git-commit-ack)\n1037 (define-key map (kbd \"M-p\") 'log-edit-previous-comment)\n1038 (define-key map (kbd \"M-n\") 'log-edit-next-comment)\n1039 map))\n\n1041 (defface git-gutter+-commit-header-face\n1042 '((t :inherit font-lock-comment-face))\n1043 \"Highlights the commit message header\"\n1044 :group 'git-gutter+-faces)\n\n1046 (defconst git-gutter+-commit-header-regex\n1047 (concat \"\\\\(?:.\\\\|\\n\\\\)*?\" (regexp-quote git-gutter+-commit-header-end)))\n\n1049 (defconst git-gutter+-skip-commit-header-regex\n1050 (concat \"\\\\`\\\\(?:\" git-gutter+-commit-header-regex \"\\\\)?\"))\n\n1052 ;; Modify git-commit-summary-regexp to ignore the commit header\n1053 (defadvice git-commit-summary-regexp\n1054 (after ignore-git-gutter+-commit-header activate compile)\n1055 (if (eq major-mode 'git-gutter+-commit-mode)\n1056 (setq ad-return-value\n1057 (concat git-gutter+-skip-commit-header-regex\n1058 (substring ; Remove leading \"\\\\`\"\n1059 ad-return-value 2)))))\n\n1061 (defun git-gutter+-commit-font-lock-keywords ()\n1062 \"Like `git-commit-mode-font-lock-keywords' but with commit header highlighting\"\n1063 `((,(concat \"\\\\`\" git-gutter+-commit-header-regex) . 'git-gutter+-commit-header-face)\n1064 ,@(git-commit-mode-font-lock-keywords)))\n\n\n1067 ;;; Magit synchronization\n1068 ;; Force Magit to refresh git-gutter+ when updating the VC mode line.\n\n1070 (defvar git-gutter+-orig-vc-find-file-hook)\n\n1072 (defvar git-gutter+-vc-find-file-hook-with-refresh\n1073 (lambda ()\n1074 (funcall git-gutter+-orig-vc-find-file-hook)\n1075 (if git-gutter+-mode (git-gutter+-refresh))))\n\n1077 (defadvice magit-update-vc-modeline (around refresh-git-gutter+ compile activate)\n1078 ;; `magit-update-vc-modeline' calls `vc-find-file-hook' (a function!) on each\n1079 ;; buffer in the repo. Temporarily rebind it to `vc-find-file-hook-with-refresh',\n1080 ;; which calls git-gutter+-refresh after updating the VC mode line.\n1081 ;;\n1082 ;; Using `flet' would have been much simpler, but it's deprecated since 24.3.\n1083 (setq git-gutter+-orig-vc-find-file-hook (symbol-function 'vc-find-file-hook))\n1084 (fset 'vc-find-file-hook git-gutter+-vc-find-file-hook-with-refresh)\n1085 (unwind-protect\n1086 ad-do-it\n1087 (fset 'vc-find-file-hook git-gutter+-orig-vc-find-file-hook)))\n\n1089 (provide 'git-gutter+)\n\n1091 ;;; git-gutter+.el ends here\n")) (setq helm-swoop-cache t))) (candidates-in-buffer) (get-line . buffer-substring-no-properties) (keymap keymap (C-M-left . backward-sexp) (C-M-right . forward-sexp) (27 keymap (105 . helm-multi-swoop-all-from-helm-swoop)) (3 keymap (5 . helm-swoop-edit)) keymap (C-M-left . paren-backward-sexp) (C-M-right . paren-forward-sexp) (94 . helm-swoop-caret-match) (menu-bar keymap (help-menu keymap (describe keymap (describe-mode . helm-help)))) (help keymap (109 . helm-help)) (f1 keymap (109 . helm-help)) (8 keymap (109 . helm-help) (104 . undefined) (8 . undefined) (4 . helm-debug-output)) (20 . helm-toggle-resplit-and-swap-windows) (C-tab . undefined) (triple-mouse-3 . ignore) (double-mouse-3 . ignore) (mouse-3 . ignore) (drag-mouse-3 . ignore) (down-mouse-3 . ignore) (triple-mouse-2 . ignore) (double-mouse-2 . ignore) (mouse-2 . ignore) (drag-mouse-2 . ignore) (down-mouse-2 . ignore) (triple-mouse-1 . ignore) (double-mouse-1 . ignore) (mouse-1 . ignore) (drag-mouse-1 . ignore) (down-mouse-1 . ignore) (67108897 . helm-toggle-suspend-update) (3 keymap (21 . helm-force-update) (6 . helm-follow-mode) (11 . helm-kill-selection-and-quit) (25 . helm-yank-selection) (4 . helm-delete-current-selection) (45 . helm-swap-windows)) (67108987 . helm-enlarge-window) (67108989 . helm-narrow-window) (19 . undefined) (18 . undefined) (23 . helm-yank-text-at-point) (24 keymap (2 . helm-resume-list-buffers-after-quit) (98 . helm-resume-previous-session-after-quit) (6 . helm-quit-and-find-file)) (11 . helm-delete-minibuffer-contents) (67108896 . helm-toggle-visible-mark) (0 . helm-toggle-visible-mark) (C-M-up . helm-scroll-other-window-down) (C-M-down . helm-scroll-other-window) (M-prior . helm-scroll-other-window-down) (M-next . helm-scroll-other-window) (12 . helm-recenter-top-bottom-other-window) (15 . helm-next-source) (10 . helm-select-3rd-action) (5 . helm-select-2nd-action-or-end-of-line) ...) (header-line . "[C-c C-e] Edit mode, [M-i] apply all buffers") (action lambda ($line) (helm-swoop--goto-line (when (string-match "^[0-9]+" $line) (string-to-number (match-string 0 $line)))) (when (re-search-forward (mapconcat (quote identity) (split-string helm-pattern " ") "\\|") nil t) (goto-char (match-beginning 0))) (helm-swoop--recenter)) (migemo)) #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) "Swoop: " nil "^368 " "*Helm Swoop*" nil nil nil)) | |
helm(((name . "git-gutter+.el") (init lambda nil (unless helm-swoop-cache (with-current-buffer (helm-candidate-buffer (quote local)) (insert "1 ;;; git-gutter+.el --- Manage Git hunks straight from the buffer\n\n3 ;; Copyright (C) 2013 by Syohei YOSHIDA and contributors\n\n5 ;; Author: Syohei YOSHIDA <syohex@gmail.com> and contributors\n6 ;; URL: https://github.com/nonsequitur/git-gutter-plus\n7 ;; Version: 0.1\n\n9 ;; This program is free software; you can redistribute it and/or modify\n10 ;; it under the terms of the GNU General Public License as published by\n11 ;; the Free Software Foundation, either version 3 of the License, or\n12 ;; (at your option) any later version.\n\n14 ;; This program is distributed in the hope that it will be useful,\n15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n17 ;; GNU General Public License for more details.\n\n19 ;; You should have received a copy of the GNU General Public License\n20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n22 ;; Package-Requires: ((git-commit-mode \"0.14\"))\n\n24 ;;; Commentary:\n25 ;;\n26 ;; View, stage and revert Git changes straight from the buffer.\n\n28 ;;; Code:\n\n30 (eval-when-compile\n31 (require 'cl))\n\n33 (require 'tramp)\n34 (require 'log-edit)\n35 (require 'git-commit-mode)\n\n37 (defgroup git-gutter+ nil\n38 \"Manage Git hunks straight from the buffer\"\n39 :prefix \"git-gutter+-\"\n40 :group 'vc)\n\n42 (defcustom git-gutter+-window-width nil\n43 \"Character width of the gutter margin. Set this variable if the automatically\n44 calculated width looks wrong. (This can happen with some special characters.)\"\n45 :type 'integer\n46 :group 'git-gutter+)\n\n48 (defcustom git-gutter+-git-executable \"git\"\n49 \"The path of the Git executable.\"\n50 :type 'string\n51 :group 'git-gutter+)\n\n53 (defcustom git-gutter+-diff-options nil\n54 \"List of strings containing extra arguments to 'git diff'\"\n55 :type 'list\n56 :group 'git-gutter+)\n\n58 (defcustom git-gutter+-separator-sign nil\n59 \"Separator sign\"\n60 :type 'string\n61 :group 'git-gutter+)\n\n63 (defcustom git-gutter+-modified-sign \"=\"\n64 \"Modified sign\"\n65 :type 'string\n66 :group 'git-gutter+)\n\n68 (defcustom git-gutter+-added-sign \"+\"\n69 \"Added sign\"\n70 :type 'string\n71 :group 'git-gutter+)\n\n73 (defcustom git-gutter+-deleted-sign \"-\"\n74 \"Deleted sign\"\n75 :type 'string\n76 :group 'git-gutter+)\n\n78 (defcustom git-gutter+-unchanged-sign nil\n79 \"Unchanged sign\"\n80 :type 'string\n81 :group 'git-gutter+)\n\n83 (defcustom git-gutter+-hide-gutter nil\n84 \"Hide gutter if there are no changes\"\n85 :type 'boolean\n86 :group 'git-gutter+)\n\n88 (defcustom git-gutter+-lighter \" GitGutter\"\n89 \"Minor mode lighter in mode-line\"\n90 :type 'string\n91 :group 'git-gutter+)\n\n93 (defface git-gutter+-separator\n94 '((t (:foreground \"cyan\" :weight bold)))\n95 \"Face of the separator\"\n96 :group 'git-gutter+)\n\n98 (defface git-gutter+-modified\n99 '((t (:foreground \"magenta\" :weight bold)))\n100 \"Face for modified lines\"\n101 :group 'git-gutter+)\n\n103 (defface git-gutter+-added\n104 '((t (:foreground \"green\" :weight bold)))\n105 \"Face for added lines\"\n106 :group 'git-gutter+)\n\n108 (defface git-gutter+-deleted\n109 '((t (:foreground \"red\" :weight bold)))\n110 \"Face for deleted lines\"\n111 :group 'git-gutter+)\n\n113 (defface git-gutter+-unchanged\n114 '((t (:background \"yellow\")))\n115 \"Face for unchanged lines\"\n116 :group 'git-gutter+)\n\n118 (defcustom git-gutter+-disabled-modes nil\n119 \"A list of modes for which `global-git-gutter+-mode' should be disabled.\"\n120 :type '(repeat symbol)\n121 :group 'git-gutter+)\n\n123 (defvar git-gutter+-mode-map\n124 (make-sparse-keymap))\n\n126 (defvar git-gutter+-view-diff-function nil\n127 \"Function to call for displaying diffs\")\n\n129 (defvar git-gutter+-clear-function nil\n130 \"Function to call for clearing the diff display\")\n\n132 (defvar git-gutter+-window-config-change-function nil\n133 \"Function to call when the buffer's local window configuration has changed\")\n\n135 (defvar git-gutter+-diffinfos nil)\n136 (defvar git-gutter+-diff-header nil)\n137 (make-variable-buffer-local 'git-gutter+-diffinfos)\n138 (make-variable-buffer-local 'git-gutter+-diff-header)\n\n140 (defvar git-gutter+-popup-buffer \"*git-gutter+-diff*\")\n141 (defvar git-gutter+-buffers-to-reenable nil)\n\n143 (defconst git-gutter+-hunk-header-regex\n144 ;; The same as diff-hunk-header-re-unified\n145 \"^@@ -\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? \\\\+\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? @@\")\n\n147 (defalias 'git-gutter+-popup-hunk 'git-gutter+-show-hunk)\n148 (defalias 'git-gutter+-revert-hunk 'git-gutter+-revert-hunks)\n\n150 (defmacro git-gutter+-awhen (test &rest body)\n151 \"Anaphoric when.\"\n152 (declare (indent 1))\n153 `(let ((it ,test))\n154 (when it ,@body)))\n\n156 (defun git-gutter+-enable-default-display-mode ()\n157 (setq git-gutter+-view-diff-function 'git-gutter+-view-diff-infos\n158 git-gutter+-clear-function 'git-gutter+-clear-diff-infos\n159 git-gutter+-window-config-change-function 'git-gutter+-show-gutter))\n\n161 (unless git-gutter+-view-diff-function\n162 (git-gutter+-enable-default-display-mode))\n\n164 (defun git-gutter+-call-git (args &optional file)\n165 (if (and file (file-remote-p file))\n166 (apply #'process-file git-gutter+-git-executable nil t nil args)\n167 (apply #'call-process git-gutter+-git-executable nil t nil args)))\n\n169 (defun git-gutter+-in-git-repository-p (file)\n170 (with-temp-buffer\n171 (let ((args '(\"rev-parse\" \"--is-inside-work-tree\")))\n172 (when (zerop (git-gutter+-call-git args file))\n173 (goto-char (point-min))\n174 (string= \"true\" (buffer-substring-no-properties\n175 (point) (line-end-position)))))))\n\n177 (defun git-gutter+-root-directory (file)\n178 (with-temp-buffer\n179 (let* ((args '(\"rev-parse\" \"--show-toplevel\"))\n180 (ret (git-gutter+-call-git args file)))\n181 (when (zerop ret)\n182 (goto-char (point-min))\n183 (let ((root (buffer-substring-no-properties (point) (line-end-position))))\n184 (unless (string= root \"\")\n185 (file-name-as-directory root)))))))\n\n187 (defsubst git-gutter+-diff-args (file)\n188 (delq nil (list \"--no-pager\" \"diff\" \"--no-color\" \"--no-ext-diff\" \"-U0\"\n189 git-gutter+-diff-options file)))\n\n191 (defun git-gutter+-diff (curfile)\n192 (let ((args (git-gutter+-diff-args curfile))\n193 (file (buffer-file-name))) ;; for tramp\n194 (with-temp-buffer\n195 (when (zerop (git-gutter+-call-git args file))\n196 (goto-char (point-min))\n197 (let ((diff-header (git-gutter+-get-diff-header))\n198 (diffinfos (git-gutter+-get-diffinfos)))\n199 (list diff-header diffinfos))))))\n\n201 (defun git-gutter+-get-diff-header ()\n202 (save-excursion\n203 (if (re-search-forward git-gutter+-hunk-header-regex nil t)\n204 (buffer-substring (point-min) (match-beginning 0)))))\n\n206 (defsubst git-gutter+-make-diffinfo (type content start end)\n207 (list :type type :content content :start-line start :end-line end))\n\n209 (defun git-gutter+-get-diffinfos ()\n210 (loop while (re-search-forward git-gutter+-hunk-header-regex nil t)\n211 ;; Hunk header format:\n212 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n213 for del-len = (string-to-number (or (match-string 2) \"1\"))\n214 for add-line = (string-to-number (match-string 3))\n215 for add-len = (string-to-number (or (match-string 4) \"1\"))\n216 for type = (cond ((zerop del-len) 'added)\n217 ((zerop add-len) 'deleted)\n218 (t 'modified))\n219 for start-line = (if (eq type 'deleted)\n220 (1+ add-line)\n221 add-line)\n222 for end-line = (if (eq type 'deleted)\n223 start-line\n224 (1- (+ add-line add-len)))\n225 for content = (git-gutter+-diff-content)\n226 collect\n227 (git-gutter+-make-diffinfo type content start-line end-line)))\n\n229 (defun git-gutter+-diff-content ()\n230 (save-excursion\n231 (goto-char (line-beginning-position)) ; Move to beginning of hunk header\n232 (let ((hunk-start (point)))\n233 ;; Move to end of hunk\n234 (forward-line 1)\n235 (if (re-search-forward \"^@@\" nil t)\n236 (backward-char 3) ;; exclude \"\\n@@\"\n237 (goto-char (1- (point-max)))) ; Skip trailing newline\n238 (buffer-substring hunk-start (point)))))\n\n240 (defun git-gutter+-line-to-pos (line)\n241 (save-excursion\n242 (goto-char (point-min))\n243 (forward-line (1- line))\n244 (point)))\n\n246 (defun git-gutter+-before-string (sign)\n247 (let* ((sep-sign git-gutter+-separator-sign)\n248 (sep (when sep-sign\n249 (propertize sep-sign 'face 'git-gutter+-separator)))\n250 (gutter-sep (concat sign sep)))\n251 (propertize \" \" 'display `((margin left-margin) ,gutter-sep))))\n\n253 (defsubst git-gutter+-select-face (type)\n254 (case type\n255 (added 'git-gutter+-added)\n256 (modified 'git-gutter+-modified)\n257 (deleted 'git-gutter+-deleted)))\n\n259 (defsubst git-gutter+-select-sign (type)\n260 (case type\n261 (added git-gutter+-added-sign)\n262 (modified git-gutter+-modified-sign)\n263 (deleted git-gutter+-deleted-sign)))\n\n265 (defun git-gutter+-propertized-sign (type)\n266 (let ((sign (git-gutter+-select-sign type))\n267 (face (git-gutter+-select-face type)))\n268 (propertize sign 'face face)))\n\n270 (defun git-gutter+-view-region (sign start-line end-line)\n271 (let ((beg (git-gutter+-line-to-pos start-line)))\n272 (goto-char beg)\n273 (while (and (<= (line-number-at-pos) end-line) (not (eobp)))\n274 (git-gutter+-view-at-pos sign (point))\n275 (forward-line 1))))\n\n277 (defun git-gutter+-view-at-pos (sign pos)\n278 (let ((ov (make-overlay pos pos)))\n279 (overlay-put ov 'before-string (git-gutter+-before-string sign))\n280 (overlay-put ov 'git-gutter+ t)))\n\n282 (defun git-gutter+-view-diff-info (diffinfo)\n283 (let* ((start-line (plist-get diffinfo :start-line))\n284 (end-line (plist-get diffinfo :end-line))\n285 (type (plist-get diffinfo :type))\n286 (sign (git-gutter+-propertized-sign type)))\n287 (case type\n288 ((modified added) (git-gutter+-view-region sign start-line end-line))\n289 (deleted (git-gutter+-view-at-pos\n290 sign (git-gutter+-line-to-pos start-line))))))\n\n292 (defun git-gutter+-sign-width (sign)\n293 (loop for s across sign\n294 sum (char-width s)))\n\n296 (defun git-gutter+-longest-sign-width ()\n297 (let ((signs (list git-gutter+-modified-sign\n298 git-gutter+-added-sign\n299 git-gutter+-deleted-sign)))\n300 (when git-gutter+-unchanged-sign\n301 (add-to-list 'signs git-gutter+-unchanged-sign))\n302 (+ (apply 'max (mapcar 'git-gutter+-sign-width signs))\n303 (git-gutter+-sign-width git-gutter+-separator-sign))))\n\n305 (defun git-gutter+-view-for-unchanged ()\n306 (save-excursion\n307 (let ((sign (if git-gutter+-unchanged-sign\n308 (propertize git-gutter+-unchanged-sign\n309 'face 'git-gutter+-unchanged)\n310 \" \")))\n311 (goto-char (point-min))\n312 (while (not (eobp))\n313 (git-gutter+-view-at-pos sign (point))\n314 (forward-line 1)))))\n\n316 (defun git-gutter+-set-window-margin (width)\n317 (let ((curwin (get-buffer-window)))\n318 (set-window-margins curwin width (cdr (window-margins curwin)))))\n\n320 (defsubst git-gutter+-file-buffer-p ()\n321 (and (buffer-file-name)\n322 default-directory\n323 (file-directory-p default-directory)))\n\n325 ;;;###autoload\n326 (define-minor-mode git-gutter+-mode\n327 \"Git-Gutter mode\"\n328 :group 'git-gutter+\n329 :init-value nil\n330 :global nil\n331 :lighter git-gutter+-lighter\n332 (if git-gutter+-mode\n333 (if (and (git-gutter+-file-buffer-p)\n334 (git-gutter+-in-git-repository-p (buffer-file-name)))\n335 (progn\n336 (git-gutter+-add-local-hooks)\n337 (git-gutter+-refresh))\n338 (if (called-interactively-p 'any)\n339 (message \"No Git repo for current buffer\"))\n340 (git-gutter+-mode -1))\n341 (git-gutter+-remove-local-hooks)\n342 (git-gutter+-clear)))\n\n344 (defun git-gutter+-add-local-hooks ()\n345 (add-hook 'after-save-hook 'git-gutter+-refresh nil t)\n346 ;; Turn off `git-gutter+-mode' while reverting to prevent any redundant calls to\n347 ;; `git-gutter+-refresh'.\n348 (add-hook 'before-revert-hook 'git-gutter+-turn-off nil t)\n349 (add-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change nil t)\n350 (if git-gutter+-window-config-change-function\n351 (add-hook 'window-configuration-change-hook\n352 git-gutter+-window-config-change-function nil t)))\n\n354 (defun git-gutter+-remove-local-hooks ()\n355 (remove-hook 'after-save-hook 'git-gutter+-refresh t)\n356 (remove-hook 'before-revert-hook 'git-gutter+-turn-off t)\n357 (remove-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change t)\n358 (if git-gutter+-window-config-change-function\n359 (remove-hook 'window-configuration-change-hook\n360 git-gutter+-window-config-change-function t)))\n\n362 (defmacro git-gutter+-in-all-buffers (&rest body)\n363 `(dolist (buf (buffer-list))\n364 (with-current-buffer buf\n365 ,@body)))\n\n367 ;; When `define-globalized-minor-mode' is used to define `global-git-gutter+-mode',\n368 ;; `git-gutter+-mode' and thus `git-gutter+-refresh' get run twice when a new file\n369 ;; is opened. (First for `fundamental-mode', then for the file-specific mode.)\n370 ;; The following definition of `global-git-gutter+-mode' avoids any redundant calls to\n371 ;; `git-gutter+-refresh'.\n\n373 ;;;###autoload\n374 (define-minor-mode global-git-gutter+-mode ()\n375 \"Global Git-Gutter mode\"\n376 :group 'git-gutter+\n377 :init-value nil\n378 :global t\n379 (if global-git-gutter+-mode\n380 (progn\n381 (add-hook 'find-file-hook 'git-gutter+-turn-on)\n382 (add-hook 'after-revert-hook 'git-gutter+-turn-on)\n383 (add-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n384 (git-gutter+-in-all-buffers (git-gutter+-turn-on)))\n385 (remove-hook 'find-file-hook 'git-gutter+-turn-on)\n386 (remove-hook 'after-revert-hook 'git-gutter+-turn-on)\n387 (remove-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n388 (git-gutter+-in-all-buffers (git-gutter+-turn-off))))\n\n390 (defun git-gutter+-turn-on ()\n391 (when (and (buffer-file-name)\n392 (not (memq major-mode git-gutter+-disabled-modes))\n393 (not git-gutter+-mode))\n394 (git-gutter+-mode t)))\n\n396 (defun git-gutter+-turn-off ()\n397 (if git-gutter+-mode (git-gutter+-mode -1)))\n\n399 (defun git-gutter+-reenable-after-major-mode-change ()\n400 (if global-git-gutter+-mode\n401 (add-to-list 'git-gutter+-buffers-to-reenable (current-buffer))))\n\n403 (defun git-gutter+-reenable-buffers ()\n404 (dolist (buf git-gutter+-buffers-to-reenable)\n405 (with-current-buffer buf\n406 (git-gutter+-turn-on)))\n407 (setq git-gutter+-buffers-to-reenable nil))\n\n409 (defsubst git-gutter+-show-gutter-p (diffinfos)\n410 (if git-gutter+-hide-gutter\n411 (or diffinfos git-gutter+-unchanged-sign)\n412 (or global-git-gutter+-mode git-gutter+-unchanged-sign diffinfos)))\n\n414 (defun git-gutter+-show-gutter (&optional diffinfos)\n415 (when (git-gutter+-show-gutter-p (or diffinfos git-gutter+-diffinfos))\n416 (let ((win-width (or git-gutter+-window-width\n417 (git-gutter+-longest-sign-width))))\n418 (git-gutter+-set-window-margin win-width))))\n\n420 (defun git-gutter+-view-diff-infos (diffinfos)\n421 (when (or git-gutter+-unchanged-sign\n422 git-gutter+-separator-sign)\n423 (git-gutter+-view-for-unchanged))\n424 (when diffinfos\n425 (save-excursion\n426 (mapc 'git-gutter+-view-diff-info diffinfos)))\n427 (git-gutter+-show-gutter diffinfos))\n\n429 (defsubst git-gutter+-reset-window-margin-p ()\n430 (or git-gutter+-hide-gutter\n431 (not global-git-gutter+-mode)))\n\n433 (defun git-gutter+-clear-diff-infos ()\n434 (when (git-gutter+-reset-window-margin-p)\n435 (git-gutter+-set-window-margin 0))\n436 (remove-overlays (point-min) (point-max) 'git-gutter+ t))\n\n438 (defun git-gutter+-process-diff (curfile)\n439 (destructuring-bind\n440 (diff-header diffinfos) (git-gutter+-diff curfile)\n441 (setq git-gutter+-diff-header diff-header\n442 git-gutter+-diffinfos diffinfos)\n443 (save-restriction\n444 (widen)\n445 (funcall git-gutter+-view-diff-function diffinfos))))\n\n447 (defun git-gutter+-search-near-diff-index (diffinfos is-reverse)\n448 (loop with current-line = (line-number-at-pos)\n449 with cmp-fn = (if is-reverse '> '<)\n450 for diffinfo in (if is-reverse (reverse diffinfos) diffinfos)\n451 for index = 0 then (1+ index)\n452 for start-line = (plist-get diffinfo :start-line)\n453 when (funcall cmp-fn current-line start-line)\n454 return (if is-reverse\n455 (1- (- (length diffinfos) index))\n456 index)))\n\n458 (defun git-gutter+-diffinfo-at-point ()\n459 (save-restriction\n460 (widen)\n461 (loop with current-line = (line-number-at-pos)\n462 for diffinfo in git-gutter+-diffinfos\n463 for start = (plist-get diffinfo :start-line)\n464 for end = (or (plist-get diffinfo :end-line) (1+ start))\n465 when (and (>= current-line start) (<= current-line end))\n466 return diffinfo)))\n\n468 (defun git-gutter+-collect-deleted-line (str)\n469 (with-temp-buffer\n470 (insert str)\n471 (goto-char (point-min))\n472 (loop while (re-search-forward \"^-\\\\(.*?\\\\)$\" nil t)\n473 collect (match-string 1) into deleted-lines\n474 finally return deleted-lines)))\n\n476 (defun git-gutter+-delete-added-lines (start-line end-line)\n477 (forward-line (1- start-line))\n478 (let ((start-point (point)))\n479 (forward-line (1+ (- end-line start-line)))\n480 (delete-region start-point (point))))\n\n482 (defun git-gutter+-insert-deleted-lines (content)\n483 (dolist (line (git-gutter+-collect-deleted-line content))\n484 (insert (concat line \"\\n\"))))\n\n486 (defun git-gutter+-do-revert-hunk (diffinfo)\n487 (save-excursion\n488 (save-restriction\n489 (widen)\n490 (goto-char (point-min))\n491 (let ((start-line (plist-get diffinfo :start-line))\n492 (end-line (plist-get diffinfo :end-line))\n493 (content (plist-get diffinfo :content)))\n494 (case (plist-get diffinfo :type)\n495 (added (git-gutter+-delete-added-lines start-line end-line))\n496 (deleted (forward-line (1- start-line))\n497 (git-gutter+-insert-deleted-lines content))\n498 (modified (git-gutter+-delete-added-lines start-line end-line)\n499 (git-gutter+-insert-deleted-lines content)))))))\n\n501 (defun git-gutter+-revert-hunks ()\n502 \"Revert hunk at point. If region is active, revert all hunks within the region.\"\n503 (interactive)\n504 (let* ((diffinfos (git-gutter+-selected-diffinfos))\n505 (one-diffinfo-p (= 1 (length diffinfos))))\n506 (save-window-excursion\n507 (if one-diffinfo-p (git-gutter+-show-hunk (car diffinfos)))\n508 (when (and diffinfos\n509 (yes-or-no-p (if one-diffinfo-p\n510 \"Revert hunk?\"\n511 (format \"Revert %d hunks?\" (length diffinfos)))))\n512 ;; Revert diffinfos in reverse so that earlier hunks don't invalidate the\n513 ;; line number information of the later hunks.\n514 (dolist (diffinfo (nreverse diffinfos))\n515 (git-gutter+-do-revert-hunk diffinfo))\n516 (save-buffer))\n517 (if one-diffinfo-p\n518 (git-gutter+-awhen (get-buffer git-gutter+-popup-buffer)\n519 (kill-buffer it))))))\n\n521 (defun git-gutter+-show-hunk (&optional diffinfo)\n522 \"Show hunk at point in another window\"\n523 (interactive)\n524 (git-gutter+-awhen (or diffinfo\n525 (git-gutter+-diffinfo-at-point))\n526 (save-selected-window\n527 (with-current-buffer (get-buffer-create git-gutter+-popup-buffer)\n528 (setq buffer-read-only nil)\n529 (erase-buffer)\n530 (insert (plist-get it :content))\n531 (insert \"\\n\")\n532 (goto-char (point-min))\n533 (diff-mode)\n534 (view-mode)\n535 (pop-to-buffer (current-buffer))))))\n\n537 (defun git-gutter+-next-hunk (arg)\n538 \"Move to next diff hunk\"\n539 (interactive \"p\")\n540 (if (not git-gutter+-diffinfos)\n541 (message \"No changes in buffer\")\n542 (save-restriction\n543 (widen)\n544 (let* ((is-reverse (< arg 0))\n545 (diffinfos git-gutter+-diffinfos)\n546 (len (length diffinfos))\n547 (index (git-gutter+-search-near-diff-index diffinfos is-reverse))\n548 (real-index (if index\n549 (let ((next (if is-reverse (1+ index) (1- index))))\n550 (mod (+ arg next) len))\n551 (if is-reverse (1- (length diffinfos)) 0)))\n552 (diffinfo (nth real-index diffinfos)))\n553 (goto-char (point-min))\n554 (forward-line (1- (plist-get diffinfo :start-line)))\n555 (when (buffer-live-p (get-buffer git-gutter+-popup-buffer))\n556 (save-window-excursion\n557 (git-gutter+-show-hunk)))))))\n\n559 (defun git-gutter+-previous-hunk (arg)\n560 \"Move to previous diff hunk\"\n561 (interactive \"p\")\n562 (git-gutter+-next-hunk (- arg)))\n\n564 (defun git-gutter+-remote-default-directory (dir file)\n565 (let* ((vec (tramp-dissect-file-name file))\n566 (method (aref vec 0))\n567 (user (aref vec 1))\n568 (host (aref vec 2)))\n569 (format \"/%s:%s%s:%s\" method (if user (concat user \"@\") \"\") host dir)))\n\n571 (defun git-gutter+-remote-file-path (dir file)\n572 (let ((file (aref (tramp-dissect-file-name file) 3)))\n573 (replace-regexp-in-string (concat \"\\\\`\" dir) \"\" file)))\n\n575 (defun git-gutter+-local-file-path (file)\n576 (if (eq system-type 'windows-nt)\n577 ;; Cygwin can't handle Windows absolute paths\n578 (file-relative-name file default-directory)\n579 file))\n\n581 (defun git-gutter+-refresh ()\n582 (git-gutter+-clear)\n583 (let ((file (buffer-file-name)))\n584 (when (and file (file-exists-p file))\n585 (if (file-remote-p file)\n586 (let* ((repo-root (git-gutter+-root-directory file))\n587 (default-directory (git-gutter+-remote-default-directory repo-root file)))\n588 (git-gutter+-process-diff (git-gutter+-remote-file-path repo-root file)))\n589 (git-gutter+-process-diff (git-gutter+-local-file-path file))))))\n\n591 (defun git-gutter+-clear ()\n592 (save-restriction\n593 (widen)\n594 (funcall git-gutter+-clear-function))\n595 (setq git-gutter+-diffinfos nil))\n\n\n598 ;;; Staging\n\n600 (defun git-gutter+-stage-hunks ()\n601 \"Stage hunk at point. If region is active, stage all hunk lines within the region.\"\n602 (interactive)\n603 (let* ((line-range (if (use-region-p)\n604 (cons (line-number-at-pos (region-beginning))\n605 (line-number-at-pos (region-end)))))\n606 (diffinfos (git-gutter+-selected-diffinfos line-range)))\n607 (when diffinfos\n608 (let ((error-msg (git-gutter+-stage-diffinfos diffinfos line-range)))\n609 (if error-msg\n610 (message \"Error staging hunks:\\n%s\" error-msg))\n611 (git-gutter+-refresh)))))\n\n613 (defun git-gutter+-selected-diffinfos (&optional line-range)\n614 (unless line-range\n615 (setq line-range (if (use-region-p)\n616 (cons (line-number-at-pos (region-beginning))\n617 (line-number-at-pos (region-end))))))\n618 (if line-range\n619 (git-gutter+-diffinfos-between-lines line-range)\n620 (git-gutter+-awhen (git-gutter+-diffinfo-at-point)\n621 (list it))))\n\n623 (defsubst git-gutter+-diffinfo-between-lines-p (diffinfo start-line end-line)\n624 (let ((diff-start (plist-get diffinfo :start-line))\n625 (diff-end (plist-get diffinfo :end-line)))\n626 (and (<= start-line diff-end)\n627 (<= diff-start end-line))))\n\n629 (defun git-gutter+-diffinfos-between-lines (line-range)\n630 (save-restriction\n631 (widen)\n632 (let ((start-line (car line-range))\n633 (end-line (cdr line-range)))\n634 (delq nil\n635 (mapcar (lambda (diffinfo)\n636 (if (git-gutter+-diffinfo-between-lines-p\n637 diffinfo start-line end-line)\n638 diffinfo))\n639 git-gutter+-diffinfos)))))\n\n641 (defun git-gutter+-stage-diffinfos (diffinfos line-range)\n642 (let ((header git-gutter+-diff-header))\n643 (with-temp-buffer\n644 (insert header)\n645 ;; Insert hunks in reverse so that earlier hunks don't invalidate the line\n646 ;; number information of the later hunks.\n647 (dolist (diffinfo (nreverse diffinfos))\n648 (git-gutter+-insert-diffinfo diffinfo line-range)\n649 (goto-char (point-max)))\n650 (git-gutter+-call-git-on-current-buffer\n651 '(\"apply\" \"--unidiff-zero\" \"--cached\" \"-\")))))\n\n653 (defun git-gutter+-insert-diffinfo (diffinfo line-range)\n654 (let ((content (plist-get diffinfo :content))\n655 (type (plist-get diffinfo :type)))\n656 (if (not line-range)\n657 (git-gutter+-insert-hunk content type)\n658 (let ((diff-start-line (plist-get diffinfo :start-line))\n659 (diff-end-line (plist-get diffinfo :end-line))\n660 (start-line (car line-range))\n661 (end-line (cdr line-range)))\n662 (git-gutter+-insert-hunk content type\n663 (1+ (- start-line diff-start-line))\n664 (1+ (- end-line diff-start-line)))))))\n\n666 (defun git-gutter+-call-git-on-current-buffer (args)\n667 \"Sends the current buffer contents to Git and replaces them with Git's output.\n\n669 RETURNS nil if Git ran successfully. Returns an error description otherwise.\"\n670 (unless (zerop (apply #'call-process-region (point-min) (point-max)\n671 git-gutter+-git-executable t t nil args))\n672 (buffer-string)))\n\n674 (defsubst git-gutter+-read-hunk-header (hunk)\n675 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n676 (string-match git-gutter+-hunk-header-regex hunk)\n677 (list (string-to-number (match-string 1 hunk))\n678 (string-to-number (or (match-string 2 hunk) \"1\"))\n679 (string-to-number (match-string 3 hunk))\n680 (string-to-number (or (match-string 4 hunk) \"1\"))))\n\n682 (defun git-gutter+-insert-hunk (hunk type &optional start end)\n683 \"If START and END are provided, only insert addition (+) lines between\n684 START and END (inclusive). START and END are both line numbers starting with 1.\"\n685 (destructuring-bind\n686 (del-line del-len add-line add-len) (git-gutter+-read-hunk-header hunk)\n687 (let* ((start (max 1 (or start 1)))\n688 (end (min add-len (or end add-len)))\n689 (insert-all-p (or (eq type :deleted)\n690 (and (= start 1) (= end add-len))))\n691 (num-lines-selected (if insert-all-p\n692 add-len\n693 (1+ (- end start)))))\n694 ;; When the user selected the last lines of a hunk with type `modified' (but\n695 ;; not the complete hunk), then don't insert any deletion (-) lines from that\n696 ;; hunk.\n697 (if (and (eq type 'modified)\n698 (> start 1) (= end add-len))\n699 (setq type 'modified-trailing))\n\n701 (save-excursion\n702 (insert hunk \"\\n\"))\n\n704 (git-gutter+-delete-hunk-header)\n\n706 (if (not insert-all-p)\n707 (git-gutter+-modify-hunk type num-lines-selected del-len start))\n\n709 (let ((hunk-header (git-gutter+-make-hunk-header type num-lines-selected\n710 del-line del-len add-line)))\n711 (insert hunk-header \"\\n\")))))\n\n713 (defun git-gutter+-delete-hunk-header ()\n714 (let ((hunk-start (point)))\n715 (forward-line 1)\n716 (delete-region hunk-start (point))))\n\n718 (defun git-gutter+-modify-hunk (type num-lines-selected del-len start)\n719 \"Remove all addition (+) lines from hunk that aren't selected.\n720 If TYPE is not `modified', also remove all deletion (-) lines.\"\n721 (let ((first-line-selected (+ del-len (1- start)))\n722 selected-lines)\n723 (save-excursion\n724 (forward-line first-line-selected)\n725 (let ((selection-start (point)))\n726 (forward-line num-lines-selected)\n727 (setq selected-lines (buffer-substring selection-start (point)))))\n728 (save-excursion\n729 (if (eq type 'modified) (forward-line del-len)) ; skip over deletion (-) lines\n730 (delete-region (point) (point-max))\n731 (insert selected-lines))))\n\n733 (defun git-gutter+-make-hunk-header (type num-lines-selected del-line del-len add-line)\n734 (let ((add-len num-lines-selected))\n735 (case type\n736 (added (setq add-line (1+ del-line)))\n737 (modified-trailing (setq add-line (+ del-line del-len)\n738 del-line (1- add-line)\n739 del-len 0))\n740 (t (setq add-line del-line)))\n741 (format \"@@ -%d,%d +%d,%d @@\"\n742 del-line del-len\n743 add-line add-len)))\n\n\n746 ;;; Committing\n747 ;; This section draws heavily from old Magit source code.\n\n749 (defvar git-gutter+-pre-commit-window-config nil)\n750 (defvar git-gutter+-commit-origin-buffer nil\n751 \"Buffer that started the commit\")\n\n753 (defconst git-gutter+-commit-buffer-name \"*Commit Message*\")\n754 (defconst git-gutter+-staged-changes-buffer-name \"*Staged Changes*\")\n\n756 ;;;###autoload\n757 (defun git-gutter+-commit ()\n758 \"Commit staged changes. If nothing is staged, ask to stage the current buffer.\"\n759 (interactive)\n\n761 (when (and (not (git-gutter+-anything-staged-p))\n762 git-gutter+-diffinfos\n763 (y-or-n-p \"Nothing staged. Stage current buffer? \"))\n764 (git-gutter+-stage-whole-buffer))\n\n766 (let ((file (buffer-file-name))\n767 (dir default-directory))\n768 (git-gutter+-save-window-config-if-needed)\n769 (setq git-gutter+-commit-origin-buffer (current-buffer))\n770 (git-gutter+-open-commit-edit-buffer dir)\n771 (git-gutter+-show-staged-changes file dir)))\n\n773 (defun git-gutter+-stage-and-commit ()\n774 (interactive)\n775 (git-gutter+-stage-hunks)\n776 (git-gutter+-commit))\n\n778 (defun git-gutter+-save-window-config-if-needed ()\n779 ;; Only save the window config if the temporary buffers that get popped-up by\n780 ;; git-gutter+ are not already visible.\n781 ;; In this way, `git-gutter+-commit' can be called twice in a row without\n782 ;; losing the original window config.\n783 (when (not (and git-gutter+-pre-commit-window-config\n784 (get-buffer-window git-gutter+-commit-buffer-name)\n785 (get-buffer-window git-gutter+-staged-changes-buffer-name)))\n786 (setq git-gutter+-pre-commit-window-config (current-window-configuration))))\n\n788 (defun git-gutter+-open-commit-edit-buffer (dir)\n789 \"Opens a buffer for composing the commit message\"\n790 (pop-to-buffer (get-buffer-create git-gutter+-commit-buffer-name))\n791 (setq default-directory dir)\n792 (git-gutter+-commit-mode)\n793 (message \"Type C-c C-c to commit (C-c C-k to cancel).\"))\n\n795 (defsubst git-gutter+-pop-to-staged-changes-buffer ()\n796 (let* ((buf (get-buffer-create git-gutter+-staged-changes-buffer-name))\n797 (window (get-buffer-window buf)))\n798 (if window\n799 ;; Buffer is already visible\n800 (select-window window)\n801 (if (<= (length (window-list)) 2)\n802 (split-window))\n803 (pop-to-buffer buf))))\n\n805 (defun git-gutter+-show-staged-changes (file dir)\n806 (save-selected-window\n807 (git-gutter+-pop-to-staged-changes-buffer)\n808 (setq buffer-read-only nil)\n809 (erase-buffer)\n810 (let ((default-directory dir))\n811 (git-gutter+-call-git '(\"diff\" \"--staged\") file))\n812 (goto-char (point-min))\n813 (diff-mode)\n814 (view-mode)))\n\n816 (defsubst git-gutter+-abort-commit-when-no-changes (allow-empty amend)\n817 (unless (or amend\n818 allow-empty\n819 (git-gutter+-anything-staged-p))\n820 (error\n821 \"Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?\"\n822 (key-description (car (where-is-internal\n823 'git-gutter+-commit-toggle-amending)))\n824 (key-description (car (where-is-internal\n825 'git-gutter+-commit-toggle-allow-empty))))))\n\n827 (defsubst git-gutter+-buffer-is-whitespace ()\n828 (save-excursion\n829 (goto-char (point-min))\n830 (looking-at-p \"[ \\t\\n]*\\\\'\")))\n\n832 (defun git-gutter+-publish-commit ()\n833 \"Publish commit\"\n834 (interactive)\n835 (let* ((fields (git-gutter+-commit-get-fields))\n836 (amend (equal \"yes\" (git-gutter+-commit-get-field 'amend fields)))\n837 (allow-empty (equal \"yes\" (git-gutter+-commit-get-field 'allow-empty fields)))\n838 (author (git-gutter+-commit-get-field 'author fields))\n839 (date (git-gutter+-commit-get-field 'date fields)))\n\n841 (git-gutter+-abort-commit-when-no-changes allow-empty amend)\n\n843 (git-gutter+-push-to-comment-ring (buffer-string))\n\n845 (git-gutter+-commit-set-fields nil) ; Delete message header\n\n847 (when (git-gutter+-buffer-is-whitespace)\n848 (erase-buffer)\n849 (insert \"(Empty description)\"))\n\n851 (let ((error-msg (git-gutter+-call-git-on-current-buffer\n852 (append '(\"--no-pager\" \"commit\" \"-F\" \"-\")\n853 (if amend '(\"--amend\"))\n854 (if allow-empty '(\"--allow-empty\"))\n855 (if author (list (concat \"--author=\" author)))\n856 (if date (list (concat \"--date=\" date)))))))\n857 (if error-msg\n858 (progn\n859 (message \"Commit error:\\n%s\" error-msg)\n860 (erase-buffer)\n861 (insert (ring-ref log-edit-comment-ring 0))) ; Reinsert commit message\n862 (message \"Commit successful.\")\n863 (git-gutter+-close-commit-edit-buffer)\n864 (git-gutter+-update-vc-modeline)))))\n\n866 (defun git-gutter+-close-commit-edit-buffer ()\n867 \"Abort edits and discard commit message being composed.\"\n868 (interactive)\n869 (kill-buffer)\n870 (set-window-configuration git-gutter+-pre-commit-window-config))\n\n872 (defun git-gutter+-update-vc-modeline ()\n873 (when (buffer-live-p git-gutter+-commit-origin-buffer)\n874 (with-current-buffer git-gutter+-commit-origin-buffer\n875 ;; Updating the modeline has no effect if the buffer still has\n876 ;; changes - it will remain in the 'modified' state. So skip it then.\n877 (unless git-gutter+-diffinfos\n878 (ignore-errors (vc-find-file-hook))))))\n\n880 (defun git-gutter+-stage-whole-buffer ()\n881 (save-excursion\n882 (mark-whole-buffer)\n883 (git-gutter+-stage-hunks)))\n\n885 (defun git-gutter+-anything-staged-p ()\n886 \"Return t if the current repo has staged changes\"\n887 (not (zerop (git-gutter+-call-git '(\"diff\" \"--quiet\" \"--cached\")))))\n\n889 (defun git-gutter+-commit-toggle-amending ()\n890 \"Toggle whether this will be an amendment to the previous commit.\n891 \\(i.e., whether commit is run via 'git commit --amend')\"\n892 (interactive)\n893 ;; Remove the newline that 'git-commit-mode' adds to a new commit\n894 ;; message buffer by default. This prevents an ugly visual\n895 ;; gap between the commit message header and the previous commit\n896 ;; message.\n897 (when (git-gutter+-buffer-is-whitespace)\n898 (erase-buffer))\n\n900 (let ((amend-was-already-set (git-gutter+-commit-get-field 'amend)))\n901 (git-gutter+-commit-toggle-field 'amend t)\n902 (unless amend-was-already-set\n903 ;; Insert previous commit message\n904 (goto-char (point-max))\n905 (unless (zerop (current-column))\n906 (insert \"\\n\"))\n907 (insert (git-gutter+-get-last-commit-msg)\n908 \"\\n\"))))\n\n910 (defun git-gutter+-commit-toggle-allow-empty ()\n911 \"Toggle whether this commit is allowed to be empty.\n912 \\(i.e., whether commit is run via 'git commit --allow-empty')\"\n913 (interactive)\n914 (git-gutter+-commit-toggle-field 'allow-empty t))\n\n916 (defun git-gutter+-format-author (author email)\n917 (format \"%s <%s>\" author email))\n\n919 (defun git-gutter+-commit-toggle-author ()\n920 \"Toggle whether this commit should have a user-defined author.\"\n921 (interactive)\n922 (git-gutter+-commit-toggle-input\n923 'author (git-gutter+-format-author\n924 (or (git-gutter+-get-cfg \"user\" \"name\") \"Author Name\")\n925 (or (git-gutter+-get-cfg \"user\" \"email\") \"author@email\"))))\n\n927 (defun git-gutter+-commit-toggle-date ()\n928 \"Toggle whether this commit should have a user-defined date.\"\n929 (interactive)\n930 (git-gutter+-commit-toggle-input 'date\n931 ;; ISO 8601\n932 (format-time-string \"%Y-%m-%dT%T%z\" (current-time))))\n\n934 (defun git-gutter+-push-to-comment-ring (comment)\n935 (when (or (ring-empty-p log-edit-comment-ring)\n936 (not (equal comment (ring-ref log-edit-comment-ring 0))))\n937 (ring-insert log-edit-comment-ring comment)))\n\n939 (defun git-gutter+-get-last-commit-msg ()\n940 (git-gutter+-git-output '(\"log\" \"--max-count=1\" \"--pretty=format:%s%n%n%b\" \"HEAD\")))\n\n942 (defun git-gutter+-get-cfg (&rest keys)\n943 (git-gutter+-git-output (list \"config\" (mapconcat 'identity keys \".\"))))\n\n945 (defun git-gutter+-git-output (args)\n946 (with-temp-buffer\n947 (git-gutter+-call-git args)\n948 ;; Delete trailing newlines\n949 (goto-char (point-min))\n950 (if (re-search-forward \"\\n+\\\\'\" nil t)\n951 (replace-match \"\"))\n952 (buffer-string)))\n\n\n955 ;;; Commit message header\n\n957 (defconst git-gutter+-commit-header-end \"-- End of commit options header --\\n\")\n\n959 (defun git-gutter+-commit-get-field (name &optional fields)\n960 (cdr (assq name (or fields (git-gutter+-commit-get-fields)))))\n\n962 (defun git-gutter+-commit-set-field (name value)\n963 (let* ((fields (git-gutter+-commit-get-fields))\n964 (cell (assq name fields)))\n965 (cond (cell\n966 (if value\n967 (rplacd cell value)\n968 (setq fields (delq cell fields))))\n969 (t\n970 (if value\n971 (setq fields (append fields (list (cons name value)))))))\n972 (git-gutter+-commit-set-fields fields)))\n\n974 (defun git-gutter+-commit-toggle-field (name default)\n975 \"Toggle the commit header field named NAME.\n976 If it's currently unset, set it to DEFAULT (t or nil).\"\n977 (let* ((fields (git-gutter+-commit-get-fields))\n978 (cell (assq name fields)))\n979 (if cell\n980 (rplacd cell (if (equal (cdr cell) \"yes\") \"no\" \"yes\"))\n981 (setq fields (acons name (if default \"yes\" \"no\") fields)))\n982 (git-gutter+-commit-set-fields fields)))\n\n984 (defun git-gutter+-commit-toggle-input (name default)\n985 \"Toggle the commit header input named NAME.\n986 If it's currently unset, set it to DEFAULT (a string). If it is\n987 set remove it.\"\n988 (let* ((fields (git-gutter+-commit-get-fields))\n989 (cell (assq name fields)))\n990 (if cell\n991 (setq fields (assq-delete-all name fields))\n992 (setq fields (acons name default fields)))\n993 (git-gutter+-commit-set-fields fields)))\n\n995 (defun git-gutter+-commit-get-fields ()\n996 (let (result)\n997 (goto-char (point-min))\n998 (while (looking-at \"^\\\\([A-Za-z0-9-_]+\\\\): *\\\\(.+\\\\)?$\")\n999 (let ((name (intern (downcase (match-string 1))))\n1000 (value (read (or (match-string 2) \"nil\"))))\n1001 (push (cons name value) result))\n1002 (forward-line))\n1003 (if (looking-at (regexp-quote git-gutter+-commit-header-end))\n1004 (nreverse result))))\n\n1006 (defun git-gutter+-commit-set-fields (fields)\n1007 (goto-char (point-min))\n1008 ;; Delete commit header\n1009 (if (search-forward-regexp (format \"^\\\\(?:[A-Za-z0-9-_]+:.*\\n\\\\)*%s\"\n1010 (regexp-quote git-gutter+-commit-header-end))\n1011 nil t)\n1012 (delete-region (match-beginning 0) (match-end 0)))\n1013 (goto-char (point-min))\n1014 (when fields\n1015 (dolist (field fields)\n1016 (insert (capitalize (symbol-name (car field))) \": \"\n1017 (prin1-to-string (cdr field)) \"\\n\"))\n1018 (insert git-gutter+-commit-header-end)))\n\n\n1021 ;;; git-gutter+-commit-mode\n1022 ;; Like git-commit-mode, but adds keybindings to git-gutter+ commands and\n1023 ;; highlighting support for the commit message header.\n\n1025 (define-derived-mode git-gutter+-commit-mode git-commit-mode \"Git-Gutter-Commit\"\n1026 (setq font-lock-defaults (list (git-gutter+-commit-font-lock-keywords) t)))\n\n1028 (setq git-gutter+-commit-mode-map\n1029 (let ((map (copy-keymap git-commit-mode-map)))\n1030 (define-key map (kbd \"C-c C-c\") 'git-gutter+-publish-commit)\n1031 (define-key map (kbd \"C-c C-k\") 'git-gutter+-close-commit-edit-buffer)\n1032 (define-key map (kbd \"C-c C-a\") 'git-gutter+-commit-toggle-amending)\n1033 (define-key map (kbd \"C-c C-e\") 'git-gutter+-commit-toggle-allow-empty)\n1034 (define-key map (kbd \"C-c C-u\") 'git-gutter+-commit-toggle-author)\n1035 (define-key map (kbd \"C-c C-d\") 'git-gutter+-commit-toggle-date)\n1036 (define-key map (kbd \"C-c C-b\") 'git-commit-ack)\n1037 (define-key map (kbd \"M-p\") 'log-edit-previous-comment)\n1038 (define-key map (kbd \"M-n\") 'log-edit-next-comment)\n1039 map))\n\n1041 (defface git-gutter+-commit-header-face\n1042 '((t :inherit font-lock-comment-face))\n1043 \"Highlights the commit message header\"\n1044 :group 'git-gutter+-faces)\n\n1046 (defconst git-gutter+-commit-header-regex\n1047 (concat \"\\\\(?:.\\\\|\\n\\\\)*?\" (regexp-quote git-gutter+-commit-header-end)))\n\n1049 (defconst git-gutter+-skip-commit-header-regex\n1050 (concat \"\\\\`\\\\(?:\" git-gutter+-commit-header-regex \"\\\\)?\"))\n\n1052 ;; Modify git-commit-summary-regexp to ignore the commit header\n1053 (defadvice git-commit-summary-regexp\n1054 (after ignore-git-gutter+-commit-header activate compile)\n1055 (if (eq major-mode 'git-gutter+-commit-mode)\n1056 (setq ad-return-value\n1057 (concat git-gutter+-skip-commit-header-regex\n1058 (substring ; Remove leading \"\\\\`\"\n1059 ad-return-value 2)))))\n\n1061 (defun git-gutter+-commit-font-lock-keywords ()\n1062 \"Like `git-commit-mode-font-lock-keywords' but with commit header highlighting\"\n1063 `((,(concat \"\\\\`\" git-gutter+-commit-header-regex) . 'git-gutter+-commit-header-face)\n1064 ,@(git-commit-mode-font-lock-keywords)))\n\n\n1067 ;;; Magit synchronization\n1068 ;; Force Magit to refresh git-gutter+ when updating the VC mode line.\n\n1070 (defvar git-gutter+-orig-vc-find-file-hook)\n\n1072 (defvar git-gutter+-vc-find-file-hook-with-refresh\n1073 (lambda ()\n1074 (funcall git-gutter+-orig-vc-find-file-hook)\n1075 (if git-gutter+-mode (git-gutter+-refresh))))\n\n1077 (defadvice magit-update-vc-modeline (around refresh-git-gutter+ compile activate)\n1078 ;; `magit-update-vc-modeline' calls `vc-find-file-hook' (a function!) on each\n1079 ;; buffer in the repo. Temporarily rebind it to `vc-find-file-hook-with-refresh',\n1080 ;; which calls git-gutter+-refresh after updating the VC mode line.\n1081 ;;\n1082 ;; Using `flet' would have been much simpler, but it's deprecated since 24.3.\n1083 (setq git-gutter+-orig-vc-find-file-hook (symbol-function 'vc-find-file-hook))\n1084 (fset 'vc-find-file-hook git-gutter+-vc-find-file-hook-with-refresh)\n1085 (unwind-protect\n1086 ad-do-it\n1087 (fset 'vc-find-file-hook git-gutter+-orig-vc-find-file-hook)))\n\n1089 (provide 'git-gutter+)\n\n1091 ;;; git-gutter+.el ends here\n")) (setq helm-swoop-cache t))) (candidates-in-buffer) (get-line . buffer-substring-no-properties) (keymap keymap (C-M-left . backward-sexp) (C-M-right . forward-sexp) (27 keymap (105 . helm-multi-swoop-all-from-helm-swoop)) (3 keymap (5 . helm-swoop-edit)) keymap (C-M-left . paren-backward-sexp) (C-M-right . paren-forward-sexp) (94 . helm-swoop-caret-match) (menu-bar keymap (help-menu keymap (describe keymap (describe-mode . helm-help)))) (help keymap (109 . helm-help)) (f1 keymap (109 . helm-help)) (8 keymap (109 . helm-help) (104 . undefined) (8 . undefined) (4 . helm-debug-output)) (20 . helm-toggle-resplit-and-swap-windows) (C-tab . undefined) (triple-mouse-3 . ignore) (double-mouse-3 . ignore) (mouse-3 . ignore) (drag-mouse-3 . ignore) (down-mouse-3 . ignore) (triple-mouse-2 . ignore) (double-mouse-2 . ignore) (mouse-2 . ignore) (drag-mouse-2 . ignore) (down-mouse-2 . ignore) (triple-mouse-1 . ignore) (double-mouse-1 . ignore) (mouse-1 . ignore) (drag-mouse-1 . ignore) (down-mouse-1 . ignore) (67108897 . helm-toggle-suspend-update) (3 keymap (21 . helm-force-update) (6 . helm-follow-mode) (11 . helm-kill-selection-and-quit) (25 . helm-yank-selection) (4 . helm-delete-current-selection) (45 . helm-swap-windows)) (67108987 . helm-enlarge-window) (67108989 . helm-narrow-window) (19 . undefined) (18 . undefined) (23 . helm-yank-text-at-point) (24 keymap (2 . helm-resume-list-buffers-after-quit) (98 . helm-resume-previous-session-after-quit) (6 . helm-quit-and-find-file)) (11 . helm-delete-minibuffer-contents) (67108896 . helm-toggle-visible-mark) (0 . helm-toggle-visible-mark) (C-M-up . helm-scroll-other-window-down) (C-M-down . helm-scroll-other-window) (M-prior . helm-scroll-other-window-down) (M-next . helm-scroll-other-window) (12 . helm-recenter-top-bottom-other-window) (15 . helm-next-source) (10 . helm-select-3rd-action) (5 . helm-select-2nd-action-or-end-of-line) ...) (header-line . "[C-c C-e] Edit mode, [M-i] apply all buffers") (action lambda ($line) (helm-swoop--goto-line (when (string-match "^[0-9]+" $line) (string-to-number (match-string 0 $line)))) (when (re-search-forward (mapconcat (quote identity) (split-string helm-pattern " ") "\\|") nil t) (goto-char (match-beginning 0))) (helm-swoop--recenter)) (migemo)) #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) "Swoop: " nil "^368 " "*Helm Swoop*" nil nil nil) | |
apply(helm (((name . "git-gutter+.el") (init lambda nil (unless helm-swoop-cache (with-current-buffer (helm-candidate-buffer (quote local)) (insert "1 ;;; git-gutter+.el --- Manage Git hunks straight from the buffer\n\n3 ;; Copyright (C) 2013 by Syohei YOSHIDA and contributors\n\n5 ;; Author: Syohei YOSHIDA <syohex@gmail.com> and contributors\n6 ;; URL: https://github.com/nonsequitur/git-gutter-plus\n7 ;; Version: 0.1\n\n9 ;; This program is free software; you can redistribute it and/or modify\n10 ;; it under the terms of the GNU General Public License as published by\n11 ;; the Free Software Foundation, either version 3 of the License, or\n12 ;; (at your option) any later version.\n\n14 ;; This program is distributed in the hope that it will be useful,\n15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n17 ;; GNU General Public License for more details.\n\n19 ;; You should have received a copy of the GNU General Public License\n20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n22 ;; Package-Requires: ((git-commit-mode \"0.14\"))\n\n24 ;;; Commentary:\n25 ;;\n26 ;; View, stage and revert Git changes straight from the buffer.\n\n28 ;;; Code:\n\n30 (eval-when-compile\n31 (require 'cl))\n\n33 (require 'tramp)\n34 (require 'log-edit)\n35 (require 'git-commit-mode)\n\n37 (defgroup git-gutter+ nil\n38 \"Manage Git hunks straight from the buffer\"\n39 :prefix \"git-gutter+-\"\n40 :group 'vc)\n\n42 (defcustom git-gutter+-window-width nil\n43 \"Character width of the gutter margin. Set this variable if the automatically\n44 calculated width looks wrong. (This can happen with some special characters.)\"\n45 :type 'integer\n46 :group 'git-gutter+)\n\n48 (defcustom git-gutter+-git-executable \"git\"\n49 \"The path of the Git executable.\"\n50 :type 'string\n51 :group 'git-gutter+)\n\n53 (defcustom git-gutter+-diff-options nil\n54 \"List of strings containing extra arguments to 'git diff'\"\n55 :type 'list\n56 :group 'git-gutter+)\n\n58 (defcustom git-gutter+-separator-sign nil\n59 \"Separator sign\"\n60 :type 'string\n61 :group 'git-gutter+)\n\n63 (defcustom git-gutter+-modified-sign \"=\"\n64 \"Modified sign\"\n65 :type 'string\n66 :group 'git-gutter+)\n\n68 (defcustom git-gutter+-added-sign \"+\"\n69 \"Added sign\"\n70 :type 'string\n71 :group 'git-gutter+)\n\n73 (defcustom git-gutter+-deleted-sign \"-\"\n74 \"Deleted sign\"\n75 :type 'string\n76 :group 'git-gutter+)\n\n78 (defcustom git-gutter+-unchanged-sign nil\n79 \"Unchanged sign\"\n80 :type 'string\n81 :group 'git-gutter+)\n\n83 (defcustom git-gutter+-hide-gutter nil\n84 \"Hide gutter if there are no changes\"\n85 :type 'boolean\n86 :group 'git-gutter+)\n\n88 (defcustom git-gutter+-lighter \" GitGutter\"\n89 \"Minor mode lighter in mode-line\"\n90 :type 'string\n91 :group 'git-gutter+)\n\n93 (defface git-gutter+-separator\n94 '((t (:foreground \"cyan\" :weight bold)))\n95 \"Face of the separator\"\n96 :group 'git-gutter+)\n\n98 (defface git-gutter+-modified\n99 '((t (:foreground \"magenta\" :weight bold)))\n100 \"Face for modified lines\"\n101 :group 'git-gutter+)\n\n103 (defface git-gutter+-added\n104 '((t (:foreground \"green\" :weight bold)))\n105 \"Face for added lines\"\n106 :group 'git-gutter+)\n\n108 (defface git-gutter+-deleted\n109 '((t (:foreground \"red\" :weight bold)))\n110 \"Face for deleted lines\"\n111 :group 'git-gutter+)\n\n113 (defface git-gutter+-unchanged\n114 '((t (:background \"yellow\")))\n115 \"Face for unchanged lines\"\n116 :group 'git-gutter+)\n\n118 (defcustom git-gutter+-disabled-modes nil\n119 \"A list of modes for which `global-git-gutter+-mode' should be disabled.\"\n120 :type '(repeat symbol)\n121 :group 'git-gutter+)\n\n123 (defvar git-gutter+-mode-map\n124 (make-sparse-keymap))\n\n126 (defvar git-gutter+-view-diff-function nil\n127 \"Function to call for displaying diffs\")\n\n129 (defvar git-gutter+-clear-function nil\n130 \"Function to call for clearing the diff display\")\n\n132 (defvar git-gutter+-window-config-change-function nil\n133 \"Function to call when the buffer's local window configuration has changed\")\n\n135 (defvar git-gutter+-diffinfos nil)\n136 (defvar git-gutter+-diff-header nil)\n137 (make-variable-buffer-local 'git-gutter+-diffinfos)\n138 (make-variable-buffer-local 'git-gutter+-diff-header)\n\n140 (defvar git-gutter+-popup-buffer \"*git-gutter+-diff*\")\n141 (defvar git-gutter+-buffers-to-reenable nil)\n\n143 (defconst git-gutter+-hunk-header-regex\n144 ;; The same as diff-hunk-header-re-unified\n145 \"^@@ -\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? \\\\+\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? @@\")\n\n147 (defalias 'git-gutter+-popup-hunk 'git-gutter+-show-hunk)\n148 (defalias 'git-gutter+-revert-hunk 'git-gutter+-revert-hunks)\n\n150 (defmacro git-gutter+-awhen (test &rest body)\n151 \"Anaphoric when.\"\n152 (declare (indent 1))\n153 `(let ((it ,test))\n154 (when it ,@body)))\n\n156 (defun git-gutter+-enable-default-display-mode ()\n157 (setq git-gutter+-view-diff-function 'git-gutter+-view-diff-infos\n158 git-gutter+-clear-function 'git-gutter+-clear-diff-infos\n159 git-gutter+-window-config-change-function 'git-gutter+-show-gutter))\n\n161 (unless git-gutter+-view-diff-function\n162 (git-gutter+-enable-default-display-mode))\n\n164 (defun git-gutter+-call-git (args &optional file)\n165 (if (and file (file-remote-p file))\n166 (apply #'process-file git-gutter+-git-executable nil t nil args)\n167 (apply #'call-process git-gutter+-git-executable nil t nil args)))\n\n169 (defun git-gutter+-in-git-repository-p (file)\n170 (with-temp-buffer\n171 (let ((args '(\"rev-parse\" \"--is-inside-work-tree\")))\n172 (when (zerop (git-gutter+-call-git args file))\n173 (goto-char (point-min))\n174 (string= \"true\" (buffer-substring-no-properties\n175 (point) (line-end-position)))))))\n\n177 (defun git-gutter+-root-directory (file)\n178 (with-temp-buffer\n179 (let* ((args '(\"rev-parse\" \"--show-toplevel\"))\n180 (ret (git-gutter+-call-git args file)))\n181 (when (zerop ret)\n182 (goto-char (point-min))\n183 (let ((root (buffer-substring-no-properties (point) (line-end-position))))\n184 (unless (string= root \"\")\n185 (file-name-as-directory root)))))))\n\n187 (defsubst git-gutter+-diff-args (file)\n188 (delq nil (list \"--no-pager\" \"diff\" \"--no-color\" \"--no-ext-diff\" \"-U0\"\n189 git-gutter+-diff-options file)))\n\n191 (defun git-gutter+-diff (curfile)\n192 (let ((args (git-gutter+-diff-args curfile))\n193 (file (buffer-file-name))) ;; for tramp\n194 (with-temp-buffer\n195 (when (zerop (git-gutter+-call-git args file))\n196 (goto-char (point-min))\n197 (let ((diff-header (git-gutter+-get-diff-header))\n198 (diffinfos (git-gutter+-get-diffinfos)))\n199 (list diff-header diffinfos))))))\n\n201 (defun git-gutter+-get-diff-header ()\n202 (save-excursion\n203 (if (re-search-forward git-gutter+-hunk-header-regex nil t)\n204 (buffer-substring (point-min) (match-beginning 0)))))\n\n206 (defsubst git-gutter+-make-diffinfo (type content start end)\n207 (list :type type :content content :start-line start :end-line end))\n\n209 (defun git-gutter+-get-diffinfos ()\n210 (loop while (re-search-forward git-gutter+-hunk-header-regex nil t)\n211 ;; Hunk header format:\n212 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n213 for del-len = (string-to-number (or (match-string 2) \"1\"))\n214 for add-line = (string-to-number (match-string 3))\n215 for add-len = (string-to-number (or (match-string 4) \"1\"))\n216 for type = (cond ((zerop del-len) 'added)\n217 ((zerop add-len) 'deleted)\n218 (t 'modified))\n219 for start-line = (if (eq type 'deleted)\n220 (1+ add-line)\n221 add-line)\n222 for end-line = (if (eq type 'deleted)\n223 start-line\n224 (1- (+ add-line add-len)))\n225 for content = (git-gutter+-diff-content)\n226 collect\n227 (git-gutter+-make-diffinfo type content start-line end-line)))\n\n229 (defun git-gutter+-diff-content ()\n230 (save-excursion\n231 (goto-char (line-beginning-position)) ; Move to beginning of hunk header\n232 (let ((hunk-start (point)))\n233 ;; Move to end of hunk\n234 (forward-line 1)\n235 (if (re-search-forward \"^@@\" nil t)\n236 (backward-char 3) ;; exclude \"\\n@@\"\n237 (goto-char (1- (point-max)))) ; Skip trailing newline\n238 (buffer-substring hunk-start (point)))))\n\n240 (defun git-gutter+-line-to-pos (line)\n241 (save-excursion\n242 (goto-char (point-min))\n243 (forward-line (1- line))\n244 (point)))\n\n246 (defun git-gutter+-before-string (sign)\n247 (let* ((sep-sign git-gutter+-separator-sign)\n248 (sep (when sep-sign\n249 (propertize sep-sign 'face 'git-gutter+-separator)))\n250 (gutter-sep (concat sign sep)))\n251 (propertize \" \" 'display `((margin left-margin) ,gutter-sep))))\n\n253 (defsubst git-gutter+-select-face (type)\n254 (case type\n255 (added 'git-gutter+-added)\n256 (modified 'git-gutter+-modified)\n257 (deleted 'git-gutter+-deleted)))\n\n259 (defsubst git-gutter+-select-sign (type)\n260 (case type\n261 (added git-gutter+-added-sign)\n262 (modified git-gutter+-modified-sign)\n263 (deleted git-gutter+-deleted-sign)))\n\n265 (defun git-gutter+-propertized-sign (type)\n266 (let ((sign (git-gutter+-select-sign type))\n267 (face (git-gutter+-select-face type)))\n268 (propertize sign 'face face)))\n\n270 (defun git-gutter+-view-region (sign start-line end-line)\n271 (let ((beg (git-gutter+-line-to-pos start-line)))\n272 (goto-char beg)\n273 (while (and (<= (line-number-at-pos) end-line) (not (eobp)))\n274 (git-gutter+-view-at-pos sign (point))\n275 (forward-line 1))))\n\n277 (defun git-gutter+-view-at-pos (sign pos)\n278 (let ((ov (make-overlay pos pos)))\n279 (overlay-put ov 'before-string (git-gutter+-before-string sign))\n280 (overlay-put ov 'git-gutter+ t)))\n\n282 (defun git-gutter+-view-diff-info (diffinfo)\n283 (let* ((start-line (plist-get diffinfo :start-line))\n284 (end-line (plist-get diffinfo :end-line))\n285 (type (plist-get diffinfo :type))\n286 (sign (git-gutter+-propertized-sign type)))\n287 (case type\n288 ((modified added) (git-gutter+-view-region sign start-line end-line))\n289 (deleted (git-gutter+-view-at-pos\n290 sign (git-gutter+-line-to-pos start-line))))))\n\n292 (defun git-gutter+-sign-width (sign)\n293 (loop for s across sign\n294 sum (char-width s)))\n\n296 (defun git-gutter+-longest-sign-width ()\n297 (let ((signs (list git-gutter+-modified-sign\n298 git-gutter+-added-sign\n299 git-gutter+-deleted-sign)))\n300 (when git-gutter+-unchanged-sign\n301 (add-to-list 'signs git-gutter+-unchanged-sign))\n302 (+ (apply 'max (mapcar 'git-gutter+-sign-width signs))\n303 (git-gutter+-sign-width git-gutter+-separator-sign))))\n\n305 (defun git-gutter+-view-for-unchanged ()\n306 (save-excursion\n307 (let ((sign (if git-gutter+-unchanged-sign\n308 (propertize git-gutter+-unchanged-sign\n309 'face 'git-gutter+-unchanged)\n310 \" \")))\n311 (goto-char (point-min))\n312 (while (not (eobp))\n313 (git-gutter+-view-at-pos sign (point))\n314 (forward-line 1)))))\n\n316 (defun git-gutter+-set-window-margin (width)\n317 (let ((curwin (get-buffer-window)))\n318 (set-window-margins curwin width (cdr (window-margins curwin)))))\n\n320 (defsubst git-gutter+-file-buffer-p ()\n321 (and (buffer-file-name)\n322 default-directory\n323 (file-directory-p default-directory)))\n\n325 ;;;###autoload\n326 (define-minor-mode git-gutter+-mode\n327 \"Git-Gutter mode\"\n328 :group 'git-gutter+\n329 :init-value nil\n330 :global nil\n331 :lighter git-gutter+-lighter\n332 (if git-gutter+-mode\n333 (if (and (git-gutter+-file-buffer-p)\n334 (git-gutter+-in-git-repository-p (buffer-file-name)))\n335 (progn\n336 (git-gutter+-add-local-hooks)\n337 (git-gutter+-refresh))\n338 (if (called-interactively-p 'any)\n339 (message \"No Git repo for current buffer\"))\n340 (git-gutter+-mode -1))\n341 (git-gutter+-remove-local-hooks)\n342 (git-gutter+-clear)))\n\n344 (defun git-gutter+-add-local-hooks ()\n345 (add-hook 'after-save-hook 'git-gutter+-refresh nil t)\n346 ;; Turn off `git-gutter+-mode' while reverting to prevent any redundant calls to\n347 ;; `git-gutter+-refresh'.\n348 (add-hook 'before-revert-hook 'git-gutter+-turn-off nil t)\n349 (add-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change nil t)\n350 (if git-gutter+-window-config-change-function\n351 (add-hook 'window-configuration-change-hook\n352 git-gutter+-window-config-change-function nil t)))\n\n354 (defun git-gutter+-remove-local-hooks ()\n355 (remove-hook 'after-save-hook 'git-gutter+-refresh t)\n356 (remove-hook 'before-revert-hook 'git-gutter+-turn-off t)\n357 (remove-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change t)\n358 (if git-gutter+-window-config-change-function\n359 (remove-hook 'window-configuration-change-hook\n360 git-gutter+-window-config-change-function t)))\n\n362 (defmacro git-gutter+-in-all-buffers (&rest body)\n363 `(dolist (buf (buffer-list))\n364 (with-current-buffer buf\n365 ,@body)))\n\n367 ;; When `define-globalized-minor-mode' is used to define `global-git-gutter+-mode',\n368 ;; `git-gutter+-mode' and thus `git-gutter+-refresh' get run twice when a new file\n369 ;; is opened. (First for `fundamental-mode', then for the file-specific mode.)\n370 ;; The following definition of `global-git-gutter+-mode' avoids any redundant calls to\n371 ;; `git-gutter+-refresh'.\n\n373 ;;;###autoload\n374 (define-minor-mode global-git-gutter+-mode ()\n375 \"Global Git-Gutter mode\"\n376 :group 'git-gutter+\n377 :init-value nil\n378 :global t\n379 (if global-git-gutter+-mode\n380 (progn\n381 (add-hook 'find-file-hook 'git-gutter+-turn-on)\n382 (add-hook 'after-revert-hook 'git-gutter+-turn-on)\n383 (add-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n384 (git-gutter+-in-all-buffers (git-gutter+-turn-on)))\n385 (remove-hook 'find-file-hook 'git-gutter+-turn-on)\n386 (remove-hook 'after-revert-hook 'git-gutter+-turn-on)\n387 (remove-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n388 (git-gutter+-in-all-buffers (git-gutter+-turn-off))))\n\n390 (defun git-gutter+-turn-on ()\n391 (when (and (buffer-file-name)\n392 (not (memq major-mode git-gutter+-disabled-modes))\n393 (not git-gutter+-mode))\n394 (git-gutter+-mode t)))\n\n396 (defun git-gutter+-turn-off ()\n397 (if git-gutter+-mode (git-gutter+-mode -1)))\n\n399 (defun git-gutter+-reenable-after-major-mode-change ()\n400 (if global-git-gutter+-mode\n401 (add-to-list 'git-gutter+-buffers-to-reenable (current-buffer))))\n\n403 (defun git-gutter+-reenable-buffers ()\n404 (dolist (buf git-gutter+-buffers-to-reenable)\n405 (with-current-buffer buf\n406 (git-gutter+-turn-on)))\n407 (setq git-gutter+-buffers-to-reenable nil))\n\n409 (defsubst git-gutter+-show-gutter-p (diffinfos)\n410 (if git-gutter+-hide-gutter\n411 (or diffinfos git-gutter+-unchanged-sign)\n412 (or global-git-gutter+-mode git-gutter+-unchanged-sign diffinfos)))\n\n414 (defun git-gutter+-show-gutter (&optional diffinfos)\n415 (when (git-gutter+-show-gutter-p (or diffinfos git-gutter+-diffinfos))\n416 (let ((win-width (or git-gutter+-window-width\n417 (git-gutter+-longest-sign-width))))\n418 (git-gutter+-set-window-margin win-width))))\n\n420 (defun git-gutter+-view-diff-infos (diffinfos)\n421 (when (or git-gutter+-unchanged-sign\n422 git-gutter+-separator-sign)\n423 (git-gutter+-view-for-unchanged))\n424 (when diffinfos\n425 (save-excursion\n426 (mapc 'git-gutter+-view-diff-info diffinfos)))\n427 (git-gutter+-show-gutter diffinfos))\n\n429 (defsubst git-gutter+-reset-window-margin-p ()\n430 (or git-gutter+-hide-gutter\n431 (not global-git-gutter+-mode)))\n\n433 (defun git-gutter+-clear-diff-infos ()\n434 (when (git-gutter+-reset-window-margin-p)\n435 (git-gutter+-set-window-margin 0))\n436 (remove-overlays (point-min) (point-max) 'git-gutter+ t))\n\n438 (defun git-gutter+-process-diff (curfile)\n439 (destructuring-bind\n440 (diff-header diffinfos) (git-gutter+-diff curfile)\n441 (setq git-gutter+-diff-header diff-header\n442 git-gutter+-diffinfos diffinfos)\n443 (save-restriction\n444 (widen)\n445 (funcall git-gutter+-view-diff-function diffinfos))))\n\n447 (defun git-gutter+-search-near-diff-index (diffinfos is-reverse)\n448 (loop with current-line = (line-number-at-pos)\n449 with cmp-fn = (if is-reverse '> '<)\n450 for diffinfo in (if is-reverse (reverse diffinfos) diffinfos)\n451 for index = 0 then (1+ index)\n452 for start-line = (plist-get diffinfo :start-line)\n453 when (funcall cmp-fn current-line start-line)\n454 return (if is-reverse\n455 (1- (- (length diffinfos) index))\n456 index)))\n\n458 (defun git-gutter+-diffinfo-at-point ()\n459 (save-restriction\n460 (widen)\n461 (loop with current-line = (line-number-at-pos)\n462 for diffinfo in git-gutter+-diffinfos\n463 for start = (plist-get diffinfo :start-line)\n464 for end = (or (plist-get diffinfo :end-line) (1+ start))\n465 when (and (>= current-line start) (<= current-line end))\n466 return diffinfo)))\n\n468 (defun git-gutter+-collect-deleted-line (str)\n469 (with-temp-buffer\n470 (insert str)\n471 (goto-char (point-min))\n472 (loop while (re-search-forward \"^-\\\\(.*?\\\\)$\" nil t)\n473 collect (match-string 1) into deleted-lines\n474 finally return deleted-lines)))\n\n476 (defun git-gutter+-delete-added-lines (start-line end-line)\n477 (forward-line (1- start-line))\n478 (let ((start-point (point)))\n479 (forward-line (1+ (- end-line start-line)))\n480 (delete-region start-point (point))))\n\n482 (defun git-gutter+-insert-deleted-lines (content)\n483 (dolist (line (git-gutter+-collect-deleted-line content))\n484 (insert (concat line \"\\n\"))))\n\n486 (defun git-gutter+-do-revert-hunk (diffinfo)\n487 (save-excursion\n488 (save-restriction\n489 (widen)\n490 (goto-char (point-min))\n491 (let ((start-line (plist-get diffinfo :start-line))\n492 (end-line (plist-get diffinfo :end-line))\n493 (content (plist-get diffinfo :content)))\n494 (case (plist-get diffinfo :type)\n495 (added (git-gutter+-delete-added-lines start-line end-line))\n496 (deleted (forward-line (1- start-line))\n497 (git-gutter+-insert-deleted-lines content))\n498 (modified (git-gutter+-delete-added-lines start-line end-line)\n499 (git-gutter+-insert-deleted-lines content)))))))\n\n501 (defun git-gutter+-revert-hunks ()\n502 \"Revert hunk at point. If region is active, revert all hunks within the region.\"\n503 (interactive)\n504 (let* ((diffinfos (git-gutter+-selected-diffinfos))\n505 (one-diffinfo-p (= 1 (length diffinfos))))\n506 (save-window-excursion\n507 (if one-diffinfo-p (git-gutter+-show-hunk (car diffinfos)))\n508 (when (and diffinfos\n509 (yes-or-no-p (if one-diffinfo-p\n510 \"Revert hunk?\"\n511 (format \"Revert %d hunks?\" (length diffinfos)))))\n512 ;; Revert diffinfos in reverse so that earlier hunks don't invalidate the\n513 ;; line number information of the later hunks.\n514 (dolist (diffinfo (nreverse diffinfos))\n515 (git-gutter+-do-revert-hunk diffinfo))\n516 (save-buffer))\n517 (if one-diffinfo-p\n518 (git-gutter+-awhen (get-buffer git-gutter+-popup-buffer)\n519 (kill-buffer it))))))\n\n521 (defun git-gutter+-show-hunk (&optional diffinfo)\n522 \"Show hunk at point in another window\"\n523 (interactive)\n524 (git-gutter+-awhen (or diffinfo\n525 (git-gutter+-diffinfo-at-point))\n526 (save-selected-window\n527 (with-current-buffer (get-buffer-create git-gutter+-popup-buffer)\n528 (setq buffer-read-only nil)\n529 (erase-buffer)\n530 (insert (plist-get it :content))\n531 (insert \"\\n\")\n532 (goto-char (point-min))\n533 (diff-mode)\n534 (view-mode)\n535 (pop-to-buffer (current-buffer))))))\n\n537 (defun git-gutter+-next-hunk (arg)\n538 \"Move to next diff hunk\"\n539 (interactive \"p\")\n540 (if (not git-gutter+-diffinfos)\n541 (message \"No changes in buffer\")\n542 (save-restriction\n543 (widen)\n544 (let* ((is-reverse (< arg 0))\n545 (diffinfos git-gutter+-diffinfos)\n546 (len (length diffinfos))\n547 (index (git-gutter+-search-near-diff-index diffinfos is-reverse))\n548 (real-index (if index\n549 (let ((next (if is-reverse (1+ index) (1- index))))\n550 (mod (+ arg next) len))\n551 (if is-reverse (1- (length diffinfos)) 0)))\n552 (diffinfo (nth real-index diffinfos)))\n553 (goto-char (point-min))\n554 (forward-line (1- (plist-get diffinfo :start-line)))\n555 (when (buffer-live-p (get-buffer git-gutter+-popup-buffer))\n556 (save-window-excursion\n557 (git-gutter+-show-hunk)))))))\n\n559 (defun git-gutter+-previous-hunk (arg)\n560 \"Move to previous diff hunk\"\n561 (interactive \"p\")\n562 (git-gutter+-next-hunk (- arg)))\n\n564 (defun git-gutter+-remote-default-directory (dir file)\n565 (let* ((vec (tramp-dissect-file-name file))\n566 (method (aref vec 0))\n567 (user (aref vec 1))\n568 (host (aref vec 2)))\n569 (format \"/%s:%s%s:%s\" method (if user (concat user \"@\") \"\") host dir)))\n\n571 (defun git-gutter+-remote-file-path (dir file)\n572 (let ((file (aref (tramp-dissect-file-name file) 3)))\n573 (replace-regexp-in-string (concat \"\\\\`\" dir) \"\" file)))\n\n575 (defun git-gutter+-local-file-path (file)\n576 (if (eq system-type 'windows-nt)\n577 ;; Cygwin can't handle Windows absolute paths\n578 (file-relative-name file default-directory)\n579 file))\n\n581 (defun git-gutter+-refresh ()\n582 (git-gutter+-clear)\n583 (let ((file (buffer-file-name)))\n584 (when (and file (file-exists-p file))\n585 (if (file-remote-p file)\n586 (let* ((repo-root (git-gutter+-root-directory file))\n587 (default-directory (git-gutter+-remote-default-directory repo-root file)))\n588 (git-gutter+-process-diff (git-gutter+-remote-file-path repo-root file)))\n589 (git-gutter+-process-diff (git-gutter+-local-file-path file))))))\n\n591 (defun git-gutter+-clear ()\n592 (save-restriction\n593 (widen)\n594 (funcall git-gutter+-clear-function))\n595 (setq git-gutter+-diffinfos nil))\n\n\n598 ;;; Staging\n\n600 (defun git-gutter+-stage-hunks ()\n601 \"Stage hunk at point. If region is active, stage all hunk lines within the region.\"\n602 (interactive)\n603 (let* ((line-range (if (use-region-p)\n604 (cons (line-number-at-pos (region-beginning))\n605 (line-number-at-pos (region-end)))))\n606 (diffinfos (git-gutter+-selected-diffinfos line-range)))\n607 (when diffinfos\n608 (let ((error-msg (git-gutter+-stage-diffinfos diffinfos line-range)))\n609 (if error-msg\n610 (message \"Error staging hunks:\\n%s\" error-msg))\n611 (git-gutter+-refresh)))))\n\n613 (defun git-gutter+-selected-diffinfos (&optional line-range)\n614 (unless line-range\n615 (setq line-range (if (use-region-p)\n616 (cons (line-number-at-pos (region-beginning))\n617 (line-number-at-pos (region-end))))))\n618 (if line-range\n619 (git-gutter+-diffinfos-between-lines line-range)\n620 (git-gutter+-awhen (git-gutter+-diffinfo-at-point)\n621 (list it))))\n\n623 (defsubst git-gutter+-diffinfo-between-lines-p (diffinfo start-line end-line)\n624 (let ((diff-start (plist-get diffinfo :start-line))\n625 (diff-end (plist-get diffinfo :end-line)))\n626 (and (<= start-line diff-end)\n627 (<= diff-start end-line))))\n\n629 (defun git-gutter+-diffinfos-between-lines (line-range)\n630 (save-restriction\n631 (widen)\n632 (let ((start-line (car line-range))\n633 (end-line (cdr line-range)))\n634 (delq nil\n635 (mapcar (lambda (diffinfo)\n636 (if (git-gutter+-diffinfo-between-lines-p\n637 diffinfo start-line end-line)\n638 diffinfo))\n639 git-gutter+-diffinfos)))))\n\n641 (defun git-gutter+-stage-diffinfos (diffinfos line-range)\n642 (let ((header git-gutter+-diff-header))\n643 (with-temp-buffer\n644 (insert header)\n645 ;; Insert hunks in reverse so that earlier hunks don't invalidate the line\n646 ;; number information of the later hunks.\n647 (dolist (diffinfo (nreverse diffinfos))\n648 (git-gutter+-insert-diffinfo diffinfo line-range)\n649 (goto-char (point-max)))\n650 (git-gutter+-call-git-on-current-buffer\n651 '(\"apply\" \"--unidiff-zero\" \"--cached\" \"-\")))))\n\n653 (defun git-gutter+-insert-diffinfo (diffinfo line-range)\n654 (let ((content (plist-get diffinfo :content))\n655 (type (plist-get diffinfo :type)))\n656 (if (not line-range)\n657 (git-gutter+-insert-hunk content type)\n658 (let ((diff-start-line (plist-get diffinfo :start-line))\n659 (diff-end-line (plist-get diffinfo :end-line))\n660 (start-line (car line-range))\n661 (end-line (cdr line-range)))\n662 (git-gutter+-insert-hunk content type\n663 (1+ (- start-line diff-start-line))\n664 (1+ (- end-line diff-start-line)))))))\n\n666 (defun git-gutter+-call-git-on-current-buffer (args)\n667 \"Sends the current buffer contents to Git and replaces them with Git's output.\n\n669 RETURNS nil if Git ran successfully. Returns an error description otherwise.\"\n670 (unless (zerop (apply #'call-process-region (point-min) (point-max)\n671 git-gutter+-git-executable t t nil args))\n672 (buffer-string)))\n\n674 (defsubst git-gutter+-read-hunk-header (hunk)\n675 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n676 (string-match git-gutter+-hunk-header-regex hunk)\n677 (list (string-to-number (match-string 1 hunk))\n678 (string-to-number (or (match-string 2 hunk) \"1\"))\n679 (string-to-number (match-string 3 hunk))\n680 (string-to-number (or (match-string 4 hunk) \"1\"))))\n\n682 (defun git-gutter+-insert-hunk (hunk type &optional start end)\n683 \"If START and END are provided, only insert addition (+) lines between\n684 START and END (inclusive). START and END are both line numbers starting with 1.\"\n685 (destructuring-bind\n686 (del-line del-len add-line add-len) (git-gutter+-read-hunk-header hunk)\n687 (let* ((start (max 1 (or start 1)))\n688 (end (min add-len (or end add-len)))\n689 (insert-all-p (or (eq type :deleted)\n690 (and (= start 1) (= end add-len))))\n691 (num-lines-selected (if insert-all-p\n692 add-len\n693 (1+ (- end start)))))\n694 ;; When the user selected the last lines of a hunk with type `modified' (but\n695 ;; not the complete hunk), then don't insert any deletion (-) lines from that\n696 ;; hunk.\n697 (if (and (eq type 'modified)\n698 (> start 1) (= end add-len))\n699 (setq type 'modified-trailing))\n\n701 (save-excursion\n702 (insert hunk \"\\n\"))\n\n704 (git-gutter+-delete-hunk-header)\n\n706 (if (not insert-all-p)\n707 (git-gutter+-modify-hunk type num-lines-selected del-len start))\n\n709 (let ((hunk-header (git-gutter+-make-hunk-header type num-lines-selected\n710 del-line del-len add-line)))\n711 (insert hunk-header \"\\n\")))))\n\n713 (defun git-gutter+-delete-hunk-header ()\n714 (let ((hunk-start (point)))\n715 (forward-line 1)\n716 (delete-region hunk-start (point))))\n\n718 (defun git-gutter+-modify-hunk (type num-lines-selected del-len start)\n719 \"Remove all addition (+) lines from hunk that aren't selected.\n720 If TYPE is not `modified', also remove all deletion (-) lines.\"\n721 (let ((first-line-selected (+ del-len (1- start)))\n722 selected-lines)\n723 (save-excursion\n724 (forward-line first-line-selected)\n725 (let ((selection-start (point)))\n726 (forward-line num-lines-selected)\n727 (setq selected-lines (buffer-substring selection-start (point)))))\n728 (save-excursion\n729 (if (eq type 'modified) (forward-line del-len)) ; skip over deletion (-) lines\n730 (delete-region (point) (point-max))\n731 (insert selected-lines))))\n\n733 (defun git-gutter+-make-hunk-header (type num-lines-selected del-line del-len add-line)\n734 (let ((add-len num-lines-selected))\n735 (case type\n736 (added (setq add-line (1+ del-line)))\n737 (modified-trailing (setq add-line (+ del-line del-len)\n738 del-line (1- add-line)\n739 del-len 0))\n740 (t (setq add-line del-line)))\n741 (format \"@@ -%d,%d +%d,%d @@\"\n742 del-line del-len\n743 add-line add-len)))\n\n\n746 ;;; Committing\n747 ;; This section draws heavily from old Magit source code.\n\n749 (defvar git-gutter+-pre-commit-window-config nil)\n750 (defvar git-gutter+-commit-origin-buffer nil\n751 \"Buffer that started the commit\")\n\n753 (defconst git-gutter+-commit-buffer-name \"*Commit Message*\")\n754 (defconst git-gutter+-staged-changes-buffer-name \"*Staged Changes*\")\n\n756 ;;;###autoload\n757 (defun git-gutter+-commit ()\n758 \"Commit staged changes. If nothing is staged, ask to stage the current buffer.\"\n759 (interactive)\n\n761 (when (and (not (git-gutter+-anything-staged-p))\n762 git-gutter+-diffinfos\n763 (y-or-n-p \"Nothing staged. Stage current buffer? \"))\n764 (git-gutter+-stage-whole-buffer))\n\n766 (let ((file (buffer-file-name))\n767 (dir default-directory))\n768 (git-gutter+-save-window-config-if-needed)\n769 (setq git-gutter+-commit-origin-buffer (current-buffer))\n770 (git-gutter+-open-commit-edit-buffer dir)\n771 (git-gutter+-show-staged-changes file dir)))\n\n773 (defun git-gutter+-stage-and-commit ()\n774 (interactive)\n775 (git-gutter+-stage-hunks)\n776 (git-gutter+-commit))\n\n778 (defun git-gutter+-save-window-config-if-needed ()\n779 ;; Only save the window config if the temporary buffers that get popped-up by\n780 ;; git-gutter+ are not already visible.\n781 ;; In this way, `git-gutter+-commit' can be called twice in a row without\n782 ;; losing the original window config.\n783 (when (not (and git-gutter+-pre-commit-window-config\n784 (get-buffer-window git-gutter+-commit-buffer-name)\n785 (get-buffer-window git-gutter+-staged-changes-buffer-name)))\n786 (setq git-gutter+-pre-commit-window-config (current-window-configuration))))\n\n788 (defun git-gutter+-open-commit-edit-buffer (dir)\n789 \"Opens a buffer for composing the commit message\"\n790 (pop-to-buffer (get-buffer-create git-gutter+-commit-buffer-name))\n791 (setq default-directory dir)\n792 (git-gutter+-commit-mode)\n793 (message \"Type C-c C-c to commit (C-c C-k to cancel).\"))\n\n795 (defsubst git-gutter+-pop-to-staged-changes-buffer ()\n796 (let* ((buf (get-buffer-create git-gutter+-staged-changes-buffer-name))\n797 (window (get-buffer-window buf)))\n798 (if window\n799 ;; Buffer is already visible\n800 (select-window window)\n801 (if (<= (length (window-list)) 2)\n802 (split-window))\n803 (pop-to-buffer buf))))\n\n805 (defun git-gutter+-show-staged-changes (file dir)\n806 (save-selected-window\n807 (git-gutter+-pop-to-staged-changes-buffer)\n808 (setq buffer-read-only nil)\n809 (erase-buffer)\n810 (let ((default-directory dir))\n811 (git-gutter+-call-git '(\"diff\" \"--staged\") file))\n812 (goto-char (point-min))\n813 (diff-mode)\n814 (view-mode)))\n\n816 (defsubst git-gutter+-abort-commit-when-no-changes (allow-empty amend)\n817 (unless (or amend\n818 allow-empty\n819 (git-gutter+-anything-staged-p))\n820 (error\n821 \"Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?\"\n822 (key-description (car (where-is-internal\n823 'git-gutter+-commit-toggle-amending)))\n824 (key-description (car (where-is-internal\n825 'git-gutter+-commit-toggle-allow-empty))))))\n\n827 (defsubst git-gutter+-buffer-is-whitespace ()\n828 (save-excursion\n829 (goto-char (point-min))\n830 (looking-at-p \"[ \\t\\n]*\\\\'\")))\n\n832 (defun git-gutter+-publish-commit ()\n833 \"Publish commit\"\n834 (interactive)\n835 (let* ((fields (git-gutter+-commit-get-fields))\n836 (amend (equal \"yes\" (git-gutter+-commit-get-field 'amend fields)))\n837 (allow-empty (equal \"yes\" (git-gutter+-commit-get-field 'allow-empty fields)))\n838 (author (git-gutter+-commit-get-field 'author fields))\n839 (date (git-gutter+-commit-get-field 'date fields)))\n\n841 (git-gutter+-abort-commit-when-no-changes allow-empty amend)\n\n843 (git-gutter+-push-to-comment-ring (buffer-string))\n\n845 (git-gutter+-commit-set-fields nil) ; Delete message header\n\n847 (when (git-gutter+-buffer-is-whitespace)\n848 (erase-buffer)\n849 (insert \"(Empty description)\"))\n\n851 (let ((error-msg (git-gutter+-call-git-on-current-buffer\n852 (append '(\"--no-pager\" \"commit\" \"-F\" \"-\")\n853 (if amend '(\"--amend\"))\n854 (if allow-empty '(\"--allow-empty\"))\n855 (if author (list (concat \"--author=\" author)))\n856 (if date (list (concat \"--date=\" date)))))))\n857 (if error-msg\n858 (progn\n859 (message \"Commit error:\\n%s\" error-msg)\n860 (erase-buffer)\n861 (insert (ring-ref log-edit-comment-ring 0))) ; Reinsert commit message\n862 (message \"Commit successful.\")\n863 (git-gutter+-close-commit-edit-buffer)\n864 (git-gutter+-update-vc-modeline)))))\n\n866 (defun git-gutter+-close-commit-edit-buffer ()\n867 \"Abort edits and discard commit message being composed.\"\n868 (interactive)\n869 (kill-buffer)\n870 (set-window-configuration git-gutter+-pre-commit-window-config))\n\n872 (defun git-gutter+-update-vc-modeline ()\n873 (when (buffer-live-p git-gutter+-commit-origin-buffer)\n874 (with-current-buffer git-gutter+-commit-origin-buffer\n875 ;; Updating the modeline has no effect if the buffer still has\n876 ;; changes - it will remain in the 'modified' state. So skip it then.\n877 (unless git-gutter+-diffinfos\n878 (ignore-errors (vc-find-file-hook))))))\n\n880 (defun git-gutter+-stage-whole-buffer ()\n881 (save-excursion\n882 (mark-whole-buffer)\n883 (git-gutter+-stage-hunks)))\n\n885 (defun git-gutter+-anything-staged-p ()\n886 \"Return t if the current repo has staged changes\"\n887 (not (zerop (git-gutter+-call-git '(\"diff\" \"--quiet\" \"--cached\")))))\n\n889 (defun git-gutter+-commit-toggle-amending ()\n890 \"Toggle whether this will be an amendment to the previous commit.\n891 \\(i.e., whether commit is run via 'git commit --amend')\"\n892 (interactive)\n893 ;; Remove the newline that 'git-commit-mode' adds to a new commit\n894 ;; message buffer by default. This prevents an ugly visual\n895 ;; gap between the commit message header and the previous commit\n896 ;; message.\n897 (when (git-gutter+-buffer-is-whitespace)\n898 (erase-buffer))\n\n900 (let ((amend-was-already-set (git-gutter+-commit-get-field 'amend)))\n901 (git-gutter+-commit-toggle-field 'amend t)\n902 (unless amend-was-already-set\n903 ;; Insert previous commit message\n904 (goto-char (point-max))\n905 (unless (zerop (current-column))\n906 (insert \"\\n\"))\n907 (insert (git-gutter+-get-last-commit-msg)\n908 \"\\n\"))))\n\n910 (defun git-gutter+-commit-toggle-allow-empty ()\n911 \"Toggle whether this commit is allowed to be empty.\n912 \\(i.e., whether commit is run via 'git commit --allow-empty')\"\n913 (interactive)\n914 (git-gutter+-commit-toggle-field 'allow-empty t))\n\n916 (defun git-gutter+-format-author (author email)\n917 (format \"%s <%s>\" author email))\n\n919 (defun git-gutter+-commit-toggle-author ()\n920 \"Toggle whether this commit should have a user-defined author.\"\n921 (interactive)\n922 (git-gutter+-commit-toggle-input\n923 'author (git-gutter+-format-author\n924 (or (git-gutter+-get-cfg \"user\" \"name\") \"Author Name\")\n925 (or (git-gutter+-get-cfg \"user\" \"email\") \"author@email\"))))\n\n927 (defun git-gutter+-commit-toggle-date ()\n928 \"Toggle whether this commit should have a user-defined date.\"\n929 (interactive)\n930 (git-gutter+-commit-toggle-input 'date\n931 ;; ISO 8601\n932 (format-time-string \"%Y-%m-%dT%T%z\" (current-time))))\n\n934 (defun git-gutter+-push-to-comment-ring (comment)\n935 (when (or (ring-empty-p log-edit-comment-ring)\n936 (not (equal comment (ring-ref log-edit-comment-ring 0))))\n937 (ring-insert log-edit-comment-ring comment)))\n\n939 (defun git-gutter+-get-last-commit-msg ()\n940 (git-gutter+-git-output '(\"log\" \"--max-count=1\" \"--pretty=format:%s%n%n%b\" \"HEAD\")))\n\n942 (defun git-gutter+-get-cfg (&rest keys)\n943 (git-gutter+-git-output (list \"config\" (mapconcat 'identity keys \".\"))))\n\n945 (defun git-gutter+-git-output (args)\n946 (with-temp-buffer\n947 (git-gutter+-call-git args)\n948 ;; Delete trailing newlines\n949 (goto-char (point-min))\n950 (if (re-search-forward \"\\n+\\\\'\" nil t)\n951 (replace-match \"\"))\n952 (buffer-string)))\n\n\n955 ;;; Commit message header\n\n957 (defconst git-gutter+-commit-header-end \"-- End of commit options header --\\n\")\n\n959 (defun git-gutter+-commit-get-field (name &optional fields)\n960 (cdr (assq name (or fields (git-gutter+-commit-get-fields)))))\n\n962 (defun git-gutter+-commit-set-field (name value)\n963 (let* ((fields (git-gutter+-commit-get-fields))\n964 (cell (assq name fields)))\n965 (cond (cell\n966 (if value\n967 (rplacd cell value)\n968 (setq fields (delq cell fields))))\n969 (t\n970 (if value\n971 (setq fields (append fields (list (cons name value)))))))\n972 (git-gutter+-commit-set-fields fields)))\n\n974 (defun git-gutter+-commit-toggle-field (name default)\n975 \"Toggle the commit header field named NAME.\n976 If it's currently unset, set it to DEFAULT (t or nil).\"\n977 (let* ((fields (git-gutter+-commit-get-fields))\n978 (cell (assq name fields)))\n979 (if cell\n980 (rplacd cell (if (equal (cdr cell) \"yes\") \"no\" \"yes\"))\n981 (setq fields (acons name (if default \"yes\" \"no\") fields)))\n982 (git-gutter+-commit-set-fields fields)))\n\n984 (defun git-gutter+-commit-toggle-input (name default)\n985 \"Toggle the commit header input named NAME.\n986 If it's currently unset, set it to DEFAULT (a string). If it is\n987 set remove it.\"\n988 (let* ((fields (git-gutter+-commit-get-fields))\n989 (cell (assq name fields)))\n990 (if cell\n991 (setq fields (assq-delete-all name fields))\n992 (setq fields (acons name default fields)))\n993 (git-gutter+-commit-set-fields fields)))\n\n995 (defun git-gutter+-commit-get-fields ()\n996 (let (result)\n997 (goto-char (point-min))\n998 (while (looking-at \"^\\\\([A-Za-z0-9-_]+\\\\): *\\\\(.+\\\\)?$\")\n999 (let ((name (intern (downcase (match-string 1))))\n1000 (value (read (or (match-string 2) \"nil\"))))\n1001 (push (cons name value) result))\n1002 (forward-line))\n1003 (if (looking-at (regexp-quote git-gutter+-commit-header-end))\n1004 (nreverse result))))\n\n1006 (defun git-gutter+-commit-set-fields (fields)\n1007 (goto-char (point-min))\n1008 ;; Delete commit header\n1009 (if (search-forward-regexp (format \"^\\\\(?:[A-Za-z0-9-_]+:.*\\n\\\\)*%s\"\n1010 (regexp-quote git-gutter+-commit-header-end))\n1011 nil t)\n1012 (delete-region (match-beginning 0) (match-end 0)))\n1013 (goto-char (point-min))\n1014 (when fields\n1015 (dolist (field fields)\n1016 (insert (capitalize (symbol-name (car field))) \": \"\n1017 (prin1-to-string (cdr field)) \"\\n\"))\n1018 (insert git-gutter+-commit-header-end)))\n\n\n1021 ;;; git-gutter+-commit-mode\n1022 ;; Like git-commit-mode, but adds keybindings to git-gutter+ commands and\n1023 ;; highlighting support for the commit message header.\n\n1025 (define-derived-mode git-gutter+-commit-mode git-commit-mode \"Git-Gutter-Commit\"\n1026 (setq font-lock-defaults (list (git-gutter+-commit-font-lock-keywords) t)))\n\n1028 (setq git-gutter+-commit-mode-map\n1029 (let ((map (copy-keymap git-commit-mode-map)))\n1030 (define-key map (kbd \"C-c C-c\") 'git-gutter+-publish-commit)\n1031 (define-key map (kbd \"C-c C-k\") 'git-gutter+-close-commit-edit-buffer)\n1032 (define-key map (kbd \"C-c C-a\") 'git-gutter+-commit-toggle-amending)\n1033 (define-key map (kbd \"C-c C-e\") 'git-gutter+-commit-toggle-allow-empty)\n1034 (define-key map (kbd \"C-c C-u\") 'git-gutter+-commit-toggle-author)\n1035 (define-key map (kbd \"C-c C-d\") 'git-gutter+-commit-toggle-date)\n1036 (define-key map (kbd \"C-c C-b\") 'git-commit-ack)\n1037 (define-key map (kbd \"M-p\") 'log-edit-previous-comment)\n1038 (define-key map (kbd \"M-n\") 'log-edit-next-comment)\n1039 map))\n\n1041 (defface git-gutter+-commit-header-face\n1042 '((t :inherit font-lock-comment-face))\n1043 \"Highlights the commit message header\"\n1044 :group 'git-gutter+-faces)\n\n1046 (defconst git-gutter+-commit-header-regex\n1047 (concat \"\\\\(?:.\\\\|\\n\\\\)*?\" (regexp-quote git-gutter+-commit-header-end)))\n\n1049 (defconst git-gutter+-skip-commit-header-regex\n1050 (concat \"\\\\`\\\\(?:\" git-gutter+-commit-header-regex \"\\\\)?\"))\n\n1052 ;; Modify git-commit-summary-regexp to ignore the commit header\n1053 (defadvice git-commit-summary-regexp\n1054 (after ignore-git-gutter+-commit-header activate compile)\n1055 (if (eq major-mode 'git-gutter+-commit-mode)\n1056 (setq ad-return-value\n1057 (concat git-gutter+-skip-commit-header-regex\n1058 (substring ; Remove leading \"\\\\`\"\n1059 ad-return-value 2)))))\n\n1061 (defun git-gutter+-commit-font-lock-keywords ()\n1062 \"Like `git-commit-mode-font-lock-keywords' but with commit header highlighting\"\n1063 `((,(concat \"\\\\`\" git-gutter+-commit-header-regex) . 'git-gutter+-commit-header-face)\n1064 ,@(git-commit-mode-font-lock-keywords)))\n\n\n1067 ;;; Magit synchronization\n1068 ;; Force Magit to refresh git-gutter+ when updating the VC mode line.\n\n1070 (defvar git-gutter+-orig-vc-find-file-hook)\n\n1072 (defvar git-gutter+-vc-find-file-hook-with-refresh\n1073 (lambda ()\n1074 (funcall git-gutter+-orig-vc-find-file-hook)\n1075 (if git-gutter+-mode (git-gutter+-refresh))))\n\n1077 (defadvice magit-update-vc-modeline (around refresh-git-gutter+ compile activate)\n1078 ;; `magit-update-vc-modeline' calls `vc-find-file-hook' (a function!) on each\n1079 ;; buffer in the repo. Temporarily rebind it to `vc-find-file-hook-with-refresh',\n1080 ;; which calls git-gutter+-refresh after updating the VC mode line.\n1081 ;;\n1082 ;; Using `flet' would have been much simpler, but it's deprecated since 24.3.\n1083 (setq git-gutter+-orig-vc-find-file-hook (symbol-function 'vc-find-file-hook))\n1084 (fset 'vc-find-file-hook git-gutter+-vc-find-file-hook-with-refresh)\n1085 (unwind-protect\n1086 ad-do-it\n1087 (fset 'vc-find-file-hook git-gutter+-orig-vc-find-file-hook)))\n\n1089 (provide 'git-gutter+)\n\n1091 ;;; git-gutter+.el ends here\n")) (setq helm-swoop-cache t))) (candidates-in-buffer) (get-line . buffer-substring-no-properties) (keymap keymap (C-M-left . backward-sexp) (C-M-right . forward-sexp) (27 keymap (105 . helm-multi-swoop-all-from-helm-swoop)) (3 keymap (5 . helm-swoop-edit)) keymap (C-M-left . paren-backward-sexp) (C-M-right . paren-forward-sexp) (94 . helm-swoop-caret-match) (menu-bar keymap (help-menu keymap (describe keymap (describe-mode . helm-help)))) (help keymap (109 . helm-help)) (f1 keymap (109 . helm-help)) (8 keymap (109 . helm-help) (104 . undefined) (8 . undefined) (4 . helm-debug-output)) (20 . helm-toggle-resplit-and-swap-windows) (C-tab . undefined) (triple-mouse-3 . ignore) (double-mouse-3 . ignore) (mouse-3 . ignore) (drag-mouse-3 . ignore) (down-mouse-3 . ignore) (triple-mouse-2 . ignore) (double-mouse-2 . ignore) (mouse-2 . ignore) (drag-mouse-2 . ignore) (down-mouse-2 . ignore) (triple-mouse-1 . ignore) (double-mouse-1 . ignore) (mouse-1 . ignore) (drag-mouse-1 . ignore) (down-mouse-1 . ignore) (67108897 . helm-toggle-suspend-update) (3 keymap (21 . helm-force-update) (6 . helm-follow-mode) (11 . helm-kill-selection-and-quit) (25 . helm-yank-selection) (4 . helm-delete-current-selection) (45 . helm-swap-windows)) (67108987 . helm-enlarge-window) (67108989 . helm-narrow-window) (19 . undefined) (18 . undefined) (23 . helm-yank-text-at-point) (24 keymap (2 . helm-resume-list-buffers-after-quit) (98 . helm-resume-previous-session-after-quit) (6 . helm-quit-and-find-file)) (11 . helm-delete-minibuffer-contents) (67108896 . helm-toggle-visible-mark) (0 . helm-toggle-visible-mark) (C-M-up . helm-scroll-other-window-down) (C-M-down . helm-scroll-other-window) (M-prior . helm-scroll-other-window-down) (M-next . helm-scroll-other-window) (12 . helm-recenter-top-bottom-other-window) (15 . helm-next-source) (10 . helm-select-3rd-action) (5 . helm-select-2nd-action-or-end-of-line) ...) (header-line . "[C-c C-e] Edit mode, [M-i] apply all buffers") (action lambda ($line) (helm-swoop--goto-line (when (string-match "^[0-9]+" $line) (string-to-number (match-string 0 $line)))) (when (re-search-forward (mapconcat (quote identity) (split-string helm-pattern " ") "\\|") nil t) (goto-char (match-beginning 0))) (helm-swoop--recenter)) (migemo)) #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) "Swoop: " nil "^368 " "*Helm Swoop*" nil nil nil)) | |
#[0 "\303\301\304\305\306\307\310\311\300!\312\"\313\314%\n\"\"\207" [(:sources ((name . "git-gutter+.el") (init lambda nil (unless helm-swoop-cache (with-current-buffer (helm-candidate-buffer ...) (insert "1 ;;; git-gutter+.el --- Manage Git hunks straight from the buffer\n\n3 ;; Copyright (C) 2013 by Syohei YOSHIDA and contributors\n\n5 ;; Author: Syohei YOSHIDA <syohex@gmail.com> and contributors\n6 ;; URL: https://github.com/nonsequitur/git-gutter-plus\n7 ;; Version: 0.1\n\n9 ;; This program is free software; you can redistribute it and/or modify\n10 ;; it under the terms of the GNU General Public License as published by\n11 ;; the Free Software Foundation, either version 3 of the License, or\n12 ;; (at your option) any later version.\n\n14 ;; This program is distributed in the hope that it will be useful,\n15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n17 ;; GNU General Public License for more details.\n\n19 ;; You should have received a copy of the GNU General Public License\n20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n22 ;; Package-Requires: ((git-commit-mode \"0.14\"))\n\n24 ;;; Commentary:\n25 ;;\n26 ;; View, stage and revert Git changes straight from the buffer.\n\n28 ;;; Code:\n\n30 (eval-when-compile\n31 (require 'cl))\n\n33 (require 'tramp)\n34 (require 'log-edit)\n35 (require 'git-commit-mode)\n\n37 (defgroup git-gutter+ nil\n38 \"Manage Git hunks straight from the buffer\"\n39 :prefix \"git-gutter+-\"\n40 :group 'vc)\n\n42 (defcustom git-gutter+-window-width nil\n43 \"Character width of the gutter margin. Set this variable if the automatically\n44 calculated width looks wrong. (This can happen with some special characters.)\"\n45 :type 'integer\n46 :group 'git-gutter+)\n\n48 (defcustom git-gutter+-git-executable \"git\"\n49 \"The path of the Git executable.\"\n50 :type 'string\n51 :group 'git-gutter+)\n\n53 (defcustom git-gutter+-diff-options nil\n54 \"List of strings containing extra arguments to 'git diff'\"\n55 :type 'list\n56 :group 'git-gutter+)\n\n58 (defcustom git-gutter+-separator-sign nil\n59 \"Separator sign\"\n60 :type 'string\n61 :group 'git-gutter+)\n\n63 (defcustom git-gutter+-modified-sign \"=\"\n64 \"Modified sign\"\n65 :type 'string\n66 :group 'git-gutter+)\n\n68 (defcustom git-gutter+-added-sign \"+\"\n69 \"Added sign\"\n70 :type 'string\n71 :group 'git-gutter+)\n\n73 (defcustom git-gutter+-deleted-sign \"-\"\n74 \"Deleted sign\"\n75 :type 'string\n76 :group 'git-gutter+)\n\n78 (defcustom git-gutter+-unchanged-sign nil\n79 \"Unchanged sign\"\n80 :type 'string\n81 :group 'git-gutter+)\n\n83 (defcustom git-gutter+-hide-gutter nil\n84 \"Hide gutter if there are no changes\"\n85 :type 'boolean\n86 :group 'git-gutter+)\n\n88 (defcustom git-gutter+-lighter \" GitGutter\"\n89 \"Minor mode lighter in mode-line\"\n90 :type 'string\n91 :group 'git-gutter+)\n\n93 (defface git-gutter+-separator\n94 '((t (:foreground \"cyan\" :weight bold)))\n95 \"Face of the separator\"\n96 :group 'git-gutter+)\n\n98 (defface git-gutter+-modified\n99 '((t (:foreground \"magenta\" :weight bold)))\n100 \"Face for modified lines\"\n101 :group 'git-gutter+)\n\n103 (defface git-gutter+-added\n104 '((t (:foreground \"green\" :weight bold)))\n105 \"Face for added lines\"\n106 :group 'git-gutter+)\n\n108 (defface git-gutter+-deleted\n109 '((t (:foreground \"red\" :weight bold)))\n110 \"Face for deleted lines\"\n111 :group 'git-gutter+)\n\n113 (defface git-gutter+-unchanged\n114 '((t (:background \"yellow\")))\n115 \"Face for unchanged lines\"\n116 :group 'git-gutter+)\n\n118 (defcustom git-gutter+-disabled-modes nil\n119 \"A list of modes for which `global-git-gutter+-mode' should be disabled.\"\n120 :type '(repeat symbol)\n121 :group 'git-gutter+)\n\n123 (defvar git-gutter+-mode-map\n124 (make-sparse-keymap))\n\n126 (defvar git-gutter+-view-diff-function nil\n127 \"Function to call for displaying diffs\")\n\n129 (defvar git-gutter+-clear-function nil\n130 \"Function to call for clearing the diff display\")\n\n132 (defvar git-gutter+-window-config-change-function nil\n133 \"Function to call when the buffer's local window configuration has changed\")\n\n135 (defvar git-gutter+-diffinfos nil)\n136 (defvar git-gutter+-diff-header nil)\n137 (make-variable-buffer-local 'git-gutter+-diffinfos)\n138 (make-variable-buffer-local 'git-gutter+-diff-header)\n\n140 (defvar git-gutter+-popup-buffer \"*git-gutter+-diff*\")\n141 (defvar git-gutter+-buffers-to-reenable nil)\n\n143 (defconst git-gutter+-hunk-header-regex\n144 ;; The same as diff-hunk-header-re-unified\n145 \"^@@ -\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? \\\\+\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? @@\")\n\n147 (defalias 'git-gutter+-popup-hunk 'git-gutter+-show-hunk)\n148 (defalias 'git-gutter+-revert-hunk 'git-gutter+-revert-hunks)\n\n150 (defmacro git-gutter+-awhen (test &rest body)\n151 \"Anaphoric when.\"\n152 (declare (indent 1))\n153 `(let ((it ,test))\n154 (when it ,@body)))\n\n156 (defun git-gutter+-enable-default-display-mode ()\n157 (setq git-gutter+-view-diff-function 'git-gutter+-view-diff-infos\n158 git-gutter+-clear-function 'git-gutter+-clear-diff-infos\n159 git-gutter+-window-config-change-function 'git-gutter+-show-gutter))\n\n161 (unless git-gutter+-view-diff-function\n162 (git-gutter+-enable-default-display-mode))\n\n164 (defun git-gutter+-call-git (args &optional file)\n165 (if (and file (file-remote-p file))\n166 (apply #'process-file git-gutter+-git-executable nil t nil args)\n167 (apply #'call-process git-gutter+-git-executable nil t nil args)))\n\n169 (defun git-gutter+-in-git-repository-p (file)\n170 (with-temp-buffer\n171 (let ((args '(\"rev-parse\" \"--is-inside-work-tree\")))\n172 (when (zerop (git-gutter+-call-git args file))\n173 (goto-char (point-min))\n174 (string= \"true\" (buffer-substring-no-properties\n175 (point) (line-end-position)))))))\n\n177 (defun git-gutter+-root-directory (file)\n178 (with-temp-buffer\n179 (let* ((args '(\"rev-parse\" \"--show-toplevel\"))\n180 (ret (git-gutter+-call-git args file)))\n181 (when (zerop ret)\n182 (goto-char (point-min))\n183 (let ((root (buffer-substring-no-properties (point) (line-end-position))))\n184 (unless (string= root \"\")\n185 (file-name-as-directory root)))))))\n\n187 (defsubst git-gutter+-diff-args (file)\n188 (delq nil (list \"--no-pager\" \"diff\" \"--no-color\" \"--no-ext-diff\" \"-U0\"\n189 git-gutter+-diff-options file)))\n\n191 (defun git-gutter+-diff (curfile)\n192 (let ((args (git-gutter+-diff-args curfile))\n193 (file (buffer-file-name))) ;; for tramp\n194 (with-temp-buffer\n195 (when (zerop (git-gutter+-call-git args file))\n196 (goto-char (point-min))\n197 (let ((diff-header (git-gutter+-get-diff-header))\n198 (diffinfos (git-gutter+-get-diffinfos)))\n199 (list diff-header diffinfos))))))\n\n201 (defun git-gutter+-get-diff-header ()\n202 (save-excursion\n203 (if (re-search-forward git-gutter+-hunk-header-regex nil t)\n204 (buffer-substring (point-min) (match-beginning 0)))))\n\n206 (defsubst git-gutter+-make-diffinfo (type content start end)\n207 (list :type type :content content :start-line start :end-line end))\n\n209 (defun git-gutter+-get-diffinfos ()\n210 (loop while (re-search-forward git-gutter+-hunk-header-regex nil t)\n211 ;; Hunk header format:\n212 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n213 for del-len = (string-to-number (or (match-string 2) \"1\"))\n214 for add-line = (string-to-number (match-string 3))\n215 for add-len = (string-to-number (or (match-string 4) \"1\"))\n216 for type = (cond ((zerop del-len) 'added)\n217 ((zerop add-len) 'deleted)\n218 (t 'modified))\n219 for start-line = (if (eq type 'deleted)\n220 (1+ add-line)\n221 add-line)\n222 for end-line = (if (eq type 'deleted)\n223 start-line\n224 (1- (+ add-line add-len)))\n225 for content = (git-gutter+-diff-content)\n226 collect\n227 (git-gutter+-make-diffinfo type content start-line end-line)))\n\n229 (defun git-gutter+-diff-content ()\n230 (save-excursion\n231 (goto-char (line-beginning-position)) ; Move to beginning of hunk header\n232 (let ((hunk-start (point)))\n233 ;; Move to end of hunk\n234 (forward-line 1)\n235 (if (re-search-forward \"^@@\" nil t)\n236 (backward-char 3) ;; exclude \"\\n@@\"\n237 (goto-char (1- (point-max)))) ; Skip trailing newline\n238 (buffer-substring hunk-start (point)))))\n\n240 (defun git-gutter+-line-to-pos (line)\n241 (save-excursion\n242 (goto-char (point-min))\n243 (forward-line (1- line))\n244 (point)))\n\n246 (defun git-gutter+-before-string (sign)\n247 (let* ((sep-sign git-gutter+-separator-sign)\n248 (sep (when sep-sign\n249 (propertize sep-sign 'face 'git-gutter+-separator)))\n250 (gutter-sep (concat sign sep)))\n251 (propertize \" \" 'display `((margin left-margin) ,gutter-sep))))\n\n253 (defsubst git-gutter+-select-face (type)\n254 (case type\n255 (added 'git-gutter+-added)\n256 (modified 'git-gutter+-modified)\n257 (deleted 'git-gutter+-deleted)))\n\n259 (defsubst git-gutter+-select-sign (type)\n260 (case type\n261 (added git-gutter+-added-sign)\n262 (modified git-gutter+-modified-sign)\n263 (deleted git-gutter+-deleted-sign)))\n\n265 (defun git-gutter+-propertized-sign (type)\n266 (let ((sign (git-gutter+-select-sign type))\n267 (face (git-gutter+-select-face type)))\n268 (propertize sign 'face face)))\n\n270 (defun git-gutter+-view-region (sign start-line end-line)\n271 (let ((beg (git-gutter+-line-to-pos start-line)))\n272 (goto-char beg)\n273 (while (and (<= (line-number-at-pos) end-line) (not (eobp)))\n274 (git-gutter+-view-at-pos sign (point))\n275 (forward-line 1))))\n\n277 (defun git-gutter+-view-at-pos (sign pos)\n278 (let ((ov (make-overlay pos pos)))\n279 (overlay-put ov 'before-string (git-gutter+-before-string sign))\n280 (overlay-put ov 'git-gutter+ t)))\n\n282 (defun git-gutter+-view-diff-info (diffinfo)\n283 (let* ((start-line (plist-get diffinfo :start-line))\n284 (end-line (plist-get diffinfo :end-line))\n285 (type (plist-get diffinfo :type))\n286 (sign (git-gutter+-propertized-sign type)))\n287 (case type\n288 ((modified added) (git-gutter+-view-region sign start-line end-line))\n289 (deleted (git-gutter+-view-at-pos\n290 sign (git-gutter+-line-to-pos start-line))))))\n\n292 (defun git-gutter+-sign-width (sign)\n293 (loop for s across sign\n294 sum (char-width s)))\n\n296 (defun git-gutter+-longest-sign-width ()\n297 (let ((signs (list git-gutter+-modified-sign\n298 git-gutter+-added-sign\n299 git-gutter+-deleted-sign)))\n300 (when git-gutter+-unchanged-sign\n301 (add-to-list 'signs git-gutter+-unchanged-sign))\n302 (+ (apply 'max (mapcar 'git-gutter+-sign-width signs))\n303 (git-gutter+-sign-width git-gutter+-separator-sign))))\n\n305 (defun git-gutter+-view-for-unchanged ()\n306 (save-excursion\n307 (let ((sign (if git-gutter+-unchanged-sign\n308 (propertize git-gutter+-unchanged-sign\n309 'face 'git-gutter+-unchanged)\n310 \" \")))\n311 (goto-char (point-min))\n312 (while (not (eobp))\n313 (git-gutter+-view-at-pos sign (point))\n314 (forward-line 1)))))\n\n316 (defun git-gutter+-set-window-margin (width)\n317 (let ((curwin (get-buffer-window)))\n318 (set-window-margins curwin width (cdr (window-margins curwin)))))\n\n320 (defsubst git-gutter+-file-buffer-p ()\n321 (and (buffer-file-name)\n322 default-directory\n323 (file-directory-p default-directory)))\n\n325 ;;;###autoload\n326 (define-minor-mode git-gutter+-mode\n327 \"Git-Gutter mode\"\n328 :group 'git-gutter+\n329 :init-value nil\n330 :global nil\n331 :lighter git-gutter+-lighter\n332 (if git-gutter+-mode\n333 (if (and (git-gutter+-file-buffer-p)\n334 (git-gutter+-in-git-repository-p (buffer-file-name)))\n335 (progn\n336 (git-gutter+-add-local-hooks)\n337 (git-gutter+-refresh))\n338 (if (called-interactively-p 'any)\n339 (message \"No Git repo for current buffer\"))\n340 (git-gutter+-mode -1))\n341 (git-gutter+-remove-local-hooks)\n342 (git-gutter+-clear)))\n\n344 (defun git-gutter+-add-local-hooks ()\n345 (add-hook 'after-save-hook 'git-gutter+-refresh nil t)\n346 ;; Turn off `git-gutter+-mode' while reverting to prevent any redundant calls to\n347 ;; `git-gutter+-refresh'.\n348 (add-hook 'before-revert-hook 'git-gutter+-turn-off nil t)\n349 (add-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change nil t)\n350 (if git-gutter+-window-config-change-function\n351 (add-hook 'window-configuration-change-hook\n352 git-gutter+-window-config-change-function nil t)))\n\n354 (defun git-gutter+-remove-local-hooks ()\n355 (remove-hook 'after-save-hook 'git-gutter+-refresh t)\n356 (remove-hook 'before-revert-hook 'git-gutter+-turn-off t)\n357 (remove-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change t)\n358 (if git-gutter+-window-config-change-function\n359 (remove-hook 'window-configuration-change-hook\n360 git-gutter+-window-config-change-function t)))\n\n362 (defmacro git-gutter+-in-all-buffers (&rest body)\n363 `(dolist (buf (buffer-list))\n364 (with-current-buffer buf\n365 ,@body)))\n\n367 ;; When `define-globalized-minor-mode' is used to define `global-git-gutter+-mode',\n368 ;; `git-gutter+-mode' and thus `git-gutter+-refresh' get run twice when a new file\n369 ;; is opened. (First for `fundamental-mode', then for the file-specific mode.)\n370 ;; The following definition of `global-git-gutter+-mode' avoids any redundant calls to\n371 ;; `git-gutter+-refresh'.\n\n373 ;;;###autoload\n374 (define-minor-mode global-git-gutter+-mode ()\n375 \"Global Git-Gutter mode\"\n376 :group 'git-gutter+\n377 :init-value nil\n378 :global t\n379 (if global-git-gutter+-mode\n380 (progn\n381 (add-hook 'find-file-hook 'git-gutter+-turn-on)\n382 (add-hook 'after-revert-hook 'git-gutter+-turn-on)\n383 (add-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n384 (git-gutter+-in-all-buffers (git-gutter+-turn-on)))\n385 (remove-hook 'find-file-hook 'git-gutter+-turn-on)\n386 (remove-hook 'after-revert-hook 'git-gutter+-turn-on)\n387 (remove-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n388 (git-gutter+-in-all-buffers (git-gutter+-turn-off))))\n\n390 (defun git-gutter+-turn-on ()\n391 (when (and (buffer-file-name)\n392 (not (memq major-mode git-gutter+-disabled-modes))\n393 (not git-gutter+-mode))\n394 (git-gutter+-mode t)))\n\n396 (defun git-gutter+-turn-off ()\n397 (if git-gutter+-mode (git-gutter+-mode -1)))\n\n399 (defun git-gutter+-reenable-after-major-mode-change ()\n400 (if global-git-gutter+-mode\n401 (add-to-list 'git-gutter+-buffers-to-reenable (current-buffer))))\n\n403 (defun git-gutter+-reenable-buffers ()\n404 (dolist (buf git-gutter+-buffers-to-reenable)\n405 (with-current-buffer buf\n406 (git-gutter+-turn-on)))\n407 (setq git-gutter+-buffers-to-reenable nil))\n\n409 (defsubst git-gutter+-show-gutter-p (diffinfos)\n410 (if git-gutter+-hide-gutter\n411 (or diffinfos git-gutter+-unchanged-sign)\n412 (or global-git-gutter+-mode git-gutter+-unchanged-sign diffinfos)))\n\n414 (defun git-gutter+-show-gutter (&optional diffinfos)\n415 (when (git-gutter+-show-gutter-p (or diffinfos git-gutter+-diffinfos))\n416 (let ((win-width (or git-gutter+-window-width\n417 (git-gutter+-longest-sign-width))))\n418 (git-gutter+-set-window-margin win-width))))\n\n420 (defun git-gutter+-view-diff-infos (diffinfos)\n421 (when (or git-gutter+-unchanged-sign\n422 git-gutter+-separator-sign)\n423 (git-gutter+-view-for-unchanged))\n424 (when diffinfos\n425 (save-excursion\n426 (mapc 'git-gutter+-view-diff-info diffinfos)))\n427 (git-gutter+-show-gutter diffinfos))\n\n429 (defsubst git-gutter+-reset-window-margin-p ()\n430 (or git-gutter+-hide-gutter\n431 (not global-git-gutter+-mode)))\n\n433 (defun git-gutter+-clear-diff-infos ()\n434 (when (git-gutter+-reset-window-margin-p)\n435 (git-gutter+-set-window-margin 0))\n436 (remove-overlays (point-min) (point-max) 'git-gutter+ t))\n\n438 (defun git-gutter+-process-diff (curfile)\n439 (destructuring-bind\n440 (diff-header diffinfos) (git-gutter+-diff curfile)\n441 (setq git-gutter+-diff-header diff-header\n442 git-gutter+-diffinfos diffinfos)\n443 (save-restriction\n444 (widen)\n445 (funcall git-gutter+-view-diff-function diffinfos))))\n\n447 (defun git-gutter+-search-near-diff-index (diffinfos is-reverse)\n448 (loop with current-line = (line-number-at-pos)\n449 with cmp-fn = (if is-reverse '> '<)\n450 for diffinfo in (if is-reverse (reverse diffinfos) diffinfos)\n451 for index = 0 then (1+ index)\n452 for start-line = (plist-get diffinfo :start-line)\n453 when (funcall cmp-fn current-line start-line)\n454 return (if is-reverse\n455 (1- (- (length diffinfos) index))\n456 index)))\n\n458 (defun git-gutter+-diffinfo-at-point ()\n459 (save-restriction\n460 (widen)\n461 (loop with current-line = (line-number-at-pos)\n462 for diffinfo in git-gutter+-diffinfos\n463 for start = (plist-get diffinfo :start-line)\n464 for end = (or (plist-get diffinfo :end-line) (1+ start))\n465 when (and (>= current-line start) (<= current-line end))\n466 return diffinfo)))\n\n468 (defun git-gutter+-collect-deleted-line (str)\n469 (with-temp-buffer\n470 (insert str)\n471 (goto-char (point-min))\n472 (loop while (re-search-forward \"^-\\\\(.*?\\\\)$\" nil t)\n473 collect (match-string 1) into deleted-lines\n474 finally return deleted-lines)))\n\n476 (defun git-gutter+-delete-added-lines (start-line end-line)\n477 (forward-line (1- start-line))\n478 (let ((start-point (point)))\n479 (forward-line (1+ (- end-line start-line)))\n480 (delete-region start-point (point))))\n\n482 (defun git-gutter+-insert-deleted-lines (content)\n483 (dolist (line (git-gutter+-collect-deleted-line content))\n484 (insert (concat line \"\\n\"))))\n\n486 (defun git-gutter+-do-revert-hunk (diffinfo)\n487 (save-excursion\n488 (save-restriction\n489 (widen)\n490 (goto-char (point-min))\n491 (let ((start-line (plist-get diffinfo :start-line))\n492 (end-line (plist-get diffinfo :end-line))\n493 (content (plist-get diffinfo :content)))\n494 (case (plist-get diffinfo :type)\n495 (added (git-gutter+-delete-added-lines start-line end-line))\n496 (deleted (forward-line (1- start-line))\n497 (git-gutter+-insert-deleted-lines content))\n498 (modified (git-gutter+-delete-added-lines start-line end-line)\n499 (git-gutter+-insert-deleted-lines content)))))))\n\n501 (defun git-gutter+-revert-hunks ()\n502 \"Revert hunk at point. If region is active, revert all hunks within the region.\"\n503 (interactive)\n504 (let* ((diffinfos (git-gutter+-selected-diffinfos))\n505 (one-diffinfo-p (= 1 (length diffinfos))))\n506 (save-window-excursion\n507 (if one-diffinfo-p (git-gutter+-show-hunk (car diffinfos)))\n508 (when (and diffinfos\n509 (yes-or-no-p (if one-diffinfo-p\n510 \"Revert hunk?\"\n511 (format \"Revert %d hunks?\" (length diffinfos)))))\n512 ;; Revert diffinfos in reverse so that earlier hunks don't invalidate the\n513 ;; line number information of the later hunks.\n514 (dolist (diffinfo (nreverse diffinfos))\n515 (git-gutter+-do-revert-hunk diffinfo))\n516 (save-buffer))\n517 (if one-diffinfo-p\n518 (git-gutter+-awhen (get-buffer git-gutter+-popup-buffer)\n519 (kill-buffer it))))))\n\n521 (defun git-gutter+-show-hunk (&optional diffinfo)\n522 \"Show hunk at point in another window\"\n523 (interactive)\n524 (git-gutter+-awhen (or diffinfo\n525 (git-gutter+-diffinfo-at-point))\n526 (save-selected-window\n527 (with-current-buffer (get-buffer-create git-gutter+-popup-buffer)\n528 (setq buffer-read-only nil)\n529 (erase-buffer)\n530 (insert (plist-get it :content))\n531 (insert \"\\n\")\n532 (goto-char (point-min))\n533 (diff-mode)\n534 (view-mode)\n535 (pop-to-buffer (current-buffer))))))\n\n537 (defun git-gutter+-next-hunk (arg)\n538 \"Move to next diff hunk\"\n539 (interactive \"p\")\n540 (if (not git-gutter+-diffinfos)\n541 (message \"No changes in buffer\")\n542 (save-restriction\n543 (widen)\n544 (let* ((is-reverse (< arg 0))\n545 (diffinfos git-gutter+-diffinfos)\n546 (len (length diffinfos))\n547 (index (git-gutter+-search-near-diff-index diffinfos is-reverse))\n548 (real-index (if index\n549 (let ((next (if is-reverse (1+ index) (1- index))))\n550 (mod (+ arg next) len))\n551 (if is-reverse (1- (length diffinfos)) 0)))\n552 (diffinfo (nth real-index diffinfos)))\n553 (goto-char (point-min))\n554 (forward-line (1- (plist-get diffinfo :start-line)))\n555 (when (buffer-live-p (get-buffer git-gutter+-popup-buffer))\n556 (save-window-excursion\n557 (git-gutter+-show-hunk)))))))\n\n559 (defun git-gutter+-previous-hunk (arg)\n560 \"Move to previous diff hunk\"\n561 (interactive \"p\")\n562 (git-gutter+-next-hunk (- arg)))\n\n564 (defun git-gutter+-remote-default-directory (dir file)\n565 (let* ((vec (tramp-dissect-file-name file))\n566 (method (aref vec 0))\n567 (user (aref vec 1))\n568 (host (aref vec 2)))\n569 (format \"/%s:%s%s:%s\" method (if user (concat user \"@\") \"\") host dir)))\n\n571 (defun git-gutter+-remote-file-path (dir file)\n572 (let ((file (aref (tramp-dissect-file-name file) 3)))\n573 (replace-regexp-in-string (concat \"\\\\`\" dir) \"\" file)))\n\n575 (defun git-gutter+-local-file-path (file)\n576 (if (eq system-type 'windows-nt)\n577 ;; Cygwin can't handle Windows absolute paths\n578 (file-relative-name file default-directory)\n579 file))\n\n581 (defun git-gutter+-refresh ()\n582 (git-gutter+-clear)\n583 (let ((file (buffer-file-name)))\n584 (when (and file (file-exists-p file))\n585 (if (file-remote-p file)\n586 (let* ((repo-root (git-gutter+-root-directory file))\n587 (default-directory (git-gutter+-remote-default-directory repo-root file)))\n588 (git-gutter+-process-diff (git-gutter+-remote-file-path repo-root file)))\n589 (git-gutter+-process-diff (git-gutter+-local-file-path file))))))\n\n591 (defun git-gutter+-clear ()\n592 (save-restriction\n593 (widen)\n594 (funcall git-gutter+-clear-function))\n595 (setq git-gutter+-diffinfos nil))\n\n\n598 ;;; Staging\n\n600 (defun git-gutter+-stage-hunks ()\n601 \"Stage hunk at point. If region is active, stage all hunk lines within the region.\"\n602 (interactive)\n603 (let* ((line-range (if (use-region-p)\n604 (cons (line-number-at-pos (region-beginning))\n605 (line-number-at-pos (region-end)))))\n606 (diffinfos (git-gutter+-selected-diffinfos line-range)))\n607 (when diffinfos\n608 (let ((error-msg (git-gutter+-stage-diffinfos diffinfos line-range)))\n609 (if error-msg\n610 (message \"Error staging hunks:\\n%s\" error-msg))\n611 (git-gutter+-refresh)))))\n\n613 (defun git-gutter+-selected-diffinfos (&optional line-range)\n614 (unless line-range\n615 (setq line-range (if (use-region-p)\n616 (cons (line-number-at-pos (region-beginning))\n617 (line-number-at-pos (region-end))))))\n618 (if line-range\n619 (git-gutter+-diffinfos-between-lines line-range)\n620 (git-gutter+-awhen (git-gutter+-diffinfo-at-point)\n621 (list it))))\n\n623 (defsubst git-gutter+-diffinfo-between-lines-p (diffinfo start-line end-line)\n624 (let ((diff-start (plist-get diffinfo :start-line))\n625 (diff-end (plist-get diffinfo :end-line)))\n626 (and (<= start-line diff-end)\n627 (<= diff-start end-line))))\n\n629 (defun git-gutter+-diffinfos-between-lines (line-range)\n630 (save-restriction\n631 (widen)\n632 (let ((start-line (car line-range))\n633 (end-line (cdr line-range)))\n634 (delq nil\n635 (mapcar (lambda (diffinfo)\n636 (if (git-gutter+-diffinfo-between-lines-p\n637 diffinfo start-line end-line)\n638 diffinfo))\n639 git-gutter+-diffinfos)))))\n\n641 (defun git-gutter+-stage-diffinfos (diffinfos line-range)\n642 (let ((header git-gutter+-diff-header))\n643 (with-temp-buffer\n644 (insert header)\n645 ;; Insert hunks in reverse so that earlier hunks don't invalidate the line\n646 ;; number information of the later hunks.\n647 (dolist (diffinfo (nreverse diffinfos))\n648 (git-gutter+-insert-diffinfo diffinfo line-range)\n649 (goto-char (point-max)))\n650 (git-gutter+-call-git-on-current-buffer\n651 '(\"apply\" \"--unidiff-zero\" \"--cached\" \"-\")))))\n\n653 (defun git-gutter+-insert-diffinfo (diffinfo line-range)\n654 (let ((content (plist-get diffinfo :content))\n655 (type (plist-get diffinfo :type)))\n656 (if (not line-range)\n657 (git-gutter+-insert-hunk content type)\n658 (let ((diff-start-line (plist-get diffinfo :start-line))\n659 (diff-end-line (plist-get diffinfo :end-line))\n660 (start-line (car line-range))\n661 (end-line (cdr line-range)))\n662 (git-gutter+-insert-hunk content type\n663 (1+ (- start-line diff-start-line))\n664 (1+ (- end-line diff-start-line)))))))\n\n666 (defun git-gutter+-call-git-on-current-buffer (args)\n667 \"Sends the current buffer contents to Git and replaces them with Git's output.\n\n669 RETURNS nil if Git ran successfully. Returns an error description otherwise.\"\n670 (unless (zerop (apply #'call-process-region (point-min) (point-max)\n671 git-gutter+-git-executable t t nil args))\n672 (buffer-string)))\n\n674 (defsubst git-gutter+-read-hunk-header (hunk)\n675 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n676 (string-match git-gutter+-hunk-header-regex hunk)\n677 (list (string-to-number (match-string 1 hunk))\n678 (string-to-number (or (match-string 2 hunk) \"1\"))\n679 (string-to-number (match-string 3 hunk))\n680 (string-to-number (or (match-string 4 hunk) \"1\"))))\n\n682 (defun git-gutter+-insert-hunk (hunk type &optional start end)\n683 \"If START and END are provided, only insert addition (+) lines between\n684 START and END (inclusive). START and END are both line numbers starting with 1.\"\n685 (destructuring-bind\n686 (del-line del-len add-line add-len) (git-gutter+-read-hunk-header hunk)\n687 (let* ((start (max 1 (or start 1)))\n688 (end (min add-len (or end add-len)))\n689 (insert-all-p (or (eq type :deleted)\n690 (and (= start 1) (= end add-len))))\n691 (num-lines-selected (if insert-all-p\n692 add-len\n693 (1+ (- end start)))))\n694 ;; When the user selected the last lines of a hunk with type `modified' (but\n695 ;; not the complete hunk), then don't insert any deletion (-) lines from that\n696 ;; hunk.\n697 (if (and (eq type 'modified)\n698 (> start 1) (= end add-len))\n699 (setq type 'modified-trailing))\n\n701 (save-excursion\n702 (insert hunk \"\\n\"))\n\n704 (git-gutter+-delete-hunk-header)\n\n706 (if (not insert-all-p)\n707 (git-gutter+-modify-hunk type num-lines-selected del-len start))\n\n709 (let ((hunk-header (git-gutter+-make-hunk-header type num-lines-selected\n710 del-line del-len add-line)))\n711 (insert hunk-header \"\\n\")))))\n\n713 (defun git-gutter+-delete-hunk-header ()\n714 (let ((hunk-start (point)))\n715 (forward-line 1)\n716 (delete-region hunk-start (point))))\n\n718 (defun git-gutter+-modify-hunk (type num-lines-selected del-len start)\n719 \"Remove all addition (+) lines from hunk that aren't selected.\n720 If TYPE is not `modified', also remove all deletion (-) lines.\"\n721 (let ((first-line-selected (+ del-len (1- start)))\n722 selected-lines)\n723 (save-excursion\n724 (forward-line first-line-selected)\n725 (let ((selection-start (point)))\n726 (forward-line num-lines-selected)\n727 (setq selected-lines (buffer-substring selection-start (point)))))\n728 (save-excursion\n729 (if (eq type 'modified) (forward-line del-len)) ; skip over deletion (-) lines\n730 (delete-region (point) (point-max))\n731 (insert selected-lines))))\n\n733 (defun git-gutter+-make-hunk-header (type num-lines-selected del-line del-len add-line)\n734 (let ((add-len num-lines-selected))\n735 (case type\n736 (added (setq add-line (1+ del-line)))\n737 (modified-trailing (setq add-line (+ del-line del-len)\n738 del-line (1- add-line)\n739 del-len 0))\n740 (t (setq add-line del-line)))\n741 (format \"@@ -%d,%d +%d,%d @@\"\n742 del-line del-len\n743 add-line add-len)))\n\n\n746 ;;; Committing\n747 ;; This section draws heavily from old Magit source code.\n\n749 (defvar git-gutter+-pre-commit-window-config nil)\n750 (defvar git-gutter+-commit-origin-buffer nil\n751 \"Buffer that started the commit\")\n\n753 (defconst git-gutter+-commit-buffer-name \"*Commit Message*\")\n754 (defconst git-gutter+-staged-changes-buffer-name \"*Staged Changes*\")\n\n756 ;;;###autoload\n757 (defun git-gutter+-commit ()\n758 \"Commit staged changes. If nothing is staged, ask to stage the current buffer.\"\n759 (interactive)\n\n761 (when (and (not (git-gutter+-anything-staged-p))\n762 git-gutter+-diffinfos\n763 (y-or-n-p \"Nothing staged. Stage current buffer? \"))\n764 (git-gutter+-stage-whole-buffer))\n\n766 (let ((file (buffer-file-name))\n767 (dir default-directory))\n768 (git-gutter+-save-window-config-if-needed)\n769 (setq git-gutter+-commit-origin-buffer (current-buffer))\n770 (git-gutter+-open-commit-edit-buffer dir)\n771 (git-gutter+-show-staged-changes file dir)))\n\n773 (defun git-gutter+-stage-and-commit ()\n774 (interactive)\n775 (git-gutter+-stage-hunks)\n776 (git-gutter+-commit))\n\n778 (defun git-gutter+-save-window-config-if-needed ()\n779 ;; Only save the window config if the temporary buffers that get popped-up by\n780 ;; git-gutter+ are not already visible.\n781 ;; In this way, `git-gutter+-commit' can be called twice in a row without\n782 ;; losing the original window config.\n783 (when (not (and git-gutter+-pre-commit-window-config\n784 (get-buffer-window git-gutter+-commit-buffer-name)\n785 (get-buffer-window git-gutter+-staged-changes-buffer-name)))\n786 (setq git-gutter+-pre-commit-window-config (current-window-configuration))))\n\n788 (defun git-gutter+-open-commit-edit-buffer (dir)\n789 \"Opens a buffer for composing the commit message\"\n790 (pop-to-buffer (get-buffer-create git-gutter+-commit-buffer-name))\n791 (setq default-directory dir)\n792 (git-gutter+-commit-mode)\n793 (message \"Type C-c C-c to commit (C-c C-k to cancel).\"))\n\n795 (defsubst git-gutter+-pop-to-staged-changes-buffer ()\n796 (let* ((buf (get-buffer-create git-gutter+-staged-changes-buffer-name))\n797 (window (get-buffer-window buf)))\n798 (if window\n799 ;; Buffer is already visible\n800 (select-window window)\n801 (if (<= (length (window-list)) 2)\n802 (split-window))\n803 (pop-to-buffer buf))))\n\n805 (defun git-gutter+-show-staged-changes (file dir)\n806 (save-selected-window\n807 (git-gutter+-pop-to-staged-changes-buffer)\n808 (setq buffer-read-only nil)\n809 (erase-buffer)\n810 (let ((default-directory dir))\n811 (git-gutter+-call-git '(\"diff\" \"--staged\") file))\n812 (goto-char (point-min))\n813 (diff-mode)\n814 (view-mode)))\n\n816 (defsubst git-gutter+-abort-commit-when-no-changes (allow-empty amend)\n817 (unless (or amend\n818 allow-empty\n819 (git-gutter+-anything-staged-p))\n820 (error\n821 \"Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?\"\n822 (key-description (car (where-is-internal\n823 'git-gutter+-commit-toggle-amending)))\n824 (key-description (car (where-is-internal\n825 'git-gutter+-commit-toggle-allow-empty))))))\n\n827 (defsubst git-gutter+-buffer-is-whitespace ()\n828 (save-excursion\n829 (goto-char (point-min))\n830 (looking-at-p \"[ \\t\\n]*\\\\'\")))\n\n832 (defun git-gutter+-publish-commit ()\n833 \"Publish commit\"\n834 (interactive)\n835 (let* ((fields (git-gutter+-commit-get-fields))\n836 (amend (equal \"yes\" (git-gutter+-commit-get-field 'amend fields)))\n837 (allow-empty (equal \"yes\" (git-gutter+-commit-get-field 'allow-empty fields)))\n838 (author (git-gutter+-commit-get-field 'author fields))\n839 (date (git-gutter+-commit-get-field 'date fields)))\n\n841 (git-gutter+-abort-commit-when-no-changes allow-empty amend)\n\n843 (git-gutter+-push-to-comment-ring (buffer-string))\n\n845 (git-gutter+-commit-set-fields nil) ; Delete message header\n\n847 (when (git-gutter+-buffer-is-whitespace)\n848 (erase-buffer)\n849 (insert \"(Empty description)\"))\n\n851 (let ((error-msg (git-gutter+-call-git-on-current-buffer\n852 (append '(\"--no-pager\" \"commit\" \"-F\" \"-\")\n853 (if amend '(\"--amend\"))\n854 (if allow-empty '(\"--allow-empty\"))\n855 (if author (list (concat \"--author=\" author)))\n856 (if date (list (concat \"--date=\" date)))))))\n857 (if error-msg\n858 (progn\n859 (message \"Commit error:\\n%s\" error-msg)\n860 (erase-buffer)\n861 (insert (ring-ref log-edit-comment-ring 0))) ; Reinsert commit message\n862 (message \"Commit successful.\")\n863 (git-gutter+-close-commit-edit-buffer)\n864 (git-gutter+-update-vc-modeline)))))\n\n866 (defun git-gutter+-close-commit-edit-buffer ()\n867 \"Abort edits and discard commit message being composed.\"\n868 (interactive)\n869 (kill-buffer)\n870 (set-window-configuration git-gutter+-pre-commit-window-config))\n\n872 (defun git-gutter+-update-vc-modeline ()\n873 (when (buffer-live-p git-gutter+-commit-origin-buffer)\n874 (with-current-buffer git-gutter+-commit-origin-buffer\n875 ;; Updating the modeline has no effect if the buffer still has\n876 ;; changes - it will remain in the 'modified' state. So skip it then.\n877 (unless git-gutter+-diffinfos\n878 (ignore-errors (vc-find-file-hook))))))\n\n880 (defun git-gutter+-stage-whole-buffer ()\n881 (save-excursion\n882 (mark-whole-buffer)\n883 (git-gutter+-stage-hunks)))\n\n885 (defun git-gutter+-anything-staged-p ()\n886 \"Return t if the current repo has staged changes\"\n887 (not (zerop (git-gutter+-call-git '(\"diff\" \"--quiet\" \"--cached\")))))\n\n889 (defun git-gutter+-commit-toggle-amending ()\n890 \"Toggle whether this will be an amendment to the previous commit.\n891 \\(i.e., whether commit is run via 'git commit --amend')\"\n892 (interactive)\n893 ;; Remove the newline that 'git-commit-mode' adds to a new commit\n894 ;; message buffer by default. This prevents an ugly visual\n895 ;; gap between the commit message header and the previous commit\n896 ;; message.\n897 (when (git-gutter+-buffer-is-whitespace)\n898 (erase-buffer))\n\n900 (let ((amend-was-already-set (git-gutter+-commit-get-field 'amend)))\n901 (git-gutter+-commit-toggle-field 'amend t)\n902 (unless amend-was-already-set\n903 ;; Insert previous commit message\n904 (goto-char (point-max))\n905 (unless (zerop (current-column))\n906 (insert \"\\n\"))\n907 (insert (git-gutter+-get-last-commit-msg)\n908 \"\\n\"))))\n\n910 (defun git-gutter+-commit-toggle-allow-empty ()\n911 \"Toggle whether this commit is allowed to be empty.\n912 \\(i.e., whether commit is run via 'git commit --allow-empty')\"\n913 (interactive)\n914 (git-gutter+-commit-toggle-field 'allow-empty t))\n\n916 (defun git-gutter+-format-author (author email)\n917 (format \"%s <%s>\" author email))\n\n919 (defun git-gutter+-commit-toggle-author ()\n920 \"Toggle whether this commit should have a user-defined author.\"\n921 (interactive)\n922 (git-gutter+-commit-toggle-input\n923 'author (git-gutter+-format-author\n924 (or (git-gutter+-get-cfg \"user\" \"name\") \"Author Name\")\n925 (or (git-gutter+-get-cfg \"user\" \"email\") \"author@email\"))))\n\n927 (defun git-gutter+-commit-toggle-date ()\n928 \"Toggle whether this commit should have a user-defined date.\"\n929 (interactive)\n930 (git-gutter+-commit-toggle-input 'date\n931 ;; ISO 8601\n932 (format-time-string \"%Y-%m-%dT%T%z\" (current-time))))\n\n934 (defun git-gutter+-push-to-comment-ring (comment)\n935 (when (or (ring-empty-p log-edit-comment-ring)\n936 (not (equal comment (ring-ref log-edit-comment-ring 0))))\n937 (ring-insert log-edit-comment-ring comment)))\n\n939 (defun git-gutter+-get-last-commit-msg ()\n940 (git-gutter+-git-output '(\"log\" \"--max-count=1\" \"--pretty=format:%s%n%n%b\" \"HEAD\")))\n\n942 (defun git-gutter+-get-cfg (&rest keys)\n943 (git-gutter+-git-output (list \"config\" (mapconcat 'identity keys \".\"))))\n\n945 (defun git-gutter+-git-output (args)\n946 (with-temp-buffer\n947 (git-gutter+-call-git args)\n948 ;; Delete trailing newlines\n949 (goto-char (point-min))\n950 (if (re-search-forward \"\\n+\\\\'\" nil t)\n951 (replace-match \"\"))\n952 (buffer-string)))\n\n\n955 ;;; Commit message header\n\n957 (defconst git-gutter+-commit-header-end \"-- End of commit options header --\\n\")\n\n959 (defun git-gutter+-commit-get-field (name &optional fields)\n960 (cdr (assq name (or fields (git-gutter+-commit-get-fields)))))\n\n962 (defun git-gutter+-commit-set-field (name value)\n963 (let* ((fields (git-gutter+-commit-get-fields))\n964 (cell (assq name fields)))\n965 (cond (cell\n966 (if value\n967 (rplacd cell value)\n968 (setq fields (delq cell fields))))\n969 (t\n970 (if value\n971 (setq fields (append fields (list (cons name value)))))))\n972 (git-gutter+-commit-set-fields fields)))\n\n974 (defun git-gutter+-commit-toggle-field (name default)\n975 \"Toggle the commit header field named NAME.\n976 If it's currently unset, set it to DEFAULT (t or nil).\"\n977 (let* ((fields (git-gutter+-commit-get-fields))\n978 (cell (assq name fields)))\n979 (if cell\n980 (rplacd cell (if (equal (cdr cell) \"yes\") \"no\" \"yes\"))\n981 (setq fields (acons name (if default \"yes\" \"no\") fields)))\n982 (git-gutter+-commit-set-fields fields)))\n\n984 (defun git-gutter+-commit-toggle-input (name default)\n985 \"Toggle the commit header input named NAME.\n986 If it's currently unset, set it to DEFAULT (a string). If it is\n987 set remove it.\"\n988 (let* ((fields (git-gutter+-commit-get-fields))\n989 (cell (assq name fields)))\n990 (if cell\n991 (setq fields (assq-delete-all name fields))\n992 (setq fields (acons name default fields)))\n993 (git-gutter+-commit-set-fields fields)))\n\n995 (defun git-gutter+-commit-get-fields ()\n996 (let (result)\n997 (goto-char (point-min))\n998 (while (looking-at \"^\\\\([A-Za-z0-9-_]+\\\\): *\\\\(.+\\\\)?$\")\n999 (let ((name (intern (downcase (match-string 1))))\n1000 (value (read (or (match-string 2) \"nil\"))))\n1001 (push (cons name value) result))\n1002 (forward-line))\n1003 (if (looking-at (regexp-quote git-gutter+-commit-header-end))\n1004 (nreverse result))))\n\n1006 (defun git-gutter+-commit-set-fields (fields)\n1007 (goto-char (point-min))\n1008 ;; Delete commit header\n1009 (if (search-forward-regexp (format \"^\\\\(?:[A-Za-z0-9-_]+:.*\\n\\\\)*%s\"\n1010 (regexp-quote git-gutter+-commit-header-end))\n1011 nil t)\n1012 (delete-region (match-beginning 0) (match-end 0)))\n1013 (goto-char (point-min))\n1014 (when fields\n1015 (dolist (field fields)\n1016 (insert (capitalize (symbol-name (car field))) \": \"\n1017 (prin1-to-string (cdr field)) \"\\n\"))\n1018 (insert git-gutter+-commit-header-end)))\n\n\n1021 ;;; git-gutter+-commit-mode\n1022 ;; Like git-commit-mode, but adds keybindings to git-gutter+ commands and\n1023 ;; highlighting support for the commit message header.\n\n1025 (define-derived-mode git-gutter+-commit-mode git-commit-mode \"Git-Gutter-Commit\"\n1026 (setq font-lock-defaults (list (git-gutter+-commit-font-lock-keywords) t)))\n\n1028 (setq git-gutter+-commit-mode-map\n1029 (let ((map (copy-keymap git-commit-mode-map)))\n1030 (define-key map (kbd \"C-c C-c\") 'git-gutter+-publish-commit)\n1031 (define-key map (kbd \"C-c C-k\") 'git-gutter+-close-commit-edit-buffer)\n1032 (define-key map (kbd \"C-c C-a\") 'git-gutter+-commit-toggle-amending)\n1033 (define-key map (kbd \"C-c C-e\") 'git-gutter+-commit-toggle-allow-empty)\n1034 (define-key map (kbd \"C-c C-u\") 'git-gutter+-commit-toggle-author)\n1035 (define-key map (kbd \"C-c C-d\") 'git-gutter+-commit-toggle-date)\n1036 (define-key map (kbd \"C-c C-b\") 'git-commit-ack)\n1037 (define-key map (kbd \"M-p\") 'log-edit-previous-comment)\n1038 (define-key map (kbd \"M-n\") 'log-edit-next-comment)\n1039 map))\n\n1041 (defface git-gutter+-commit-header-face\n1042 '((t :inherit font-lock-comment-face))\n1043 \"Highlights the commit message header\"\n1044 :group 'git-gutter+-faces)\n\n1046 (defconst git-gutter+-commit-header-regex\n1047 (concat \"\\\\(?:.\\\\|\\n\\\\)*?\" (regexp-quote git-gutter+-commit-header-end)))\n\n1049 (defconst git-gutter+-skip-commit-header-regex\n1050 (concat \"\\\\`\\\\(?:\" git-gutter+-commit-header-regex \"\\\\)?\"))\n\n1052 ;; Modify git-commit-summary-regexp to ignore the commit header\n1053 (defadvice git-commit-summary-regexp\n1054 (after ignore-git-gutter+-commit-header activate compile)\n1055 (if (eq major-mode 'git-gutter+-commit-mode)\n1056 (setq ad-return-value\n1057 (concat git-gutter+-skip-commit-header-regex\n1058 (substring ; Remove leading \"\\\\`\"\n1059 ad-return-value 2)))))\n\n1061 (defun git-gutter+-commit-font-lock-keywords ()\n1062 \"Like `git-commit-mode-font-lock-keywords' but with commit header highlighting\"\n1063 `((,(concat \"\\\\`\" git-gutter+-commit-header-regex) . 'git-gutter+-commit-header-face)\n1064 ,@(git-commit-mode-font-lock-keywords)))\n\n\n1067 ;;; Magit synchronization\n1068 ;; Force Magit to refresh git-gutter+ when updating the VC mode line.\n\n1070 (defvar git-gutter+-orig-vc-find-file-hook)\n\n1072 (defvar git-gutter+-vc-find-file-hook-with-refresh\n1073 (lambda ()\n1074 (funcall git-gutter+-orig-vc-find-file-hook)\n1075 (if git-gutter+-mode (git-gutter+-refresh))))\n\n1077 (defadvice magit-update-vc-modeline (around refresh-git-gutter+ compile activate)\n1078 ;; `magit-update-vc-modeline' calls `vc-find-file-hook' (a function!) on each\n1079 ;; buffer in the repo. Temporarily rebind it to `vc-find-file-hook-with-refresh',\n1080 ;; which calls git-gutter+-refresh after updating the VC mode line.\n1081 ;;\n1082 ;; Using `flet' would have been much simpler, but it's deprecated since 24.3.\n1083 (setq git-gutter+-orig-vc-find-file-hook (symbol-function 'vc-find-file-hook))\n1084 (fset 'vc-find-file-hook git-gutter+-vc-find-file-hook-with-refresh)\n1085 (unwind-protect\n1086 ad-do-it\n1087 (fset 'vc-find-file-hook git-gutter+-orig-vc-find-file-hook)))\n\n1089 (provide 'git-gutter+)\n\n1091 ;;; git-gutter+.el ends here\n")) (setq helm-swoop-cache t))) (candidates-in-buffer) (get-line . buffer-substring-no-properties) (keymap keymap (C-M-left . backward-sexp) (C-M-right . forward-sexp) (27 keymap (105 . helm-multi-swoop-all-from-helm-swoop)) (3 keymap (5 . helm-swoop-edit)) keymap (C-M-left . paren-backward-sexp) (C-M-right . paren-forward-sexp) (94 . helm-swoop-caret-match) (menu-bar keymap (help-menu keymap (describe keymap ...))) (help keymap (109 . helm-help)) (f1 keymap (109 . helm-help)) (8 keymap (109 . helm-help) (104 . undefined) (8 . undefined) (4 . helm-debug-output)) (20 . helm-toggle-resplit-and-swap-windows) (C-tab . undefined) (triple-mouse-3 . ignore) (double-mouse-3 . ignore) (mouse-3 . ignore) (drag-mouse-3 . ignore) (down-mouse-3 . ignore) (triple-mouse-2 . ignore) (double-mouse-2 . ignore) (mouse-2 . ignore) (drag-mouse-2 . ignore) (down-mouse-2 . ignore) (triple-mouse-1 . ignore) (double-mouse-1 . ignore) (mouse-1 . ignore) (drag-mouse-1 . ignore) (down-mouse-1 . ignore) (67108897 . helm-toggle-suspend-update) (3 keymap (21 . helm-force-update) (6 . helm-follow-mode) (11 . helm-kill-selection-and-quit) (25 . helm-yank-selection) (4 . helm-delete-current-selection) (45 . helm-swap-windows)) (67108987 . helm-enlarge-window) (67108989 . helm-narrow-window) (19 . undefined) (18 . undefined) (23 . helm-yank-text-at-point) (24 keymap (2 . helm-resume-list-buffers-after-quit) (98 . helm-resume-previous-session-after-quit) (6 . helm-quit-and-find-file)) (11 . helm-delete-minibuffer-contents) (67108896 . helm-toggle-visible-mark) (0 . helm-toggle-visible-mark) (C-M-up . helm-scroll-other-window-down) (C-M-down . helm-scroll-other-window) (M-prior . helm-scroll-other-window-down) (M-next . helm-scroll-other-window) (12 . helm-recenter-top-bottom-other-window) (15 . helm-next-source) (10 . helm-select-3rd-action) (5 . helm-select-2nd-action-or-end-of-line) ...) (header-line . "[C-c C-e] Edit mode, [M-i] apply all buffers") (action lambda ($line) (helm-swoop--goto-line (when (string-match "^[0-9]+" $line) (string-to-number ...))) (when (re-search-forward (mapconcat ... ... "\\|") nil t) (goto-char (match-beginning 0))) (helm-swoop--recenter)) (migemo)) :buffer "*Helm Swoop*" :input #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) :prompt "Swoop: " :preselect "^368 " :candidate-number-limit 19999) helm helm-argument-keys apply mapcar make-byte-code 257 "\301\300\"\207" vconcat vector [plist-get] 4 "\n\n(fn KEY)"] 9 "\n\n(fn)"]() | |
helm-let-internal(((helm-candidate-number-limit . 19999)) #[0 "\303\301\304\305\306\307\310\311\300!\312\"\313\314%\n\"\"\207" [(:sources ((name . "git-gutter+.el") (init lambda nil (unless helm-swoop-cache (with-current-buffer (helm-candidate-buffer ...) (insert "1 ;;; git-gutter+.el --- Manage Git hunks straight from the buffer\n\n3 ;; Copyright (C) 2013 by Syohei YOSHIDA and contributors\n\n5 ;; Author: Syohei YOSHIDA <syohex@gmail.com> and contributors\n6 ;; URL: https://github.com/nonsequitur/git-gutter-plus\n7 ;; Version: 0.1\n\n9 ;; This program is free software; you can redistribute it and/or modify\n10 ;; it under the terms of the GNU General Public License as published by\n11 ;; the Free Software Foundation, either version 3 of the License, or\n12 ;; (at your option) any later version.\n\n14 ;; This program is distributed in the hope that it will be useful,\n15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n17 ;; GNU General Public License for more details.\n\n19 ;; You should have received a copy of the GNU General Public License\n20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n22 ;; Package-Requires: ((git-commit-mode \"0.14\"))\n\n24 ;;; Commentary:\n25 ;;\n26 ;; View, stage and revert Git changes straight from the buffer.\n\n28 ;;; Code:\n\n30 (eval-when-compile\n31 (require 'cl))\n\n33 (require 'tramp)\n34 (require 'log-edit)\n35 (require 'git-commit-mode)\n\n37 (defgroup git-gutter+ nil\n38 \"Manage Git hunks straight from the buffer\"\n39 :prefix \"git-gutter+-\"\n40 :group 'vc)\n\n42 (defcustom git-gutter+-window-width nil\n43 \"Character width of the gutter margin. Set this variable if the automatically\n44 calculated width looks wrong. (This can happen with some special characters.)\"\n45 :type 'integer\n46 :group 'git-gutter+)\n\n48 (defcustom git-gutter+-git-executable \"git\"\n49 \"The path of the Git executable.\"\n50 :type 'string\n51 :group 'git-gutter+)\n\n53 (defcustom git-gutter+-diff-options nil\n54 \"List of strings containing extra arguments to 'git diff'\"\n55 :type 'list\n56 :group 'git-gutter+)\n\n58 (defcustom git-gutter+-separator-sign nil\n59 \"Separator sign\"\n60 :type 'string\n61 :group 'git-gutter+)\n\n63 (defcustom git-gutter+-modified-sign \"=\"\n64 \"Modified sign\"\n65 :type 'string\n66 :group 'git-gutter+)\n\n68 (defcustom git-gutter+-added-sign \"+\"\n69 \"Added sign\"\n70 :type 'string\n71 :group 'git-gutter+)\n\n73 (defcustom git-gutter+-deleted-sign \"-\"\n74 \"Deleted sign\"\n75 :type 'string\n76 :group 'git-gutter+)\n\n78 (defcustom git-gutter+-unchanged-sign nil\n79 \"Unchanged sign\"\n80 :type 'string\n81 :group 'git-gutter+)\n\n83 (defcustom git-gutter+-hide-gutter nil\n84 \"Hide gutter if there are no changes\"\n85 :type 'boolean\n86 :group 'git-gutter+)\n\n88 (defcustom git-gutter+-lighter \" GitGutter\"\n89 \"Minor mode lighter in mode-line\"\n90 :type 'string\n91 :group 'git-gutter+)\n\n93 (defface git-gutter+-separator\n94 '((t (:foreground \"cyan\" :weight bold)))\n95 \"Face of the separator\"\n96 :group 'git-gutter+)\n\n98 (defface git-gutter+-modified\n99 '((t (:foreground \"magenta\" :weight bold)))\n100 \"Face for modified lines\"\n101 :group 'git-gutter+)\n\n103 (defface git-gutter+-added\n104 '((t (:foreground \"green\" :weight bold)))\n105 \"Face for added lines\"\n106 :group 'git-gutter+)\n\n108 (defface git-gutter+-deleted\n109 '((t (:foreground \"red\" :weight bold)))\n110 \"Face for deleted lines\"\n111 :group 'git-gutter+)\n\n113 (defface git-gutter+-unchanged\n114 '((t (:background \"yellow\")))\n115 \"Face for unchanged lines\"\n116 :group 'git-gutter+)\n\n118 (defcustom git-gutter+-disabled-modes nil\n119 \"A list of modes for which `global-git-gutter+-mode' should be disabled.\"\n120 :type '(repeat symbol)\n121 :group 'git-gutter+)\n\n123 (defvar git-gutter+-mode-map\n124 (make-sparse-keymap))\n\n126 (defvar git-gutter+-view-diff-function nil\n127 \"Function to call for displaying diffs\")\n\n129 (defvar git-gutter+-clear-function nil\n130 \"Function to call for clearing the diff display\")\n\n132 (defvar git-gutter+-window-config-change-function nil\n133 \"Function to call when the buffer's local window configuration has changed\")\n\n135 (defvar git-gutter+-diffinfos nil)\n136 (defvar git-gutter+-diff-header nil)\n137 (make-variable-buffer-local 'git-gutter+-diffinfos)\n138 (make-variable-buffer-local 'git-gutter+-diff-header)\n\n140 (defvar git-gutter+-popup-buffer \"*git-gutter+-diff*\")\n141 (defvar git-gutter+-buffers-to-reenable nil)\n\n143 (defconst git-gutter+-hunk-header-regex\n144 ;; The same as diff-hunk-header-re-unified\n145 \"^@@ -\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? \\\\+\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? @@\")\n\n147 (defalias 'git-gutter+-popup-hunk 'git-gutter+-show-hunk)\n148 (defalias 'git-gutter+-revert-hunk 'git-gutter+-revert-hunks)\n\n150 (defmacro git-gutter+-awhen (test &rest body)\n151 \"Anaphoric when.\"\n152 (declare (indent 1))\n153 `(let ((it ,test))\n154 (when it ,@body)))\n\n156 (defun git-gutter+-enable-default-display-mode ()\n157 (setq git-gutter+-view-diff-function 'git-gutter+-view-diff-infos\n158 git-gutter+-clear-function 'git-gutter+-clear-diff-infos\n159 git-gutter+-window-config-change-function 'git-gutter+-show-gutter))\n\n161 (unless git-gutter+-view-diff-function\n162 (git-gutter+-enable-default-display-mode))\n\n164 (defun git-gutter+-call-git (args &optional file)\n165 (if (and file (file-remote-p file))\n166 (apply #'process-file git-gutter+-git-executable nil t nil args)\n167 (apply #'call-process git-gutter+-git-executable nil t nil args)))\n\n169 (defun git-gutter+-in-git-repository-p (file)\n170 (with-temp-buffer\n171 (let ((args '(\"rev-parse\" \"--is-inside-work-tree\")))\n172 (when (zerop (git-gutter+-call-git args file))\n173 (goto-char (point-min))\n174 (string= \"true\" (buffer-substring-no-properties\n175 (point) (line-end-position)))))))\n\n177 (defun git-gutter+-root-directory (file)\n178 (with-temp-buffer\n179 (let* ((args '(\"rev-parse\" \"--show-toplevel\"))\n180 (ret (git-gutter+-call-git args file)))\n181 (when (zerop ret)\n182 (goto-char (point-min))\n183 (let ((root (buffer-substring-no-properties (point) (line-end-position))))\n184 (unless (string= root \"\")\n185 (file-name-as-directory root)))))))\n\n187 (defsubst git-gutter+-diff-args (file)\n188 (delq nil (list \"--no-pager\" \"diff\" \"--no-color\" \"--no-ext-diff\" \"-U0\"\n189 git-gutter+-diff-options file)))\n\n191 (defun git-gutter+-diff (curfile)\n192 (let ((args (git-gutter+-diff-args curfile))\n193 (file (buffer-file-name))) ;; for tramp\n194 (with-temp-buffer\n195 (when (zerop (git-gutter+-call-git args file))\n196 (goto-char (point-min))\n197 (let ((diff-header (git-gutter+-get-diff-header))\n198 (diffinfos (git-gutter+-get-diffinfos)))\n199 (list diff-header diffinfos))))))\n\n201 (defun git-gutter+-get-diff-header ()\n202 (save-excursion\n203 (if (re-search-forward git-gutter+-hunk-header-regex nil t)\n204 (buffer-substring (point-min) (match-beginning 0)))))\n\n206 (defsubst git-gutter+-make-diffinfo (type content start end)\n207 (list :type type :content content :start-line start :end-line end))\n\n209 (defun git-gutter+-get-diffinfos ()\n210 (loop while (re-search-forward git-gutter+-hunk-header-regex nil t)\n211 ;; Hunk header format:\n212 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n213 for del-len = (string-to-number (or (match-string 2) \"1\"))\n214 for add-line = (string-to-number (match-string 3))\n215 for add-len = (string-to-number (or (match-string 4) \"1\"))\n216 for type = (cond ((zerop del-len) 'added)\n217 ((zerop add-len) 'deleted)\n218 (t 'modified))\n219 for start-line = (if (eq type 'deleted)\n220 (1+ add-line)\n221 add-line)\n222 for end-line = (if (eq type 'deleted)\n223 start-line\n224 (1- (+ add-line add-len)))\n225 for content = (git-gutter+-diff-content)\n226 collect\n227 (git-gutter+-make-diffinfo type content start-line end-line)))\n\n229 (defun git-gutter+-diff-content ()\n230 (save-excursion\n231 (goto-char (line-beginning-position)) ; Move to beginning of hunk header\n232 (let ((hunk-start (point)))\n233 ;; Move to end of hunk\n234 (forward-line 1)\n235 (if (re-search-forward \"^@@\" nil t)\n236 (backward-char 3) ;; exclude \"\\n@@\"\n237 (goto-char (1- (point-max)))) ; Skip trailing newline\n238 (buffer-substring hunk-start (point)))))\n\n240 (defun git-gutter+-line-to-pos (line)\n241 (save-excursion\n242 (goto-char (point-min))\n243 (forward-line (1- line))\n244 (point)))\n\n246 (defun git-gutter+-before-string (sign)\n247 (let* ((sep-sign git-gutter+-separator-sign)\n248 (sep (when sep-sign\n249 (propertize sep-sign 'face 'git-gutter+-separator)))\n250 (gutter-sep (concat sign sep)))\n251 (propertize \" \" 'display `((margin left-margin) ,gutter-sep))))\n\n253 (defsubst git-gutter+-select-face (type)\n254 (case type\n255 (added 'git-gutter+-added)\n256 (modified 'git-gutter+-modified)\n257 (deleted 'git-gutter+-deleted)))\n\n259 (defsubst git-gutter+-select-sign (type)\n260 (case type\n261 (added git-gutter+-added-sign)\n262 (modified git-gutter+-modified-sign)\n263 (deleted git-gutter+-deleted-sign)))\n\n265 (defun git-gutter+-propertized-sign (type)\n266 (let ((sign (git-gutter+-select-sign type))\n267 (face (git-gutter+-select-face type)))\n268 (propertize sign 'face face)))\n\n270 (defun git-gutter+-view-region (sign start-line end-line)\n271 (let ((beg (git-gutter+-line-to-pos start-line)))\n272 (goto-char beg)\n273 (while (and (<= (line-number-at-pos) end-line) (not (eobp)))\n274 (git-gutter+-view-at-pos sign (point))\n275 (forward-line 1))))\n\n277 (defun git-gutter+-view-at-pos (sign pos)\n278 (let ((ov (make-overlay pos pos)))\n279 (overlay-put ov 'before-string (git-gutter+-before-string sign))\n280 (overlay-put ov 'git-gutter+ t)))\n\n282 (defun git-gutter+-view-diff-info (diffinfo)\n283 (let* ((start-line (plist-get diffinfo :start-line))\n284 (end-line (plist-get diffinfo :end-line))\n285 (type (plist-get diffinfo :type))\n286 (sign (git-gutter+-propertized-sign type)))\n287 (case type\n288 ((modified added) (git-gutter+-view-region sign start-line end-line))\n289 (deleted (git-gutter+-view-at-pos\n290 sign (git-gutter+-line-to-pos start-line))))))\n\n292 (defun git-gutter+-sign-width (sign)\n293 (loop for s across sign\n294 sum (char-width s)))\n\n296 (defun git-gutter+-longest-sign-width ()\n297 (let ((signs (list git-gutter+-modified-sign\n298 git-gutter+-added-sign\n299 git-gutter+-deleted-sign)))\n300 (when git-gutter+-unchanged-sign\n301 (add-to-list 'signs git-gutter+-unchanged-sign))\n302 (+ (apply 'max (mapcar 'git-gutter+-sign-width signs))\n303 (git-gutter+-sign-width git-gutter+-separator-sign))))\n\n305 (defun git-gutter+-view-for-unchanged ()\n306 (save-excursion\n307 (let ((sign (if git-gutter+-unchanged-sign\n308 (propertize git-gutter+-unchanged-sign\n309 'face 'git-gutter+-unchanged)\n310 \" \")))\n311 (goto-char (point-min))\n312 (while (not (eobp))\n313 (git-gutter+-view-at-pos sign (point))\n314 (forward-line 1)))))\n\n316 (defun git-gutter+-set-window-margin (width)\n317 (let ((curwin (get-buffer-window)))\n318 (set-window-margins curwin width (cdr (window-margins curwin)))))\n\n320 (defsubst git-gutter+-file-buffer-p ()\n321 (and (buffer-file-name)\n322 default-directory\n323 (file-directory-p default-directory)))\n\n325 ;;;###autoload\n326 (define-minor-mode git-gutter+-mode\n327 \"Git-Gutter mode\"\n328 :group 'git-gutter+\n329 :init-value nil\n330 :global nil\n331 :lighter git-gutter+-lighter\n332 (if git-gutter+-mode\n333 (if (and (git-gutter+-file-buffer-p)\n334 (git-gutter+-in-git-repository-p (buffer-file-name)))\n335 (progn\n336 (git-gutter+-add-local-hooks)\n337 (git-gutter+-refresh))\n338 (if (called-interactively-p 'any)\n339 (message \"No Git repo for current buffer\"))\n340 (git-gutter+-mode -1))\n341 (git-gutter+-remove-local-hooks)\n342 (git-gutter+-clear)))\n\n344 (defun git-gutter+-add-local-hooks ()\n345 (add-hook 'after-save-hook 'git-gutter+-refresh nil t)\n346 ;; Turn off `git-gutter+-mode' while reverting to prevent any redundant calls to\n347 ;; `git-gutter+-refresh'.\n348 (add-hook 'before-revert-hook 'git-gutter+-turn-off nil t)\n349 (add-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change nil t)\n350 (if git-gutter+-window-config-change-function\n351 (add-hook 'window-configuration-change-hook\n352 git-gutter+-window-config-change-function nil t)))\n\n354 (defun git-gutter+-remove-local-hooks ()\n355 (remove-hook 'after-save-hook 'git-gutter+-refresh t)\n356 (remove-hook 'before-revert-hook 'git-gutter+-turn-off t)\n357 (remove-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change t)\n358 (if git-gutter+-window-config-change-function\n359 (remove-hook 'window-configuration-change-hook\n360 git-gutter+-window-config-change-function t)))\n\n362 (defmacro git-gutter+-in-all-buffers (&rest body)\n363 `(dolist (buf (buffer-list))\n364 (with-current-buffer buf\n365 ,@body)))\n\n367 ;; When `define-globalized-minor-mode' is used to define `global-git-gutter+-mode',\n368 ;; `git-gutter+-mode' and thus `git-gutter+-refresh' get run twice when a new file\n369 ;; is opened. (First for `fundamental-mode', then for the file-specific mode.)\n370 ;; The following definition of `global-git-gutter+-mode' avoids any redundant calls to\n371 ;; `git-gutter+-refresh'.\n\n373 ;;;###autoload\n374 (define-minor-mode global-git-gutter+-mode ()\n375 \"Global Git-Gutter mode\"\n376 :group 'git-gutter+\n377 :init-value nil\n378 :global t\n379 (if global-git-gutter+-mode\n380 (progn\n381 (add-hook 'find-file-hook 'git-gutter+-turn-on)\n382 (add-hook 'after-revert-hook 'git-gutter+-turn-on)\n383 (add-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n384 (git-gutter+-in-all-buffers (git-gutter+-turn-on)))\n385 (remove-hook 'find-file-hook 'git-gutter+-turn-on)\n386 (remove-hook 'after-revert-hook 'git-gutter+-turn-on)\n387 (remove-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n388 (git-gutter+-in-all-buffers (git-gutter+-turn-off))))\n\n390 (defun git-gutter+-turn-on ()\n391 (when (and (buffer-file-name)\n392 (not (memq major-mode git-gutter+-disabled-modes))\n393 (not git-gutter+-mode))\n394 (git-gutter+-mode t)))\n\n396 (defun git-gutter+-turn-off ()\n397 (if git-gutter+-mode (git-gutter+-mode -1)))\n\n399 (defun git-gutter+-reenable-after-major-mode-change ()\n400 (if global-git-gutter+-mode\n401 (add-to-list 'git-gutter+-buffers-to-reenable (current-buffer))))\n\n403 (defun git-gutter+-reenable-buffers ()\n404 (dolist (buf git-gutter+-buffers-to-reenable)\n405 (with-current-buffer buf\n406 (git-gutter+-turn-on)))\n407 (setq git-gutter+-buffers-to-reenable nil))\n\n409 (defsubst git-gutter+-show-gutter-p (diffinfos)\n410 (if git-gutter+-hide-gutter\n411 (or diffinfos git-gutter+-unchanged-sign)\n412 (or global-git-gutter+-mode git-gutter+-unchanged-sign diffinfos)))\n\n414 (defun git-gutter+-show-gutter (&optional diffinfos)\n415 (when (git-gutter+-show-gutter-p (or diffinfos git-gutter+-diffinfos))\n416 (let ((win-width (or git-gutter+-window-width\n417 (git-gutter+-longest-sign-width))))\n418 (git-gutter+-set-window-margin win-width))))\n\n420 (defun git-gutter+-view-diff-infos (diffinfos)\n421 (when (or git-gutter+-unchanged-sign\n422 git-gutter+-separator-sign)\n423 (git-gutter+-view-for-unchanged))\n424 (when diffinfos\n425 (save-excursion\n426 (mapc 'git-gutter+-view-diff-info diffinfos)))\n427 (git-gutter+-show-gutter diffinfos))\n\n429 (defsubst git-gutter+-reset-window-margin-p ()\n430 (or git-gutter+-hide-gutter\n431 (not global-git-gutter+-mode)))\n\n433 (defun git-gutter+-clear-diff-infos ()\n434 (when (git-gutter+-reset-window-margin-p)\n435 (git-gutter+-set-window-margin 0))\n436 (remove-overlays (point-min) (point-max) 'git-gutter+ t))\n\n438 (defun git-gutter+-process-diff (curfile)\n439 (destructuring-bind\n440 (diff-header diffinfos) (git-gutter+-diff curfile)\n441 (setq git-gutter+-diff-header diff-header\n442 git-gutter+-diffinfos diffinfos)\n443 (save-restriction\n444 (widen)\n445 (funcall git-gutter+-view-diff-function diffinfos))))\n\n447 (defun git-gutter+-search-near-diff-index (diffinfos is-reverse)\n448 (loop with current-line = (line-number-at-pos)\n449 with cmp-fn = (if is-reverse '> '<)\n450 for diffinfo in (if is-reverse (reverse diffinfos) diffinfos)\n451 for index = 0 then (1+ index)\n452 for start-line = (plist-get diffinfo :start-line)\n453 when (funcall cmp-fn current-line start-line)\n454 return (if is-reverse\n455 (1- (- (length diffinfos) index))\n456 index)))\n\n458 (defun git-gutter+-diffinfo-at-point ()\n459 (save-restriction\n460 (widen)\n461 (loop with current-line = (line-number-at-pos)\n462 for diffinfo in git-gutter+-diffinfos\n463 for start = (plist-get diffinfo :start-line)\n464 for end = (or (plist-get diffinfo :end-line) (1+ start))\n465 when (and (>= current-line start) (<= current-line end))\n466 return diffinfo)))\n\n468 (defun git-gutter+-collect-deleted-line (str)\n469 (with-temp-buffer\n470 (insert str)\n471 (goto-char (point-min))\n472 (loop while (re-search-forward \"^-\\\\(.*?\\\\)$\" nil t)\n473 collect (match-string 1) into deleted-lines\n474 finally return deleted-lines)))\n\n476 (defun git-gutter+-delete-added-lines (start-line end-line)\n477 (forward-line (1- start-line))\n478 (let ((start-point (point)))\n479 (forward-line (1+ (- end-line start-line)))\n480 (delete-region start-point (point))))\n\n482 (defun git-gutter+-insert-deleted-lines (content)\n483 (dolist (line (git-gutter+-collect-deleted-line content))\n484 (insert (concat line \"\\n\"))))\n\n486 (defun git-gutter+-do-revert-hunk (diffinfo)\n487 (save-excursion\n488 (save-restriction\n489 (widen)\n490 (goto-char (point-min))\n491 (let ((start-line (plist-get diffinfo :start-line))\n492 (end-line (plist-get diffinfo :end-line))\n493 (content (plist-get diffinfo :content)))\n494 (case (plist-get diffinfo :type)\n495 (added (git-gutter+-delete-added-lines start-line end-line))\n496 (deleted (forward-line (1- start-line))\n497 (git-gutter+-insert-deleted-lines content))\n498 (modified (git-gutter+-delete-added-lines start-line end-line)\n499 (git-gutter+-insert-deleted-lines content)))))))\n\n501 (defun git-gutter+-revert-hunks ()\n502 \"Revert hunk at point. If region is active, revert all hunks within the region.\"\n503 (interactive)\n504 (let* ((diffinfos (git-gutter+-selected-diffinfos))\n505 (one-diffinfo-p (= 1 (length diffinfos))))\n506 (save-window-excursion\n507 (if one-diffinfo-p (git-gutter+-show-hunk (car diffinfos)))\n508 (when (and diffinfos\n509 (yes-or-no-p (if one-diffinfo-p\n510 \"Revert hunk?\"\n511 (format \"Revert %d hunks?\" (length diffinfos)))))\n512 ;; Revert diffinfos in reverse so that earlier hunks don't invalidate the\n513 ;; line number information of the later hunks.\n514 (dolist (diffinfo (nreverse diffinfos))\n515 (git-gutter+-do-revert-hunk diffinfo))\n516 (save-buffer))\n517 (if one-diffinfo-p\n518 (git-gutter+-awhen (get-buffer git-gutter+-popup-buffer)\n519 (kill-buffer it))))))\n\n521 (defun git-gutter+-show-hunk (&optional diffinfo)\n522 \"Show hunk at point in another window\"\n523 (interactive)\n524 (git-gutter+-awhen (or diffinfo\n525 (git-gutter+-diffinfo-at-point))\n526 (save-selected-window\n527 (with-current-buffer (get-buffer-create git-gutter+-popup-buffer)\n528 (setq buffer-read-only nil)\n529 (erase-buffer)\n530 (insert (plist-get it :content))\n531 (insert \"\\n\")\n532 (goto-char (point-min))\n533 (diff-mode)\n534 (view-mode)\n535 (pop-to-buffer (current-buffer))))))\n\n537 (defun git-gutter+-next-hunk (arg)\n538 \"Move to next diff hunk\"\n539 (interactive \"p\")\n540 (if (not git-gutter+-diffinfos)\n541 (message \"No changes in buffer\")\n542 (save-restriction\n543 (widen)\n544 (let* ((is-reverse (< arg 0))\n545 (diffinfos git-gutter+-diffinfos)\n546 (len (length diffinfos))\n547 (index (git-gutter+-search-near-diff-index diffinfos is-reverse))\n548 (real-index (if index\n549 (let ((next (if is-reverse (1+ index) (1- index))))\n550 (mod (+ arg next) len))\n551 (if is-reverse (1- (length diffinfos)) 0)))\n552 (diffinfo (nth real-index diffinfos)))\n553 (goto-char (point-min))\n554 (forward-line (1- (plist-get diffinfo :start-line)))\n555 (when (buffer-live-p (get-buffer git-gutter+-popup-buffer))\n556 (save-window-excursion\n557 (git-gutter+-show-hunk)))))))\n\n559 (defun git-gutter+-previous-hunk (arg)\n560 \"Move to previous diff hunk\"\n561 (interactive \"p\")\n562 (git-gutter+-next-hunk (- arg)))\n\n564 (defun git-gutter+-remote-default-directory (dir file)\n565 (let* ((vec (tramp-dissect-file-name file))\n566 (method (aref vec 0))\n567 (user (aref vec 1))\n568 (host (aref vec 2)))\n569 (format \"/%s:%s%s:%s\" method (if user (concat user \"@\") \"\") host dir)))\n\n571 (defun git-gutter+-remote-file-path (dir file)\n572 (let ((file (aref (tramp-dissect-file-name file) 3)))\n573 (replace-regexp-in-string (concat \"\\\\`\" dir) \"\" file)))\n\n575 (defun git-gutter+-local-file-path (file)\n576 (if (eq system-type 'windows-nt)\n577 ;; Cygwin can't handle Windows absolute paths\n578 (file-relative-name file default-directory)\n579 file))\n\n581 (defun git-gutter+-refresh ()\n582 (git-gutter+-clear)\n583 (let ((file (buffer-file-name)))\n584 (when (and file (file-exists-p file))\n585 (if (file-remote-p file)\n586 (let* ((repo-root (git-gutter+-root-directory file))\n587 (default-directory (git-gutter+-remote-default-directory repo-root file)))\n588 (git-gutter+-process-diff (git-gutter+-remote-file-path repo-root file)))\n589 (git-gutter+-process-diff (git-gutter+-local-file-path file))))))\n\n591 (defun git-gutter+-clear ()\n592 (save-restriction\n593 (widen)\n594 (funcall git-gutter+-clear-function))\n595 (setq git-gutter+-diffinfos nil))\n\n\n598 ;;; Staging\n\n600 (defun git-gutter+-stage-hunks ()\n601 \"Stage hunk at point. If region is active, stage all hunk lines within the region.\"\n602 (interactive)\n603 (let* ((line-range (if (use-region-p)\n604 (cons (line-number-at-pos (region-beginning))\n605 (line-number-at-pos (region-end)))))\n606 (diffinfos (git-gutter+-selected-diffinfos line-range)))\n607 (when diffinfos\n608 (let ((error-msg (git-gutter+-stage-diffinfos diffinfos line-range)))\n609 (if error-msg\n610 (message \"Error staging hunks:\\n%s\" error-msg))\n611 (git-gutter+-refresh)))))\n\n613 (defun git-gutter+-selected-diffinfos (&optional line-range)\n614 (unless line-range\n615 (setq line-range (if (use-region-p)\n616 (cons (line-number-at-pos (region-beginning))\n617 (line-number-at-pos (region-end))))))\n618 (if line-range\n619 (git-gutter+-diffinfos-between-lines line-range)\n620 (git-gutter+-awhen (git-gutter+-diffinfo-at-point)\n621 (list it))))\n\n623 (defsubst git-gutter+-diffinfo-between-lines-p (diffinfo start-line end-line)\n624 (let ((diff-start (plist-get diffinfo :start-line))\n625 (diff-end (plist-get diffinfo :end-line)))\n626 (and (<= start-line diff-end)\n627 (<= diff-start end-line))))\n\n629 (defun git-gutter+-diffinfos-between-lines (line-range)\n630 (save-restriction\n631 (widen)\n632 (let ((start-line (car line-range))\n633 (end-line (cdr line-range)))\n634 (delq nil\n635 (mapcar (lambda (diffinfo)\n636 (if (git-gutter+-diffinfo-between-lines-p\n637 diffinfo start-line end-line)\n638 diffinfo))\n639 git-gutter+-diffinfos)))))\n\n641 (defun git-gutter+-stage-diffinfos (diffinfos line-range)\n642 (let ((header git-gutter+-diff-header))\n643 (with-temp-buffer\n644 (insert header)\n645 ;; Insert hunks in reverse so that earlier hunks don't invalidate the line\n646 ;; number information of the later hunks.\n647 (dolist (diffinfo (nreverse diffinfos))\n648 (git-gutter+-insert-diffinfo diffinfo line-range)\n649 (goto-char (point-max)))\n650 (git-gutter+-call-git-on-current-buffer\n651 '(\"apply\" \"--unidiff-zero\" \"--cached\" \"-\")))))\n\n653 (defun git-gutter+-insert-diffinfo (diffinfo line-range)\n654 (let ((content (plist-get diffinfo :content))\n655 (type (plist-get diffinfo :type)))\n656 (if (not line-range)\n657 (git-gutter+-insert-hunk content type)\n658 (let ((diff-start-line (plist-get diffinfo :start-line))\n659 (diff-end-line (plist-get diffinfo :end-line))\n660 (start-line (car line-range))\n661 (end-line (cdr line-range)))\n662 (git-gutter+-insert-hunk content type\n663 (1+ (- start-line diff-start-line))\n664 (1+ (- end-line diff-start-line)))))))\n\n666 (defun git-gutter+-call-git-on-current-buffer (args)\n667 \"Sends the current buffer contents to Git and replaces them with Git's output.\n\n669 RETURNS nil if Git ran successfully. Returns an error description otherwise.\"\n670 (unless (zerop (apply #'call-process-region (point-min) (point-max)\n671 git-gutter+-git-executable t t nil args))\n672 (buffer-string)))\n\n674 (defsubst git-gutter+-read-hunk-header (hunk)\n675 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n676 (string-match git-gutter+-hunk-header-regex hunk)\n677 (list (string-to-number (match-string 1 hunk))\n678 (string-to-number (or (match-string 2 hunk) \"1\"))\n679 (string-to-number (match-string 3 hunk))\n680 (string-to-number (or (match-string 4 hunk) \"1\"))))\n\n682 (defun git-gutter+-insert-hunk (hunk type &optional start end)\n683 \"If START and END are provided, only insert addition (+) lines between\n684 START and END (inclusive). START and END are both line numbers starting with 1.\"\n685 (destructuring-bind\n686 (del-line del-len add-line add-len) (git-gutter+-read-hunk-header hunk)\n687 (let* ((start (max 1 (or start 1)))\n688 (end (min add-len (or end add-len)))\n689 (insert-all-p (or (eq type :deleted)\n690 (and (= start 1) (= end add-len))))\n691 (num-lines-selected (if insert-all-p\n692 add-len\n693 (1+ (- end start)))))\n694 ;; When the user selected the last lines of a hunk with type `modified' (but\n695 ;; not the complete hunk), then don't insert any deletion (-) lines from that\n696 ;; hunk.\n697 (if (and (eq type 'modified)\n698 (> start 1) (= end add-len))\n699 (setq type 'modified-trailing))\n\n701 (save-excursion\n702 (insert hunk \"\\n\"))\n\n704 (git-gutter+-delete-hunk-header)\n\n706 (if (not insert-all-p)\n707 (git-gutter+-modify-hunk type num-lines-selected del-len start))\n\n709 (let ((hunk-header (git-gutter+-make-hunk-header type num-lines-selected\n710 del-line del-len add-line)))\n711 (insert hunk-header \"\\n\")))))\n\n713 (defun git-gutter+-delete-hunk-header ()\n714 (let ((hunk-start (point)))\n715 (forward-line 1)\n716 (delete-region hunk-start (point))))\n\n718 (defun git-gutter+-modify-hunk (type num-lines-selected del-len start)\n719 \"Remove all addition (+) lines from hunk that aren't selected.\n720 If TYPE is not `modified', also remove all deletion (-) lines.\"\n721 (let ((first-line-selected (+ del-len (1- start)))\n722 selected-lines)\n723 (save-excursion\n724 (forward-line first-line-selected)\n725 (let ((selection-start (point)))\n726 (forward-line num-lines-selected)\n727 (setq selected-lines (buffer-substring selection-start (point)))))\n728 (save-excursion\n729 (if (eq type 'modified) (forward-line del-len)) ; skip over deletion (-) lines\n730 (delete-region (point) (point-max))\n731 (insert selected-lines))))\n\n733 (defun git-gutter+-make-hunk-header (type num-lines-selected del-line del-len add-line)\n734 (let ((add-len num-lines-selected))\n735 (case type\n736 (added (setq add-line (1+ del-line)))\n737 (modified-trailing (setq add-line (+ del-line del-len)\n738 del-line (1- add-line)\n739 del-len 0))\n740 (t (setq add-line del-line)))\n741 (format \"@@ -%d,%d +%d,%d @@\"\n742 del-line del-len\n743 add-line add-len)))\n\n\n746 ;;; Committing\n747 ;; This section draws heavily from old Magit source code.\n\n749 (defvar git-gutter+-pre-commit-window-config nil)\n750 (defvar git-gutter+-commit-origin-buffer nil\n751 \"Buffer that started the commit\")\n\n753 (defconst git-gutter+-commit-buffer-name \"*Commit Message*\")\n754 (defconst git-gutter+-staged-changes-buffer-name \"*Staged Changes*\")\n\n756 ;;;###autoload\n757 (defun git-gutter+-commit ()\n758 \"Commit staged changes. If nothing is staged, ask to stage the current buffer.\"\n759 (interactive)\n\n761 (when (and (not (git-gutter+-anything-staged-p))\n762 git-gutter+-diffinfos\n763 (y-or-n-p \"Nothing staged. Stage current buffer? \"))\n764 (git-gutter+-stage-whole-buffer))\n\n766 (let ((file (buffer-file-name))\n767 (dir default-directory))\n768 (git-gutter+-save-window-config-if-needed)\n769 (setq git-gutter+-commit-origin-buffer (current-buffer))\n770 (git-gutter+-open-commit-edit-buffer dir)\n771 (git-gutter+-show-staged-changes file dir)))\n\n773 (defun git-gutter+-stage-and-commit ()\n774 (interactive)\n775 (git-gutter+-stage-hunks)\n776 (git-gutter+-commit))\n\n778 (defun git-gutter+-save-window-config-if-needed ()\n779 ;; Only save the window config if the temporary buffers that get popped-up by\n780 ;; git-gutter+ are not already visible.\n781 ;; In this way, `git-gutter+-commit' can be called twice in a row without\n782 ;; losing the original window config.\n783 (when (not (and git-gutter+-pre-commit-window-config\n784 (get-buffer-window git-gutter+-commit-buffer-name)\n785 (get-buffer-window git-gutter+-staged-changes-buffer-name)))\n786 (setq git-gutter+-pre-commit-window-config (current-window-configuration))))\n\n788 (defun git-gutter+-open-commit-edit-buffer (dir)\n789 \"Opens a buffer for composing the commit message\"\n790 (pop-to-buffer (get-buffer-create git-gutter+-commit-buffer-name))\n791 (setq default-directory dir)\n792 (git-gutter+-commit-mode)\n793 (message \"Type C-c C-c to commit (C-c C-k to cancel).\"))\n\n795 (defsubst git-gutter+-pop-to-staged-changes-buffer ()\n796 (let* ((buf (get-buffer-create git-gutter+-staged-changes-buffer-name))\n797 (window (get-buffer-window buf)))\n798 (if window\n799 ;; Buffer is already visible\n800 (select-window window)\n801 (if (<= (length (window-list)) 2)\n802 (split-window))\n803 (pop-to-buffer buf))))\n\n805 (defun git-gutter+-show-staged-changes (file dir)\n806 (save-selected-window\n807 (git-gutter+-pop-to-staged-changes-buffer)\n808 (setq buffer-read-only nil)\n809 (erase-buffer)\n810 (let ((default-directory dir))\n811 (git-gutter+-call-git '(\"diff\" \"--staged\") file))\n812 (goto-char (point-min))\n813 (diff-mode)\n814 (view-mode)))\n\n816 (defsubst git-gutter+-abort-commit-when-no-changes (allow-empty amend)\n817 (unless (or amend\n818 allow-empty\n819 (git-gutter+-anything-staged-p))\n820 (error\n821 \"Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?\"\n822 (key-description (car (where-is-internal\n823 'git-gutter+-commit-toggle-amending)))\n824 (key-description (car (where-is-internal\n825 'git-gutter+-commit-toggle-allow-empty))))))\n\n827 (defsubst git-gutter+-buffer-is-whitespace ()\n828 (save-excursion\n829 (goto-char (point-min))\n830 (looking-at-p \"[ \\t\\n]*\\\\'\")))\n\n832 (defun git-gutter+-publish-commit ()\n833 \"Publish commit\"\n834 (interactive)\n835 (let* ((fields (git-gutter+-commit-get-fields))\n836 (amend (equal \"yes\" (git-gutter+-commit-get-field 'amend fields)))\n837 (allow-empty (equal \"yes\" (git-gutter+-commit-get-field 'allow-empty fields)))\n838 (author (git-gutter+-commit-get-field 'author fields))\n839 (date (git-gutter+-commit-get-field 'date fields)))\n\n841 (git-gutter+-abort-commit-when-no-changes allow-empty amend)\n\n843 (git-gutter+-push-to-comment-ring (buffer-string))\n\n845 (git-gutter+-commit-set-fields nil) ; Delete message header\n\n847 (when (git-gutter+-buffer-is-whitespace)\n848 (erase-buffer)\n849 (insert \"(Empty description)\"))\n\n851 (let ((error-msg (git-gutter+-call-git-on-current-buffer\n852 (append '(\"--no-pager\" \"commit\" \"-F\" \"-\")\n853 (if amend '(\"--amend\"))\n854 (if allow-empty '(\"--allow-empty\"))\n855 (if author (list (concat \"--author=\" author)))\n856 (if date (list (concat \"--date=\" date)))))))\n857 (if error-msg\n858 (progn\n859 (message \"Commit error:\\n%s\" error-msg)\n860 (erase-buffer)\n861 (insert (ring-ref log-edit-comment-ring 0))) ; Reinsert commit message\n862 (message \"Commit successful.\")\n863 (git-gutter+-close-commit-edit-buffer)\n864 (git-gutter+-update-vc-modeline)))))\n\n866 (defun git-gutter+-close-commit-edit-buffer ()\n867 \"Abort edits and discard commit message being composed.\"\n868 (interactive)\n869 (kill-buffer)\n870 (set-window-configuration git-gutter+-pre-commit-window-config))\n\n872 (defun git-gutter+-update-vc-modeline ()\n873 (when (buffer-live-p git-gutter+-commit-origin-buffer)\n874 (with-current-buffer git-gutter+-commit-origin-buffer\n875 ;; Updating the modeline has no effect if the buffer still has\n876 ;; changes - it will remain in the 'modified' state. So skip it then.\n877 (unless git-gutter+-diffinfos\n878 (ignore-errors (vc-find-file-hook))))))\n\n880 (defun git-gutter+-stage-whole-buffer ()\n881 (save-excursion\n882 (mark-whole-buffer)\n883 (git-gutter+-stage-hunks)))\n\n885 (defun git-gutter+-anything-staged-p ()\n886 \"Return t if the current repo has staged changes\"\n887 (not (zerop (git-gutter+-call-git '(\"diff\" \"--quiet\" \"--cached\")))))\n\n889 (defun git-gutter+-commit-toggle-amending ()\n890 \"Toggle whether this will be an amendment to the previous commit.\n891 \\(i.e., whether commit is run via 'git commit --amend')\"\n892 (interactive)\n893 ;; Remove the newline that 'git-commit-mode' adds to a new commit\n894 ;; message buffer by default. This prevents an ugly visual\n895 ;; gap between the commit message header and the previous commit\n896 ;; message.\n897 (when (git-gutter+-buffer-is-whitespace)\n898 (erase-buffer))\n\n900 (let ((amend-was-already-set (git-gutter+-commit-get-field 'amend)))\n901 (git-gutter+-commit-toggle-field 'amend t)\n902 (unless amend-was-already-set\n903 ;; Insert previous commit message\n904 (goto-char (point-max))\n905 (unless (zerop (current-column))\n906 (insert \"\\n\"))\n907 (insert (git-gutter+-get-last-commit-msg)\n908 \"\\n\"))))\n\n910 (defun git-gutter+-commit-toggle-allow-empty ()\n911 \"Toggle whether this commit is allowed to be empty.\n912 \\(i.e., whether commit is run via 'git commit --allow-empty')\"\n913 (interactive)\n914 (git-gutter+-commit-toggle-field 'allow-empty t))\n\n916 (defun git-gutter+-format-author (author email)\n917 (format \"%s <%s>\" author email))\n\n919 (defun git-gutter+-commit-toggle-author ()\n920 \"Toggle whether this commit should have a user-defined author.\"\n921 (interactive)\n922 (git-gutter+-commit-toggle-input\n923 'author (git-gutter+-format-author\n924 (or (git-gutter+-get-cfg \"user\" \"name\") \"Author Name\")\n925 (or (git-gutter+-get-cfg \"user\" \"email\") \"author@email\"))))\n\n927 (defun git-gutter+-commit-toggle-date ()\n928 \"Toggle whether this commit should have a user-defined date.\"\n929 (interactive)\n930 (git-gutter+-commit-toggle-input 'date\n931 ;; ISO 8601\n932 (format-time-string \"%Y-%m-%dT%T%z\" (current-time))))\n\n934 (defun git-gutter+-push-to-comment-ring (comment)\n935 (when (or (ring-empty-p log-edit-comment-ring)\n936 (not (equal comment (ring-ref log-edit-comment-ring 0))))\n937 (ring-insert log-edit-comment-ring comment)))\n\n939 (defun git-gutter+-get-last-commit-msg ()\n940 (git-gutter+-git-output '(\"log\" \"--max-count=1\" \"--pretty=format:%s%n%n%b\" \"HEAD\")))\n\n942 (defun git-gutter+-get-cfg (&rest keys)\n943 (git-gutter+-git-output (list \"config\" (mapconcat 'identity keys \".\"))))\n\n945 (defun git-gutter+-git-output (args)\n946 (with-temp-buffer\n947 (git-gutter+-call-git args)\n948 ;; Delete trailing newlines\n949 (goto-char (point-min))\n950 (if (re-search-forward \"\\n+\\\\'\" nil t)\n951 (replace-match \"\"))\n952 (buffer-string)))\n\n\n955 ;;; Commit message header\n\n957 (defconst git-gutter+-commit-header-end \"-- End of commit options header --\\n\")\n\n959 (defun git-gutter+-commit-get-field (name &optional fields)\n960 (cdr (assq name (or fields (git-gutter+-commit-get-fields)))))\n\n962 (defun git-gutter+-commit-set-field (name value)\n963 (let* ((fields (git-gutter+-commit-get-fields))\n964 (cell (assq name fields)))\n965 (cond (cell\n966 (if value\n967 (rplacd cell value)\n968 (setq fields (delq cell fields))))\n969 (t\n970 (if value\n971 (setq fields (append fields (list (cons name value)))))))\n972 (git-gutter+-commit-set-fields fields)))\n\n974 (defun git-gutter+-commit-toggle-field (name default)\n975 \"Toggle the commit header field named NAME.\n976 If it's currently unset, set it to DEFAULT (t or nil).\"\n977 (let* ((fields (git-gutter+-commit-get-fields))\n978 (cell (assq name fields)))\n979 (if cell\n980 (rplacd cell (if (equal (cdr cell) \"yes\") \"no\" \"yes\"))\n981 (setq fields (acons name (if default \"yes\" \"no\") fields)))\n982 (git-gutter+-commit-set-fields fields)))\n\n984 (defun git-gutter+-commit-toggle-input (name default)\n985 \"Toggle the commit header input named NAME.\n986 If it's currently unset, set it to DEFAULT (a string). If it is\n987 set remove it.\"\n988 (let* ((fields (git-gutter+-commit-get-fields))\n989 (cell (assq name fields)))\n990 (if cell\n991 (setq fields (assq-delete-all name fields))\n992 (setq fields (acons name default fields)))\n993 (git-gutter+-commit-set-fields fields)))\n\n995 (defun git-gutter+-commit-get-fields ()\n996 (let (result)\n997 (goto-char (point-min))\n998 (while (looking-at \"^\\\\([A-Za-z0-9-_]+\\\\): *\\\\(.+\\\\)?$\")\n999 (let ((name (intern (downcase (match-string 1))))\n1000 (value (read (or (match-string 2) \"nil\"))))\n1001 (push (cons name value) result))\n1002 (forward-line))\n1003 (if (looking-at (regexp-quote git-gutter+-commit-header-end))\n1004 (nreverse result))))\n\n1006 (defun git-gutter+-commit-set-fields (fields)\n1007 (goto-char (point-min))\n1008 ;; Delete commit header\n1009 (if (search-forward-regexp (format \"^\\\\(?:[A-Za-z0-9-_]+:.*\\n\\\\)*%s\"\n1010 (regexp-quote git-gutter+-commit-header-end))\n1011 nil t)\n1012 (delete-region (match-beginning 0) (match-end 0)))\n1013 (goto-char (point-min))\n1014 (when fields\n1015 (dolist (field fields)\n1016 (insert (capitalize (symbol-name (car field))) \": \"\n1017 (prin1-to-string (cdr field)) \"\\n\"))\n1018 (insert git-gutter+-commit-header-end)))\n\n\n1021 ;;; git-gutter+-commit-mode\n1022 ;; Like git-commit-mode, but adds keybindings to git-gutter+ commands and\n1023 ;; highlighting support for the commit message header.\n\n1025 (define-derived-mode git-gutter+-commit-mode git-commit-mode \"Git-Gutter-Commit\"\n1026 (setq font-lock-defaults (list (git-gutter+-commit-font-lock-keywords) t)))\n\n1028 (setq git-gutter+-commit-mode-map\n1029 (let ((map (copy-keymap git-commit-mode-map)))\n1030 (define-key map (kbd \"C-c C-c\") 'git-gutter+-publish-commit)\n1031 (define-key map (kbd \"C-c C-k\") 'git-gutter+-close-commit-edit-buffer)\n1032 (define-key map (kbd \"C-c C-a\") 'git-gutter+-commit-toggle-amending)\n1033 (define-key map (kbd \"C-c C-e\") 'git-gutter+-commit-toggle-allow-empty)\n1034 (define-key map (kbd \"C-c C-u\") 'git-gutter+-commit-toggle-author)\n1035 (define-key map (kbd \"C-c C-d\") 'git-gutter+-commit-toggle-date)\n1036 (define-key map (kbd \"C-c C-b\") 'git-commit-ack)\n1037 (define-key map (kbd \"M-p\") 'log-edit-previous-comment)\n1038 (define-key map (kbd \"M-n\") 'log-edit-next-comment)\n1039 map))\n\n1041 (defface git-gutter+-commit-header-face\n1042 '((t :inherit font-lock-comment-face))\n1043 \"Highlights the commit message header\"\n1044 :group 'git-gutter+-faces)\n\n1046 (defconst git-gutter+-commit-header-regex\n1047 (concat \"\\\\(?:.\\\\|\\n\\\\)*?\" (regexp-quote git-gutter+-commit-header-end)))\n\n1049 (defconst git-gutter+-skip-commit-header-regex\n1050 (concat \"\\\\`\\\\(?:\" git-gutter+-commit-header-regex \"\\\\)?\"))\n\n1052 ;; Modify git-commit-summary-regexp to ignore the commit header\n1053 (defadvice git-commit-summary-regexp\n1054 (after ignore-git-gutter+-commit-header activate compile)\n1055 (if (eq major-mode 'git-gutter+-commit-mode)\n1056 (setq ad-return-value\n1057 (concat git-gutter+-skip-commit-header-regex\n1058 (substring ; Remove leading \"\\\\`\"\n1059 ad-return-value 2)))))\n\n1061 (defun git-gutter+-commit-font-lock-keywords ()\n1062 \"Like `git-commit-mode-font-lock-keywords' but with commit header highlighting\"\n1063 `((,(concat \"\\\\`\" git-gutter+-commit-header-regex) . 'git-gutter+-commit-header-face)\n1064 ,@(git-commit-mode-font-lock-keywords)))\n\n\n1067 ;;; Magit synchronization\n1068 ;; Force Magit to refresh git-gutter+ when updating the VC mode line.\n\n1070 (defvar git-gutter+-orig-vc-find-file-hook)\n\n1072 (defvar git-gutter+-vc-find-file-hook-with-refresh\n1073 (lambda ()\n1074 (funcall git-gutter+-orig-vc-find-file-hook)\n1075 (if git-gutter+-mode (git-gutter+-refresh))))\n\n1077 (defadvice magit-update-vc-modeline (around refresh-git-gutter+ compile activate)\n1078 ;; `magit-update-vc-modeline' calls `vc-find-file-hook' (a function!) on each\n1079 ;; buffer in the repo. Temporarily rebind it to `vc-find-file-hook-with-refresh',\n1080 ;; which calls git-gutter+-refresh after updating the VC mode line.\n1081 ;;\n1082 ;; Using `flet' would have been much simpler, but it's deprecated since 24.3.\n1083 (setq git-gutter+-orig-vc-find-file-hook (symbol-function 'vc-find-file-hook))\n1084 (fset 'vc-find-file-hook git-gutter+-vc-find-file-hook-with-refresh)\n1085 (unwind-protect\n1086 ad-do-it\n1087 (fset 'vc-find-file-hook git-gutter+-orig-vc-find-file-hook)))\n\n1089 (provide 'git-gutter+)\n\n1091 ;;; git-gutter+.el ends here\n")) (setq helm-swoop-cache t))) (candidates-in-buffer) (get-line . buffer-substring-no-properties) (keymap keymap (C-M-left . backward-sexp) (C-M-right . forward-sexp) (27 keymap (105 . helm-multi-swoop-all-from-helm-swoop)) (3 keymap (5 . helm-swoop-edit)) keymap (C-M-left . paren-backward-sexp) (C-M-right . paren-forward-sexp) (94 . helm-swoop-caret-match) (menu-bar keymap (help-menu keymap (describe keymap ...))) (help keymap (109 . helm-help)) (f1 keymap (109 . helm-help)) (8 keymap (109 . helm-help) (104 . undefined) (8 . undefined) (4 . helm-debug-output)) (20 . helm-toggle-resplit-and-swap-windows) (C-tab . undefined) (triple-mouse-3 . ignore) (double-mouse-3 . ignore) (mouse-3 . ignore) (drag-mouse-3 . ignore) (down-mouse-3 . ignore) (triple-mouse-2 . ignore) (double-mouse-2 . ignore) (mouse-2 . ignore) (drag-mouse-2 . ignore) (down-mouse-2 . ignore) (triple-mouse-1 . ignore) (double-mouse-1 . ignore) (mouse-1 . ignore) (drag-mouse-1 . ignore) (down-mouse-1 . ignore) (67108897 . helm-toggle-suspend-update) (3 keymap (21 . helm-force-update) (6 . helm-follow-mode) (11 . helm-kill-selection-and-quit) (25 . helm-yank-selection) (4 . helm-delete-current-selection) (45 . helm-swap-windows)) (67108987 . helm-enlarge-window) (67108989 . helm-narrow-window) (19 . undefined) (18 . undefined) (23 . helm-yank-text-at-point) (24 keymap (2 . helm-resume-list-buffers-after-quit) (98 . helm-resume-previous-session-after-quit) (6 . helm-quit-and-find-file)) (11 . helm-delete-minibuffer-contents) (67108896 . helm-toggle-visible-mark) (0 . helm-toggle-visible-mark) (C-M-up . helm-scroll-other-window-down) (C-M-down . helm-scroll-other-window) (M-prior . helm-scroll-other-window-down) (M-next . helm-scroll-other-window) (12 . helm-recenter-top-bottom-other-window) (15 . helm-next-source) (10 . helm-select-3rd-action) (5 . helm-select-2nd-action-or-end-of-line) ...) (header-line . "[C-c C-e] Edit mode, [M-i] apply all buffers") (action lambda ($line) (helm-swoop--goto-line (when (string-match "^[0-9]+" $line) (string-to-number ...))) (when (re-search-forward (mapconcat ... ... "\\|") nil t) (goto-char (match-beginning 0))) (helm-swoop--recenter)) (migemo)) :buffer "*Helm Swoop*" :input #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) :prompt "Swoop: " :preselect "^368 " :candidate-number-limit 19999) helm helm-argument-keys apply mapcar make-byte-code 257 "\301\300\"\207" vconcat vector [plist-get] 4 "\n\n(fn KEY)"] 9 "\n\n(fn)"]) | |
helm(:sources ((name . "git-gutter+.el") (init lambda nil (unless helm-swoop-cache (with-current-buffer (helm-candidate-buffer (quote local)) (insert "1 ;;; git-gutter+.el --- Manage Git hunks straight from the buffer\n\n3 ;; Copyright (C) 2013 by Syohei YOSHIDA and contributors\n\n5 ;; Author: Syohei YOSHIDA <syohex@gmail.com> and contributors\n6 ;; URL: https://github.com/nonsequitur/git-gutter-plus\n7 ;; Version: 0.1\n\n9 ;; This program is free software; you can redistribute it and/or modify\n10 ;; it under the terms of the GNU General Public License as published by\n11 ;; the Free Software Foundation, either version 3 of the License, or\n12 ;; (at your option) any later version.\n\n14 ;; This program is distributed in the hope that it will be useful,\n15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n17 ;; GNU General Public License for more details.\n\n19 ;; You should have received a copy of the GNU General Public License\n20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n22 ;; Package-Requires: ((git-commit-mode \"0.14\"))\n\n24 ;;; Commentary:\n25 ;;\n26 ;; View, stage and revert Git changes straight from the buffer.\n\n28 ;;; Code:\n\n30 (eval-when-compile\n31 (require 'cl))\n\n33 (require 'tramp)\n34 (require 'log-edit)\n35 (require 'git-commit-mode)\n\n37 (defgroup git-gutter+ nil\n38 \"Manage Git hunks straight from the buffer\"\n39 :prefix \"git-gutter+-\"\n40 :group 'vc)\n\n42 (defcustom git-gutter+-window-width nil\n43 \"Character width of the gutter margin. Set this variable if the automatically\n44 calculated width looks wrong. (This can happen with some special characters.)\"\n45 :type 'integer\n46 :group 'git-gutter+)\n\n48 (defcustom git-gutter+-git-executable \"git\"\n49 \"The path of the Git executable.\"\n50 :type 'string\n51 :group 'git-gutter+)\n\n53 (defcustom git-gutter+-diff-options nil\n54 \"List of strings containing extra arguments to 'git diff'\"\n55 :type 'list\n56 :group 'git-gutter+)\n\n58 (defcustom git-gutter+-separator-sign nil\n59 \"Separator sign\"\n60 :type 'string\n61 :group 'git-gutter+)\n\n63 (defcustom git-gutter+-modified-sign \"=\"\n64 \"Modified sign\"\n65 :type 'string\n66 :group 'git-gutter+)\n\n68 (defcustom git-gutter+-added-sign \"+\"\n69 \"Added sign\"\n70 :type 'string\n71 :group 'git-gutter+)\n\n73 (defcustom git-gutter+-deleted-sign \"-\"\n74 \"Deleted sign\"\n75 :type 'string\n76 :group 'git-gutter+)\n\n78 (defcustom git-gutter+-unchanged-sign nil\n79 \"Unchanged sign\"\n80 :type 'string\n81 :group 'git-gutter+)\n\n83 (defcustom git-gutter+-hide-gutter nil\n84 \"Hide gutter if there are no changes\"\n85 :type 'boolean\n86 :group 'git-gutter+)\n\n88 (defcustom git-gutter+-lighter \" GitGutter\"\n89 \"Minor mode lighter in mode-line\"\n90 :type 'string\n91 :group 'git-gutter+)\n\n93 (defface git-gutter+-separator\n94 '((t (:foreground \"cyan\" :weight bold)))\n95 \"Face of the separator\"\n96 :group 'git-gutter+)\n\n98 (defface git-gutter+-modified\n99 '((t (:foreground \"magenta\" :weight bold)))\n100 \"Face for modified lines\"\n101 :group 'git-gutter+)\n\n103 (defface git-gutter+-added\n104 '((t (:foreground \"green\" :weight bold)))\n105 \"Face for added lines\"\n106 :group 'git-gutter+)\n\n108 (defface git-gutter+-deleted\n109 '((t (:foreground \"red\" :weight bold)))\n110 \"Face for deleted lines\"\n111 :group 'git-gutter+)\n\n113 (defface git-gutter+-unchanged\n114 '((t (:background \"yellow\")))\n115 \"Face for unchanged lines\"\n116 :group 'git-gutter+)\n\n118 (defcustom git-gutter+-disabled-modes nil\n119 \"A list of modes for which `global-git-gutter+-mode' should be disabled.\"\n120 :type '(repeat symbol)\n121 :group 'git-gutter+)\n\n123 (defvar git-gutter+-mode-map\n124 (make-sparse-keymap))\n\n126 (defvar git-gutter+-view-diff-function nil\n127 \"Function to call for displaying diffs\")\n\n129 (defvar git-gutter+-clear-function nil\n130 \"Function to call for clearing the diff display\")\n\n132 (defvar git-gutter+-window-config-change-function nil\n133 \"Function to call when the buffer's local window configuration has changed\")\n\n135 (defvar git-gutter+-diffinfos nil)\n136 (defvar git-gutter+-diff-header nil)\n137 (make-variable-buffer-local 'git-gutter+-diffinfos)\n138 (make-variable-buffer-local 'git-gutter+-diff-header)\n\n140 (defvar git-gutter+-popup-buffer \"*git-gutter+-diff*\")\n141 (defvar git-gutter+-buffers-to-reenable nil)\n\n143 (defconst git-gutter+-hunk-header-regex\n144 ;; The same as diff-hunk-header-re-unified\n145 \"^@@ -\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? \\\\+\\\\([0-9]+\\\\)\\\\(?:,\\\\([0-9]+\\\\)\\\\)? @@\")\n\n147 (defalias 'git-gutter+-popup-hunk 'git-gutter+-show-hunk)\n148 (defalias 'git-gutter+-revert-hunk 'git-gutter+-revert-hunks)\n\n150 (defmacro git-gutter+-awhen (test &rest body)\n151 \"Anaphoric when.\"\n152 (declare (indent 1))\n153 `(let ((it ,test))\n154 (when it ,@body)))\n\n156 (defun git-gutter+-enable-default-display-mode ()\n157 (setq git-gutter+-view-diff-function 'git-gutter+-view-diff-infos\n158 git-gutter+-clear-function 'git-gutter+-clear-diff-infos\n159 git-gutter+-window-config-change-function 'git-gutter+-show-gutter))\n\n161 (unless git-gutter+-view-diff-function\n162 (git-gutter+-enable-default-display-mode))\n\n164 (defun git-gutter+-call-git (args &optional file)\n165 (if (and file (file-remote-p file))\n166 (apply #'process-file git-gutter+-git-executable nil t nil args)\n167 (apply #'call-process git-gutter+-git-executable nil t nil args)))\n\n169 (defun git-gutter+-in-git-repository-p (file)\n170 (with-temp-buffer\n171 (let ((args '(\"rev-parse\" \"--is-inside-work-tree\")))\n172 (when (zerop (git-gutter+-call-git args file))\n173 (goto-char (point-min))\n174 (string= \"true\" (buffer-substring-no-properties\n175 (point) (line-end-position)))))))\n\n177 (defun git-gutter+-root-directory (file)\n178 (with-temp-buffer\n179 (let* ((args '(\"rev-parse\" \"--show-toplevel\"))\n180 (ret (git-gutter+-call-git args file)))\n181 (when (zerop ret)\n182 (goto-char (point-min))\n183 (let ((root (buffer-substring-no-properties (point) (line-end-position))))\n184 (unless (string= root \"\")\n185 (file-name-as-directory root)))))))\n\n187 (defsubst git-gutter+-diff-args (file)\n188 (delq nil (list \"--no-pager\" \"diff\" \"--no-color\" \"--no-ext-diff\" \"-U0\"\n189 git-gutter+-diff-options file)))\n\n191 (defun git-gutter+-diff (curfile)\n192 (let ((args (git-gutter+-diff-args curfile))\n193 (file (buffer-file-name))) ;; for tramp\n194 (with-temp-buffer\n195 (when (zerop (git-gutter+-call-git args file))\n196 (goto-char (point-min))\n197 (let ((diff-header (git-gutter+-get-diff-header))\n198 (diffinfos (git-gutter+-get-diffinfos)))\n199 (list diff-header diffinfos))))))\n\n201 (defun git-gutter+-get-diff-header ()\n202 (save-excursion\n203 (if (re-search-forward git-gutter+-hunk-header-regex nil t)\n204 (buffer-substring (point-min) (match-beginning 0)))))\n\n206 (defsubst git-gutter+-make-diffinfo (type content start end)\n207 (list :type type :content content :start-line start :end-line end))\n\n209 (defun git-gutter+-get-diffinfos ()\n210 (loop while (re-search-forward git-gutter+-hunk-header-regex nil t)\n211 ;; Hunk header format:\n212 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n213 for del-len = (string-to-number (or (match-string 2) \"1\"))\n214 for add-line = (string-to-number (match-string 3))\n215 for add-len = (string-to-number (or (match-string 4) \"1\"))\n216 for type = (cond ((zerop del-len) 'added)\n217 ((zerop add-len) 'deleted)\n218 (t 'modified))\n219 for start-line = (if (eq type 'deleted)\n220 (1+ add-line)\n221 add-line)\n222 for end-line = (if (eq type 'deleted)\n223 start-line\n224 (1- (+ add-line add-len)))\n225 for content = (git-gutter+-diff-content)\n226 collect\n227 (git-gutter+-make-diffinfo type content start-line end-line)))\n\n229 (defun git-gutter+-diff-content ()\n230 (save-excursion\n231 (goto-char (line-beginning-position)) ; Move to beginning of hunk header\n232 (let ((hunk-start (point)))\n233 ;; Move to end of hunk\n234 (forward-line 1)\n235 (if (re-search-forward \"^@@\" nil t)\n236 (backward-char 3) ;; exclude \"\\n@@\"\n237 (goto-char (1- (point-max)))) ; Skip trailing newline\n238 (buffer-substring hunk-start (point)))))\n\n240 (defun git-gutter+-line-to-pos (line)\n241 (save-excursion\n242 (goto-char (point-min))\n243 (forward-line (1- line))\n244 (point)))\n\n246 (defun git-gutter+-before-string (sign)\n247 (let* ((sep-sign git-gutter+-separator-sign)\n248 (sep (when sep-sign\n249 (propertize sep-sign 'face 'git-gutter+-separator)))\n250 (gutter-sep (concat sign sep)))\n251 (propertize \" \" 'display `((margin left-margin) ,gutter-sep))))\n\n253 (defsubst git-gutter+-select-face (type)\n254 (case type\n255 (added 'git-gutter+-added)\n256 (modified 'git-gutter+-modified)\n257 (deleted 'git-gutter+-deleted)))\n\n259 (defsubst git-gutter+-select-sign (type)\n260 (case type\n261 (added git-gutter+-added-sign)\n262 (modified git-gutter+-modified-sign)\n263 (deleted git-gutter+-deleted-sign)))\n\n265 (defun git-gutter+-propertized-sign (type)\n266 (let ((sign (git-gutter+-select-sign type))\n267 (face (git-gutter+-select-face type)))\n268 (propertize sign 'face face)))\n\n270 (defun git-gutter+-view-region (sign start-line end-line)\n271 (let ((beg (git-gutter+-line-to-pos start-line)))\n272 (goto-char beg)\n273 (while (and (<= (line-number-at-pos) end-line) (not (eobp)))\n274 (git-gutter+-view-at-pos sign (point))\n275 (forward-line 1))))\n\n277 (defun git-gutter+-view-at-pos (sign pos)\n278 (let ((ov (make-overlay pos pos)))\n279 (overlay-put ov 'before-string (git-gutter+-before-string sign))\n280 (overlay-put ov 'git-gutter+ t)))\n\n282 (defun git-gutter+-view-diff-info (diffinfo)\n283 (let* ((start-line (plist-get diffinfo :start-line))\n284 (end-line (plist-get diffinfo :end-line))\n285 (type (plist-get diffinfo :type))\n286 (sign (git-gutter+-propertized-sign type)))\n287 (case type\n288 ((modified added) (git-gutter+-view-region sign start-line end-line))\n289 (deleted (git-gutter+-view-at-pos\n290 sign (git-gutter+-line-to-pos start-line))))))\n\n292 (defun git-gutter+-sign-width (sign)\n293 (loop for s across sign\n294 sum (char-width s)))\n\n296 (defun git-gutter+-longest-sign-width ()\n297 (let ((signs (list git-gutter+-modified-sign\n298 git-gutter+-added-sign\n299 git-gutter+-deleted-sign)))\n300 (when git-gutter+-unchanged-sign\n301 (add-to-list 'signs git-gutter+-unchanged-sign))\n302 (+ (apply 'max (mapcar 'git-gutter+-sign-width signs))\n303 (git-gutter+-sign-width git-gutter+-separator-sign))))\n\n305 (defun git-gutter+-view-for-unchanged ()\n306 (save-excursion\n307 (let ((sign (if git-gutter+-unchanged-sign\n308 (propertize git-gutter+-unchanged-sign\n309 'face 'git-gutter+-unchanged)\n310 \" \")))\n311 (goto-char (point-min))\n312 (while (not (eobp))\n313 (git-gutter+-view-at-pos sign (point))\n314 (forward-line 1)))))\n\n316 (defun git-gutter+-set-window-margin (width)\n317 (let ((curwin (get-buffer-window)))\n318 (set-window-margins curwin width (cdr (window-margins curwin)))))\n\n320 (defsubst git-gutter+-file-buffer-p ()\n321 (and (buffer-file-name)\n322 default-directory\n323 (file-directory-p default-directory)))\n\n325 ;;;###autoload\n326 (define-minor-mode git-gutter+-mode\n327 \"Git-Gutter mode\"\n328 :group 'git-gutter+\n329 :init-value nil\n330 :global nil\n331 :lighter git-gutter+-lighter\n332 (if git-gutter+-mode\n333 (if (and (git-gutter+-file-buffer-p)\n334 (git-gutter+-in-git-repository-p (buffer-file-name)))\n335 (progn\n336 (git-gutter+-add-local-hooks)\n337 (git-gutter+-refresh))\n338 (if (called-interactively-p 'any)\n339 (message \"No Git repo for current buffer\"))\n340 (git-gutter+-mode -1))\n341 (git-gutter+-remove-local-hooks)\n342 (git-gutter+-clear)))\n\n344 (defun git-gutter+-add-local-hooks ()\n345 (add-hook 'after-save-hook 'git-gutter+-refresh nil t)\n346 ;; Turn off `git-gutter+-mode' while reverting to prevent any redundant calls to\n347 ;; `git-gutter+-refresh'.\n348 (add-hook 'before-revert-hook 'git-gutter+-turn-off nil t)\n349 (add-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change nil t)\n350 (if git-gutter+-window-config-change-function\n351 (add-hook 'window-configuration-change-hook\n352 git-gutter+-window-config-change-function nil t)))\n\n354 (defun git-gutter+-remove-local-hooks ()\n355 (remove-hook 'after-save-hook 'git-gutter+-refresh t)\n356 (remove-hook 'before-revert-hook 'git-gutter+-turn-off t)\n357 (remove-hook 'change-major-mode-hook 'git-gutter+-reenable-after-major-mode-change t)\n358 (if git-gutter+-window-config-change-function\n359 (remove-hook 'window-configuration-change-hook\n360 git-gutter+-window-config-change-function t)))\n\n362 (defmacro git-gutter+-in-all-buffers (&rest body)\n363 `(dolist (buf (buffer-list))\n364 (with-current-buffer buf\n365 ,@body)))\n\n367 ;; When `define-globalized-minor-mode' is used to define `global-git-gutter+-mode',\n368 ;; `git-gutter+-mode' and thus `git-gutter+-refresh' get run twice when a new file\n369 ;; is opened. (First for `fundamental-mode', then for the file-specific mode.)\n370 ;; The following definition of `global-git-gutter+-mode' avoids any redundant calls to\n371 ;; `git-gutter+-refresh'.\n\n373 ;;;###autoload\n374 (define-minor-mode global-git-gutter+-mode ()\n375 \"Global Git-Gutter mode\"\n376 :group 'git-gutter+\n377 :init-value nil\n378 :global t\n379 (if global-git-gutter+-mode\n380 (progn\n381 (add-hook 'find-file-hook 'git-gutter+-turn-on)\n382 (add-hook 'after-revert-hook 'git-gutter+-turn-on)\n383 (add-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n384 (git-gutter+-in-all-buffers (git-gutter+-turn-on)))\n385 (remove-hook 'find-file-hook 'git-gutter+-turn-on)\n386 (remove-hook 'after-revert-hook 'git-gutter+-turn-on)\n387 (remove-hook 'after-change-major-mode-hook 'git-gutter+-reenable-buffers)\n388 (git-gutter+-in-all-buffers (git-gutter+-turn-off))))\n\n390 (defun git-gutter+-turn-on ()\n391 (when (and (buffer-file-name)\n392 (not (memq major-mode git-gutter+-disabled-modes))\n393 (not git-gutter+-mode))\n394 (git-gutter+-mode t)))\n\n396 (defun git-gutter+-turn-off ()\n397 (if git-gutter+-mode (git-gutter+-mode -1)))\n\n399 (defun git-gutter+-reenable-after-major-mode-change ()\n400 (if global-git-gutter+-mode\n401 (add-to-list 'git-gutter+-buffers-to-reenable (current-buffer))))\n\n403 (defun git-gutter+-reenable-buffers ()\n404 (dolist (buf git-gutter+-buffers-to-reenable)\n405 (with-current-buffer buf\n406 (git-gutter+-turn-on)))\n407 (setq git-gutter+-buffers-to-reenable nil))\n\n409 (defsubst git-gutter+-show-gutter-p (diffinfos)\n410 (if git-gutter+-hide-gutter\n411 (or diffinfos git-gutter+-unchanged-sign)\n412 (or global-git-gutter+-mode git-gutter+-unchanged-sign diffinfos)))\n\n414 (defun git-gutter+-show-gutter (&optional diffinfos)\n415 (when (git-gutter+-show-gutter-p (or diffinfos git-gutter+-diffinfos))\n416 (let ((win-width (or git-gutter+-window-width\n417 (git-gutter+-longest-sign-width))))\n418 (git-gutter+-set-window-margin win-width))))\n\n420 (defun git-gutter+-view-diff-infos (diffinfos)\n421 (when (or git-gutter+-unchanged-sign\n422 git-gutter+-separator-sign)\n423 (git-gutter+-view-for-unchanged))\n424 (when diffinfos\n425 (save-excursion\n426 (mapc 'git-gutter+-view-diff-info diffinfos)))\n427 (git-gutter+-show-gutter diffinfos))\n\n429 (defsubst git-gutter+-reset-window-margin-p ()\n430 (or git-gutter+-hide-gutter\n431 (not global-git-gutter+-mode)))\n\n433 (defun git-gutter+-clear-diff-infos ()\n434 (when (git-gutter+-reset-window-margin-p)\n435 (git-gutter+-set-window-margin 0))\n436 (remove-overlays (point-min) (point-max) 'git-gutter+ t))\n\n438 (defun git-gutter+-process-diff (curfile)\n439 (destructuring-bind\n440 (diff-header diffinfos) (git-gutter+-diff curfile)\n441 (setq git-gutter+-diff-header diff-header\n442 git-gutter+-diffinfos diffinfos)\n443 (save-restriction\n444 (widen)\n445 (funcall git-gutter+-view-diff-function diffinfos))))\n\n447 (defun git-gutter+-search-near-diff-index (diffinfos is-reverse)\n448 (loop with current-line = (line-number-at-pos)\n449 with cmp-fn = (if is-reverse '> '<)\n450 for diffinfo in (if is-reverse (reverse diffinfos) diffinfos)\n451 for index = 0 then (1+ index)\n452 for start-line = (plist-get diffinfo :start-line)\n453 when (funcall cmp-fn current-line start-line)\n454 return (if is-reverse\n455 (1- (- (length diffinfos) index))\n456 index)))\n\n458 (defun git-gutter+-diffinfo-at-point ()\n459 (save-restriction\n460 (widen)\n461 (loop with current-line = (line-number-at-pos)\n462 for diffinfo in git-gutter+-diffinfos\n463 for start = (plist-get diffinfo :start-line)\n464 for end = (or (plist-get diffinfo :end-line) (1+ start))\n465 when (and (>= current-line start) (<= current-line end))\n466 return diffinfo)))\n\n468 (defun git-gutter+-collect-deleted-line (str)\n469 (with-temp-buffer\n470 (insert str)\n471 (goto-char (point-min))\n472 (loop while (re-search-forward \"^-\\\\(.*?\\\\)$\" nil t)\n473 collect (match-string 1) into deleted-lines\n474 finally return deleted-lines)))\n\n476 (defun git-gutter+-delete-added-lines (start-line end-line)\n477 (forward-line (1- start-line))\n478 (let ((start-point (point)))\n479 (forward-line (1+ (- end-line start-line)))\n480 (delete-region start-point (point))))\n\n482 (defun git-gutter+-insert-deleted-lines (content)\n483 (dolist (line (git-gutter+-collect-deleted-line content))\n484 (insert (concat line \"\\n\"))))\n\n486 (defun git-gutter+-do-revert-hunk (diffinfo)\n487 (save-excursion\n488 (save-restriction\n489 (widen)\n490 (goto-char (point-min))\n491 (let ((start-line (plist-get diffinfo :start-line))\n492 (end-line (plist-get diffinfo :end-line))\n493 (content (plist-get diffinfo :content)))\n494 (case (plist-get diffinfo :type)\n495 (added (git-gutter+-delete-added-lines start-line end-line))\n496 (deleted (forward-line (1- start-line))\n497 (git-gutter+-insert-deleted-lines content))\n498 (modified (git-gutter+-delete-added-lines start-line end-line)\n499 (git-gutter+-insert-deleted-lines content)))))))\n\n501 (defun git-gutter+-revert-hunks ()\n502 \"Revert hunk at point. If region is active, revert all hunks within the region.\"\n503 (interactive)\n504 (let* ((diffinfos (git-gutter+-selected-diffinfos))\n505 (one-diffinfo-p (= 1 (length diffinfos))))\n506 (save-window-excursion\n507 (if one-diffinfo-p (git-gutter+-show-hunk (car diffinfos)))\n508 (when (and diffinfos\n509 (yes-or-no-p (if one-diffinfo-p\n510 \"Revert hunk?\"\n511 (format \"Revert %d hunks?\" (length diffinfos)))))\n512 ;; Revert diffinfos in reverse so that earlier hunks don't invalidate the\n513 ;; line number information of the later hunks.\n514 (dolist (diffinfo (nreverse diffinfos))\n515 (git-gutter+-do-revert-hunk diffinfo))\n516 (save-buffer))\n517 (if one-diffinfo-p\n518 (git-gutter+-awhen (get-buffer git-gutter+-popup-buffer)\n519 (kill-buffer it))))))\n\n521 (defun git-gutter+-show-hunk (&optional diffinfo)\n522 \"Show hunk at point in another window\"\n523 (interactive)\n524 (git-gutter+-awhen (or diffinfo\n525 (git-gutter+-diffinfo-at-point))\n526 (save-selected-window\n527 (with-current-buffer (get-buffer-create git-gutter+-popup-buffer)\n528 (setq buffer-read-only nil)\n529 (erase-buffer)\n530 (insert (plist-get it :content))\n531 (insert \"\\n\")\n532 (goto-char (point-min))\n533 (diff-mode)\n534 (view-mode)\n535 (pop-to-buffer (current-buffer))))))\n\n537 (defun git-gutter+-next-hunk (arg)\n538 \"Move to next diff hunk\"\n539 (interactive \"p\")\n540 (if (not git-gutter+-diffinfos)\n541 (message \"No changes in buffer\")\n542 (save-restriction\n543 (widen)\n544 (let* ((is-reverse (< arg 0))\n545 (diffinfos git-gutter+-diffinfos)\n546 (len (length diffinfos))\n547 (index (git-gutter+-search-near-diff-index diffinfos is-reverse))\n548 (real-index (if index\n549 (let ((next (if is-reverse (1+ index) (1- index))))\n550 (mod (+ arg next) len))\n551 (if is-reverse (1- (length diffinfos)) 0)))\n552 (diffinfo (nth real-index diffinfos)))\n553 (goto-char (point-min))\n554 (forward-line (1- (plist-get diffinfo :start-line)))\n555 (when (buffer-live-p (get-buffer git-gutter+-popup-buffer))\n556 (save-window-excursion\n557 (git-gutter+-show-hunk)))))))\n\n559 (defun git-gutter+-previous-hunk (arg)\n560 \"Move to previous diff hunk\"\n561 (interactive \"p\")\n562 (git-gutter+-next-hunk (- arg)))\n\n564 (defun git-gutter+-remote-default-directory (dir file)\n565 (let* ((vec (tramp-dissect-file-name file))\n566 (method (aref vec 0))\n567 (user (aref vec 1))\n568 (host (aref vec 2)))\n569 (format \"/%s:%s%s:%s\" method (if user (concat user \"@\") \"\") host dir)))\n\n571 (defun git-gutter+-remote-file-path (dir file)\n572 (let ((file (aref (tramp-dissect-file-name file) 3)))\n573 (replace-regexp-in-string (concat \"\\\\`\" dir) \"\" file)))\n\n575 (defun git-gutter+-local-file-path (file)\n576 (if (eq system-type 'windows-nt)\n577 ;; Cygwin can't handle Windows absolute paths\n578 (file-relative-name file default-directory)\n579 file))\n\n581 (defun git-gutter+-refresh ()\n582 (git-gutter+-clear)\n583 (let ((file (buffer-file-name)))\n584 (when (and file (file-exists-p file))\n585 (if (file-remote-p file)\n586 (let* ((repo-root (git-gutter+-root-directory file))\n587 (default-directory (git-gutter+-remote-default-directory repo-root file)))\n588 (git-gutter+-process-diff (git-gutter+-remote-file-path repo-root file)))\n589 (git-gutter+-process-diff (git-gutter+-local-file-path file))))))\n\n591 (defun git-gutter+-clear ()\n592 (save-restriction\n593 (widen)\n594 (funcall git-gutter+-clear-function))\n595 (setq git-gutter+-diffinfos nil))\n\n\n598 ;;; Staging\n\n600 (defun git-gutter+-stage-hunks ()\n601 \"Stage hunk at point. If region is active, stage all hunk lines within the region.\"\n602 (interactive)\n603 (let* ((line-range (if (use-region-p)\n604 (cons (line-number-at-pos (region-beginning))\n605 (line-number-at-pos (region-end)))))\n606 (diffinfos (git-gutter+-selected-diffinfos line-range)))\n607 (when diffinfos\n608 (let ((error-msg (git-gutter+-stage-diffinfos diffinfos line-range)))\n609 (if error-msg\n610 (message \"Error staging hunks:\\n%s\" error-msg))\n611 (git-gutter+-refresh)))))\n\n613 (defun git-gutter+-selected-diffinfos (&optional line-range)\n614 (unless line-range\n615 (setq line-range (if (use-region-p)\n616 (cons (line-number-at-pos (region-beginning))\n617 (line-number-at-pos (region-end))))))\n618 (if line-range\n619 (git-gutter+-diffinfos-between-lines line-range)\n620 (git-gutter+-awhen (git-gutter+-diffinfo-at-point)\n621 (list it))))\n\n623 (defsubst git-gutter+-diffinfo-between-lines-p (diffinfo start-line end-line)\n624 (let ((diff-start (plist-get diffinfo :start-line))\n625 (diff-end (plist-get diffinfo :end-line)))\n626 (and (<= start-line diff-end)\n627 (<= diff-start end-line))))\n\n629 (defun git-gutter+-diffinfos-between-lines (line-range)\n630 (save-restriction\n631 (widen)\n632 (let ((start-line (car line-range))\n633 (end-line (cdr line-range)))\n634 (delq nil\n635 (mapcar (lambda (diffinfo)\n636 (if (git-gutter+-diffinfo-between-lines-p\n637 diffinfo start-line end-line)\n638 diffinfo))\n639 git-gutter+-diffinfos)))))\n\n641 (defun git-gutter+-stage-diffinfos (diffinfos line-range)\n642 (let ((header git-gutter+-diff-header))\n643 (with-temp-buffer\n644 (insert header)\n645 ;; Insert hunks in reverse so that earlier hunks don't invalidate the line\n646 ;; number information of the later hunks.\n647 (dolist (diffinfo (nreverse diffinfos))\n648 (git-gutter+-insert-diffinfo diffinfo line-range)\n649 (goto-char (point-max)))\n650 (git-gutter+-call-git-on-current-buffer\n651 '(\"apply\" \"--unidiff-zero\" \"--cached\" \"-\")))))\n\n653 (defun git-gutter+-insert-diffinfo (diffinfo line-range)\n654 (let ((content (plist-get diffinfo :content))\n655 (type (plist-get diffinfo :type)))\n656 (if (not line-range)\n657 (git-gutter+-insert-hunk content type)\n658 (let ((diff-start-line (plist-get diffinfo :start-line))\n659 (diff-end-line (plist-get diffinfo :end-line))\n660 (start-line (car line-range))\n661 (end-line (cdr line-range)))\n662 (git-gutter+-insert-hunk content type\n663 (1+ (- start-line diff-start-line))\n664 (1+ (- end-line diff-start-line)))))))\n\n666 (defun git-gutter+-call-git-on-current-buffer (args)\n667 \"Sends the current buffer contents to Git and replaces them with Git's output.\n\n669 RETURNS nil if Git ran successfully. Returns an error description otherwise.\"\n670 (unless (zerop (apply #'call-process-region (point-min) (point-max)\n671 git-gutter+-git-executable t t nil args))\n672 (buffer-string)))\n\n674 (defsubst git-gutter+-read-hunk-header (hunk)\n675 ;; @@ -{del-line},{del-len} +{add-line},{add-len} @@\n676 (string-match git-gutter+-hunk-header-regex hunk)\n677 (list (string-to-number (match-string 1 hunk))\n678 (string-to-number (or (match-string 2 hunk) \"1\"))\n679 (string-to-number (match-string 3 hunk))\n680 (string-to-number (or (match-string 4 hunk) \"1\"))))\n\n682 (defun git-gutter+-insert-hunk (hunk type &optional start end)\n683 \"If START and END are provided, only insert addition (+) lines between\n684 START and END (inclusive). START and END are both line numbers starting with 1.\"\n685 (destructuring-bind\n686 (del-line del-len add-line add-len) (git-gutter+-read-hunk-header hunk)\n687 (let* ((start (max 1 (or start 1)))\n688 (end (min add-len (or end add-len)))\n689 (insert-all-p (or (eq type :deleted)\n690 (and (= start 1) (= end add-len))))\n691 (num-lines-selected (if insert-all-p\n692 add-len\n693 (1+ (- end start)))))\n694 ;; When the user selected the last lines of a hunk with type `modified' (but\n695 ;; not the complete hunk), then don't insert any deletion (-) lines from that\n696 ;; hunk.\n697 (if (and (eq type 'modified)\n698 (> start 1) (= end add-len))\n699 (setq type 'modified-trailing))\n\n701 (save-excursion\n702 (insert hunk \"\\n\"))\n\n704 (git-gutter+-delete-hunk-header)\n\n706 (if (not insert-all-p)\n707 (git-gutter+-modify-hunk type num-lines-selected del-len start))\n\n709 (let ((hunk-header (git-gutter+-make-hunk-header type num-lines-selected\n710 del-line del-len add-line)))\n711 (insert hunk-header \"\\n\")))))\n\n713 (defun git-gutter+-delete-hunk-header ()\n714 (let ((hunk-start (point)))\n715 (forward-line 1)\n716 (delete-region hunk-start (point))))\n\n718 (defun git-gutter+-modify-hunk (type num-lines-selected del-len start)\n719 \"Remove all addition (+) lines from hunk that aren't selected.\n720 If TYPE is not `modified', also remove all deletion (-) lines.\"\n721 (let ((first-line-selected (+ del-len (1- start)))\n722 selected-lines)\n723 (save-excursion\n724 (forward-line first-line-selected)\n725 (let ((selection-start (point)))\n726 (forward-line num-lines-selected)\n727 (setq selected-lines (buffer-substring selection-start (point)))))\n728 (save-excursion\n729 (if (eq type 'modified) (forward-line del-len)) ; skip over deletion (-) lines\n730 (delete-region (point) (point-max))\n731 (insert selected-lines))))\n\n733 (defun git-gutter+-make-hunk-header (type num-lines-selected del-line del-len add-line)\n734 (let ((add-len num-lines-selected))\n735 (case type\n736 (added (setq add-line (1+ del-line)))\n737 (modified-trailing (setq add-line (+ del-line del-len)\n738 del-line (1- add-line)\n739 del-len 0))\n740 (t (setq add-line del-line)))\n741 (format \"@@ -%d,%d +%d,%d @@\"\n742 del-line del-len\n743 add-line add-len)))\n\n\n746 ;;; Committing\n747 ;; This section draws heavily from old Magit source code.\n\n749 (defvar git-gutter+-pre-commit-window-config nil)\n750 (defvar git-gutter+-commit-origin-buffer nil\n751 \"Buffer that started the commit\")\n\n753 (defconst git-gutter+-commit-buffer-name \"*Commit Message*\")\n754 (defconst git-gutter+-staged-changes-buffer-name \"*Staged Changes*\")\n\n756 ;;;###autoload\n757 (defun git-gutter+-commit ()\n758 \"Commit staged changes. If nothing is staged, ask to stage the current buffer.\"\n759 (interactive)\n\n761 (when (and (not (git-gutter+-anything-staged-p))\n762 git-gutter+-diffinfos\n763 (y-or-n-p \"Nothing staged. Stage current buffer? \"))\n764 (git-gutter+-stage-whole-buffer))\n\n766 (let ((file (buffer-file-name))\n767 (dir default-directory))\n768 (git-gutter+-save-window-config-if-needed)\n769 (setq git-gutter+-commit-origin-buffer (current-buffer))\n770 (git-gutter+-open-commit-edit-buffer dir)\n771 (git-gutter+-show-staged-changes file dir)))\n\n773 (defun git-gutter+-stage-and-commit ()\n774 (interactive)\n775 (git-gutter+-stage-hunks)\n776 (git-gutter+-commit))\n\n778 (defun git-gutter+-save-window-config-if-needed ()\n779 ;; Only save the window config if the temporary buffers that get popped-up by\n780 ;; git-gutter+ are not already visible.\n781 ;; In this way, `git-gutter+-commit' can be called twice in a row without\n782 ;; losing the original window config.\n783 (when (not (and git-gutter+-pre-commit-window-config\n784 (get-buffer-window git-gutter+-commit-buffer-name)\n785 (get-buffer-window git-gutter+-staged-changes-buffer-name)))\n786 (setq git-gutter+-pre-commit-window-config (current-window-configuration))))\n\n788 (defun git-gutter+-open-commit-edit-buffer (dir)\n789 \"Opens a buffer for composing the commit message\"\n790 (pop-to-buffer (get-buffer-create git-gutter+-commit-buffer-name))\n791 (setq default-directory dir)\n792 (git-gutter+-commit-mode)\n793 (message \"Type C-c C-c to commit (C-c C-k to cancel).\"))\n\n795 (defsubst git-gutter+-pop-to-staged-changes-buffer ()\n796 (let* ((buf (get-buffer-create git-gutter+-staged-changes-buffer-name))\n797 (window (get-buffer-window buf)))\n798 (if window\n799 ;; Buffer is already visible\n800 (select-window window)\n801 (if (<= (length (window-list)) 2)\n802 (split-window))\n803 (pop-to-buffer buf))))\n\n805 (defun git-gutter+-show-staged-changes (file dir)\n806 (save-selected-window\n807 (git-gutter+-pop-to-staged-changes-buffer)\n808 (setq buffer-read-only nil)\n809 (erase-buffer)\n810 (let ((default-directory dir))\n811 (git-gutter+-call-git '(\"diff\" \"--staged\") file))\n812 (goto-char (point-min))\n813 (diff-mode)\n814 (view-mode)))\n\n816 (defsubst git-gutter+-abort-commit-when-no-changes (allow-empty amend)\n817 (unless (or amend\n818 allow-empty\n819 (git-gutter+-anything-staged-p))\n820 (error\n821 \"Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?\"\n822 (key-description (car (where-is-internal\n823 'git-gutter+-commit-toggle-amending)))\n824 (key-description (car (where-is-internal\n825 'git-gutter+-commit-toggle-allow-empty))))))\n\n827 (defsubst git-gutter+-buffer-is-whitespace ()\n828 (save-excursion\n829 (goto-char (point-min))\n830 (looking-at-p \"[ \\t\\n]*\\\\'\")))\n\n832 (defun git-gutter+-publish-commit ()\n833 \"Publish commit\"\n834 (interactive)\n835 (let* ((fields (git-gutter+-commit-get-fields))\n836 (amend (equal \"yes\" (git-gutter+-commit-get-field 'amend fields)))\n837 (allow-empty (equal \"yes\" (git-gutter+-commit-get-field 'allow-empty fields)))\n838 (author (git-gutter+-commit-get-field 'author fields))\n839 (date (git-gutter+-commit-get-field 'date fields)))\n\n841 (git-gutter+-abort-commit-when-no-changes allow-empty amend)\n\n843 (git-gutter+-push-to-comment-ring (buffer-string))\n\n845 (git-gutter+-commit-set-fields nil) ; Delete message header\n\n847 (when (git-gutter+-buffer-is-whitespace)\n848 (erase-buffer)\n849 (insert \"(Empty description)\"))\n\n851 (let ((error-msg (git-gutter+-call-git-on-current-buffer\n852 (append '(\"--no-pager\" \"commit\" \"-F\" \"-\")\n853 (if amend '(\"--amend\"))\n854 (if allow-empty '(\"--allow-empty\"))\n855 (if author (list (concat \"--author=\" author)))\n856 (if date (list (concat \"--date=\" date)))))))\n857 (if error-msg\n858 (progn\n859 (message \"Commit error:\\n%s\" error-msg)\n860 (erase-buffer)\n861 (insert (ring-ref log-edit-comment-ring 0))) ; Reinsert commit message\n862 (message \"Commit successful.\")\n863 (git-gutter+-close-commit-edit-buffer)\n864 (git-gutter+-update-vc-modeline)))))\n\n866 (defun git-gutter+-close-commit-edit-buffer ()\n867 \"Abort edits and discard commit message being composed.\"\n868 (interactive)\n869 (kill-buffer)\n870 (set-window-configuration git-gutter+-pre-commit-window-config))\n\n872 (defun git-gutter+-update-vc-modeline ()\n873 (when (buffer-live-p git-gutter+-commit-origin-buffer)\n874 (with-current-buffer git-gutter+-commit-origin-buffer\n875 ;; Updating the modeline has no effect if the buffer still has\n876 ;; changes - it will remain in the 'modified' state. So skip it then.\n877 (unless git-gutter+-diffinfos\n878 (ignore-errors (vc-find-file-hook))))))\n\n880 (defun git-gutter+-stage-whole-buffer ()\n881 (save-excursion\n882 (mark-whole-buffer)\n883 (git-gutter+-stage-hunks)))\n\n885 (defun git-gutter+-anything-staged-p ()\n886 \"Return t if the current repo has staged changes\"\n887 (not (zerop (git-gutter+-call-git '(\"diff\" \"--quiet\" \"--cached\")))))\n\n889 (defun git-gutter+-commit-toggle-amending ()\n890 \"Toggle whether this will be an amendment to the previous commit.\n891 \\(i.e., whether commit is run via 'git commit --amend')\"\n892 (interactive)\n893 ;; Remove the newline that 'git-commit-mode' adds to a new commit\n894 ;; message buffer by default. This prevents an ugly visual\n895 ;; gap between the commit message header and the previous commit\n896 ;; message.\n897 (when (git-gutter+-buffer-is-whitespace)\n898 (erase-buffer))\n\n900 (let ((amend-was-already-set (git-gutter+-commit-get-field 'amend)))\n901 (git-gutter+-commit-toggle-field 'amend t)\n902 (unless amend-was-already-set\n903 ;; Insert previous commit message\n904 (goto-char (point-max))\n905 (unless (zerop (current-column))\n906 (insert \"\\n\"))\n907 (insert (git-gutter+-get-last-commit-msg)\n908 \"\\n\"))))\n\n910 (defun git-gutter+-commit-toggle-allow-empty ()\n911 \"Toggle whether this commit is allowed to be empty.\n912 \\(i.e., whether commit is run via 'git commit --allow-empty')\"\n913 (interactive)\n914 (git-gutter+-commit-toggle-field 'allow-empty t))\n\n916 (defun git-gutter+-format-author (author email)\n917 (format \"%s <%s>\" author email))\n\n919 (defun git-gutter+-commit-toggle-author ()\n920 \"Toggle whether this commit should have a user-defined author.\"\n921 (interactive)\n922 (git-gutter+-commit-toggle-input\n923 'author (git-gutter+-format-author\n924 (or (git-gutter+-get-cfg \"user\" \"name\") \"Author Name\")\n925 (or (git-gutter+-get-cfg \"user\" \"email\") \"author@email\"))))\n\n927 (defun git-gutter+-commit-toggle-date ()\n928 \"Toggle whether this commit should have a user-defined date.\"\n929 (interactive)\n930 (git-gutter+-commit-toggle-input 'date\n931 ;; ISO 8601\n932 (format-time-string \"%Y-%m-%dT%T%z\" (current-time))))\n\n934 (defun git-gutter+-push-to-comment-ring (comment)\n935 (when (or (ring-empty-p log-edit-comment-ring)\n936 (not (equal comment (ring-ref log-edit-comment-ring 0))))\n937 (ring-insert log-edit-comment-ring comment)))\n\n939 (defun git-gutter+-get-last-commit-msg ()\n940 (git-gutter+-git-output '(\"log\" \"--max-count=1\" \"--pretty=format:%s%n%n%b\" \"HEAD\")))\n\n942 (defun git-gutter+-get-cfg (&rest keys)\n943 (git-gutter+-git-output (list \"config\" (mapconcat 'identity keys \".\"))))\n\n945 (defun git-gutter+-git-output (args)\n946 (with-temp-buffer\n947 (git-gutter+-call-git args)\n948 ;; Delete trailing newlines\n949 (goto-char (point-min))\n950 (if (re-search-forward \"\\n+\\\\'\" nil t)\n951 (replace-match \"\"))\n952 (buffer-string)))\n\n\n955 ;;; Commit message header\n\n957 (defconst git-gutter+-commit-header-end \"-- End of commit options header --\\n\")\n\n959 (defun git-gutter+-commit-get-field (name &optional fields)\n960 (cdr (assq name (or fields (git-gutter+-commit-get-fields)))))\n\n962 (defun git-gutter+-commit-set-field (name value)\n963 (let* ((fields (git-gutter+-commit-get-fields))\n964 (cell (assq name fields)))\n965 (cond (cell\n966 (if value\n967 (rplacd cell value)\n968 (setq fields (delq cell fields))))\n969 (t\n970 (if value\n971 (setq fields (append fields (list (cons name value)))))))\n972 (git-gutter+-commit-set-fields fields)))\n\n974 (defun git-gutter+-commit-toggle-field (name default)\n975 \"Toggle the commit header field named NAME.\n976 If it's currently unset, set it to DEFAULT (t or nil).\"\n977 (let* ((fields (git-gutter+-commit-get-fields))\n978 (cell (assq name fields)))\n979 (if cell\n980 (rplacd cell (if (equal (cdr cell) \"yes\") \"no\" \"yes\"))\n981 (setq fields (acons name (if default \"yes\" \"no\") fields)))\n982 (git-gutter+-commit-set-fields fields)))\n\n984 (defun git-gutter+-commit-toggle-input (name default)\n985 \"Toggle the commit header input named NAME.\n986 If it's currently unset, set it to DEFAULT (a string). If it is\n987 set remove it.\"\n988 (let* ((fields (git-gutter+-commit-get-fields))\n989 (cell (assq name fields)))\n990 (if cell\n991 (setq fields (assq-delete-all name fields))\n992 (setq fields (acons name default fields)))\n993 (git-gutter+-commit-set-fields fields)))\n\n995 (defun git-gutter+-commit-get-fields ()\n996 (let (result)\n997 (goto-char (point-min))\n998 (while (looking-at \"^\\\\([A-Za-z0-9-_]+\\\\): *\\\\(.+\\\\)?$\")\n999 (let ((name (intern (downcase (match-string 1))))\n1000 (value (read (or (match-string 2) \"nil\"))))\n1001 (push (cons name value) result))\n1002 (forward-line))\n1003 (if (looking-at (regexp-quote git-gutter+-commit-header-end))\n1004 (nreverse result))))\n\n1006 (defun git-gutter+-commit-set-fields (fields)\n1007 (goto-char (point-min))\n1008 ;; Delete commit header\n1009 (if (search-forward-regexp (format \"^\\\\(?:[A-Za-z0-9-_]+:.*\\n\\\\)*%s\"\n1010 (regexp-quote git-gutter+-commit-header-end))\n1011 nil t)\n1012 (delete-region (match-beginning 0) (match-end 0)))\n1013 (goto-char (point-min))\n1014 (when fields\n1015 (dolist (field fields)\n1016 (insert (capitalize (symbol-name (car field))) \": \"\n1017 (prin1-to-string (cdr field)) \"\\n\"))\n1018 (insert git-gutter+-commit-header-end)))\n\n\n1021 ;;; git-gutter+-commit-mode\n1022 ;; Like git-commit-mode, but adds keybindings to git-gutter+ commands and\n1023 ;; highlighting support for the commit message header.\n\n1025 (define-derived-mode git-gutter+-commit-mode git-commit-mode \"Git-Gutter-Commit\"\n1026 (setq font-lock-defaults (list (git-gutter+-commit-font-lock-keywords) t)))\n\n1028 (setq git-gutter+-commit-mode-map\n1029 (let ((map (copy-keymap git-commit-mode-map)))\n1030 (define-key map (kbd \"C-c C-c\") 'git-gutter+-publish-commit)\n1031 (define-key map (kbd \"C-c C-k\") 'git-gutter+-close-commit-edit-buffer)\n1032 (define-key map (kbd \"C-c C-a\") 'git-gutter+-commit-toggle-amending)\n1033 (define-key map (kbd \"C-c C-e\") 'git-gutter+-commit-toggle-allow-empty)\n1034 (define-key map (kbd \"C-c C-u\") 'git-gutter+-commit-toggle-author)\n1035 (define-key map (kbd \"C-c C-d\") 'git-gutter+-commit-toggle-date)\n1036 (define-key map (kbd \"C-c C-b\") 'git-commit-ack)\n1037 (define-key map (kbd \"M-p\") 'log-edit-previous-comment)\n1038 (define-key map (kbd \"M-n\") 'log-edit-next-comment)\n1039 map))\n\n1041 (defface git-gutter+-commit-header-face\n1042 '((t :inherit font-lock-comment-face))\n1043 \"Highlights the commit message header\"\n1044 :group 'git-gutter+-faces)\n\n1046 (defconst git-gutter+-commit-header-regex\n1047 (concat \"\\\\(?:.\\\\|\\n\\\\)*?\" (regexp-quote git-gutter+-commit-header-end)))\n\n1049 (defconst git-gutter+-skip-commit-header-regex\n1050 (concat \"\\\\`\\\\(?:\" git-gutter+-commit-header-regex \"\\\\)?\"))\n\n1052 ;; Modify git-commit-summary-regexp to ignore the commit header\n1053 (defadvice git-commit-summary-regexp\n1054 (after ignore-git-gutter+-commit-header activate compile)\n1055 (if (eq major-mode 'git-gutter+-commit-mode)\n1056 (setq ad-return-value\n1057 (concat git-gutter+-skip-commit-header-regex\n1058 (substring ; Remove leading \"\\\\`\"\n1059 ad-return-value 2)))))\n\n1061 (defun git-gutter+-commit-font-lock-keywords ()\n1062 \"Like `git-commit-mode-font-lock-keywords' but with commit header highlighting\"\n1063 `((,(concat \"\\\\`\" git-gutter+-commit-header-regex) . 'git-gutter+-commit-header-face)\n1064 ,@(git-commit-mode-font-lock-keywords)))\n\n\n1067 ;;; Magit synchronization\n1068 ;; Force Magit to refresh git-gutter+ when updating the VC mode line.\n\n1070 (defvar git-gutter+-orig-vc-find-file-hook)\n\n1072 (defvar git-gutter+-vc-find-file-hook-with-refresh\n1073 (lambda ()\n1074 (funcall git-gutter+-orig-vc-find-file-hook)\n1075 (if git-gutter+-mode (git-gutter+-refresh))))\n\n1077 (defadvice magit-update-vc-modeline (around refresh-git-gutter+ compile activate)\n1078 ;; `magit-update-vc-modeline' calls `vc-find-file-hook' (a function!) on each\n1079 ;; buffer in the repo. Temporarily rebind it to `vc-find-file-hook-with-refresh',\n1080 ;; which calls git-gutter+-refresh after updating the VC mode line.\n1081 ;;\n1082 ;; Using `flet' would have been much simpler, but it's deprecated since 24.3.\n1083 (setq git-gutter+-orig-vc-find-file-hook (symbol-function 'vc-find-file-hook))\n1084 (fset 'vc-find-file-hook git-gutter+-vc-find-file-hook-with-refresh)\n1085 (unwind-protect\n1086 ad-do-it\n1087 (fset 'vc-find-file-hook git-gutter+-orig-vc-find-file-hook)))\n\n1089 (provide 'git-gutter+)\n\n1091 ;;; git-gutter+.el ends here\n")) (setq helm-swoop-cache t))) (candidates-in-buffer) (get-line . buffer-substring-no-properties) (keymap keymap (C-M-left . backward-sexp) (C-M-right . forward-sexp) (27 keymap (105 . helm-multi-swoop-all-from-helm-swoop)) (3 keymap (5 . helm-swoop-edit)) keymap (C-M-left . paren-backward-sexp) (C-M-right . paren-forward-sexp) (94 . helm-swoop-caret-match) (menu-bar keymap (help-menu keymap (describe keymap (describe-mode . helm-help)))) (help keymap (109 . helm-help)) (f1 keymap (109 . helm-help)) (8 keymap (109 . helm-help) (104 . undefined) (8 . undefined) (4 . helm-debug-output)) (20 . helm-toggle-resplit-and-swap-windows) (C-tab . undefined) (triple-mouse-3 . ignore) (double-mouse-3 . ignore) (mouse-3 . ignore) (drag-mouse-3 . ignore) (down-mouse-3 . ignore) (triple-mouse-2 . ignore) (double-mouse-2 . ignore) (mouse-2 . ignore) (drag-mouse-2 . ignore) (down-mouse-2 . ignore) (triple-mouse-1 . ignore) (double-mouse-1 . ignore) (mouse-1 . ignore) (drag-mouse-1 . ignore) (down-mouse-1 . ignore) (67108897 . helm-toggle-suspend-update) (3 keymap (21 . helm-force-update) (6 . helm-follow-mode) (11 . helm-kill-selection-and-quit) (25 . helm-yank-selection) (4 . helm-delete-current-selection) (45 . helm-swap-windows)) (67108987 . helm-enlarge-window) (67108989 . helm-narrow-window) (19 . undefined) (18 . undefined) (23 . helm-yank-text-at-point) (24 keymap (2 . helm-resume-list-buffers-after-quit) (98 . helm-resume-previous-session-after-quit) (6 . helm-quit-and-find-file)) (11 . helm-delete-minibuffer-contents) (67108896 . helm-toggle-visible-mark) (0 . helm-toggle-visible-mark) (C-M-up . helm-scroll-other-window-down) (C-M-down . helm-scroll-other-window) (M-prior . helm-scroll-other-window-down) (M-next . helm-scroll-other-window) (12 . helm-recenter-top-bottom-other-window) (15 . helm-next-source) (10 . helm-select-3rd-action) (5 . helm-select-2nd-action-or-end-of-line) ...) (header-line . "[C-c C-e] Edit mode, [M-i] apply all buffers") (action lambda ($line) (helm-swoop--goto-line (when (string-match "^[0-9]+" $line) (string-to-number (match-string 0 $line)))) (when (re-search-forward (mapconcat (quote identity) (split-string helm-pattern " ") "\\|") nil t) (goto-char (match-beginning 0))) (helm-swoop--recenter)) (migemo)) :buffer "*Helm Swoop*" :input #("git-gutter+-refresh" 0 19 (fontified t face whitespace-line)) :prompt "Swoop: " :preselect "^368 " :candidate-number-limit 19999) | |
(let ((helm-display-function helm-swoop-split-window-function) (helm-display-source-at-screen-top nil) (helm-completion-window-scroll-margin 5)) (helm :sources (or $source (if (> helm-swoop-last-prefix-number 1) (helm-c-source-swoop-multiline helm-swoop-last-prefix-number) (helm-c-source-swoop))) :buffer helm-swoop-buffer :input $query :prompt helm-swoop-prompt :preselect (if (string-match "^[ \n ]*$" (helm-swoop--get-string-at-line)) (save-excursion (if (re-search-forward "[^ \n ]" nil t) (format "^%s " (line-number-at-pos)) (re-search-backward "[^ \n ]" nil t) (format "^%s " (line-number-at-pos)))) (format "^%s " (line-number-at-pos))) :candidate-number-limit helm-swoop-candidate-number-limit)) | |
(progn (ad-enable-advice (quote helm-next-line) (quote around) (quote helm-swoop-next-line)) (ad-activate (quote helm-next-line)) (ad-enable-advice (quote helm-previous-line) (quote around) (quote helm-swoop-previous-line)) (ad-activate (quote helm-previous-line)) (ad-enable-advice (quote helm-move--next-line-fn) (quote around) (quote helm-multi-swoop-next-line-cycle)) (ad-activate (quote helm-move--next-line-fn)) (ad-enable-advice (quote helm-move--previous-line-fn) (quote around) (quote helm-multi-swoop-previous-line-cycle)) (ad-activate (quote helm-move--previous-line-fn)) (add-hook (quote helm-update-hook) (quote helm-swoop--pattern-match)) (add-hook (quote helm-after-update-hook) (quote helm-swoop--keep-nearest-position) t) (cond ($query (if (string-match "\\(\\^\\[0\\-9\\]\\+\\.\\)\\(.*\\)" $query) $query $query)) (mark-active (let (($st (buffer-substring-no-properties (region-beginning) (region-end)))) (if (string-match "\n" $st) (message "Multi line region is not allowed") (setq $query $st)))) ((setq $query (funcall helm-swoop-pre-input-function))) (t (setq $query ""))) (helm-swoop--recenter) (move-beginning-of-line 1) (helm-swoop--target-line-overlay-move) (let ((helm-display-function helm-swoop-split-window-function) (helm-display-source-at-screen-top nil) (helm-completion-window-scroll-margin 5)) (helm :sources (or $source (if (> helm-swoop-last-prefix-number 1) (helm-c-source-swoop-multiline helm-swoop-last-prefix-number) (helm-c-source-swoop))) :buffer helm-swoop-buffer :input $query :prompt helm-swoop-prompt :preselect (if (string-match "^[ \n ]*$" (helm-swoop--get-string-at-line)) (save-excursion (if (re-search-forward "[^ \n ]" nil t) (format "^%s " (line-number-at-pos)) (re-search-backward "[^ \n ]" nil t) (format "^%s " (line-number-at-pos)))) (format "^%s " (line-number-at-pos))) :candidate-number-limit helm-swoop-candidate-number-limit))) | |
(unwind-protect (progn (ad-enable-advice (quote helm-next-line) (quote around) (quote helm-swoop-next-line)) (ad-activate (quote helm-next-line)) (ad-enable-advice (quote helm-previous-line) (quote around) (quote helm-swoop-previous-line)) (ad-activate (quote helm-previous-line)) (ad-enable-advice (quote helm-move--next-line-fn) (quote around) (quote helm-multi-swoop-next-line-cycle)) (ad-activate (quote helm-move--next-line-fn)) (ad-enable-advice (quote helm-move--previous-line-fn) (quote around) (quote helm-multi-swoop-previous-line-cycle)) (ad-activate (quote helm-move--previous-line-fn)) (add-hook (quote helm-update-hook) (quote helm-swoop--pattern-match)) (add-hook (quote helm-after-update-hook) (quote helm-swoop--keep-nearest-position) t) (cond ($query (if (string-match "\\(\\^\\[0\\-9\\]\\+\\.\\)\\(.*\\)" $query) $query $query)) (mark-active (let (($st (buffer-substring-no-properties ... ...))) (if (string-match "\n" $st) (message "Multi line region is not allowed") (setq $query $st)))) ((setq $query (funcall helm-swoop-pre-input-function))) (t (setq $query ""))) (helm-swoop--recenter) (move-beginning-of-line 1) (helm-swoop--target-line-overlay-move) (let ((helm-display-function helm-swoop-split-window-function) (helm-display-source-at-screen-top nil) (helm-completion-window-scroll-margin 5)) (helm :sources (or $source (if (> helm-swoop-last-prefix-number 1) (helm-c-source-swoop-multiline helm-swoop-last-prefix-number) (helm-c-source-swoop))) :buffer helm-swoop-buffer :input $query :prompt helm-swoop-prompt :preselect (if (string-match "^[ \n ]*$" (helm-swoop--get-string-at-line)) (save-excursion (if (re-search-forward "[^ \n ]" nil t) (format "^%s " ...) (re-search-backward "[^ \n ]" nil t) (format "^%s " ...))) (format "^%s " (line-number-at-pos))) :candidate-number-limit helm-swoop-candidate-number-limit))) (helm-swoop--restore)) | |
(progn (setq helm-swoop-synchronizing-window (selected-window)) (setq helm-swoop-last-point (cons (point) (buffer-name (current-buffer)))) (setq helm-swoop-last-line-info (cons (current-buffer) (line-number-at-pos))) (if (boundp (quote helm-swoop-last-query)) nil (set (make-local-variable (quote helm-swoop-last-query)) "")) (setq helm-swoop-target-buffer (current-buffer)) (helm-swoop--set-prefix $multiline) (setq helm-swoop-line-overlay (make-overlay (point) (point))) (overlay-put helm-swoop-line-overlay (quote face) (if (< 1 helm-swoop-last-prefix-number) (quote helm-swoop-target-line-block-face) (quote helm-swoop-target-line-face))) (cond ((not (boundp (quote helm-swoop-cache))) (set (make-local-variable (quote helm-swoop-cache)) nil)) ((buffer-modified-p) (setq helm-swoop-cache nil))) (cond ((not (boundp (quote helm-swoop-list-cache))) (set (make-local-variable (quote helm-swoop-list-cache)) nil)) ((buffer-modified-p) (setq helm-swoop-list-cache nil))) (unwind-protect (progn (ad-enable-advice (quote helm-next-line) (quote around) (quote helm-swoop-next-line)) (ad-activate (quote helm-next-line)) (ad-enable-advice (quote helm-previous-line) (quote around) (quote helm-swoop-previous-line)) (ad-activate (quote helm-previous-line)) (ad-enable-advice (quote helm-move--next-line-fn) (quote around) (quote helm-multi-swoop-next-line-cycle)) (ad-activate (quote helm-move--next-line-fn)) (ad-enable-advice (quote helm-move--previous-line-fn) (quote around) (quote helm-multi-swoop-previous-line-cycle)) (ad-activate (quote helm-move--previous-line-fn)) (add-hook (quote helm-update-hook) (quote helm-swoop--pattern-match)) (add-hook (quote helm-after-update-hook) (quote helm-swoop--keep-nearest-position) t) (cond ($query (if (string-match "\\(\\^\\[0\\-9\\]\\+\\.\\)\\(.*\\)" $query) $query $query)) (mark-active (let (($st ...)) (if (string-match "\n" $st) (message "Multi line region is not allowed") (setq $query $st)))) ((setq $query (funcall helm-swoop-pre-input-function))) (t (setq $query ""))) (helm-swoop--recenter) (move-beginning-of-line 1) (helm-swoop--target-line-overlay-move) (let ((helm-display-function helm-swoop-split-window-function) (helm-display-source-at-screen-top nil) (helm-completion-window-scroll-margin 5)) (helm :sources (or $source (if (> helm-swoop-last-prefix-number 1) (helm-c-source-swoop-multiline helm-swoop-last-prefix-number) (helm-c-source-swoop))) :buffer helm-swoop-buffer :input $query :prompt helm-swoop-prompt :preselect (if (string-match "^[ \n ]*$" (helm-swoop--get-string-at-line)) (save-excursion (if ... ... ... ...)) (format "^%s " (line-number-at-pos))) :candidate-number-limit helm-swoop-candidate-number-limit))) (helm-swoop--restore))) | |
(let* (($query (car (cdr (memq (quote :$query) --cl-rest--)))) ($source (car (cdr (memq (quote :$source) --cl-rest--)))) ($multiline (car (cdr (or (memq (quote :$multiline) --cl-rest--) (list nil current-prefix-arg)))))) (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq (car --cl-keys--) (quote (:$query :$source :$multiline :allow-other-keys))) (setq --cl-keys-- (cdr (cdr --cl-keys--)))) ((car (cdr (memq ... --cl-rest--))) (setq --cl-keys-- nil)) (t (error "Keyword argument %s not one of (:$query :$source :$multiline)" (car --cl-keys--)))))) (progn (setq helm-swoop-synchronizing-window (selected-window)) (setq helm-swoop-last-point (cons (point) (buffer-name (current-buffer)))) (setq helm-swoop-last-line-info (cons (current-buffer) (line-number-at-pos))) (if (boundp (quote helm-swoop-last-query)) nil (set (make-local-variable (quote helm-swoop-last-query)) "")) (setq helm-swoop-target-buffer (current-buffer)) (helm-swoop--set-prefix $multiline) (setq helm-swoop-line-overlay (make-overlay (point) (point))) (overlay-put helm-swoop-line-overlay (quote face) (if (< 1 helm-swoop-last-prefix-number) (quote helm-swoop-target-line-block-face) (quote helm-swoop-target-line-face))) (cond ((not (boundp (quote helm-swoop-cache))) (set (make-local-variable (quote helm-swoop-cache)) nil)) ((buffer-modified-p) (setq helm-swoop-cache nil))) (cond ((not (boundp (quote helm-swoop-list-cache))) (set (make-local-variable (quote helm-swoop-list-cache)) nil)) ((buffer-modified-p) (setq helm-swoop-list-cache nil))) (unwind-protect (progn (ad-enable-advice (quote helm-next-line) (quote around) (quote helm-swoop-next-line)) (ad-activate (quote helm-next-line)) (ad-enable-advice (quote helm-previous-line) (quote around) (quote helm-swoop-previous-line)) (ad-activate (quote helm-previous-line)) (ad-enable-advice (quote helm-move--next-line-fn) (quote around) (quote helm-multi-swoop-next-line-cycle)) (ad-activate (quote helm-move--next-line-fn)) (ad-enable-advice (quote helm-move--previous-line-fn) (quote around) (quote helm-multi-swoop-previous-line-cycle)) (ad-activate (quote helm-move--previous-line-fn)) (add-hook (quote helm-update-hook) (quote helm-swoop--pattern-match)) (add-hook (quote helm-after-update-hook) (quote helm-swoop--keep-nearest-position) t) (cond ($query (if (string-match "\\(\\^\\[0\\-9\\]\\+\\.\\)\\(.*\\)" $query) $query $query)) (mark-active (let (...) (if ... ... ...))) ((setq $query (funcall helm-swoop-pre-input-function))) (t (setq $query ""))) (helm-swoop--recenter) (move-beginning-of-line 1) (helm-swoop--target-line-overlay-move) (let ((helm-display-function helm-swoop-split-window-function) (helm-display-source-at-screen-top nil) (helm-completion-window-scroll-margin 5)) (helm :sources (or $source (if ... ... ...)) :buffer helm-swoop-buffer :input $query :prompt helm-swoop-prompt :preselect (if (string-match "^[ \n ]*$" ...) (save-excursion ...) (format "^%s " ...)) :candidate-number-limit helm-swoop-candidate-number-limit))) (helm-swoop--restore)))) | |
helm-swoop() | |
call-interactively(helm-swoop nil nil) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment