├── .gitattributes ├── README.md └── ace-jump-mode.el /.gitattributes: -------------------------------------------------------------------------------- 1 | # Declare files that will always have CRLF line endings on checkout. 2 | *.el text eol=lf 3 | *.md text eol=lf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ace Jump Mode 2 | ============= 3 | 4 | Ace jump mode is a minor mode of emacs, which help you to move the 5 | cursor within Emacs. You can move your cursor to **ANY** position ( 6 | across window and frame ) in emacs by using only **3 times key 7 | press**. Have a try and I am sure you will love it. 8 | 9 | 10 | What's new in 2.0 version? 11 | -------------------------- 12 | 13 | In 1.0 version, ace jump mode can only work in current window. 14 | 15 | However, this limitation has already been broken in 2.0 version. With 16 | ace jump mode 2.0, you can jump to any position you wish across the 17 | bounder of window(c-x 2/3) and even frame(c-x 5). 18 | 19 | 20 | Is there a demo to show the usage? 21 | ------------------------------------ 22 | Here is a simple one for you to learn how to use ace jump, [Demo](http://dl.dropbox.com/u/3254819/AceJumpModeDemo/AceJumpDemo.htm) 23 | 24 | Usage: 25 | 26 | "C-c SPC" ==> ace-jump-word-mode 27 | 28 | >enter first character of a word, select the highlighted key to move to it. 29 | 30 | "C-u C-c SPC" ==> ace-jump-char-mode 31 | 32 | >enter a character for query, select the highlighted key to move to it. 33 | 34 | "C-u C-u C-c SPC" ==> ace-jump-line-mode 35 | 36 | >each non-empty line will be marked, select the highlighted key to move to it. 37 | 38 | Thanks emacsrocks website, they make a great show to ace jump mode, 39 | refer to [here](http://www.youtube.com/watch?feature=player_embedded&v=UZkpmegySnc#!). 40 | 41 | 42 | How to install it? 43 | ------------------ 44 | 45 | ;; 46 | ;; ace jump mode major function 47 | ;; 48 | (add-to-list 'load-path "/full/path/where/ace-jump-mode.el/in/") 49 | (autoload 50 | 'ace-jump-mode 51 | "ace-jump-mode" 52 | "Emacs quick move minor mode" 53 | t) 54 | ;; you can select the key you prefer to 55 | (define-key global-map (kbd "C-c SPC") 'ace-jump-mode) 56 | 57 | 58 | 59 | ;; 60 | ;; enable a more powerful jump back function from ace jump mode 61 | ;; 62 | (autoload 63 | 'ace-jump-mode-pop-mark 64 | "ace-jump-mode" 65 | "Ace jump back:-)" 66 | t) 67 | (eval-after-load "ace-jump-mode" 68 | '(ace-jump-mode-enable-mark-sync)) 69 | (define-key global-map (kbd "C-x SPC") 'ace-jump-mode-pop-mark) 70 | 71 | ;;If you use viper mode : 72 | (define-key viper-vi-global-user-map (kbd "SPC") 'ace-jump-mode) 73 | ;;If you use evil 74 | (define-key evil-normal-state-map (kbd "SPC") 'ace-jump-mode) 75 | 76 | 77 | I want to know more about customized configuration? 78 | --------------------------------------------------- 79 | See [FAQ ](http://github.com/winterTTr/ace-jump-mode/wiki/AceJump-FAQ) -------------------------------------------------------------------------------- /ace-jump-mode.el: -------------------------------------------------------------------------------- 1 | ;;; ace-jump-mode.el --- a quick cursor location minor mode for emacs -*- coding: utf-8-unix -*- 2 | 3 | ;; Copyright (C) 2012 Free Software Foundation, Inc. 4 | 5 | ;; Author : winterTTr 6 | ;; URL : https://github.com/winterTTr/ace-jump-mode/ 7 | ;; Version : 2.0.RC 8 | ;; Keywords : motion, location, cursor 9 | 10 | ;; This file is part of GNU Emacs. 11 | 12 | ;; GNU Emacs is free software: you can redistribute it and/or modify 13 | ;; it under the terms of the GNU General Public License as published by 14 | ;; the Free Software Foundation, either version 3 of the License, or 15 | ;; (at your option) any later version. 16 | 17 | ;; GNU Emacs is distributed in the hope that it will be useful, 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | ;; GNU General Public License for more details. 21 | 22 | ;; You should have received a copy of the GNU General Public License 23 | ;; along with GNU Emacs. If not, see . 24 | 25 | ;;; Commentary: 26 | 27 | ;;; INTRODUCTION 28 | ;; 29 | 30 | ;; What's this? 31 | ;; 32 | ;; It is a minor mode for Emacs. It can help you to move your cursor 33 | ;; to ANY position in emacs by using only 3 times key press. 34 | 35 | ;; Where does ace jump mode come from ? 36 | ;; 37 | ;; I firstly see such kind of moving style is in a vim plugin called 38 | ;; EasyMotion. It really attract me a lot. So I decide to write 39 | ;; one for Emacs and MAKE IT BETTER. 40 | ;; 41 | ;; So I want to thank to : 42 | ;; Bartlomiej P. for his PreciseJump 43 | ;; Kim Silkebækken for his EasyMotion 44 | 45 | 46 | ;; What's ace-jump-mode ? 47 | ;; 48 | ;; ace-jump-mode is an fast/direct cursor location minor mode. It will 49 | ;; create the N-Branch search tree internal and marks all the possible 50 | ;; position with predefined keys in within the whole emacs view. 51 | ;; Allowing you to move to the character/word/line almost directly. 52 | ;; 53 | 54 | ;;; Usage 55 | ;; 56 | ;; Add the following code to your init file, of course you can select 57 | ;; the key that you prefer to. 58 | ;; ---------------------------------------------------------- 59 | ;; ;; 60 | ;; ;; ace jump mode major function 61 | ;; ;; 62 | ;; (add-to-list 'load-path "/full/path/where/ace-jump-mode.el/in/") 63 | ;; (autoload 64 | ;; 'ace-jump-mode 65 | ;; "ace-jump-mode" 66 | ;; "Emacs quick move minor mode" 67 | ;; t) 68 | ;; ;; you can select the key you prefer to 69 | ;; (define-key global-map (kbd "C-c SPC") 'ace-jump-mode) 70 | ;; 71 | ;; ;; 72 | ;; ;; enable a more powerful jump back function from ace jump mode 73 | ;; ;; 74 | ;; (autoload 75 | ;; 'ace-jump-mode-pop-mark 76 | ;; "ace-jump-mode" 77 | ;; "Ace jump back:-)" 78 | ;; t) 79 | ;; (eval-after-load "ace-jump-mode" 80 | ;; '(ace-jump-mode-enable-mark-sync)) 81 | ;; (define-key global-map (kbd "C-x SPC") 'ace-jump-mode-pop-mark) 82 | ;; 83 | ;; ;;If you use viper mode : 84 | ;; (define-key viper-vi-global-user-map (kbd "SPC") 'ace-jump-mode) 85 | ;; ;;If you use evil 86 | ;; (define-key evil-normal-state-map (kbd "SPC") 'ace-jump-mode) 87 | ;; ---------------------------------------------------------- 88 | 89 | ;;; For more information 90 | ;; Intro Doc: https://github.com/winterTTr/ace-jump-mode/wiki 91 | ;; FAQ : https://github.com/winterTTr/ace-jump-mode/wiki/AceJump-FAQ 92 | 93 | ;;; Code: 94 | 95 | (require 'cl) 96 | 97 | ;;;; ============================================ 98 | ;;;; Utilities for ace-jump-mode 99 | ;;;; ============================================ 100 | 101 | ;; --------------------- 102 | ;; aj-position 103 | ;; --------------------- 104 | 105 | ;; make a position in a visual area 106 | (defstruct aj-position offset visual-area) 107 | 108 | (defmacro aj-position-buffer (aj-pos) 109 | "Get the buffer object from `aj-position'." 110 | `(aj-visual-area-buffer (aj-position-visual-area ,aj-pos))) 111 | 112 | (defmacro aj-position-window (aj-pos) 113 | "Get the window object from `aj-position'." 114 | `(aj-visual-area-window (aj-position-visual-area ,aj-pos))) 115 | 116 | (defmacro aj-position-frame (aj-pos) 117 | "Get the frame object from `aj-position'." 118 | `(aj-visual-area-frame (aj-position-visual-area ,aj-pos))) 119 | 120 | (defmacro aj-position-recover-buffer (aj-pos) 121 | "Get the recover-buffer object from `aj-position'." 122 | `(aj-visual-area-recover-buffer (aj-position-visual-area ,aj-pos))) 123 | 124 | 125 | ;; --------------------- 126 | ;; aj-visual-area 127 | ;; --------------------- 128 | 129 | ;; a record for all the possible visual area 130 | ;; a visual area is a window that showing some buffer in some frame. 131 | (defstruct aj-visual-area buffer window frame recover-buffer) 132 | 133 | 134 | ;; --------------------- 135 | ;; a FIFO queue implementation 136 | ;; --------------------- 137 | (defstruct aj-queue head tail) 138 | 139 | (defun aj-queue-push (item q) 140 | "enqueue" 141 | (let ( (head (aj-queue-head q) ) 142 | (tail (aj-queue-tail q) ) 143 | (c (list item) ) ) 144 | (cond 145 | ((null (aj-queue-head q)) 146 | (setf (aj-queue-head q) c) 147 | (setf (aj-queue-tail q) c)) 148 | (t 149 | (setf (cdr (aj-queue-tail q)) c) 150 | (setf (aj-queue-tail q) c))))) 151 | 152 | (defun aj-queue-pop (q) 153 | "dequeue" 154 | (if (null (aj-queue-head q)) 155 | (error "[AceJump] Interal Error: Empty queue")) 156 | 157 | (let ((ret (aj-queue-head q))) 158 | (if (eq ret (aj-queue-tail q)) 159 | ;; only one item left 160 | (progn 161 | (setf (aj-queue-head q) nil) 162 | (setf (aj-queue-tail q) nil)) 163 | ;; multi item left, move forward the head 164 | (setf (aj-queue-head q) (cdr ret))) 165 | (car ret))) 166 | 167 | 168 | 169 | ;;; main code start here 170 | 171 | ;; register as a minor mode 172 | (or (assq 'ace-jump-mode minor-mode-alist) 173 | (nconc minor-mode-alist 174 | (list '(ace-jump-mode ace-jump-mode)))) 175 | 176 | ;; custoize variable 177 | (defvar ace-jump-word-mode-use-query-char t 178 | "If we need to ask for the query char before enter `ace-jump-word-mode'") 179 | 180 | (defvar ace-jump-mode-case-fold case-fold-search 181 | "If non-nil, the ace-jump mode will ignore case. 182 | 183 | The default value is set to the same as `case-fold-search'.") 184 | 185 | (defvar ace-jump-mode-mark-ring nil 186 | "The list that is used to store the history for jump back.") 187 | 188 | (defvar ace-jump-mode-mark-ring-max 100 189 | "The max length of `ace-jump-mode-mark-ring'") 190 | 191 | 192 | (defvar ace-jump-mode-gray-background t 193 | "By default, when there is more than one candidate, the ace 194 | jump mode will gray the background and then mark the possible 195 | candidate position. Set this to nil means do not gray 196 | background.") 197 | 198 | (defvar ace-jump-mode-scope 'global 199 | "Define what is the scope that ace-jump-mode works. 200 | 201 | Now, there are four kinds of values for this: 202 | 1. 'global : ace jump can work across any window and frame, this is also the default. 203 | 2. 'frame : ace jump will work for the all windows in current frame. 204 | 3. 'visible : ace jump will work for all windows in visible frames. 205 | 3. 'window : ace jump will only work on current window only. 206 | This is the same behavior for 1.0 version.") 207 | 208 | (defvar ace-jump-mode-detect-punc t 209 | "When this is non-nil, the ace jump word mode will detect the 210 | char that is not alpha or number. Then, if the query char is a 211 | printable punctuaction, we will use char mode to start the ace 212 | jump mode. If it is nil, an error will come up when 213 | non-alpha-number is given under word mode.") 214 | 215 | 216 | (defvar ace-jump-mode-submode-list 217 | '(ace-jump-word-mode 218 | ace-jump-char-mode 219 | ace-jump-line-mode) 220 | "*The mode list when start ace jump mode. 221 | The sequence is the calling sequence when give prefix argument. 222 | 223 | Such as: 224 | If you use the default sequence, which is 225 | '(ace-jump-word-mode 226 | ace-jump-char-mode 227 | ace-jump-line-mode) 228 | and using key to start up ace jump mode, such as 'C-c SPC', 229 | then the usage to start each mode is as below: 230 | 231 | C-c SPC ==> ace-jump-word-mode 232 | C-u C-c SPC ==> ace-jump-char-mode 233 | C-u C-u C-c SPC ==> ace-jump-line-mode 234 | 235 | Currently, the valid submode is: 236 | `ace-jump-word-mode' 237 | `ace-jump-char-mode' 238 | `ace-jump-line-mode' 239 | 240 | ") 241 | 242 | (defvar ace-jump-mode-move-keys 243 | (nconc (loop for i from ?a to ?z collect i) 244 | (loop for i from ?A to ?Z collect i)) 245 | "*The keys that used to move when enter AceJump mode. 246 | Each key should only an printable character, whose name will 247 | fill each possible location. 248 | 249 | If you want your own moving keys, you can custom that as follow, 250 | for example, you only want to use lower case character: 251 | \(setq ace-jump-mode-move-keys (loop for i from ?a to ?z collect i)) ") 252 | 253 | 254 | ;;; some internal variable for ace jump 255 | (defvar ace-jump-mode nil 256 | "AceJump minor mode.") 257 | (defvar ace-jump-background-overlay-list nil 258 | "Background overlay which will grey all the display.") 259 | (defvar ace-jump-search-tree nil 260 | "N-branch Search tree. Every leaf node holds the overlay that 261 | is used to highlight the target positions.") 262 | (defvar ace-jump-query-char nil 263 | "Save the query char used between internal mode.") 264 | (defvar ace-jump-current-mode nil 265 | "Save the current mode. 266 | See `ace-jump-mode-submode-list' for possible value.") 267 | 268 | (defvar ace-jump-sync-emacs-mark-ring nil 269 | "When this variable is not-nil, everytime `ace-jump-mode-pop-mark' is called, 270 | ace jump will try to remove the same mark from buffer local mark 271 | ring and global-mark-ring, which help you to sync the mark 272 | information between emacs and ace jump. 273 | 274 | Note, never try to set this variable manually, this is for ace 275 | jump internal use. If you want to change it, use 276 | `ace-jump-mode-enable-mark-sync' or 277 | `ace-jump-mode-disable-mark-sync'.") 278 | 279 | (defvar ace-jump-search-filter nil 280 | "This should be nil or a point-dependant predicate 281 | that `ace-jump-search-candidate' will use as an additional filter.") 282 | 283 | (defgroup ace-jump nil 284 | "ace jump group" 285 | :group 'convenience) 286 | 287 | ;;; define the face 288 | (defface ace-jump-face-background 289 | '((t (:foreground "gray40"))) 290 | "Face for background of AceJump motion" 291 | :group 'ace-jump) 292 | 293 | 294 | (defface ace-jump-face-foreground 295 | '((((class color)) (:foreground "red" :underline nil)) 296 | (((background dark)) (:foreground "gray100" :underline nil)) 297 | (((background light)) (:foreground "gray0" :underline nil)) 298 | (t (:foreground "gray100" :underline nil))) 299 | "Face for foreground of AceJump motion" 300 | :group 'ace-jump) 301 | 302 | 303 | (defvar ace-jump-mode-before-jump-hook nil 304 | "Function(s) to call just before moving the cursor to a selected match") 305 | 306 | (defvar ace-jump-mode-end-hook nil 307 | "Function(s) to call when ace-jump-mode is going to end up") 308 | 309 | (defvar ace-jump-allow-invisible nil 310 | "Control if ace-jump should select the invisible char as candidate. 311 | Normally, the ace jump mark cannot be seen if the target character is invisible. 312 | So default to be nil, which will not include those invisible character as candidate.") 313 | 314 | 315 | (defun ace-jump-char-category ( query-char ) 316 | "Detect the type of the char. 317 | For the ascii table, refer to http://www.asciitable.com/ 318 | 319 | There is four possible return value: 320 | 1. 'digit: the number character 321 | 2. 'alpha: A-Z and a-z 322 | 3. 'punc : all the printable punctuaiton 323 | 4. 'other: all the others" 324 | (cond 325 | ;; digit 326 | ((and (>= query-char #x30) (<= query-char #x39)) 327 | 'digit) 328 | ((or 329 | ;; capital letter 330 | (and (>= query-char #x41) (<= query-char #x5A)) 331 | ;; lowercase letter 332 | (and (>= query-char #x61) (<= query-char #x7A))) 333 | 'alpha) 334 | ((or 335 | ;; tab 336 | (equal query-char #x9) 337 | ;; punc before digit 338 | (and (>= query-char #x20) (<= query-char #x2F)) 339 | ;; punc after digit before capital letter 340 | (and (>= query-char #x3A) (<= query-char #x40)) 341 | ;; punc after capital letter before lowercase letter 342 | (and (>= query-char #x5B) (<= query-char #x60)) 343 | ;; punc after lowercase letter 344 | (and (>= query-char #x7B) (<= query-char #x7E))) 345 | 'punc) 346 | (t 347 | 'other))) 348 | 349 | 350 | (defun ace-jump-search-candidate (re-query-string visual-area-list) 351 | "Search the RE-QUERY-STRING in current view, and return the candidate position list. 352 | RE-QUERY-STRING should be an valid regex used for `search-forward-regexp'. 353 | 354 | You can control whether use the case sensitive or not by `ace-jump-mode-case-fold'. 355 | 356 | Every possible `match-beginning' will be collected. 357 | The returned value is a list of `aj-position' record." 358 | (loop for va in visual-area-list 359 | append (let* ((current-window (aj-visual-area-window va)) 360 | (start-point (window-start current-window)) 361 | (end-point (window-end current-window t))) 362 | (with-selected-window current-window 363 | (save-excursion 364 | (goto-char start-point) 365 | (let ((case-fold-search ace-jump-mode-case-fold)) 366 | (loop while (re-search-forward re-query-string nil t) 367 | until (or 368 | (> (point) end-point) 369 | (eobp)) 370 | if (and (or ace-jump-allow-invisible (not (invisible-p (match-beginning 0)))) 371 | (or (null ace-jump-search-filter) 372 | (ignore-errors 373 | (funcall ace-jump-search-filter)))) 374 | collect (make-aj-position :offset (match-beginning 0) 375 | :visual-area va) 376 | ;; when we use "^" to search line mode, 377 | ;; re-search-backward will not move one 378 | ;; char after search success, as line 379 | ;; begin is not a valid visible char. 380 | ;; We need to help it to move forward. 381 | do (if (string-equal re-query-string "^") 382 | (goto-char (1+ (match-beginning 0))))))))))) 383 | 384 | (defun ace-jump-tree-breadth-first-construct (total-leaf-node max-child-node) 385 | "Constrct the search tree, each item in the tree is a cons cell. 386 | The (car tree-node) is the type, which should be only 'branch or 'leaf. 387 | The (cdr tree-node) is data stored in a leaf when type is 'leaf, 388 | while a child node list when type is 'branch" 389 | (let ((left-leaf-node (- total-leaf-node 1)) 390 | (q (make-aj-queue)) 391 | (node nil) 392 | (root (cons 'leaf nil)) ) 393 | ;; we push the node into queue and make candidate-sum -1, so 394 | ;; create the start condition for the while loop 395 | (aj-queue-push root q) 396 | (while (> left-leaf-node 0) 397 | (setq node (aj-queue-pop q)) 398 | ;; when a node is picked up from stack, it will be changed to a 399 | ;; branch node, we lose a leaft node 400 | (setf (car node) 'branch) 401 | ;; so we need to add the sum of leaf nodes that we wish to create 402 | (setq left-leaf-node (1+ left-leaf-node)) 403 | (if (<= left-leaf-node max-child-node) 404 | ;; current child can fill the left leaf 405 | (progn 406 | (setf (cdr node) 407 | (loop for i from 1 to left-leaf-node 408 | collect (cons 'leaf nil))) 409 | ;; so this should be the last action for while 410 | (setq left-leaf-node 0)) 411 | ;; the child can not cover the left leaf 412 | (progn 413 | ;; fill as much as possible. Push them to queue, so it have 414 | ;; the oppotunity to become 'branch node if necessary 415 | (setf (cdr node) 416 | (loop for i from 1 to max-child-node 417 | collect (let ((n (cons 'leaf nil))) 418 | (aj-queue-push n q) 419 | n))) 420 | (setq left-leaf-node (- left-leaf-node max-child-node))))) 421 | ;; return the root node 422 | root)) 423 | 424 | (defun ace-jump-tree-preorder-traverse (tree &optional leaf-func branch-func) 425 | "we move over tree via preorder, and call BRANCH-FUNC on each branch 426 | node and call LEAF-FUNC on each leaf node" 427 | ;; use stack to do preorder traverse 428 | (let ((s (list tree))) 429 | (while (not (null s)) 430 | ;; pick up one from stack 431 | (let ((node (car s))) 432 | ;; update stack 433 | (setq s (cdr s)) 434 | (cond 435 | ((eq (car node) 'branch) 436 | ;; a branch node 437 | (when branch-func 438 | (funcall branch-func node)) 439 | ;; push all child node into stack 440 | (setq s (append (cdr node) s))) 441 | ((eq (car node) 'leaf) 442 | (when leaf-func 443 | (funcall leaf-func node))) 444 | (t 445 | (message "[AceJump] Internal Error: invalid tree node type"))))))) 446 | 447 | 448 | (defun ace-jump-populate-overlay-to-search-tree (tree candidate-list) 449 | "Populate the overlay to search tree, every leaf will give one overlay" 450 | 451 | (lexical-let* (;; create the locally dynamic variable for the following function 452 | (position-list candidate-list) 453 | 454 | ;; make the function to create overlay for each leaf node, 455 | ;; here we only create each overlay for each candidate 456 | ;; position, , but leave the 'display property to be empty, 457 | ;; which will be fill in "update-overlay" function 458 | (func-create-overlay (lambda (node) 459 | (let* ((p (car position-list)) 460 | (o (aj-position-offset p)) 461 | (w (aj-position-window p)) 462 | (b (aj-position-buffer p)) 463 | ;; create one char overlay 464 | (ol (make-overlay o (1+ o) b))) 465 | ;; update leaf node to remember the ol 466 | (setf (cdr node) ol) 467 | (overlay-put ol 'face 'ace-jump-face-foreground) 468 | ;; this is important, because sometimes the different 469 | ;; window may dispaly the same buffer, in that case, 470 | ;; overlay for different window (but the same buffer) 471 | ;; will show at the same time on both window 472 | ;; So we make it only on the specific window 473 | (overlay-put ol 'window w) 474 | ;; associate the aj-position data with overlay 475 | ;; so that we can use it to do the final jump 476 | (overlay-put ol 'aj-data p) 477 | ;; next candidate node 478 | (setq position-list (cdr position-list)))))) 479 | (ace-jump-tree-preorder-traverse tree func-create-overlay) 480 | tree)) 481 | 482 | 483 | (defun ace-jump-delete-overlay-in-search-tree (tree) 484 | "Delete all the overlay in search tree leaf node" 485 | (let ((func-delete-overlay (lambda (node) 486 | (delete-overlay (cdr node)) 487 | (setf (cdr node) nil)))) 488 | (ace-jump-tree-preorder-traverse tree func-delete-overlay))) 489 | 490 | (defun ace-jump-buffer-substring (pos) 491 | "Get the char under the POS, which is aj-position structure." 492 | (let* ((w (aj-position-window pos)) 493 | (offset (aj-position-offset pos))) 494 | (with-selected-window w 495 | (buffer-substring offset (1+ offset))))) 496 | 497 | (defun ace-jump-update-overlay-in-search-tree (tree keys) 498 | "Update overlay 'display property using each name in keys" 499 | (lexical-let* (;; create dynamic variable for following function 500 | (key ?\0) 501 | ;; populdate each leaf node to be the specific key, 502 | ;; this only update 'display' property of overlay, 503 | ;; so that user can see the key from screen and select 504 | (func-update-overlay 505 | (lambda (node) 506 | (let ((ol (cdr node))) 507 | (overlay-put 508 | ol 509 | 'display 510 | (concat (make-string 1 key) 511 | (let* ((pos (overlay-get ol 'aj-data)) 512 | (subs (ace-jump-buffer-substring pos))) 513 | (cond 514 | ;; when tab, we use more space to prevent screen 515 | ;; from messing up 516 | ((string-equal subs "\t") 517 | (make-string (1- tab-width) ? )) 518 | ;; when enter, we need to add one more enter 519 | ;; to make the screen not change 520 | ((string-equal subs "\n") 521 | "\n") 522 | (t 523 | ;; there are wide-width characters 524 | ;; so, we need paddings 525 | (make-string (max 0 (1- (string-width subs))) ? )))))))))) 526 | (loop for k in keys 527 | for n in (cdr tree) 528 | do (progn 529 | ;; update "key" variable so that the function can use 530 | ;; the correct context 531 | (setq key k) 532 | (if (eq (car n) 'branch) 533 | (ace-jump-tree-preorder-traverse n 534 | func-update-overlay) 535 | (funcall func-update-overlay n)))))) 536 | 537 | 538 | 539 | (defun ace-jump-list-visual-area() 540 | "Based on `ace-jump-mode-scope', search the possible buffers that is showing now." 541 | (cond 542 | ((eq ace-jump-mode-scope 'global) 543 | (loop for f in (frame-list) 544 | append (loop for w in (window-list f) 545 | collect (make-aj-visual-area :buffer (window-buffer w) 546 | :window w 547 | :frame f)))) 548 | ((eq ace-jump-mode-scope 'visible) 549 | (loop for f in (frame-list) 550 | if (eq t (frame-visible-p f)) 551 | append (loop for w in (window-list f) 552 | collect (make-aj-visual-area :buffer (window-buffer w) 553 | :window w 554 | :frame f)))) 555 | ((eq ace-jump-mode-scope 'frame) 556 | (loop for w in (window-list (selected-frame)) 557 | collect (make-aj-visual-area :buffer (window-buffer w) 558 | :window w 559 | :frame (selected-frame)))) 560 | ((eq ace-jump-mode-scope 'window) 561 | (list 562 | (make-aj-visual-area :buffer (current-buffer) 563 | :window (selected-window) 564 | :frame (selected-frame)))) 565 | (t 566 | (error "[AceJump] Invalid ace-jump-mode-scope, please check your configuration")))) 567 | 568 | 569 | 570 | (defun ace-jump-do( re-query-string ) 571 | "The main function to start the AceJump mode. 572 | QUERY-STRING should be a valid regexp string, which finally pass to `search-forward-regexp'. 573 | 574 | You can constrol whether use the case sensitive via `ace-jump-mode-case-fold'. 575 | " 576 | ;; we check the move key to make it valid, cause it can be customized by user 577 | (if (or (null ace-jump-mode-move-keys) 578 | (< (length ace-jump-mode-move-keys) 2) 579 | (not (every #'characterp ace-jump-mode-move-keys))) 580 | (error "[AceJump] Invalid move keys: check ace-jump-mode-move-keys")) 581 | ;; search candidate position 582 | (let* ((visual-area-list (ace-jump-list-visual-area)) 583 | (candidate-list (ace-jump-search-candidate re-query-string visual-area-list))) 584 | (cond 585 | ;; cannot find any one 586 | ((null candidate-list) 587 | (setq ace-jump-current-mode nil) 588 | (error "[AceJump] No one found")) 589 | ;; we only find one, so move to it directly 590 | ((eq (cdr candidate-list) nil) 591 | (ace-jump-push-mark) 592 | (run-hooks 'ace-jump-mode-before-jump-hook) 593 | (ace-jump-jump-to (car candidate-list)) 594 | (message "[AceJump] One candidate, move to it directly") 595 | (run-hooks 'ace-jump-mode-end-hook)) 596 | ;; more than one, we need to enter AceJump mode 597 | (t 598 | ;; create background for each visual area 599 | (if ace-jump-mode-gray-background 600 | (setq ace-jump-background-overlay-list 601 | (loop for va in visual-area-list 602 | collect (let* ((w (aj-visual-area-window va)) 603 | (b (aj-visual-area-buffer va)) 604 | (ol (make-overlay (window-start w) 605 | (window-end w) 606 | b))) 607 | (overlay-put ol 'face 'ace-jump-face-background) 608 | ol)))) 609 | 610 | ;; construct search tree and populate overlay into tree 611 | (setq ace-jump-search-tree 612 | (ace-jump-tree-breadth-first-construct (length candidate-list) 613 | (length ace-jump-mode-move-keys))) 614 | (ace-jump-populate-overlay-to-search-tree ace-jump-search-tree 615 | candidate-list) 616 | (ace-jump-update-overlay-in-search-tree ace-jump-search-tree 617 | ace-jump-mode-move-keys) 618 | 619 | ;; do minor mode configuration 620 | (cond 621 | ((eq ace-jump-current-mode 'ace-jump-char-mode) 622 | (setq ace-jump-mode " AceJump - Char")) 623 | ((eq ace-jump-current-mode 'ace-jump-word-mode) 624 | (setq ace-jump-mode " AceJump - Word")) 625 | ((eq ace-jump-current-mode 'ace-jump-line-mode) 626 | (setq ace-jump-mode " AceJump - Line")) 627 | (t 628 | (setq ace-jump-mode " AceJump"))) 629 | (force-mode-line-update) 630 | 631 | 632 | ;; override the local key map 633 | (setq overriding-local-map 634 | (let ( (map (make-keymap)) ) 635 | (dolist (key-code ace-jump-mode-move-keys) 636 | (define-key map (make-string 1 key-code) 'ace-jump-move)) 637 | (define-key map (kbd "C-c C-c") 'ace-jump-quick-exchange) 638 | (define-key map [t] 'ace-jump-done) 639 | map)) 640 | 641 | (add-hook 'mouse-leave-buffer-hook 'ace-jump-done) 642 | (add-hook 'kbd-macro-termination-hook 'ace-jump-done))))) 643 | 644 | 645 | (defun ace-jump-jump-to (position) 646 | "Jump to the POSITION, which is a `aj-position' structure storing the position information" 647 | (let ((offset (aj-position-offset position)) 648 | (frame (aj-position-frame position)) 649 | (window (aj-position-window position)) 650 | (buffer (aj-position-buffer position)) 651 | (line-mode-column 0)) 652 | 653 | ;; save the column before do line jump, so that we can jump to the 654 | ;; same column as previous line, make it the same behavior as C-n/C-p 655 | (if (eq ace-jump-current-mode 'ace-jump-line-mode) 656 | (setq line-mode-column (current-column))) 657 | 658 | ;; focus to the frame 659 | (if (and (frame-live-p frame) 660 | (not (eq frame (selected-frame)))) 661 | (select-frame-set-input-focus (window-frame window))) 662 | 663 | ;; select the correct window 664 | (if (and (window-live-p window) 665 | (not (eq window (selected-window)))) 666 | (select-window window)) 667 | 668 | ;; swith to buffer 669 | (if (and (buffer-live-p buffer) 670 | (not (eq buffer (window-buffer window)))) 671 | (switch-to-buffer buffer)) 672 | 673 | ;; move to correct position 674 | (if (and (buffer-live-p buffer) 675 | (eq (current-buffer) buffer)) 676 | (goto-char offset)) 677 | 678 | ;; recover to the same column if we use the line jump mode 679 | (if (eq ace-jump-current-mode 'ace-jump-line-mode) 680 | (move-to-column line-mode-column)) 681 | )) 682 | 683 | (defun ace-jump-push-mark () 684 | "Push the current position information onto the `ace-jump-mode-mark-ring'." 685 | ;; add mark to the emacs basic push mark 686 | (push-mark (point) t) 687 | ;; we also push the mark on the `ace-jump-mode-mark-ring', which has 688 | ;; more information for better jump back 689 | (let ((pos (make-aj-position :offset (point) 690 | :visual-area (make-aj-visual-area :buffer (current-buffer) 691 | :window (selected-window) 692 | :frame (selected-frame))))) 693 | (setq ace-jump-mode-mark-ring (cons pos ace-jump-mode-mark-ring))) 694 | ;; when exeed the max count, discard the last one 695 | (if (> (length ace-jump-mode-mark-ring) ace-jump-mode-mark-ring-max) 696 | (setcdr (nthcdr (1- ace-jump-mode-mark-ring-max) ace-jump-mode-mark-ring) nil))) 697 | 698 | 699 | ;;;###autoload 700 | (defun ace-jump-mode-pop-mark () 701 | "Pop up a postion from `ace-jump-mode-mark-ring', and jump back to that position" 702 | (interactive) 703 | ;; we jump over the killed buffer position 704 | (while (and ace-jump-mode-mark-ring 705 | (not (buffer-live-p (aj-position-buffer 706 | (car ace-jump-mode-mark-ring))))) 707 | (setq ace-jump-mode-mark-ring (cdr ace-jump-mode-mark-ring))) 708 | 709 | (if (null ace-jump-mode-mark-ring) 710 | ;; no valid history exist 711 | (error "[AceJump] No more history")) 712 | 713 | (if ace-jump-sync-emacs-mark-ring 714 | (let ((p (car ace-jump-mode-mark-ring))) 715 | ;; if we are jump back in the current buffer, that means we 716 | ;; only need to sync the buffer local mark-ring 717 | (if (eq (current-buffer) (aj-position-buffer p)) 718 | (if (equal (aj-position-offset p) (marker-position (mark-marker))) 719 | ;; if the current marker is the same as where we need 720 | ;; to jump back, we do the same as pop-mark actually, 721 | ;; copy implementation from pop-mark, cannot use it 722 | ;; directly, as there is advice on it 723 | (when mark-ring 724 | (setq mark-ring (nconc mark-ring (list (copy-marker (mark-marker))))) 725 | (set-marker (mark-marker) (+ 0 (car mark-ring)) (current-buffer)) 726 | (move-marker (car mark-ring) nil) 727 | (setq mark-ring (cdr mark-ring)) 728 | (deactivate-mark)) 729 | 730 | ;; But if there is other marker put before the wanted destination, the following scenario 731 | ;; 732 | ;; +---+---+---+---+ +---+---+---+---+ 733 | ;; Mark Ring | 2 | 3 | 4 | 5 | | 2 | 4 | 5 | 3 | 734 | ;; +---+---+---+---+ +---+---+---+---+ 735 | ;; +---+ +---+ 736 | ;; Marker | 1 | | 1 | <-- Maker (not changed) 737 | ;; +---+ +---+ 738 | ;; +---+ +---+ 739 | ;; Cursor | X | Pop up AJ mark 3 | 3 | <-- Cursor position 740 | ;; +---+ +---+ 741 | ;; +---+---+---+ +---+---+---+ 742 | ;; AJ Ring | 3 | 4 | 5 | | 4 | 5 | 3 | 743 | ;; +---+---+---+ +---+---+---+ 744 | ;; 745 | ;; So what we need to do, is put the found mark in mark-ring to the end 746 | (lexical-let ((po (aj-position-offset p))) 747 | (setq mark-ring 748 | (ace-jump-move-first-to-end-if mark-ring 749 | (lambda (x) 750 | (equal (marker-position x) po)))))) 751 | 752 | 753 | ;; when we jump back to another buffer, do as the 754 | ;; pop-global-mark does. But we move the marker with the 755 | ;; same target buffer to the end, not always the first one 756 | (lexical-let ((pb (aj-position-buffer p))) 757 | (setq global-mark-ring 758 | (ace-jump-move-first-to-end-if global-mark-ring 759 | (lambda (x) 760 | (eq (marker-buffer x) pb)))))))) 761 | 762 | 763 | ;; move the first element to the end of the ring 764 | (ace-jump-jump-to (car ace-jump-mode-mark-ring)) 765 | (setq ace-jump-mode-mark-ring (nconc (cdr ace-jump-mode-mark-ring) 766 | (list (car ace-jump-mode-mark-ring))))) 767 | 768 | (defun ace-jump-quick-exchange () 769 | "The function that we can use to quick exhange the current mode between 770 | word-mode and char-mode" 771 | (interactive) 772 | (cond 773 | ((eq ace-jump-current-mode 'ace-jump-char-mode) 774 | (if ace-jump-query-char 775 | ;; ace-jump-done will clean the query char, so we need to save it 776 | (let ((query-char ace-jump-query-char)) 777 | (ace-jump-done) 778 | (ace-jump-word-mode query-char)))) 779 | ((eq ace-jump-current-mode 'ace-jump-word-mode) 780 | (if ace-jump-query-char 781 | ;; ace-jump-done will clean the query char, so we need to save it 782 | (let ((query-char ace-jump-query-char)) 783 | (ace-jump-done) 784 | ;; restore the flag 785 | (ace-jump-char-mode query-char)))) 786 | ((eq ace-jump-current-mode 'ace-jump-line-mode) 787 | nil) 788 | (t 789 | nil))) 790 | 791 | 792 | 793 | 794 | ;;;###autoload 795 | (defun ace-jump-char-mode (query-char) 796 | "AceJump char mode" 797 | (interactive (list (read-char "Query Char:"))) 798 | 799 | ;; We should prevent recursion call this function. This can happen 800 | ;; when you trigger the key for ace jump again when already in ace 801 | ;; jump mode. So we stop the previous one first. 802 | (if ace-jump-current-mode (ace-jump-done)) 803 | 804 | (if (eq (ace-jump-char-category query-char) 'other) 805 | (error "[AceJump] Non-printable character")) 806 | 807 | ;; others : digit , alpha, punc 808 | (setq ace-jump-query-char query-char) 809 | (setq ace-jump-current-mode 'ace-jump-char-mode) 810 | (ace-jump-do (regexp-quote (make-string 1 query-char)))) 811 | 812 | 813 | ;;;###autoload 814 | (defun ace-jump-word-mode (head-char) 815 | "AceJump word mode. 816 | You can set `ace-jump-word-mode-use-query-char' to nil to prevent 817 | asking for a head char, that will mark all the word in current 818 | buffer." 819 | (interactive (list (if ace-jump-word-mode-use-query-char 820 | (read-char "Head Char:") 821 | nil))) 822 | 823 | ;; We should prevent recursion call this function. This can happen 824 | ;; when you trigger the key for ace jump again when already in ace 825 | ;; jump mode. So we stop the previous one first. 826 | (if ace-jump-current-mode (ace-jump-done)) 827 | 828 | (cond 829 | ((null head-char) 830 | ;; \< - start of word 831 | ;; \sw - word constituent 832 | (ace-jump-do "\\<\\sw")) 833 | ((memq (ace-jump-char-category head-char) 834 | '(digit alpha)) 835 | (setq ace-jump-query-char head-char) 836 | (setq ace-jump-current-mode 'ace-jump-word-mode) 837 | (ace-jump-do (concat "\\<" (make-string 1 head-char)))) 838 | ((eq (ace-jump-char-category head-char) 839 | 'punc) 840 | ;; we do not query punctuation under word mode 841 | (if (null ace-jump-mode-detect-punc) 842 | (error "[AceJump] Not a valid word constituent")) 843 | ;; we will use char mode to continue search 844 | (setq ace-jump-query-char head-char) 845 | (setq ace-jump-current-mode 'ace-jump-char-mode) 846 | (ace-jump-do (regexp-quote (make-string 1 head-char)))) 847 | (t 848 | (error "[AceJump] Non-printable character")))) 849 | 850 | 851 | ;;;###autoload 852 | (defun ace-jump-line-mode () 853 | "AceJump line mode. 854 | Marked each no empty line and move there" 855 | (interactive) 856 | 857 | ;; We should prevent recursion call this function. This can happen 858 | ;; when you trigger the key for ace jump again when already in ace 859 | ;; jump mode. So we stop the previous one first. 860 | (if ace-jump-current-mode (ace-jump-done)) 861 | 862 | (setq ace-jump-current-mode 'ace-jump-line-mode) 863 | (ace-jump-do "^")) 864 | 865 | ;;;###autoload 866 | (defun ace-jump-mode(&optional prefix) 867 | "AceJump mode is a minor mode for you to quick jump to a 868 | position in the curret view. 869 | There is three submode now: 870 | `ace-jump-char-mode' 871 | `ace-jump-word-mode' 872 | `ace-jump-line-mode' 873 | 874 | You can specify the sequence about which mode should enter 875 | by customize `ace-jump-mode-submode-list'. 876 | 877 | If you do not want to query char for word mode, you can change 878 | `ace-jump-word-mode-use-query-char' to nil. 879 | 880 | If you don't like the default move keys, you can change it by 881 | setting `ace-jump-mode-move-keys'. 882 | 883 | You can constrol whether use the case sensitive via 884 | `ace-jump-mode-case-fold'. 885 | " 886 | (interactive "p") 887 | (let ((index (/ prefix 4)) 888 | (submode-list-length (length ace-jump-mode-submode-list))) 889 | (if (< index 0) 890 | (error "[AceJump] Invalid prefix command")) 891 | (if (>= index submode-list-length) 892 | (setq index (1- submode-list-length))) 893 | (call-interactively (nth index ace-jump-mode-submode-list)))) 894 | 895 | (defun ace-jump-move () 896 | "move cursor based on user input" 897 | (interactive) 898 | (let* ((index (let ((ret (position (aref (this-command-keys) 0) 899 | ace-jump-mode-move-keys))) 900 | (if ret ret (length ace-jump-mode-move-keys)))) 901 | (node (nth index (cdr ace-jump-search-tree)))) 902 | (cond 903 | ;; we do not find key in search tree. This can happen, for 904 | ;; example, when there is only three selections in screen 905 | ;; (totally five move-keys), but user press the forth move key 906 | ((null node) 907 | (message "No such position candidate.") 908 | (ace-jump-done)) 909 | ;; this is a branch node, which means there need further 910 | ;; selection 911 | ((eq (car node) 'branch) 912 | (let ((old-tree ace-jump-search-tree)) 913 | ;; we use sub tree in next move, create a new root node 914 | ;; whose child is the sub tree nodes 915 | (setq ace-jump-search-tree (cons 'branch (cdr node))) 916 | (ace-jump-update-overlay-in-search-tree ace-jump-search-tree 917 | ace-jump-mode-move-keys) 918 | 919 | ;; this is important, we need remove the subtree first before 920 | ;; do delete, we set the child nodes to nil 921 | (setf (cdr node) nil) 922 | (ace-jump-delete-overlay-in-search-tree old-tree))) 923 | ;; if the node is leaf node, this is the final one 924 | ((eq (car node) 'leaf) 925 | ;; need to save aj data, as `ace-jump-done' will clean it 926 | (let ((aj-data (overlay-get (cdr node) 'aj-data))) 927 | (ace-jump-push-mark) 928 | (run-hooks 'ace-jump-mode-before-jump-hook) 929 | (ace-jump-jump-to aj-data)) 930 | (ace-jump-done) 931 | (run-hooks 'ace-jump-mode-end-hook)) 932 | (t 933 | (ace-jump-done) 934 | (error "[AceJump] Internal error: tree node type is invalid"))))) 935 | 936 | 937 | 938 | (defun ace-jump-done() 939 | "stop AceJump motion" 940 | (interactive) 941 | ;; clear the status flag 942 | (setq ace-jump-query-char nil) 943 | (setq ace-jump-current-mode nil) 944 | 945 | ;; clean the status line 946 | (setq ace-jump-mode nil) 947 | (force-mode-line-update) 948 | 949 | ;; delete background overlay 950 | (loop for ol in ace-jump-background-overlay-list 951 | do (delete-overlay ol)) 952 | (setq ace-jump-background-overlay-list nil) 953 | 954 | 955 | ;; delete overlays in search tree 956 | (ace-jump-delete-overlay-in-search-tree ace-jump-search-tree) 957 | (setq ace-jump-search-tree nil) 958 | 959 | (setq overriding-local-map nil) 960 | 961 | (remove-hook 'mouse-leave-buffer-hook 'ace-jump-done) 962 | (remove-hook 'kbd-macro-termination-hook 'ace-jump-done)) 963 | 964 | (defun ace-jump-kill-buffer(buffer) 965 | "Utility function to kill buffer for ace jump mode. 966 | We also need to handle the buffer which has clients on it" 967 | (if (and (boundp 'server-buffer-clients) 968 | server-buffer-clients) 969 | (server-buffer-done buffer t)) 970 | (kill-buffer buffer)) 971 | 972 | ;;;; ============================================ 973 | ;;;; advice to sync emacs mark ring 974 | ;;;; ============================================ 975 | 976 | (defun ace-jump-move-to-end-if ( l pred ) 977 | "Move all the element in a list to the end of list if it make 978 | the PRED to return non-nil. 979 | 980 | PRED is a function object which can pass to funcall and accept 981 | one argument, which will be every element in the list. 982 | Such as : (lambda (x) (equal x 1)) " 983 | (let (true-list false-list) 984 | (loop for e in l 985 | do (if (funcall pred e) 986 | (setq true-list (cons e true-list)) 987 | (setq false-list (cons e false-list)))) 988 | (nconc (nreverse false-list) 989 | (and true-list (nreverse true-list))))) 990 | 991 | (defun ace-jump-move-first-to-end-if (l pred) 992 | "Only move the first found one to the end of list" 993 | (lexical-let ((pred pred) 994 | found) 995 | (ace-jump-move-to-end-if l 996 | (lambda (x) 997 | (if found 998 | nil 999 | (setq found (funcall pred x))))))) 1000 | 1001 | 1002 | 1003 | (defadvice pop-mark (before ace-jump-pop-mark-advice) 1004 | "When `pop-mark' is called to jump back, this advice will sync the mark ring. 1005 | Move the same position to the end of `ace-jump-mode-mark-ring'." 1006 | (lexical-let ((mp (mark t)) 1007 | (cb (current-buffer))) 1008 | (if mp 1009 | (setq ace-jump-mode-mark-ring 1010 | (ace-jump-move-first-to-end-if ace-jump-mode-mark-ring 1011 | (lambda (x) 1012 | (and (equal (aj-position-offset x) mp) 1013 | (eq (aj-position-buffer x) cb)))))))) 1014 | 1015 | 1016 | (defadvice pop-global-mark (before ace-jump-pop-global-mark-advice) 1017 | "When `pop-global-mark' is called to jump back, this advice will sync the mark ring. 1018 | Move the aj-position with the same buffer to the end of `ace-jump-mode-mark-ring'." 1019 | (interactive) 1020 | ;; find the one that will be jump to 1021 | (let ((index global-mark-ring)) 1022 | ;; refer to the implementation of `pop-global-mark' 1023 | (while (and index (not (marker-buffer (car index)))) 1024 | (setq index (cdr index))) 1025 | (if index 1026 | ;; find the mark 1027 | (lexical-let ((mb (marker-buffer (car index)))) 1028 | (setq ace-jump-mode-mark-ring 1029 | (ace-jump-move-to-end-if ace-jump-mode-mark-ring 1030 | (lambda (x) 1031 | (eq (aj-position-buffer x) mb)))))))) 1032 | 1033 | 1034 | (defun ace-jump-mode-enable-mark-sync () 1035 | "Enable the sync funciton between ace jump mode mark ring and emacs mark ring. 1036 | 1037 | 1. This function will enable the advice which activate on 1038 | `pop-mark' and `pop-global-mark'. These advice will remove the 1039 | same marker from `ace-jump-mode-mark-ring' when user use 1040 | `pop-mark' or `global-pop-mark' to jump back. 1041 | 1042 | 2. Set variable `ace-jump-sync-emacs-mark-ring' to t, which will 1043 | sync mark information with emacs mark ring. " 1044 | (ad-enable-advice 'pop-mark 'before 'ace-jump-pop-mark-advice) 1045 | (ad-activate 'pop-mark) 1046 | (ad-enable-advice 'pop-global-mark 'before 'ace-jump-pop-global-mark-advice) 1047 | (ad-activate 'pop-global-mark) 1048 | (setq ace-jump-sync-emacs-mark-ring t)) 1049 | 1050 | (defun ace-jump-mode-disable-mark-sync () 1051 | "Disable the sync funciton between ace jump mode mark ring and emacs mark ring. 1052 | 1053 | 1. This function will diable the advice which activate on 1054 | `pop-mark' and `pop-global-mark'. These advice will remove the 1055 | same marker from `ace-jump-mode-mark-ring' when user use 1056 | `pop-mark' or `global-pop-mark' to jump back. 1057 | 1058 | 2. Set variable `ace-jump-sync-emacs-mark-ring' to nil, which 1059 | will stop synchronizing mark information with emacs mark ring. " 1060 | (ad-disable-advice 'pop-mark 'before 'ace-jump-pop-mark-advice) 1061 | (ad-activate 'pop-mark) 1062 | (ad-disable-advice 'pop-global-mark 'before 'ace-jump-pop-global-mark-advice) 1063 | (ad-activate 'pop-global-mark) 1064 | (setq ace-jump-sync-emacs-mark-ring nil)) 1065 | 1066 | 1067 | (provide 'ace-jump-mode) 1068 | 1069 | ;;; ace-jump-mode.el ends here 1070 | 1071 | ;; Local Variables: 1072 | ;; byte-compile-warnings: (not cl-functions) 1073 | ;; End: 1074 | --------------------------------------------------------------------------------