├── .gitignore ├── README.md ├── xah-fly-keys.el └── xah_fly_keys_qwerty_layout_2024-06-16.png /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.elc 3 | [#]*[#] 4 | xx* 5 | .DS_Store 6 | 7 | # ELPA-generated files 8 | /xah-fly-keys-autoloads.el 9 | /xah-fly-keys-pkg.el 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | xah-fly-keys 2 | =================== 3 | 4 | A modal keybinding for emacs (like vim), but based on command frequency and ergonomics. 5 | 6 | This is the most efficient editing system in the universe. 7 | 8 | Xah Fly Keys home page at 9 | http://xahlee.info/emacs/misc/xah-fly-keys.html 10 | 11 | 2020-04-18 News: Key Engine Rewrite 12 | =================== 13 | 14 | Major key engine rewrite by Dan Langlois (https://github.com/DanLanglois) and Will Dey (https://github.com/wi11dey) . 15 | 16 | QWERTY layout 17 | ------------------- 18 | ![xah-fly-keys qwerty layout](xah_fly_keys_qwerty_layout_2024-06-16.png) 19 | 20 | Manual Install 21 | ------------------- 22 | 23 | put the file xah-fly-keys.el in ~/.emacs.d/lisp/ 24 | create the dir if doesn't exist. 25 | 26 | put the following in your emacs init file: 27 | 28 | ```elisp 29 | 30 | (add-to-list 'load-path "~/.emacs.d/lisp/") 31 | 32 | (require 'xah-fly-keys) 33 | 34 | ;; specify a layout 35 | (xah-fly-keys-set-layout "qwerty") 36 | 37 | ;; other popular values 38 | ;; adnw (German) 39 | ;; azerty 40 | ;; azerty-be 41 | ;; bepo 42 | ;; colemak 43 | ;; colemak-dh 44 | ;; dvorak 45 | ;; neo2 (German) 46 | ;; norman 47 | ;; programer-dvorak 48 | ;; pt-nativo (Brazil) 49 | ;; qwerty-abnt (Brazil) 50 | ;; qwerty-no (Norwegian) 51 | ;; qwerty-se (Swedish) 52 | ;; qwertz 53 | ;; russian 54 | ;; workman 55 | ;; and more 56 | 57 | (xah-fly-keys 1) 58 | ``` 59 | 60 | Full Documentation 61 | ------------------- 62 | 63 | http://xahlee.info/emacs/misc/xah-fly-keys.html 64 | 65 | Been working on this since 2013, and since 2007 on ergoemacs-mode. 66 | 67 | give me 5 bucks https://paypal.com pay to Xah@XahLee.org 68 | 69 | Thanks. 70 | -------------------------------------------------------------------------------- /xah-fly-keys.el: -------------------------------------------------------------------------------- 1 | ;;; xah-fly-keys.el --- ergonomic modal keybinding minor mode. -*- coding: utf-8; lexical-binding: t; -*- 2 | 3 | ;; Copyright © 2013, 2025 by Xah Lee 4 | 5 | ;; Author: Xah Lee ( http://xahlee.info/ ) 6 | ;; Maintainer: Xah Lee 7 | ;; Version: 26.12.20250517213917 8 | ;; Created: 2013-09-10 9 | ;; Package-Requires: ((emacs "28.3")) 10 | ;; Keywords: convenience, vi, vim, ergoemacs, keybinding 11 | ;; License: GPL v3. 12 | ;; Homepage: http://xahlee.info/emacs/misc/xah-fly-keys.html 13 | 14 | ;; This file is not part of GNU Emacs. 15 | 16 | ;;; Commentary: 17 | 18 | ;; xah-fly-keys is a efficient keybinding for emacs. It is modal like 19 | ;; vi, but key choices are based on statistics of command call 20 | ;; frequency. 21 | 22 | ;;; Usage: 23 | 24 | ;; M-x xah-fly-keys to toggle the mode on/off. 25 | 26 | ;; Important command/insert mode switch keys: 27 | 28 | ;; M-x `xah-fly-command-mode-activate' 29 | ;; or press F8 or Alt+Space or Ctrl+Space or . 30 | ;; Note: if using emacs 28 or before, escape key only works when in emacs is running in graphical user interface mode. 31 | 32 | ;; M-x `xah-fly-insert-mode-activate' 33 | ;; when in command mode, press qwerty letter key f. 34 | 35 | ;; When in command mode: 36 | 37 | ;; "f" calls `xah-fly-insert-mode-activate'. 38 | 39 | ;; Space is a leader key. For example, "SPC r" calls `query-replace'. 40 | ;; Press "SPC C-h" to see the full list. 41 | 42 | ;; "SPC SPC" also activates insertion mode. 43 | 44 | ;; "SPC RET" calls `execute-extended-command'. 45 | 46 | ;; "a" calls `execute-extended-command'. 47 | 48 | ;; The leader key sequence basically supplant ALL emacs commands that 49 | ;; starts with C-x key. 50 | 51 | ;; When using xah-fly-keys, you don't need to press Control or Meta, 52 | ;; with the following exceptions: 53 | 54 | ;; "C-c" for major mode commands. 55 | ;; "C-g" for cancel. 56 | ;; "C-q" for quoted-insert. 57 | ;; "C-h" for getting a list of keys following a prefix/leader key. 58 | 59 | ;; Leader key 60 | 61 | ;; You NEVER need to press "C-x" 62 | 63 | ;; Any emacs command that has a keybinding starting with C-x, has also 64 | ;; a key sequence binding in xah-fly-keys. For example, 65 | 66 | ;; "C-x b" for `switch-to-buffer' is "SPC f" 67 | ;; "C-x C-f" for `find-file' is "SPC i e" 68 | ;; "C-x n n" for `narrow-to-region' is "SPC l l" 69 | 70 | ;; The first key we call it leader key. In the above examples, the SPC 71 | ;; is the leader key. 72 | 73 | ;; When in command mode, the "SPC" is a leader key. 74 | 75 | ;; if you want the following standard keys with Control 76 | ;; "C-TAB" `xah-next-user-buffer' 77 | ;; "C-S-TAB" `xah-previous-user-buffer' 78 | ;; "C-v" paste 79 | ;; "C-w" close 80 | ;; "C-z" undo 81 | ;; "C-n" new 82 | ;; "C-o" open 83 | ;; "C-s" save 84 | ;; "C-S-s" save as 85 | ;; "C-S-t" open last closed 86 | ;; "C-+" `text-scale-increase' 87 | ;; "C--" `text-scale-decrease' 88 | 89 | ;; add 90 | ;; (setq xah-fly-use-control-key t) 91 | 92 | ;; To disable any change to Control keybinding 93 | ;; add this to emacs init 94 | ;; (setq xah-fly-use-control-key nil) 95 | ;; before loading xah-fly-keys 96 | 97 | ;; To disable any change to meta keybinding 98 | ;; add this to emacs init 99 | ;; (setq xah-fly-use-meta-key nil) 100 | ;; before loading xah-fly-keys 101 | 102 | ;; If you have a bug, post on github. 103 | 104 | ;; For detail about design and other info, see home page at 105 | ;; http://xahlee.info/emacs/misc/xah-fly-keys.html 106 | 107 | ;; If you like this project, paypal me $30 to Xah@XahLee.org 108 | 109 | ;;; Installation: 110 | ;; here's how to manual install 111 | ;; 112 | ;; put the file xah-fly-keys.el in ~/.emacs.d/lisp/ 113 | ;; create the dir if doesn't exist. 114 | ;; 115 | ;; put the following in your emacs init file: 116 | ;; (add-to-list 'load-path "~/.emacs.d/lisp/") 117 | ;; (require 'xah-fly-keys) 118 | ;; (xah-fly-keys-set-layout "qwerty") ; optional 119 | ;; (xah-fly-keys 1) 120 | ;; 121 | ;; possible layout values: 122 | 123 | ;; adnw (German) 124 | ;; azerty 125 | ;; azerty-be 126 | ;; bepo (French) 127 | ;; colemak 128 | ;; colemak-dh 129 | ;; dvorak 130 | ;; engrammer 131 | ;; halmak 132 | ;; koy (German) 133 | ;; minimak 134 | ;; neo2 (German) 135 | ;; norman 136 | ;; programer-dvorak 137 | ;; pt-nativo (Brazil) 138 | ;; qfmlwy 139 | ;; qgmlwb 140 | ;; qwerty 141 | ;; qwerty-abnt (Brazil) 142 | ;; qwerty-no (Norwegian) 143 | ;; qwerty-se (Swedish) 144 | ;; qwertz 145 | ;; qwpr 146 | ;; russian 147 | ;; workman 148 | 149 | ;; supported layouts are stored in the variable xah-fly-layout-diagrams 150 | 151 | ;; HHHH------------------------------ 152 | ;;; Code: 153 | 154 | (require 'dired) 155 | (require 'dired-x) 156 | (require 'seq) 157 | 158 | ;; HHHH------------------------------ 159 | 160 | (defgroup xah-fly-keys nil 161 | "Ergonomic modal keybinding minor mode." 162 | :group 'keyboard) 163 | 164 | (defvar xah-fly-command-mode-activate-hook nil "Hook for `xah-fly-command-mode-activate'") 165 | (defvar xah-fly-insert-mode-activate-hook nil "Hook for `xah-fly-insert-mode-activate'") 166 | 167 | (defvar xah-fly-command-mode-indicator "c" 168 | "Character in mode line indicating command mode is active.") 169 | (defvar xah-fly-insert-mode-indicator "i" 170 | "Character in mode line indicating insert mode is active.") 171 | 172 | (defcustom xah-fly-use-control-key nil 173 | "If true, change many emacs keybinding involving control key. 174 | Keys changed: 175 | Standard shortcut for open, close, copy, paste etc. 176 | Remove many redundant emacs default keys 177 | Must be set before loading xah-fly-keys." 178 | :type 'boolean) 179 | 180 | (defcustom xah-fly-use-meta-key t 181 | "If true, change some emacs keybinding involving meta key. 182 | Remove many redundant emacs default keys. 183 | Must be set before loading xah-fly-keys." 184 | :type 'boolean) 185 | 186 | (defcustom xah-fly-use-isearch-arrows t 187 | "If true, set arrow keys for moving between occurrences, and C-v is paste, in isearch (`isearch-forward'). 188 | Must be set before loading xah-fly-keys." 189 | :type 'boolean) 190 | 191 | ;; HHHH------------------------------ 192 | ;; cursor movement 193 | 194 | (defun xah-pop-local-mark-ring () 195 | "Move cursor to last mark position of current buffer. 196 | Repeat call cycles all positions in `mark-ring'. 197 | 198 | URL `http://xahlee.info/emacs/emacs/emacs_cycle_local_mark_ring.html' 199 | Created: 2016-04-04 200 | Version: 2023-09-03" 201 | (interactive) 202 | (set-mark-command t)) 203 | 204 | (defun xah-beginning-of-line-or-block () 205 | "Move cursor to beginning of indent or line, end of previous block, in that order. 206 | 207 | If `visual-line-mode' is on, beginning of line means visual line. 208 | 209 | URL `http://xahlee.info/emacs/emacs/emacs_move_by_paragraph.html' 210 | Created: 2018-06-04 211 | Version: 2024-10-30" 212 | (interactive) 213 | (let ((xp (point))) 214 | (if (or (eq (point) (line-beginning-position)) 215 | (eq last-command this-command)) 216 | (when (re-search-backward "\n[\t\n ]*\n+" nil 1) 217 | (skip-chars-backward "\n\t ") 218 | ;; (forward-char) 219 | ) 220 | (if visual-line-mode 221 | (beginning-of-visual-line) 222 | (if (eq major-mode 'eshell-mode) 223 | (beginning-of-line) 224 | (back-to-indentation) 225 | (when (eq xp (point)) 226 | (beginning-of-line))))))) 227 | 228 | (defun xah-end-of-line-or-block () 229 | "Move cursor to end of line or next block. 230 | 231 | • When called first time, move cursor to end of line. 232 | • When called again, move cursor forward by jumping over any sequence of whitespaces containing 2 blank lines. 233 | • if `visual-line-mode' is on, end of line means visual line. 234 | 235 | URL `http://xahlee.info/emacs/emacs/emacs_move_by_paragraph.html' 236 | Created: 2018-06-04 237 | Version: 2024-10-30" 238 | (interactive) 239 | (if (or (eq (point) (line-end-position)) 240 | (eq last-command this-command)) 241 | (re-search-forward "\n[\t\n ]*\n+" nil 1) 242 | (if visual-line-mode 243 | (end-of-visual-line) 244 | (end-of-line)))) 245 | 246 | (defun xah-page-up () 247 | "Call `scroll-down-command'. (page up key.) 248 | Created: 2024-10-09 249 | Version: 2024-10-09" 250 | (interactive) 251 | (progn 252 | (scroll-down-command) 253 | (set-transient-map 254 | (let ((xkmap (make-sparse-keymap))) 255 | (define-key xkmap (kbd "") #'xah-page-up) 256 | (define-key xkmap (kbd "") #'xah-page-down) 257 | xkmap)))) 258 | 259 | (defun xah-page-down () 260 | "Call `scroll-up-command'. (page down key.) 261 | Created: 2024-10-09 262 | Version: 2024-10-09" 263 | (interactive) 264 | (progn 265 | (scroll-up-command) 266 | (set-transient-map 267 | (let ((xkmap (make-sparse-keymap))) 268 | (define-key xkmap (kbd "") #'xah-page-up) 269 | (define-key xkmap (kbd "") #'xah-page-down) 270 | xkmap)))) 271 | 272 | (defvar xah-brackets '( "“”" "()" "[]" "{}" "<>" "<>" "()" "[]" "{}" "⦅⦆" "〚〛" "⦃⦄" "‹›" "«»" "「」" "〈〉" "《》" "【】" "〔〕" "⦗⦘" "『』" "〖〗" "〘〙" "「」" "⟦⟧" "⟨⟩" "⟪⟫" "⟮⟯" "⟬⟭" "⌈⌉" "⌊⌋" "⦇⦈" "⦉⦊" "❛❜" "❝❞" "❨❩" "❪❫" "❴❵" "❬❭" "❮❯" "❰❱" "❲❳" "〈〉" "⦑⦒" "⧼⧽" "﹙﹚" "﹛﹜" "﹝﹞" "⁽⁾" "₍₎" "⦋⦌" "⦍⦎" "⦏⦐" "⁅⁆" "⸢⸣" "⸤⸥" "⟅⟆" "⦓⦔" "⦕⦖" "⸦⸧" "⸨⸩" "⦅⦆") 273 | "A list of strings, each element is a string of 2 chars, the left bracket and a matching right bracket. 274 | Used by `xah-select-text-in-quote' and others.") 275 | 276 | (defconst xah-left-brackets 277 | (mapcar (lambda (x) (substring x 0 1)) xah-brackets) 278 | "List of left bracket chars. Each element is a string.") 279 | 280 | (defconst xah-right-brackets 281 | (mapcar (lambda (x) (substring x 1 2)) xah-brackets) 282 | "List of right bracket chars. Each element is a string.") 283 | 284 | (defun xah-backward-left-bracket () 285 | "Move cursor to the previous occurrence of left bracket. 286 | The list of brackets to jump to is defined by `xah-left-brackets'. 287 | 288 | URL `http://xahlee.info/emacs/emacs/emacs_navigating_keys_for_brackets.html' 289 | Version: 2015-10-01" 290 | (interactive) 291 | (re-search-backward (regexp-opt xah-left-brackets) nil t)) 292 | 293 | (defun xah-forward-right-bracket () 294 | "Move cursor to the next occurrence of right bracket. 295 | The list of brackets to jump to is defined by `xah-right-brackets'. 296 | 297 | URL `http://xahlee.info/emacs/emacs/emacs_navigating_keys_for_brackets.html' 298 | Version: 2015-10-01" 299 | (interactive) 300 | (re-search-forward (regexp-opt xah-right-brackets) nil t)) 301 | 302 | (defun xah-goto-matching-bracket () 303 | "Move cursor to the matching bracket. 304 | If cursor is not on a bracket, call `backward-up-list'. 305 | The list of brackets to jump to is defined by `xah-left-brackets' and `xah-right-brackets'. 306 | 307 | URL `http://xahlee.info/emacs/emacs/emacs_navigating_keys_for_brackets.html' 308 | Created: 2016-11-22 309 | Version: 2024-06-15" 310 | (interactive) 311 | (if (nth 3 (syntax-ppss)) 312 | (backward-up-list 1 'ESCAPE-STRINGS 'NO-SYNTAX-CROSSING) 313 | (cond 314 | ((eq (char-after) ?\") (forward-sexp)) 315 | ((eq (char-before) ?\") (backward-sexp)) 316 | ((looking-at (regexp-opt xah-left-brackets)) 317 | (forward-sexp)) 318 | ((if (eq (point-min) (point)) 319 | nil 320 | (prog2 321 | (backward-char) 322 | (looking-at (regexp-opt xah-right-brackets)) 323 | (forward-char))) 324 | (backward-sexp) 325 | (while (looking-at "\\s'") (forward-char))) 326 | (t (backward-up-list 1 'ESCAPE-STRINGS 'NO-SYNTAX-CROSSING))))) 327 | 328 | (defvar xah-punctuation-regex nil "A regex string for the purpose of moving cursor to a punctuation.") 329 | (setq xah-punctuation-regex "\"") 330 | 331 | (defun xah-forward-punct () 332 | "Move cursor to the next occurrence of punctuation. 333 | Punctuations is defined by `xah-punctuation-regex' 334 | 335 | URL `http://xahlee.info/emacs/emacs/emacs_jump_to_punctuations.html' 336 | Created: 2017-06-26 337 | Version: 2025-03-21" 338 | (interactive) 339 | (set-transient-map 340 | (let ((xkmap (make-sparse-keymap))) 341 | (define-key xkmap (kbd "") #'xah-backward-punct) 342 | (define-key xkmap (kbd "") #'xah-forward-punct) 343 | (define-key xkmap (kbd (if (boundp 'xah-repeat-key) xah-repeat-key "SPC")) real-this-command) 344 | xkmap)) 345 | (re-search-forward xah-punctuation-regex nil t)) 346 | 347 | (defun xah-backward-punct () 348 | "Move cursor to the previous occurrence of punctuation. 349 | See `xah-forward-punct' 350 | 351 | URL `http://xahlee.info/emacs/emacs/emacs_jump_to_punctuations.html' 352 | Created: 2017-06-26 353 | Version: 2025-03-21" 354 | (interactive) 355 | (set-transient-map 356 | (let ((xkmap (make-sparse-keymap))) 357 | (define-key xkmap (kbd "") #'xah-backward-punct) 358 | (define-key xkmap (kbd "") #'xah-forward-punct) 359 | (define-key xkmap (kbd (if (boundp 'xah-repeat-key) xah-repeat-key "SPC")) real-this-command) 360 | xkmap)) 361 | (re-search-backward xah-punctuation-regex nil t)) 362 | 363 | (defun xah-sort-lines () 364 | "Like `sort-lines' but if no region, do the current block. 365 | Created: 2022-01-22 366 | Version: 2025-03-25" 367 | (interactive) 368 | (let (xbeg xend) 369 | (seq-setq (xbeg xend) (if (region-active-p) (list (region-beginning) (region-end)) (list (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point))) (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point)))))) 370 | (sort-lines current-prefix-arg xbeg xend))) 371 | 372 | (defun xah-narrow-to-region () 373 | "Same as `narrow-to-region', but if no selection, narrow to the current block. 374 | Created: 2022-01-22 375 | Version: 2025-03-25" 376 | (interactive) 377 | (let (xbeg xend) 378 | (seq-setq (xbeg xend) (if (region-active-p) (list (region-beginning) (region-end)) (list (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point))) (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point)))))) 379 | (narrow-to-region xbeg xend))) 380 | 381 | ;; HHHH------------------------------ 382 | ;; editing commands 383 | 384 | (defun xah-copy-line-or-region () 385 | "Copy current line or selection. 386 | 387 | Copy current line. When called repeatedly, append copy subsequent lines. 388 | Except: 389 | 390 | If `universal-argument' is called first, copy whole buffer (respects `narrow-to-region'). 391 | If `rectangle-mark-mode' is on, copy the rectangle. 392 | If `region-active-p', copy the region. 393 | 394 | URL `http://xahlee.info/emacs/emacs/emacs_copy_cut_current_line.html' 395 | Created: 2010-05-21 396 | Version: 2024-06-19" 397 | (interactive) 398 | (cond 399 | (current-prefix-arg (copy-region-as-kill (point-min) (point-max))) 400 | ((and (boundp 'rectangle-mark-mode) rectangle-mark-mode) 401 | (copy-region-as-kill (region-beginning) (region-end) t)) 402 | ((region-active-p) (copy-region-as-kill (region-beginning) (region-end))) 403 | ((eq last-command this-command) 404 | (if (eobp) 405 | nil 406 | (progn 407 | (kill-append "\n" nil) 408 | (kill-append (buffer-substring (line-beginning-position) (line-end-position)) nil) 409 | (end-of-line) 410 | (forward-char)))) 411 | ((eobp) 412 | (if (eq (char-before) 10) 413 | (progn) 414 | (progn 415 | (copy-region-as-kill (line-beginning-position) (line-end-position)) 416 | (end-of-line)))) 417 | (t 418 | (copy-region-as-kill (line-beginning-position) (line-end-position)) 419 | (end-of-line) 420 | (forward-char)))) 421 | 422 | (defun xah-cut-line-or-region () 423 | "Cut current line or selection. 424 | If `universal-argument' is called first, cut whole buffer (respects `narrow-to-region'). 425 | 426 | URL `http://xahlee.info/emacs/emacs/emacs_copy_cut_current_line.html' 427 | Created: 2010-05-21 428 | Version: 2015-06-10" 429 | (interactive) 430 | (if current-prefix-arg 431 | (progn ; not using kill-region because we don't want to include previous kill 432 | (kill-new (buffer-string)) 433 | (delete-region (point-min) (point-max))) 434 | (progn (if (region-active-p) 435 | (kill-region (region-beginning) (region-end) t) 436 | (kill-region (line-beginning-position) (line-beginning-position 2)))))) 437 | 438 | (defun xah-copy-all-or-region () 439 | "Copy buffer or selection content to `kill-ring'. 440 | Respects `narrow-to-region'. 441 | 442 | URL `http://xahlee.info/emacs/emacs/emacs_copy_cut_all_or_region.html' 443 | Version: 2015-08-22" 444 | (interactive) 445 | (if (region-active-p) 446 | (progn 447 | (kill-new (buffer-substring (region-beginning) (region-end))) 448 | (message "Text selection copied.")) 449 | (progn 450 | (kill-new (buffer-string)) 451 | (message "Buffer content copied.")))) 452 | 453 | (defun xah-cut-all-or-region () 454 | "Cut buffer or selection content to `kill-ring'. 455 | Respects `narrow-to-region'. 456 | 457 | URL `http://xahlee.info/emacs/emacs/emacs_copy_cut_all_or_region.html' 458 | Version: 2015-08-22" 459 | (interactive) 460 | (if (region-active-p) 461 | (progn 462 | (kill-new (buffer-substring (region-beginning) (region-end))) 463 | (delete-region (region-beginning) (region-end))) 464 | (progn 465 | (kill-new (buffer-string)) 466 | (delete-region (point-min) (point-max))))) 467 | 468 | (defun xah-copy-all () 469 | "Put the whole buffer content into the `kill-ring'. 470 | (respects `narrow-to-region') 471 | Version: 2016-10-06" 472 | (interactive) 473 | (kill-new (buffer-string)) 474 | (message "Buffer content copied.")) 475 | 476 | (defun xah-cut-all () 477 | "Cut the whole buffer content into the `kill-ring'. 478 | Respects `narrow-to-region'. 479 | Version: 2017-01-03" 480 | (interactive) 481 | (kill-new (buffer-string)) 482 | (delete-region (point-min) (point-max))) 483 | 484 | (defun xah-paste-or-paste-previous () 485 | "Paste. When called repeatedly, paste previous. 486 | This command calls `yank', and if repeated, call `yank-pop'. 487 | 488 | If `universal-argument' is called first with a number arg, paste that many times. 489 | 490 | URL `http://xahlee.info/emacs/emacs/emacs_paste_or_paste_previous.html' 491 | Created: 2017-07-25 492 | Version: 2020-09-08" 493 | (interactive) 494 | (progn 495 | (when (and delete-selection-mode (region-active-p)) 496 | (delete-region (region-beginning) (region-end))) 497 | (if current-prefix-arg 498 | (progn 499 | (dotimes (_ (prefix-numeric-value current-prefix-arg)) 500 | (yank))) 501 | (if (eq real-last-command this-command) 502 | (yank-pop 1) 503 | (yank))))) 504 | 505 | (defun xah-show-kill-ring () 506 | "Insert all `kill-ring' content in a new buffer named *copy history*. 507 | 508 | URL `http://xahlee.info/emacs/emacs/emacs_show_kill_ring.html' 509 | Created: 2019-12-02 510 | Version: 2024-05-07" 511 | (interactive) 512 | (let ((xbuf (generate-new-buffer "*copy history*")) 513 | (inhibit-read-only t)) 514 | (progn 515 | (switch-to-buffer xbuf) 516 | (funcall 'fundamental-mode) 517 | (mapc 518 | (lambda (x) 519 | (insert x "\n\nsss97707------------------------------------------------\n\n" )) 520 | kill-ring)) 521 | (goto-char (point-min)))) 522 | 523 | (defun xah-move-block-up () 524 | "Swap the current text block with the previous. 525 | After this command is called, press or to move. Any other key to exit. 526 | Version: 2022-03-04" 527 | (interactive) 528 | (let ((xp0 (point)) 529 | xc1 ; current block begin 530 | xc2 ; current Block End 531 | xbeg ; prev Block Begin 532 | xend ; prev Block end 533 | ) 534 | (if (re-search-forward "\n[ \t]*\n+" nil 1) 535 | (setq xc2 (match-beginning 0)) 536 | (setq xc2 (point))) 537 | (goto-char xp0) 538 | (if (re-search-backward "\n[ \t]*\n+" nil 1) 539 | (progn 540 | (skip-chars-backward "\n \t") 541 | (setq xend (point)) 542 | (skip-chars-forward "\n \t") 543 | (setq xc1 (point))) 544 | (error "No previous block.")) 545 | (goto-char xend) 546 | (if (re-search-backward "\n[ \t]*\n+" nil 1) 547 | (progn 548 | (setq xbeg (match-end 0))) 549 | (setq xbeg (point))) 550 | (transpose-regions xbeg xend xc1 xc2) 551 | (goto-char xbeg) 552 | (set-transient-map 553 | (let ((xkmap (make-sparse-keymap))) 554 | (define-key xkmap (kbd "") #'xah-move-block-up) 555 | (define-key xkmap (kbd "") #'xah-move-block-down) 556 | xkmap)))) 557 | 558 | (defun xah-move-block-down () 559 | "Swap the current text block with the next. 560 | After this command is called, press or to move. Any other key to exit. 561 | Version: 2022-03-04" 562 | (interactive) 563 | (let ((xp0 (point)) 564 | xc1 ; current block begin 565 | xc2 ; current Block End 566 | xn1 ; next Block Begin 567 | xn2 ; next Block end 568 | ) 569 | (if (eq (point-min) (point)) 570 | (setq xc1 (point)) 571 | (if (re-search-backward "\n\n+" nil 1) 572 | (progn 573 | (setq xc1 (match-end 0))) 574 | (setq xc1 (point)))) 575 | (goto-char xp0) 576 | (if (re-search-forward "\n[ \t]*\n+" nil 1) 577 | (progn 578 | (setq xc2 (match-beginning 0)) 579 | (setq xn1 (match-end 0))) 580 | (error "No next block.")) 581 | (if (re-search-forward "\n[ \t]*\n+" nil 1) 582 | (progn 583 | (setq xn2 (match-beginning 0))) 584 | (setq xn2 (point))) 585 | (transpose-regions xc1 xc2 xn1 xn2) 586 | (goto-char xn2)) 587 | (set-transient-map 588 | (let ((xkmap (make-sparse-keymap))) 589 | (define-key xkmap (kbd "") #'xah-move-block-up) 590 | (define-key xkmap (kbd "") #'xah-move-block-down) 591 | xkmap))) 592 | 593 | (defun xah-shrink-whitespaces () 594 | "Remove whitespaces around cursor . 595 | 596 | Shrink neighboring spaces, then newlines, then spaces again, leaving one space or newline at each step, till no more white space. 597 | 598 | URL `http://xahlee.info/emacs/emacs/emacs_shrink_whitespace.html' 599 | Created: 2014-10-21 600 | Version: 2023-07-12" 601 | (interactive) 602 | (let ((xeol-count 0) 603 | (xp0 (point)) 604 | xbeg ; whitespace begin 605 | xend ; whitespace end 606 | (xcharBefore (char-before)) 607 | (xcharAfter (char-after)) 608 | xspace-neighbor-p) 609 | (setq xspace-neighbor-p (or (eq xcharBefore 32) (eq xcharBefore 9) (eq xcharAfter 32) (eq xcharAfter 9))) 610 | (skip-chars-backward " \n\t ") 611 | (setq xbeg (point)) 612 | (goto-char xp0) 613 | (skip-chars-forward " \n\t ") 614 | (setq xend (point)) 615 | (goto-char xbeg) 616 | (while (search-forward "\n" xend t) 617 | (setq xeol-count (1+ xeol-count))) 618 | (goto-char xp0) 619 | (cond 620 | ((eq xeol-count 0) 621 | (if (> (- xend xbeg) 1) 622 | (progn 623 | (delete-horizontal-space) (insert " ")) 624 | (progn (delete-horizontal-space)))) 625 | ((eq xeol-count 1) 626 | (if xspace-neighbor-p 627 | (delete-horizontal-space) 628 | (progn (delete-space--internal "\n" nil) (insert " ")))) 629 | ((eq xeol-count 2) 630 | (if xspace-neighbor-p 631 | (delete-horizontal-space) 632 | (progn 633 | (delete-space--internal "\n" nil) 634 | (insert "\n")))) 635 | ((> xeol-count 2) 636 | (if xspace-neighbor-p 637 | (delete-horizontal-space) 638 | (progn 639 | (goto-char xend) 640 | (search-backward "\n") 641 | (delete-region xbeg (point)) 642 | (insert "\n")))) 643 | (t (progn 644 | (message "nothing done. logic error 40873. shouldn't reach here")))))) 645 | 646 | (defun xah-delete-string-backward (&optional DeleteJustQuote) 647 | "Delete string to the left of cursor. 648 | 649 | Cursor must be on the right of a string delimiter. 650 | e.g. \"▮some\" or \"some\"▮ 651 | Else, do nothing. 652 | 653 | String delimiter is determined by current syntax table. (see `describe-syntax') 654 | 655 | If DeleteJustQuote is true, delete only the quotation marks. 656 | 657 | Created: 2023-11-12 658 | Version: 2024-06-06" 659 | (when (prog2 (backward-char) (looking-at "\\s\"") (forward-char)) 660 | (let ((xp0 (point)) xbeg xend) 661 | ;; xbeg xend are the begin and end pos of the string 662 | (if (nth 3 (syntax-ppss)) 663 | (setq xbeg (1- xp0) 664 | xend 665 | (progn 666 | (backward-char) 667 | (forward-sexp) 668 | (point))) 669 | (setq xend (point) 670 | xbeg 671 | (progn (forward-sexp -1) (point)))) 672 | (if DeleteJustQuote 673 | (progn (goto-char xend) 674 | (delete-char -1) 675 | (goto-char xbeg) 676 | (delete-char 1)) 677 | (if (eq real-this-command real-last-command) 678 | (kill-append (delete-and-extract-region xbeg xend) t) 679 | (kill-region xbeg xend)))))) 680 | 681 | (defvar xah-smart-delete-dispatch 682 | nil 683 | "Used by `xah-smart-delete'. 684 | This makes that function behavior dependent on current major-mode. 685 | Value is Alist of pairs, each is of the form 686 | (‹major-mode-name› . ‹function-name›) 687 | If ‹major-mode-name› match current var `major-mode', the paired function is called. 688 | If no major mode matches, `xah-smart-delete' default behavior is used. 689 | 690 | Version: 2024-06-05") 691 | 692 | (setq xah-smart-delete-dispatch 693 | '((xah-wolfram-mode . xah-wolfram-smart-delete-backward) 694 | (xah-html-mode . xah-html-smart-delete-backward))) 695 | 696 | (defun xah-smart-delete (&optional BracketOnly SkipDispatch) 697 | "Smart backward delete. 698 | Typically, delete to the left 1 char or entire bracketed text. 699 | Behavior depends on what's left char, and current `major-mode'. 700 | 701 | If `xah-smart-delete-dispatch' match, call the matched function instead. 702 | If region active, delete region. 703 | If cursor left is space tab newline, delete them. 704 | If cursor left is bracket, delete the whole bracket block. 705 | If cursor left is string quote, delete the string. 706 | Else just delete one char to the left. 707 | 708 | If `universal-argument' is called first, do not delete bracket's innertext. 709 | 710 | In elisp code, arg BracketOnly if true, do not delete innertext. SkipDispatch if true, skip checking `xah-smart-delete-dispatch'. 711 | 712 | Created: 2023-07-22 713 | Version: 2025-02-05" 714 | (interactive (list current-prefix-arg nil)) 715 | (let (xfun) 716 | (cond 717 | ((and (not SkipDispatch) (setq xfun (assq major-mode xah-smart-delete-dispatch))) 718 | (message "calling cdr of %s" xfun) 719 | (funcall (cdr xfun))) 720 | ((region-active-p) (delete-region (region-beginning) (region-end))) 721 | ((or 722 | ;; 32 is space, 9 is tab, 10 is newline 723 | (eq (char-before) 32) 724 | (eq (char-before) 10) 725 | (eq (char-before) 9)) 726 | (if (minibufferp (current-buffer)) 727 | (while (or (eq (char-before) 32) (eq (char-before) 10) (eq (char-before) 9)) 728 | (delete-char -1)) 729 | (let ((xp0 (point)) xbeg xend) 730 | (skip-chars-backward " \t\n") 731 | (setq xbeg (point) xend xp0) 732 | (if (eq real-this-command real-last-command) 733 | (kill-append (delete-and-extract-region xbeg xend) t) 734 | (kill-region xbeg xend))))) 735 | ((prog2 (backward-char) (looking-at "\\s)") (forward-char)) 736 | ;; (message "cursor left is closing bracket") 737 | (cond 738 | ;; unmatched bracket, just delete it 739 | ((not (condition-case nil (scan-sexps (point) -1) (scan-error nil))) 740 | (warn "There was unmatched bracket: no paired opening bracket on left of cursor") 741 | (delete-char -1)) 742 | ;; delete just the brackets 743 | (BracketOnly 744 | (let ((xp0 (point)) xbeg) 745 | (forward-sexp -1) 746 | (while (looking-at "\\s'") (forward-char)) 747 | (setq xbeg (point)) 748 | (goto-char xp0) 749 | (delete-char -1) 750 | (goto-char xbeg) 751 | (delete-char 1) 752 | (goto-char (- xp0 2)))) 753 | ;; delete the bracket block 754 | (t 755 | (let ((xp0 (point)) xbeg xend) 756 | (forward-sexp -1) 757 | (while (looking-at "\\s'") (forward-char)) 758 | (setq xbeg (point) xend xp0) 759 | (if (eq real-this-command real-last-command) 760 | (kill-append (delete-and-extract-region xbeg xend) t) 761 | (kill-region xbeg xend)))))) 762 | ((prog2 (backward-char) (looking-at "\\s(") (forward-char)) 763 | ;; (message "cursor left is opening bracket") 764 | (cond 765 | ;; unmatched bracket, just delete it 766 | ((save-excursion 767 | (backward-char) 768 | (not (condition-case nil (scan-sexps (point) 1) (scan-error nil)))) 769 | (warn "There was unmatched bracket: no paired closing bracket on right of cursor") 770 | (delete-char -1)) 771 | ;; delete just the brackets 772 | (BracketOnly 773 | (let (xbeg) 774 | (backward-char) 775 | (setq xbeg (point)) 776 | (forward-sexp 1) 777 | (delete-char -1) 778 | (goto-char xbeg) 779 | (delete-char 1))) 780 | ;; delete the bracket block 781 | (t 782 | (let (xbeg xend) 783 | (backward-char) 784 | (setq xbeg (point)) 785 | (forward-sexp 1) 786 | (setq xend (point)) 787 | (if (eq real-this-command real-last-command) 788 | (kill-append (delete-and-extract-region xbeg xend) t) 789 | (kill-region xbeg xend)))))) 790 | ((prog2 (backward-char) (looking-at "\\s\"") (forward-char)) 791 | (message "calling xah-delete-string-backward") 792 | (xah-delete-string-backward BracketOnly)) 793 | (t (delete-char -1))))) 794 | 795 | (defun xah-change-bracket-pairs (FromChars ToChars) 796 | "Change bracket pairs to another type or none. 797 | For example, change all parenthesis () to square brackets []. 798 | Works on current block or selection. 799 | 800 | In lisp code, FromChars is a string with at least 2 spaces. 801 | e.g. 802 | paren ( ) 803 | french angle ‹ › 804 | double bracket [[ ]] 805 | etc. 806 | It is split by space, and last 2 items are taken as left and right brackets. 807 | 808 | ToChars is similar, with a special value of 809 | none 810 | followed by 2 spaces. 811 | ,it means replace by empty string. 812 | 813 | URL `http://xahlee.info/emacs/emacs/elisp_change_brackets.html' 814 | Created: 2020-11-01 815 | Version: 2025-03-25" 816 | (interactive 817 | (let ((xbrackets 818 | '( 819 | "square [ ]" 820 | "brace { }" 821 | "paren ( )" 822 | "less greater than < >" 823 | "QUOTATION MARK \" \"" 824 | "APOSTROPHE ' '" 825 | "emacs ` '" 826 | "GRAVE ACCENT ` `" 827 | "double square [[ ]]" 828 | "tilde ~ ~" 829 | "equal = =" 830 | "double curly quote “ ”" 831 | "single curly quote ‘ ’" 832 | "french angle quote ‹ ›" 833 | "french double angle « »" 834 | "corner 「 」" 835 | "white corner 『 』" 836 | "lenticular 【 】" 837 | "white lenticular 〖 〗" 838 | "title angle 〈 〉" 839 | "double angle 《 》" 840 | "tortoise 〔 〕" 841 | "white tortoise 〘 〙" 842 | "white square 〚 〛" 843 | "white paren ⦅ ⦆" 844 | "white curly bracket ⦃ ⦄" 845 | "pointing angle 〈 〉" 846 | "angle with dot ⦑ ⦒" 847 | "curved angle ⧼ ⧽" 848 | "math square ⟦ ⟧" 849 | "math angle ⟨ ⟩" 850 | "math double angle ⟪ ⟫" 851 | "math flattened parenthesis ⟮ ⟯" 852 | "math white tortoise shell ⟬ ⟭" 853 | "heavy single quotation mark ornament ❛ ❜" 854 | "heavy double turned comma quotation mark ornament ❝ ❞" 855 | "medium parenthesis ornament ❨ ❩" 856 | "medium flattened parenthesis ornament ❪ ❫" 857 | "medium curly ornament ❴ ❵" 858 | "medium pointing angle ornament ❬ ❭" 859 | "heavy pointing angle quotation mark ornament ❮ ❯" 860 | "heavy pointing angle ornament ❰ ❱" 861 | "none " 862 | ))) 863 | (let ((completion-ignore-case t)) 864 | (list 865 | (completing-read "Replace this:" xbrackets nil t nil nil (car xbrackets)) 866 | (completing-read "To:" xbrackets nil t nil nil (car (last xbrackets))))))) 867 | (let (xbeg xend xleft xright xtoL xtoR) 868 | (seq-setq (xbeg xend) (if (region-active-p) (list (region-beginning) (region-end)) (list (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point))) (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point)))))) 869 | (let ((xsFrom (last (split-string FromChars " ") 2)) 870 | (xsTo (last (split-string ToChars " ") 2))) 871 | 872 | ;; (when (< (length xsFrom) 3) 873 | ;; (error "cannot find input brackets %s" xsFrom)) 874 | 875 | ;; (when (< (length xsTo) 3) 876 | ;; (message "replace blacket is empty string") 877 | ;; (setq xsTo (list "" "" ""))) 878 | 879 | (setq xleft (car xsFrom) xright (car (cdr xsFrom)) 880 | xtoL (car xsTo) xtoR (car (cdr xsTo))) 881 | 882 | (save-excursion 883 | (save-restriction 884 | (narrow-to-region xbeg xend) 885 | (let ((case-fold-search nil)) 886 | (if (string-equal xleft xright) 887 | (let ((xx (regexp-quote xleft))) 888 | (goto-char (point-min)) 889 | (while 890 | (re-search-forward 891 | (format "%s\\([^%s]+?\\)%s" xx xx xx) 892 | nil t) 893 | (overlay-put (make-overlay (match-beginning 0) (match-end 0)) 'face 'highlight) 894 | (replace-match (concat xtoL "\\1" xtoR) t))) 895 | (progn 896 | (progn 897 | (goto-char (point-min)) 898 | (while (search-forward xleft nil t) 899 | (overlay-put (make-overlay (match-beginning 0) (match-end 0)) 'face 'highlight) 900 | (replace-match xtoL t t))) 901 | (progn 902 | (goto-char (point-min)) 903 | (while (search-forward xright nil t) 904 | (overlay-put (make-overlay (match-beginning 0) (match-end 0)) 'face 'highlight) 905 | (replace-match xtoR t t))))))))))) 906 | 907 | (defun xah-toggle-letter-case () 908 | "Toggle the letter case of current word or selection. 909 | Always cycle in this order: Init Caps, ALL CAPS, all lower. 910 | 911 | URL `http://xahlee.info/emacs/emacs/emacs_toggle_letter_case.html' 912 | Created: 2020-06-26 913 | Version: 2024-06-17" 914 | (interactive) 915 | (let ((deactivate-mark nil) xbeg xend) 916 | (if (region-active-p) 917 | (setq xbeg (region-beginning) xend (region-end)) 918 | (save-excursion 919 | (skip-chars-backward "[:alnum:]") 920 | (setq xbeg (point)) 921 | (skip-chars-forward "[:alnum:]") 922 | (setq xend (point)))) 923 | (when (not (eq last-command this-command)) 924 | (put this-command 'state 0)) 925 | (cond 926 | ((equal 0 (get this-command 'state)) 927 | (upcase-initials-region xbeg xend) 928 | (put this-command 'state 1)) 929 | ((equal 1 (get this-command 'state)) 930 | (upcase-region xbeg xend) 931 | (put this-command 'state 2)) 932 | ((equal 2 (get this-command 'state)) 933 | (downcase-region xbeg xend) 934 | (put this-command 'state 0))))) 935 | 936 | ;; test case 937 | ;; test_case some 938 | ;; test-case 939 | ;; tes▮t-case 940 | 941 | (defun xah-toggle-previous-letter-case () 942 | "Toggle the letter case of the letter to the left of cursor. 943 | 944 | URL `http://xahlee.info/emacs/emacs/emacs_toggle_letter_case.html' 945 | Created: 2015-12-22 946 | Version: 2023-11-14" 947 | (interactive) 948 | (let ((case-fold-search nil)) 949 | (left-char 1) 950 | (cond 951 | ((looking-at "[[:lower:]]") (upcase-region (point) (1+ (point)))) 952 | ((looking-at "[[:upper:]]") (downcase-region (point) (1+ (point))))) 953 | (right-char))) 954 | 955 | (defun xah-upcase-sentence () 956 | "Upcase first letters of sentences of current block or selection. 957 | 958 | URL `http://xahlee.info/emacs/emacs/emacs_upcase_sentence.html' 959 | Created: 2020-12-08 960 | Version: 2025-03-25" 961 | (interactive) 962 | (let (xbeg xend) 963 | (seq-setq (xbeg xend) (if (region-active-p) (list (region-beginning) (region-end)) (list (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point))) (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point)))))) 964 | (save-restriction 965 | (narrow-to-region xbeg xend) 966 | (let ((case-fold-search nil)) 967 | ;; after period or question mark or exclamation 968 | (goto-char (point-min)) 969 | (while (re-search-forward "\\(\\.\\|\\?\\|!\\)[ \n]+ *\\([a-z]\\)" nil 1) 970 | (upcase-region (match-beginning 2) (match-end 2)) 971 | (overlay-put (make-overlay (match-beginning 2) (match-end 2)) 'face 'highlight)) 972 | ;; after a blank line, after a bullet, or beginning of buffer 973 | (goto-char (point-min)) 974 | (while (re-search-forward "\\(\\`\\|• \\|\n\n\\)\\([a-z]\\)" nil 1) 975 | (upcase-region (match-beginning 2) (match-end 2)) 976 | (overlay-put (make-overlay (match-beginning 2) (match-end 2)) 'face 'highlight)) 977 | ;; for HTML. first letter after tag 978 | (when 979 | (or 980 | (eq major-mode 'xah-html-mode) 981 | (eq major-mode 'html-mode) 982 | (eq major-mode 'sgml-mode) 983 | (eq major-mode 'nxml-mode) 984 | (eq major-mode 'xml-mode) 985 | (eq major-mode 'mhtml-mode)) 986 | (goto-char (point-min)) 987 | (while 988 | (re-search-forward "\\([ \n]?\\|<h[1-6]>[ \n]?\\|<p>[ \n]?\\|<li>[ \n]?\\|<dd>[ \n]?\\|<td>[ \n]?\\|<br ?/?>[ \n]?\\|<figcaption>[ \n]?\\)\\([a-z]\\)" nil 1) 989 | (upcase-region (match-beginning 2) (match-end 2)) 990 | (overlay-put (make-overlay (match-beginning 2) (match-end 2)) 'face 'highlight)))) 991 | (goto-char (point-max))) 992 | (skip-chars-forward " \n\t"))) 993 | 994 | (defun xah-title-case-region-or-line (&optional Begin End) 995 | "Title case text between nearest brackets, or current line or selection. 996 | Capitalize first letter of each word, except words like {to, of, the, a, in, or, and}. If a word already contains cap letters such as HTTP, URL, they are left as is. 997 | 998 | When called in a elisp program, Begin End are region boundaries. 999 | 1000 | URL `http://xahlee.info/emacs/emacs/elisp_title_case_text.html' 1001 | Created: 2017-01-11 1002 | Version: 2021-09-19" 1003 | (interactive) 1004 | (let* ((xskipChars "^\"<>(){}[]“”‘’‹›«»「」『』【】〖〗《》〈〉〔〕") 1005 | (xp0 (point)) 1006 | (xbeg (if Begin 1007 | Begin 1008 | (if (region-active-p) 1009 | (region-beginning) 1010 | (progn 1011 | (skip-chars-backward xskipChars (line-beginning-position)) (point))))) 1012 | (xend (if End 1013 | End 1014 | (if (region-active-p) 1015 | (region-end) 1016 | (progn (goto-char xp0) 1017 | (skip-chars-forward xskipChars (line-end-position)) (point))))) 1018 | (xstrPairs [ 1019 | [" A " " a "] 1020 | [" An " " an "] 1021 | [" And " " and "] 1022 | [" At " " at "] 1023 | [" As " " as "] 1024 | [" By " " by "] 1025 | [" Be " " be "] 1026 | [" Into " " into "] 1027 | [" In " " in "] 1028 | [" Is " " is "] 1029 | [" It " " it "] 1030 | [" For " " for "] 1031 | [" Of " " of "] 1032 | [" Or " " or "] 1033 | [" On " " on "] 1034 | [" Via " " via "] 1035 | [" The " " the "] 1036 | [" That " " that "] 1037 | [" To " " to "] 1038 | [" Vs " " vs "] 1039 | [" With " " with "] 1040 | [" From " " from "] 1041 | ["'S " "'s "] 1042 | ["'T " "'t "] 1043 | ])) 1044 | (save-excursion 1045 | (save-restriction 1046 | (narrow-to-region xbeg xend) 1047 | (upcase-initials-region (point-min) (point-max)) 1048 | (let ((case-fold-search nil)) 1049 | (mapc 1050 | (lambda (xx) 1051 | (goto-char (point-min)) 1052 | (while 1053 | (search-forward (aref xx 0) nil t) 1054 | (replace-match (aref xx 1) t t))) 1055 | xstrPairs)))))) 1056 | 1057 | (defun xah-add-space-after-comma () 1058 | "Add a space after comma of current block or selection. 1059 | and highlight changes made. 1060 | Created: 2022-01-20 1061 | Version: 2025-03-25" 1062 | (interactive) 1063 | (let (xbeg xend) 1064 | (seq-setq (xbeg xend) (if (region-active-p) (list (region-beginning) (region-end)) (list (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point))) (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point)))))) 1065 | (save-restriction 1066 | (narrow-to-region xbeg xend) 1067 | (goto-char (point-min)) 1068 | (while 1069 | (re-search-forward ",\\b" nil t) 1070 | (replace-match ", ") 1071 | (overlay-put 1072 | (make-overlay 1073 | (match-beginning 0) 1074 | (match-end 0)) 'face 'highlight))))) 1075 | 1076 | (defun xah-fill-or-unfill () 1077 | "Reformat current block or selection to short/long line. 1078 | First call will break into multiple short lines. Repeated call toggles between short and long lines. 1079 | This commands calls `fill-region' to do its work. Set `fill-column' for short line length. 1080 | 1081 | URL `http://xahlee.info/emacs/emacs/modernization_fill-paragraph.html' 1082 | Created: 2020-11-22 1083 | Version: 2025-03-25" 1084 | (interactive) 1085 | ;; This command symbol has a property “'longline-p”, the possible values are t and nil. This property is used to easily determine whether to compact or uncompact, when this command is called again 1086 | (let ( (xisLongline (if (eq last-command this-command) (get this-command 'longline-p) t)) 1087 | (deactivate-mark nil) 1088 | xbeg xend ) 1089 | (seq-setq (xbeg xend) (if (region-active-p) (list (region-beginning) (region-end)) (list (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point))) (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point)))))) 1090 | (if xisLongline 1091 | (fill-region xbeg xend) 1092 | (let ((fill-column 99999 )) 1093 | (fill-region xbeg xend))) 1094 | (put this-command 'longline-p (not xisLongline)))) 1095 | 1096 | (defun xah-unfill-paragraph () 1097 | "Replace newline chars in current paragraph by single spaces. 1098 | This command does the inverse of `fill-paragraph'. 1099 | 1100 | URL `http://xahlee.info/emacs/emacs/emacs_unfill-paragraph.html' 1101 | Created: 2010-05-12 1102 | Version: 2022-05-20" 1103 | (interactive) 1104 | (let ((fill-column 90002000)) 1105 | (fill-paragraph))) 1106 | 1107 | (defun xah-unfill-region (Begin End) 1108 | "Replace newline chars in region by single spaces. 1109 | This command does the inverse of `fill-region'. 1110 | 1111 | URL `http://xahlee.info/emacs/emacs/emacs_unfill-paragraph.html' 1112 | Created: 2010-05-12 1113 | Version: 2022-05-20" 1114 | (interactive "r") 1115 | (let ((fill-column 90002000)) 1116 | (fill-region Begin End))) 1117 | 1118 | (defun xah-change-newline-chars-to-one (Begin End) 1119 | "Replace newline char sequence by just one. 1120 | 1121 | URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html' 1122 | Version: 2021-07-06" 1123 | (interactive "r") 1124 | (save-excursion 1125 | (save-restriction 1126 | (narrow-to-region Begin End) 1127 | (goto-char (point-min)) 1128 | (while (re-search-forward "\n\n+" nil 1) (replace-match "\n"))))) 1129 | 1130 | (defun xah-reformat-whitespaces-to-one-space (Begin End) 1131 | "Replace whitespaces by one space. 1132 | 1133 | URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html' 1134 | Created: 2017-01-11 1135 | Version: 2022-01-08" 1136 | (interactive "r") 1137 | (save-restriction 1138 | (narrow-to-region Begin End) 1139 | (goto-char (point-min)) 1140 | (while (search-forward "\n" nil 1) (replace-match " ")) 1141 | (goto-char (point-min)) 1142 | (while (search-forward "\t" nil 1) (replace-match " ")) 1143 | (goto-char (point-min)) 1144 | (while (re-search-forward " +" nil 1) (replace-match " ")) 1145 | (goto-char (point-max)))) 1146 | 1147 | (defun xah-reformat-to-multi-lines (&optional Begin End MinLength) 1148 | "Replace spaces by a newline at ~70 chars, on current block or selection. 1149 | If `universal-argument' is called first, ask user for max width. 1150 | 1151 | URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html' 1152 | Created: 2018-12-16 1153 | Version: 2025-04-08" 1154 | (interactive) 1155 | (let (xbeg xend) 1156 | (seq-setq 1157 | (xbeg xend) 1158 | (if (and Begin End) 1159 | (list Begin End) 1160 | (if (region-active-p) (list (region-beginning) (region-end)) (list (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point))) (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point))))))) 1161 | (save-excursion 1162 | (save-restriction 1163 | (narrow-to-region xbeg xend) 1164 | (goto-char (point-min)) 1165 | (while (re-search-forward " +" nil 1) 1166 | (when (> (- (point) (line-beginning-position)) (if MinLength MinLength (if current-prefix-arg (prefix-numeric-value current-prefix-arg) fill-column))) 1167 | (replace-match "\n"))))))) 1168 | 1169 | (defun xah-reformat-lines (&optional Width) 1170 | "Reformat current block or selection into short lines or 1 long line. 1171 | When called for the first time, change to one line. Second call change it to multi-lines. Repeated call toggles. 1172 | If `universal-argument' is called first, ask user to type max length of line. By default, it is 66. 1173 | 1174 | Note: this command is different from emacs `fill-region' or `fill-paragraph'. 1175 | This command never adds or delete non-whitespace chars. It only exchange whitespace sequence. 1176 | 1177 | URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html' 1178 | Created: 2016 1179 | Version: 2025-03-25" 1180 | (interactive) 1181 | ;; This symbol has a property 'is-long-p, the possible values are t and nil. This property is used to easily determine whether to compact or uncompact, when this command is called again 1182 | (let (xisLong xwidth xbeg xend) 1183 | (setq xwidth (if Width Width (if current-prefix-arg (prefix-numeric-value current-prefix-arg) 66))) 1184 | (setq xisLong (if (eq last-command this-command) (get this-command 'is-long-p) nil)) 1185 | (seq-setq (xbeg xend) (if (region-active-p) (list (region-beginning) (region-end)) (list (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point))) (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point)))))) 1186 | (if current-prefix-arg 1187 | (xah-reformat-to-multi-lines xbeg xend xwidth) 1188 | (if xisLong 1189 | (xah-reformat-to-multi-lines xbeg xend xwidth) 1190 | (progn 1191 | (xah-reformat-whitespaces-to-one-space xbeg xend)))) 1192 | (put this-command 'is-long-p (not xisLong)))) 1193 | 1194 | (defun xah-reformat-to-sentence-lines () 1195 | "Reformat current block or selection into multiple lines by ending period. 1196 | Move cursor to the beginning of next text block. 1197 | After this command is called, press `xah-repeat-key' to repeat it. 1198 | 1199 | URL `http://xahlee.info/emacs/emacs/elisp_reformat_to_sentence_lines.html' 1200 | Created: 2020-12-02 1201 | Version: 2025-05-17" 1202 | (interactive) 1203 | (let (xbeg xend) 1204 | (seq-setq (xbeg xend) (if (region-active-p) (list (region-beginning) (region-end)) (list (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point))) (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point)))))) 1205 | (save-restriction 1206 | (narrow-to-region xbeg xend) 1207 | (goto-char (point-min)) (while (search-forward "。" nil t) (replace-match "。\n")) 1208 | ;; (goto-char (point-min)) (while (search-forward " <a " nil t) (replace-match "\n<a ")) 1209 | ;; (goto-char (point-min)) (while (search-forward "</a> " nil t) (replace-match "</a>\n")) 1210 | (goto-char (point-min)) 1211 | (while (re-search-forward "\\([A-Za-z0-9]+\\)[ \t]*\n[ \t]*\\([A-Za-z0-9]+\\)" nil t) 1212 | (replace-match "\\1 \\2")) 1213 | (goto-char (point-min)) 1214 | (while (re-search-forward "\\([,]\\)[ \t]*\n[ \t]*\\([A-Za-z0-9]+\\)" nil t) 1215 | (replace-match "\\1 \\2")) 1216 | (goto-char (point-min)) 1217 | (while (re-search-forward " +" nil t) (replace-match " ")) 1218 | (goto-char (point-min)) 1219 | (while (re-search-forward "\\([.?!]\\) +\\([(0-9A-Za-z]+\\)" nil t) (replace-match "\\1\n\\2")) 1220 | (goto-char (point-min)) 1221 | (while (re-search-forward "\\(<br ?/?>\\)" nil t) (replace-match "\\1\n")) 1222 | (goto-char (point-max)) 1223 | (while (eq (char-before) 32) (delete-char -1)))) 1224 | (re-search-forward "\n+" nil 1) 1225 | (set-transient-map (let ((xkmap (make-sparse-keymap))) (define-key xkmap (kbd (if (boundp 'xah-repeat-key) xah-repeat-key "SPC")) this-command) xkmap)) 1226 | (set-transient-map (let ((xkmap (make-sparse-keymap))) (define-key xkmap (kbd "DEL") this-command) xkmap))) 1227 | 1228 | (defun xah-space-to-newline () 1229 | "Replace space sequence to a newline char in current block or selection. 1230 | 1231 | URL `http://xahlee.info/emacs/emacs/emacs_space_to_newline.html' 1232 | Created: 2017-08-19 1233 | Version: 2025-03-25" 1234 | (interactive) 1235 | (let (xbeg xend) 1236 | (seq-setq (xbeg xend) (if (region-active-p) (list (region-beginning) (region-end)) (list (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point))) (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point)))))) 1237 | (save-restriction 1238 | (narrow-to-region xbeg xend) 1239 | (goto-char (point-min)) 1240 | (while (re-search-forward " +" nil t) 1241 | (replace-match "\n"))))) 1242 | 1243 | (defun xah-slash-to-backslash (&optional Begin End) 1244 | "Replace slash by backslash on current line or region. 1245 | Created: 2021-07-14 1246 | Version: 2021-09-12" 1247 | (interactive) 1248 | (let (xbeg xend) 1249 | (if (and Begin End) 1250 | (setq xbeg Begin xend End) 1251 | (if (region-active-p) 1252 | (setq xbeg (region-beginning) xend (region-end)) 1253 | (setq xbeg (line-beginning-position) xend (line-end-position)))) 1254 | (save-restriction 1255 | (narrow-to-region xbeg xend) 1256 | (let ((case-fold-search nil)) 1257 | (goto-char (point-min)) 1258 | (while (search-forward "/" nil t) 1259 | (replace-match "\\\\")))))) 1260 | 1261 | (defun xah-backslash-to-slash (&optional Begin End) 1262 | "Replace backslash by slash on current line or region. 1263 | Version: 2021-09-11" 1264 | (interactive) 1265 | (let (xbeg xend) 1266 | (if (and Begin End) 1267 | (setq xbeg Begin xend End) 1268 | (if (region-active-p) 1269 | (setq xbeg (region-beginning) xend (region-end)) 1270 | (setq xbeg (line-beginning-position) xend (line-end-position)))) 1271 | (save-restriction 1272 | (narrow-to-region xbeg xend) 1273 | (let ((case-fold-search nil)) 1274 | (goto-char (point-min)) 1275 | (while (search-forward "\\" nil t) 1276 | (replace-match "/")))))) 1277 | 1278 | (defun xah-double-backslash (&optional Begin End) 1279 | "Replace backslash by two backslash on current line or region. 1280 | Version: 2021-11-09" 1281 | (interactive) 1282 | (let (xbeg xend) 1283 | (if (and Begin End) 1284 | (setq xbeg Begin xend End) 1285 | (if (region-active-p) 1286 | (setq xbeg (region-beginning) xend (region-end)) 1287 | (setq xbeg (line-beginning-position) xend (line-end-position)))) 1288 | (save-restriction 1289 | (narrow-to-region xbeg xend) 1290 | (let ((case-fold-search nil)) 1291 | (goto-char (point-min)) 1292 | (while (search-forward "\\" nil t) 1293 | (replace-match "\\\\\\\\")))))) 1294 | 1295 | (defun xah-double-backslash-to-single (&optional Begin End) 1296 | "Replace double backslash by single backslash on current line or region. 1297 | Version: 2021-11-09" 1298 | (interactive) 1299 | (let (xbeg xend) 1300 | (if (and Begin End) 1301 | (setq xbeg Begin xend End) 1302 | (if (region-active-p) 1303 | (setq xbeg (region-beginning) xend (region-end)) 1304 | (setq xbeg (line-beginning-position) xend (line-end-position)))) 1305 | (save-restriction 1306 | (narrow-to-region xbeg xend) 1307 | (let ((case-fold-search nil)) 1308 | (goto-char (point-min)) 1309 | (while (search-forward "\\\\" nil t) 1310 | (replace-match "\\\\")))))) 1311 | 1312 | (defun xah-slash-to-double-backslash (&optional Begin End) 1313 | "Replace slash by double backslash on current line or region. 1314 | Version: 2021-07-14" 1315 | (interactive) 1316 | (let (xbeg xend) 1317 | (if (and Begin End) 1318 | (setq xbeg Begin xend End) 1319 | (if (region-active-p) 1320 | (setq xbeg (region-beginning) xend (region-end)) 1321 | (setq xbeg (line-beginning-position) xend (line-end-position)))) 1322 | (save-restriction 1323 | (narrow-to-region xbeg xend) 1324 | (let ((case-fold-search nil)) 1325 | (goto-char (point-min)) 1326 | (while (search-forward "/" nil t) 1327 | (replace-match "\\\\\\\\")))))) 1328 | 1329 | (defun xah-double-backslash-to-slash (&optional Begin End) 1330 | "Replace double backslash by slash on current line or region. 1331 | Version: 2021-07-14" 1332 | (interactive) 1333 | (let (xbeg xend) 1334 | (if (and Begin End) 1335 | (setq xbeg Begin xend End) 1336 | (if (region-active-p) 1337 | (setq xbeg (region-beginning) xend (region-end)) 1338 | (setq xbeg (line-beginning-position) xend (line-end-position)))) 1339 | (save-restriction 1340 | (narrow-to-region xbeg xend) 1341 | (let ((case-fold-search nil)) 1342 | (goto-char (point-min)) 1343 | (while (search-forward "\\\\" nil t) 1344 | (replace-match "/")))))) 1345 | 1346 | (defun xah-comment-dwim () 1347 | "Toggle comment in programing language code. 1348 | 1349 | Like `comment-dwim', but toggle comment if cursor is not at end of line. 1350 | If cursor is at end of line, either add comment at the line end or move cursor to start of line end comment. call again to comment out whole line. 1351 | 1352 | URL `http://xahlee.info/emacs/emacs/emacs_toggle_comment_by_line.html' 1353 | Created: 2016-10-25 1354 | Version: 2023-07-10" 1355 | (interactive) 1356 | (if (region-active-p) 1357 | (comment-dwim nil) 1358 | (let ((xbegin (line-beginning-position)) 1359 | (xend (line-end-position))) 1360 | (if (eq xbegin xend) 1361 | (progn 1362 | (comment-dwim nil)) 1363 | (if (eq (point) xend) 1364 | (progn 1365 | (comment-dwim nil)) 1366 | (progn 1367 | (comment-or-uncomment-region xbegin xend) 1368 | (forward-line ))))))) 1369 | 1370 | (defun xah-quote-lines (QuoteL QuoteR Sep) 1371 | "Add quotes/brackets and separator (comma) to lines. 1372 | Act on current block or selection. 1373 | 1374 | For example, 1375 | 1376 | cat 1377 | dog 1378 | cow 1379 | 1380 | becomes 1381 | 1382 | \"cat\", 1383 | \"dog\", 1384 | \"cow\", 1385 | 1386 | or 1387 | 1388 | (cat) 1389 | (dog) 1390 | (cow) 1391 | 1392 | In lisp code, QuoteL QuoteR Sep are strings. 1393 | 1394 | URL `http://xahlee.info/emacs/emacs/emacs_quote_lines.html' 1395 | Created: 2020-06-26 1396 | Version: 2025-03-25" 1397 | (interactive 1398 | (let ((xbrackets 1399 | '( 1400 | "\"QUOTATION MARK\"" 1401 | "'APOSTROPHE'" 1402 | "(paren)" 1403 | "{brace}" 1404 | "[square]" 1405 | "<greater>" 1406 | "`emacs'" 1407 | "`markdown`" 1408 | "~tilde~" 1409 | "=equal=" 1410 | "“curly double”" 1411 | "‘curly single’" 1412 | "‹french angle›" 1413 | "«french double angle»" 1414 | "「corner」" 1415 | "none" 1416 | "other" 1417 | )) 1418 | (xcomma '("comma ," "semicolon ;" "none" "other")) 1419 | xbktChoice xsep xsepChoice xquoteL xquoteR) 1420 | (let ((completion-ignore-case t)) 1421 | (setq xbktChoice (completing-read "Quote to use:" xbrackets nil t nil nil (car xbrackets))) 1422 | (setq xsepChoice (completing-read "line separator:" xcomma nil t nil nil (car xcomma)))) 1423 | (cond 1424 | ((string-equal xbktChoice "none") 1425 | (setq xquoteL "" xquoteR "")) 1426 | ((string-equal xbktChoice "other") 1427 | (let ((xx (read-string "Enter 2 chars, for begin/end quote:"))) 1428 | (setq xquoteL (substring xx 0 1) 1429 | xquoteR (substring xx 1 2)))) 1430 | (t (setq xquoteL (substring xbktChoice 0 1) 1431 | xquoteR (substring xbktChoice -1)))) 1432 | (setq xsep 1433 | (cond 1434 | ((string-equal xsepChoice "comma ,") ",") 1435 | ((string-equal xsepChoice "semicolon ;") ";") 1436 | ((string-equal xsepChoice "none") "") 1437 | ((string-equal xsepChoice "other") (read-string "Enter separator:")) 1438 | (t xsepChoice))) 1439 | (list xquoteL xquoteR xsep))) 1440 | (let (xbeg xend (xquoteL QuoteL) (xquoteR QuoteR) (xsep Sep)) 1441 | (seq-setq (xbeg xend) (if (region-active-p) (list (region-beginning) (region-end)) (list (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point))) (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point)))))) 1442 | (save-excursion 1443 | (save-restriction 1444 | (narrow-to-region xbeg xend) 1445 | (goto-char (point-min)) 1446 | (catch 'EndReached 1447 | (while t 1448 | (skip-chars-forward "\t ") 1449 | (insert xquoteL) 1450 | (end-of-line) 1451 | (insert xquoteR xsep) 1452 | (if (eq (point) (point-max)) 1453 | (throw 'EndReached t) 1454 | (forward-char)))))))) 1455 | 1456 | (defun xah-escape-quotes (Begin End) 1457 | "Add slash before double quote in current line or selection. 1458 | Double quote is codepoint 34. 1459 | See also: `xah-unescape-quotes' 1460 | URL `http://xahlee.info/emacs/emacs/elisp_escape_quotes.html' 1461 | Version: 2017-01-11" 1462 | (interactive 1463 | (if (region-active-p) 1464 | (list (region-beginning) (region-end)) 1465 | (list (line-beginning-position) (line-end-position)))) 1466 | (save-excursion 1467 | (save-restriction 1468 | (narrow-to-region Begin End) 1469 | (goto-char (point-min)) 1470 | (while (search-forward "\"" nil t) 1471 | (replace-match "\\\"" t t))))) 1472 | 1473 | (defun xah-unescape-quotes (&optional Begin End) 1474 | "Replace 「\\\"」 by 「\"」 in current line or selection. 1475 | See also: `xah-escape-quotes' 1476 | 1477 | URL `http://xahlee.info/emacs/emacs/elisp_escape_quotes.html' 1478 | Created: 2017-01-11 1479 | Version: 2023-11-02" 1480 | (interactive) 1481 | (let (xbeg xend) 1482 | (if (and Begin End) 1483 | (setq xbeg Begin xend End) 1484 | (if (region-active-p) 1485 | (setq xbeg (region-beginning) xend (region-end)) 1486 | (setq xbeg (line-beginning-position) xend (line-end-position)))) 1487 | (save-excursion 1488 | (save-restriction 1489 | (narrow-to-region xbeg xend) 1490 | (goto-char (point-min)) 1491 | (while (search-forward "\\\"" nil t) 1492 | (replace-match "\"" t t)))))) 1493 | 1494 | (defun xah-cycle-hyphen-lowline-space (&optional Begin End) 1495 | "Cycle {hyphen lowline space} chars. 1496 | 1497 | The region to work on is by this order: 1498 | 1. if there is a selection, use that. 1499 | 2. If cursor is in a string quote or any type of bracket, and is within current line, work on that region. 1500 | 3. else, work on current line. 1501 | 1502 | After this command is called, press `xah-repeat-key' to repeat it. 1503 | 1504 | URL `http://xahlee.info/emacs/emacs/elisp_change_space-hyphen_underscore.html' 1505 | Created: 2019-02-12 1506 | Version: 2025-01-24" 1507 | (interactive) 1508 | ;; this function sets a property 'state. Possible values are 0 to length of xcharArray. 1509 | (let (xbeg xend xlen 1510 | (xcharArray ["-" "_" " "]) 1511 | (xregionWasActive-p (region-active-p)) 1512 | (xnowState (if (eq last-command this-command) (get 'xah-cycle-hyphen-lowline-space 'state) 0)) 1513 | xchangeTo) 1514 | (setq 1515 | xlen (length xcharArray) 1516 | xchangeTo (elt xcharArray xnowState)) 1517 | (if (and Begin End) 1518 | (setq xbeg Begin xend End) 1519 | (if (region-active-p) 1520 | (setq xbeg (region-beginning) xend (region-end)) 1521 | (let ((xskipChars "^\"<>(){}[]“”‘’‹›«»「」『』【】〖〗《》〈〉〔〕()")) 1522 | (skip-chars-backward xskipChars (line-beginning-position)) 1523 | (setq xbeg (point)) 1524 | (skip-chars-forward xskipChars (line-end-position)) 1525 | (setq xend (point)) 1526 | (push-mark xbeg)))) 1527 | (save-excursion 1528 | (save-restriction 1529 | (narrow-to-region xbeg xend) 1530 | (goto-char (point-min)) 1531 | (while (re-search-forward (elt xcharArray (% (+ xnowState 2) xlen)) (point-max) 1) 1532 | (replace-match xchangeTo t t)))) 1533 | (when (or (string-equal xchangeTo " ") xregionWasActive-p) 1534 | (goto-char xend) 1535 | (push-mark xbeg) 1536 | (setq deactivate-mark nil)) 1537 | (put 'xah-cycle-hyphen-lowline-space 'state (% (+ xnowState 1) xlen))) 1538 | (set-transient-map (let ((xkmap (make-sparse-keymap))) (define-key xkmap (kbd (if (boundp 'xah-repeat-key) xah-repeat-key "SPC")) this-command) xkmap))) 1539 | 1540 | (defun xah-copy-file-path (&optional DirPathOnlyQ) 1541 | "Copy current buffer file path or dired path. 1542 | Result is full path. 1543 | If `universal-argument' is called first, copy only the dir path. 1544 | 1545 | If in dired, copy the current or marked files. 1546 | 1547 | If a buffer is not file and not dired, copy value of `default-directory'. 1548 | 1549 | URL `http://xahlee.info/emacs/emacs/emacs_copy_file_path.html' 1550 | Created: 2018-06-18 1551 | Version: 2024-12-15" 1552 | (interactive "P") 1553 | (let ((xfpath 1554 | (if (eq major-mode 'dired-mode) 1555 | (progn 1556 | (let ((xresult (mapconcat #'identity 1557 | (dired-get-marked-files) "\n"))) 1558 | (if (equal (length xresult) 0) 1559 | (progn default-directory) 1560 | (progn xresult)))) 1561 | (or buffer-file-name default-directory)))) 1562 | (kill-new 1563 | (if DirPathOnlyQ 1564 | (progn 1565 | (message "Directory copied: %s" (file-name-directory xfpath)) 1566 | (file-name-directory xfpath)) 1567 | (progn 1568 | (message "File path copied: %s" xfpath) 1569 | xfpath))))) 1570 | 1571 | (defun xah-delete-current-text-block () 1572 | "Delete the current text block plus blank lines, or selection, and copy to `kill-ring'. 1573 | 1574 | If cursor is between blank lines, delete following blank lines. 1575 | 1576 | URL `http://xahlee.info/emacs/emacs/emacs_delete_block.html' 1577 | Created: 2017-07-09 1578 | Version: 2024-10-07" 1579 | (interactive) 1580 | (let (xbeg xend (xp (point))) 1581 | (if (region-active-p) 1582 | (setq xbeg (region-beginning) xend (region-end)) 1583 | (progn 1584 | (setq xbeg 1585 | (if (re-search-backward "\n[ \t]*\n+" nil 1) 1586 | (match-end 0) 1587 | (point))) 1588 | (goto-char xp) 1589 | (setq xend (if (re-search-forward "\n[ \t]*\n+" nil 1) 1590 | (match-end 0) 1591 | (point-max))))) 1592 | (kill-region xbeg xend))) 1593 | 1594 | (defun xah-copy-to-register-1 () 1595 | "Copy current line or selection to register 1. 1596 | 1597 | See also: 1598 | `xah-copy-to-register-1' 1599 | `xah-append-to-register-1' 1600 | `xah-paste-from-register-1' 1601 | `xah-clear-register-1' 1602 | 1603 | URL `http://xahlee.info/emacs/emacs/elisp_copy-paste_register_1.html' 1604 | Created: 2012-07-17 1605 | Version: 2023-08-05" 1606 | (interactive) 1607 | (let (xbeg xend) 1608 | (if (region-active-p) 1609 | (setq xbeg (region-beginning) xend (region-end)) 1610 | (setq xbeg (line-beginning-position) xend (line-end-position))) 1611 | (copy-to-register ?1 xbeg xend) 1612 | (message "Copied to register 1: [%s]." (buffer-substring xbeg xend)))) 1613 | 1614 | (defun xah-append-to-register-1 () 1615 | "Append current line or selection to register 1. 1616 | When no selection, append current line, with newline char. 1617 | 1618 | See also: 1619 | `xah-copy-to-register-1' 1620 | `xah-append-to-register-1' 1621 | `xah-paste-from-register-1' 1622 | `xah-clear-register-1' 1623 | 1624 | URL `http://xahlee.info/emacs/emacs/emacs_copy_append.html' 1625 | Created: 2015-12-08 1626 | Version: 2023-08-05" 1627 | (interactive) 1628 | (let (xbeg xend) 1629 | (if (region-active-p) 1630 | (setq xbeg (region-beginning) xend (region-end)) 1631 | (setq xbeg (line-beginning-position) xend (line-end-position))) 1632 | (append-to-register ?1 xbeg xend) 1633 | (with-temp-buffer (insert "\n") 1634 | (append-to-register ?1 (point-min) (point-max))) 1635 | (message "Appended to register 1: [%s]." (buffer-substring xbeg xend)))) 1636 | 1637 | (defun xah-paste-from-register-1 () 1638 | "Paste text from register 1. 1639 | 1640 | See also: 1641 | `xah-copy-to-register-1' 1642 | `xah-append-to-register-1' 1643 | `xah-paste-from-register-1' 1644 | `xah-clear-register-1' 1645 | 1646 | URL `http://xahlee.info/emacs/emacs/elisp_copy-paste_register_1.html' 1647 | Created: 2015-12-08 1648 | Version: 2023-04-07" 1649 | (interactive) 1650 | (when (region-active-p) 1651 | (delete-region (region-beginning) (region-end))) 1652 | (insert-register ?1 t)) 1653 | 1654 | (defun xah-clear-register-1 () 1655 | "Clear register 1. 1656 | 1657 | See also: 1658 | `xah-copy-to-register-1' 1659 | `xah-append-to-register-1' 1660 | `xah-paste-from-register-1' 1661 | `xah-clear-register-1' 1662 | 1663 | URL `http://xahlee.info/emacs/emacs/elisp_copy-paste_register_1.html' 1664 | Created: 2015-12-08 1665 | Version: 2023-04-07" 1666 | (interactive) 1667 | (progn 1668 | (copy-to-register ?1 (point-min) (point-min)) 1669 | (message "Cleared register 1."))) 1670 | 1671 | ;; HHHH------------------------------ 1672 | ;; insertion commands 1673 | 1674 | (defun xah-insert-date () 1675 | "Insert current date time. 1676 | Insert date in this format: yyyy-mm-dd. 1677 | If `universal-argument' is called first, prompt for a format to use. 1678 | If there is selection, delete it first. 1679 | 1680 | URL `http://xahlee.info/emacs/emacs/elisp_insert-date-time.html' 1681 | Created: 2013-05-10 1682 | Version: 2025-04-12" 1683 | (interactive) 1684 | (let (xmenu xstyle) 1685 | (setq 1686 | xmenu 1687 | (list 1688 | (concat "ISO date⚫" (format-time-string "%Y-%m-%d")) 1689 | (concat "coder⚫" (format-time-string "%Y-%m-%d_%H%M%S")) 1690 | (concat "all digits⚫" (format-time-string "%Y%m%d%H%M%S")) 1691 | 1692 | (concat "unix seconds⚫" (number-to-string (car (let ((current-time-list nil)) (current-time))))) 1693 | 1694 | ;; (concat "ISO full⚫" (format-time-string "%Y-%m-%dT%T") (funcall (lambda (xx) (format "%s:%s" (substring xx 0 3) (substring xx 3 5))) (format-time-string "%z"))) 1695 | 1696 | (concat "ISO full⚫" (format-time-string "%FT%T%z")) 1697 | 1698 | (concat "ISO + weekday⚫" (format-time-string "%Y-%m-%d %A")) 1699 | (concat "USA + weekday⚫" (format-time-string "%A, %B %d, %Y")) 1700 | (concat "USA + weekday abbrev⚫" (format-time-string "%a, %b %d, %Y")) 1701 | (concat "USA⚫" (format-time-string "%B %d, %Y")) 1702 | (concat "USA abbrev⚫" (format-time-string "%b %d, %Y")))) 1703 | (setq xstyle 1704 | (if current-prefix-arg 1705 | (let ((completion-ignore-case t)) 1706 | (completing-read "Style:" xmenu nil t)) 1707 | (car xmenu))) 1708 | (when (region-active-p) (delete-region (region-beginning) (region-end))) 1709 | (insert (nth 1 (split-string xstyle "⚫"))))) 1710 | 1711 | (defun xah-insert-bracket-pair (LBracket RBracket &optional WrapMethod) 1712 | "Insert brackets around selection, word, at point, and maybe move cursor in between. 1713 | 1714 | LBracket and RBracket are strings. WrapMethod must be either `line' or `block'. `block' means between empty lines. 1715 | 1716 | • If there is a active region, wrap around region. 1717 | Else 1718 | • If WrapMethod is `line', wrap around line. 1719 | • If WrapMethod is `block', wrap around block. 1720 | Else 1721 | • If cursor is at beginning of line and its not empty line and contain at least 1 space, wrap around the line. 1722 | • If cursor is at end of a word or buffer, one of the following will happen: 1723 | xyz▮ → xyz(▮) 1724 | xyz▮ → (xyz▮) if in one of the lisp modes. 1725 | • wrap brackets around word if any. e.g. xy▮z → (xyz▮). Or just (▮) 1726 | 1727 | URL `http://xahlee.info/emacs/emacs/elisp_insert_brackets_by_pair.html' 1728 | Created: 2017-01-17 1729 | Version: 2025-03-25" 1730 | (if (region-active-p) 1731 | (progn 1732 | (let ((xbeg (region-beginning)) (xend (region-end))) 1733 | (goto-char xend) (insert RBracket) 1734 | (goto-char xbeg) (insert LBracket) 1735 | (goto-char (+ xend 2)))) 1736 | (let (xbeg xend) 1737 | (cond 1738 | ((eq WrapMethod 'line) 1739 | (setq xbeg (line-beginning-position) xend (line-end-position)) 1740 | (goto-char xend) 1741 | (insert RBracket) 1742 | (goto-char xbeg) 1743 | (insert LBracket) 1744 | (goto-char (+ xend (length LBracket)))) 1745 | ((eq WrapMethod 'block) 1746 | (save-excursion 1747 | (seq-setq (xbeg xend) (if (region-active-p) (list (region-beginning) (region-end)) (list (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point))) (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point)))))) 1748 | (goto-char xend) 1749 | (insert RBracket) 1750 | (goto-char xbeg) 1751 | (insert LBracket) 1752 | (goto-char (+ xend (length LBracket))))) 1753 | ( ; do line. line must contain space 1754 | (and 1755 | (eq (point) (line-beginning-position)) 1756 | (not (eq (line-beginning-position) (line-end-position)))) 1757 | (insert LBracket) 1758 | (end-of-line) 1759 | (insert RBracket)) 1760 | ((and 1761 | (or ; cursor is at end of word or buffer. i.e. xyz▮ 1762 | (looking-at "[^-_[:alnum:]]") 1763 | (eq (point) (point-max))) 1764 | (not (or 1765 | (eq major-mode 'xah-elisp-mode) 1766 | (eq major-mode 'emacs-lisp-mode) 1767 | (eq major-mode 'lisp-mode) 1768 | (eq major-mode 'lisp-interaction-mode) 1769 | (eq major-mode 'common-lisp-mode) 1770 | (eq major-mode 'clojure-mode) 1771 | (eq major-mode 'xah-clojure-mode) 1772 | (eq major-mode 'scheme-mode)))) 1773 | (progn 1774 | (setq xbeg (point) xend (point)) 1775 | (insert LBracket RBracket) 1776 | (search-backward RBracket))) 1777 | (t (progn 1778 | ;; wrap around “word”. basically, want all alphanumeric, plus hyphen and underscore, but don't want space or punctuations. Also want chinese chars 1779 | ;; 我有一帘幽梦,不知与谁能共。多少秘密在其中,欲诉无人能懂。 1780 | (skip-chars-backward "-_[:alnum:]") 1781 | (setq xbeg (point)) 1782 | (skip-chars-forward "-_[:alnum:]") 1783 | (setq xend (point)) 1784 | (goto-char xend) 1785 | (insert RBracket) 1786 | (goto-char xbeg) 1787 | (insert LBracket) 1788 | (goto-char (+ xend (length LBracket))))))))) 1789 | 1790 | (defun xah-insert-paren () (interactive) (xah-insert-bracket-pair "(" ")")) 1791 | (defun xah-insert-square-bracket () (interactive) (xah-insert-bracket-pair "[" "]")) 1792 | (defun xah-insert-brace () (interactive) (xah-insert-bracket-pair "{" "}")) 1793 | 1794 | (defun xah-insert-ascii-double-quote () (interactive) (xah-insert-bracket-pair "\"" "\"")) 1795 | (defun xah-insert-ascii-single-quote () (interactive) (xah-insert-bracket-pair "'" "'")) 1796 | (defun xah-insert-ascii-angle-bracket () (interactive) (xah-insert-bracket-pair "<" ">")) 1797 | 1798 | (defun xah-insert-emacs-quote () (interactive) (xah-insert-bracket-pair "`" "'")) 1799 | (defun xah-insert-markdown-quote () (interactive) (xah-insert-bracket-pair "`" "`")) 1800 | (defun xah-insert-markdown-triple-quote () (interactive) (xah-insert-bracket-pair "```\n" "\n```")) 1801 | 1802 | (defun xah-insert-double-curly-quote“” () (interactive) (xah-insert-bracket-pair "“" "”")) 1803 | (defun xah-insert-curly-single-quote‘’ () (interactive) (xah-insert-bracket-pair "‘" "’")) 1804 | (defun xah-insert-single-angle-quote‹› () (interactive) (xah-insert-bracket-pair "‹" "›")) 1805 | (defun xah-insert-double-angle-quote«» () (interactive) (xah-insert-bracket-pair "«" "»")) 1806 | 1807 | (defun xah-insert-corner-bracket「」 () (interactive) (xah-insert-bracket-pair "「" "」")) 1808 | (defun xah-insert-white-corner-bracket『』 () (interactive) (xah-insert-bracket-pair "『" "』")) 1809 | (defun xah-insert-angle-bracket〈〉 () (interactive) (xah-insert-bracket-pair "〈" "〉")) 1810 | (defun xah-insert-double-angle-bracket《》 () (interactive) (xah-insert-bracket-pair "《" "》")) 1811 | (defun xah-insert-white-lenticular-bracket〖〗 () (interactive) (xah-insert-bracket-pair "〖" "〗")) 1812 | (defun xah-insert-black-lenticular-bracket【】 () (interactive) (xah-insert-bracket-pair "【" "】")) 1813 | (defun xah-insert-tortoise-shell-bracket〔〕 () (interactive) (xah-insert-bracket-pair "〔" "〕")) 1814 | (defun xah-insert-deco-angle-bracket❮❯ () (interactive) (xah-insert-bracket-pair "❮" "❯")) 1815 | (defun xah-insert-deco-angle-fat-bracket❰❱ () (interactive) (xah-insert-bracket-pair "❰" "❱")) 1816 | 1817 | (defun xah-insert-hyphen () 1818 | "Insert a HYPHEN-MINUS character." 1819 | (interactive) 1820 | (insert "-")) 1821 | 1822 | (defun xah-insert-low-line () 1823 | "Insert a LOW LINE character." 1824 | (interactive) 1825 | (insert "_")) 1826 | 1827 | (defun xah-insert-string-assignment () 1828 | "Insert =\"\"" 1829 | (interactive) 1830 | (progn (insert "=\"\"") 1831 | (left-char))) 1832 | 1833 | (defun xah-insert-space-before () 1834 | "Insert space before cursor." 1835 | (interactive) 1836 | (insert " ")) 1837 | 1838 | (defun xah-insert-space-after () 1839 | "Insert space after cursor" 1840 | (interactive) 1841 | (insert " ") 1842 | (left-char)) 1843 | 1844 | (defun xah-insert-seperator () 1845 | "Insert a visual seperator line." 1846 | (interactive) 1847 | (cond 1848 | ((and buffer-file-name (string-equal "html" (file-name-extension buffer-file-name))) (insert "<hr />\n")) 1849 | ((not comment-start) 1850 | (insert "\nHHHH------------------------------\n")) 1851 | (t (insert "\nHHHH------------------------------\n") 1852 | (backward-char) 1853 | (comment-line 1)))) 1854 | 1855 | (defvar xah-unicode-list nil 1856 | "A alist. 1857 | Each item is (prompStr . xString). Used by `xah-insert-unicode'. 1858 | prompStr is used for prompt. 1859 | xString is is the char to insert. 1860 | xString can be multiple chars or any string. 1861 | ") 1862 | 1863 | (setq 1864 | xah-unicode-list 1865 | '( 1866 | ;; 1867 | ("smile beaming 😊" . "😊") 1868 | ("tears of joy" . "😂") 1869 | ("hug 🤗" . "🤗") 1870 | ("heart eyes 😍" . "😍") 1871 | ("heart face 🥰" . "🥰") 1872 | ("angry 😠" . "😠") 1873 | ("vomit 🤮" . "🤮") 1874 | ("thumb up 👍" . "👍") 1875 | ("thumb down 👎" . "👎") 1876 | ("tv 📺" . "📺") 1877 | ("checkmark ✅" . "✅") 1878 | ("new 🆕" . "🆕") 1879 | ("glowing star 🌟" . "🌟") 1880 | ("star ⭐" . "⭐") 1881 | ("sparkles ✨" . "✨") 1882 | ("rocket 🚀" . "🚀") 1883 | ("sun 🌞" . "🌞") 1884 | ("heart 🧡" . "🧡") 1885 | ("clown 🤡" . "🤡") 1886 | ("large circle" . "⭕") 1887 | ("cross ❌" . "❌") 1888 | ("red triangle 🔺" . "🔺") 1889 | ("diamond 💠" . "💠") 1890 | ("square ⬛" . "⬛") 1891 | ("script 📜" . "📜") 1892 | ("package 📦" . "📦") 1893 | ("cursor ▮" . "▮") 1894 | ("music 🎵" . "🎵") 1895 | ("ok 🆗" . "🆗") 1896 | 1897 | ("dagger †" . "†") 1898 | ("double dagger ‡" . "‡") 1899 | 1900 | ("double angle bracket" . "《》") 1901 | ("black lenticular bracket" . "【】") 1902 | ("corner-bracket" . "「」") 1903 | ("tortoise shell bracket" . "〔〕") 1904 | ("angle bracket" . "〈〉") 1905 | ("double angle quote" . "«»") 1906 | 1907 | ("bullet •" . "•") 1908 | ("diamond ◆" . "◆") 1909 | ("...ellipsis …" . "…") 1910 | ("nbsp non breaking space" . " ") 1911 | ("chinese comma 、" . "、") 1912 | ("emdash —" . "—") 1913 | ("fullwidth ampersand &" . "&") 1914 | ("left arrow ←" . "←") 1915 | ("right arrow →" . "→") 1916 | ("up arrow ↑" . "↑") 1917 | ("down arrow ↓" . "↓") 1918 | ("f hook ƒ" . "ƒ") 1919 | ("chinese space" . " ") 1920 | 1921 | ;; 1922 | )) 1923 | 1924 | (defun xah-insert-unicode () 1925 | "Insert a unicode from a custom list `xah-unicode-list'. 1926 | URL `http://xahlee.info/emacs/emacs/emacs_insert_unicode.html' 1927 | Created: 2021-01-05 1928 | Version: 2023-09-19" 1929 | (interactive) 1930 | (let ((xkey 1931 | (let ((completion-ignore-case t)) 1932 | (completing-read "Insert:" xah-unicode-list nil t)))) 1933 | (insert (cdr (assoc xkey xah-unicode-list))))) 1934 | 1935 | ;; HHHH------------------------------ 1936 | ;; text selection 1937 | 1938 | (defun xah-select-block () 1939 | "Select the current/next block plus 1 blankline. 1940 | If region is active, extend selection downward by block. 1941 | 1942 | URL `http://xahlee.info/emacs/emacs/emacs_select_text_block.html' 1943 | Created: 2019-12-26 1944 | Version: 2023-11-14" 1945 | (interactive) 1946 | (if (region-active-p) 1947 | (re-search-forward "\n[ \t]*\n[ \t]*\n*" nil 1) 1948 | (progn 1949 | (skip-chars-forward " \n\t") 1950 | (when (re-search-backward "\n[ \t]*\n" nil 1) 1951 | (goto-char (match-end 0))) 1952 | (push-mark (point) t t) 1953 | (re-search-forward "\n[ \t]*\n" nil 1)))) 1954 | 1955 | (defun xah-select-line () 1956 | "Select current line. If region is active, extend selection downward by line. 1957 | If `visual-line-mode' is on, consider line as visual line. 1958 | 1959 | URL `http://xahlee.info/emacs/emacs/emacs_select_line.html' 1960 | Created: 2017-11-01 1961 | Version: 2023-11-14" 1962 | (interactive) 1963 | (if (region-active-p) 1964 | (if visual-line-mode 1965 | (let ((xbeg (point))) 1966 | (end-of-visual-line 1) 1967 | (when (eq xbeg (point)) 1968 | (end-of-visual-line 2))) 1969 | (progn 1970 | (forward-line 1) 1971 | (end-of-line))) 1972 | (if visual-line-mode 1973 | (progn (beginning-of-visual-line) 1974 | (push-mark (point) t t) 1975 | (end-of-visual-line)) 1976 | (progn 1977 | (push-mark (line-beginning-position) t t) 1978 | (end-of-line))))) 1979 | 1980 | (defun xah-extend-selection () 1981 | "Select the current word, bracket/quote expression, or expand selection. 1982 | Subsequent calls expands the selection. 1983 | 1984 | when there is no selection, 1985 | • If cursor is on any type of bracket (including parenthesis, quotation mark), select whole bracketed thing including bracket 1986 | • else, select current word. 1987 | 1988 | when there is a selection, the selection extension behavior is still experimental. But when cursor is on a any type of bracket (parenthesis, quote), it extends selection to outer bracket. 1989 | 1990 | URL `http://xahlee.info/emacs/emacs/emacs_extend_selection.html' 1991 | Created: 2020-02-04 1992 | Version: 2023-11-14" 1993 | (interactive) 1994 | 1995 | (cond 1996 | ((region-active-p) 1997 | (let ((xbeg (region-beginning)) (xend (region-end))) 1998 | (goto-char xbeg) 1999 | (cond 2000 | ((looking-at "\\s(") 2001 | (if (eq (nth 0 (syntax-ppss)) 0) 2002 | (progn 2003 | ;; (message "debug: left bracket, depth 0.") 2004 | (end-of-line) ; select current line 2005 | (push-mark (line-beginning-position) t t)) 2006 | (progn 2007 | ;; (message "debug: left bracket, depth not 0") 2008 | (up-list -1 t t) 2009 | (mark-sexp)))) 2010 | ((eq xbeg (line-beginning-position)) 2011 | (progn 2012 | (goto-char xbeg) 2013 | (let ((xfirstLineEndPos (line-end-position))) 2014 | (cond 2015 | ((eq xend xfirstLineEndPos) 2016 | (progn 2017 | ;; (message "debug: exactly 1 line. extend to next whole line." ) 2018 | (forward-line 1) 2019 | (end-of-line))) 2020 | ((< xend xfirstLineEndPos) 2021 | (progn 2022 | ;; (message "debug: less than 1 line. complete the line." ) 2023 | (end-of-line))) 2024 | ((> xend xfirstLineEndPos) 2025 | (progn 2026 | ;; (message "debug: beginning of line, but end is greater than 1st end of line" ) 2027 | (goto-char xend) 2028 | (if (eq (point) (line-end-position)) 2029 | (progn 2030 | ;; (message "debug: exactly multiple lines" ) 2031 | (forward-line 1) 2032 | (end-of-line)) 2033 | (progn 2034 | ;; (message "debug: multiple lines but end is not eol. make it so" ) 2035 | (goto-char xend) 2036 | (end-of-line))))) 2037 | (t (error "%s: logic error 42946" real-this-command)))))) 2038 | ((and (> (point) (line-beginning-position)) (<= (point) (line-end-position))) 2039 | (progn 2040 | ;; (message "debug: less than 1 line" ) 2041 | (end-of-line) ; select current line 2042 | (push-mark (line-beginning-position) t t))) 2043 | (t 2044 | ;; (message "debug: last resort" ) 2045 | nil)))) 2046 | 2047 | ((looking-at "\\s(") 2048 | ;; (message "debug: left bracket") 2049 | (mark-sexp)) 2050 | 2051 | ((looking-at "\\s)") 2052 | ;; (message "debug: right bracket") 2053 | (backward-up-list) (mark-sexp)) 2054 | 2055 | ((looking-at "\\s\"") 2056 | ;; (message "debug: string quote") 2057 | (mark-sexp)) 2058 | 2059 | ((looking-at "[ \t\n]") 2060 | ;; (message "debug: is white space") 2061 | (skip-chars-backward " \t\n") 2062 | (push-mark) 2063 | (skip-chars-forward " \t\n") 2064 | (setq mark-active t)) 2065 | 2066 | ((looking-at "[-_a-zA-Z0-9]") 2067 | ;; (message "debug: left is word or symbol") 2068 | (skip-chars-backward "-_a-zA-Z0-9") 2069 | (push-mark) 2070 | (skip-chars-forward "-_a-zA-Z0-9") 2071 | (setq mark-active t)) 2072 | 2073 | ;; ((and (looking-at "[[:blank:]]") 2074 | ;; (prog2 (backward-char) (looking-at "[[:blank:]]") (forward-char))) 2075 | ;; ;; (message "debug: left and right both space" ) 2076 | ;; (skip-chars-backward "[[:blank:]]") (push-mark (point) t t) 2077 | ;; (skip-chars-forward "[[:blank:]]")) 2078 | 2079 | ((and (looking-at "\n") 2080 | (eq (char-before) 10)) 2081 | ;; (message "debug: left and right both newline") 2082 | (skip-chars-forward "\n") 2083 | (push-mark (point) t t) 2084 | (re-search-forward "\n[ \t]*\n")) 2085 | 2086 | (t 2087 | ;; (message "debug: just mark sexp" ) 2088 | (mark-sexp) 2089 | (exchange-point-and-mark)))) 2090 | 2091 | (defun xah-select-text-in-quote () 2092 | "Select text between the nearest left and right delimiters. 2093 | Delimiters here includes QUOTATION MARK, GRAVE ACCENT, and anything in variable `xah-brackets'. 2094 | This command ignores nesting. For example, if text is 2095 | 「(a(b)c▮)」 2096 | the selected char is 「c」, not 「a(b)c」. 2097 | 2098 | URL `http://xahlee.info/emacs/emacs/emacs_select_quote_text.html' 2099 | Created: 2020-11-24 2100 | Version: 2023-11-14" 2101 | (interactive) 2102 | (let ((xskipChars (concat "^\"`" (mapconcat #'identity xah-brackets "")))) 2103 | (skip-chars-backward xskipChars) 2104 | (push-mark (point) t t) 2105 | (skip-chars-forward xskipChars))) 2106 | 2107 | (defun xah-cut-text-in-quote () 2108 | "Cut text between the nearest left and right delimiters. 2109 | See `xah-select-text-in-quote' 2110 | 2111 | Created: 2023-07-23 2112 | Version: 2024-10-02" 2113 | (interactive) 2114 | (let (xbeg xend 2115 | (xskipChars (concat "^\"`" (mapconcat #'identity xah-brackets "")))) 2116 | (skip-chars-backward xskipChars) 2117 | (setq xbeg (point)) 2118 | (skip-chars-forward xskipChars) 2119 | (setq xend (point)) 2120 | (kill-region xbeg xend))) 2121 | 2122 | ;; HHHH------------------------------ 2123 | ;; misc 2124 | 2125 | 2126 | 2127 | (defun xah-user-buffer-p () 2128 | "Return t if current buffer is a user buffer, else nil. 2129 | A user buffer has buffer name NOT starts with * or space, and is not dired mode, help mode, etc. 2130 | This function is used by buffer switching command and close buffer command, so that next buffer shown is a user buffer. 2131 | You can override this function to get your idea of “user buffer”. 2132 | Created: 2016-06-18 2133 | Version: 2024-09-23" 2134 | (interactive) 2135 | (cond 2136 | ((string-match "^\*" (buffer-name)) nil) 2137 | ((eq major-mode 'dired-mode) nil) 2138 | ((eq major-mode 'eww-mode) nil) 2139 | ((eq major-mode 'help-mode) nil) 2140 | (t t))) 2141 | 2142 | (defun xah-next-user-buffer () 2143 | "Switch to the next user buffer. 2144 | User Buffer here is determined by `xah-user-buffer-p'. 2145 | 2146 | Press left or right arrow key to switch to prev next user. 2147 | Press up or down arrow to switch to prev next emacs buffer. 2148 | Any other key to exit. 2149 | 2150 | URL `http://xahlee.info/emacs/emacs/elisp_next_prev_user_buffer.html' 2151 | Created: 2016-06-19 2152 | Version: 2025-04-22" 2153 | (interactive) 2154 | (next-buffer) 2155 | (let ((i 0)) 2156 | (while (< i 30) 2157 | (if (not (xah-user-buffer-p)) 2158 | (progn (next-buffer) 2159 | (setq i (1+ i))) 2160 | (progn (setq i 100)))))) 2161 | 2162 | (defun xah-previous-user-buffer () 2163 | "Switch to the previous user buffer. 2164 | User Buffer here is determined by `xah-user-buffer-p'. 2165 | 2166 | Press left or right arrow key to switch to prev next user. 2167 | Press up or down arrow to switch to prev next emacs buffer. 2168 | Any other key to exit. 2169 | 2170 | URL `http://xahlee.info/emacs/emacs/elisp_next_prev_user_buffer.html' 2171 | Created: 2016-06-19 2172 | Version: 2025-04-22" 2173 | (interactive) 2174 | (previous-buffer) 2175 | (let ((i 0)) 2176 | (while (< i 29) 2177 | (if (not (xah-user-buffer-p)) 2178 | (progn (previous-buffer) 2179 | (setq i (1+ i))) 2180 | (progn (setq i 100)))))) 2181 | 2182 | (defun xah-next-emacs-buffer () 2183 | "Switch to the next emacs buffer. 2184 | Emacs buffer here means `xah-user-buffer-p' return nil. 2185 | 2186 | Press left or right arrow key to switch to prev next user. 2187 | Press up or down arrow to switch to prev next emacs buffer. 2188 | Any other key to exit. 2189 | 2190 | URL `http://xahlee.info/emacs/emacs/elisp_next_prev_user_buffer.html' 2191 | Created: 2013-05-22 2192 | Version: 2025-04-22" 2193 | (interactive) 2194 | (next-buffer) 2195 | (let ((i 0)) 2196 | (while (and (xah-user-buffer-p) (< i 20)) 2197 | (setq i (1+ i)) (next-buffer)))) 2198 | 2199 | (defun xah-previous-emacs-buffer () 2200 | "Switch to the previous emacs buffer. 2201 | Emacs buffer here means `xah-user-buffer-p' return nil. 2202 | 2203 | Press left or right arrow key to switch to prev next user. 2204 | Press up or down arrow to switch to prev next emacs buffer. 2205 | Any other key to exit. 2206 | 2207 | URL `http://xahlee.info/emacs/emacs/elisp_next_prev_user_buffer.html' 2208 | Created: 2013-05-22 2209 | Version: 2025-04-22" 2210 | (interactive) 2211 | (previous-buffer) 2212 | (let ((i 0)) 2213 | (while (and (xah-user-buffer-p) (< i 20)) 2214 | (setq i (1+ i)) (previous-buffer)))) 2215 | 2216 | (defun xah-new-empty-buffer () 2217 | "Create a new empty buffer. 2218 | Returns the buffer object. 2219 | New buffer is named untitled, untitled<2>, etc. 2220 | 2221 | Warning: new buffer is not prompted for save when killed, see `kill-buffer'. 2222 | Or manually `save-buffer' 2223 | 2224 | URL `http://xahlee.info/emacs/emacs/emacs_new_empty_buffer.html' 2225 | Created: 2017-11-01 2226 | Version: 2022-04-05" 2227 | (interactive) 2228 | (let ((xbuf (generate-new-buffer "untitled"))) 2229 | (switch-to-buffer xbuf) 2230 | (funcall initial-major-mode) 2231 | xbuf 2232 | )) 2233 | 2234 | (declare-function minibuffer-keyboard-quit "delsel" ()) 2235 | (declare-function org-edit-src-save "org-src" ()) 2236 | 2237 | (defcustom xah-recently-closed-buffers-max 40 "The maximum length for `xah-recently-closed-buffers'." 2238 | :type 'integer) 2239 | 2240 | (defvar xah-recently-closed-buffers nil "A Alist of recently closed buffers. 2241 | Each element is (bufferName . filePath). 2242 | The max number to track is controlled by the variable `xah-recently-closed-buffers-max'.") 2243 | 2244 | (defun xah-add-to-recently-closed (&optional BufferName BufferFileName) 2245 | "Add to `xah-recently-closed-buffers'. 2246 | Version: 2023-03-02" 2247 | (let ((xbn (if BufferName BufferName (buffer-name))) 2248 | (xbfn (if BufferFileName BufferFileName buffer-file-name))) 2249 | (setq xah-recently-closed-buffers (cons (cons xbn xbfn) xah-recently-closed-buffers))) 2250 | (when (> (length xah-recently-closed-buffers) xah-recently-closed-buffers-max) 2251 | (setq xah-recently-closed-buffers (butlast xah-recently-closed-buffers 1)))) 2252 | 2253 | (defvar xah-create-buffer-backup nil "If true, `xah-close-current-buffer' creates a backup file when closing non-file buffer. Version: 2024-11-09") 2254 | 2255 | (setq xah-create-buffer-backup t) 2256 | 2257 | (defvar xah-temp-dir-path nil "Path to temp dir used by xah commands. 2258 | by default, the value is dir named temp at `user-emacs-directory'. 2259 | Version: 2023-03-21") 2260 | 2261 | (setq xah-temp-dir-path (concat user-emacs-directory "temp/")) 2262 | 2263 | (defun xah-close-current-buffer () 2264 | "Close the current buffer with possible backup. 2265 | 2266 | • If the buffer is a file and not modified, kill it. If is modified, do nothing. Print a message. 2267 | • If the buffer is not a file, and variable `xah-create-buffer-backup' is true, then save a backup to `xah-temp-dir-path' named untitled_‹datetime›_‹randomhex›.txt. 2268 | 2269 | If `universal-argument' is called first, call `kill-buffer'. (this is useful to force kill.) 2270 | 2271 | If the buffer is a file, add the path to the list `xah-recently-closed-buffers'. 2272 | 2273 | URL `http://xahlee.info/emacs/emacs/elisp_close_buffer_open_last_closed.html' 2274 | Created: 2016-06-19 2275 | Version: 2025-04-13" 2276 | (interactive) 2277 | (widen) 2278 | (cond 2279 | (current-prefix-arg (kill-buffer)) 2280 | ;; ((eq major-mode 'minibuffer-inactive-mode) (minibuffer-keyboard-quit)) 2281 | ;; ((active-minibuffer-window) (minibuffer-keyboard-quit)) 2282 | ((minibufferp (current-buffer)) (minibuffer-keyboard-quit)) 2283 | 2284 | ((eq major-mode 'dired-mode) 2285 | (xah-add-to-recently-closed (buffer-name) default-directory) 2286 | (kill-buffer)) 2287 | 2288 | ((and buffer-file-name (not (buffer-modified-p))) 2289 | (xah-add-to-recently-closed (buffer-name) buffer-file-name) 2290 | (kill-buffer)) 2291 | 2292 | ((and buffer-file-name (buffer-modified-p)) 2293 | (message "buffer file modified. Save it first.\n%s" buffer-file-name)) 2294 | ((and xah-create-buffer-backup (not buffer-file-name) (xah-user-buffer-p) (not (eq (point-max) 1))) 2295 | (let ((xnewName (format "%suntitled_%s_%x.txt" 2296 | xah-temp-dir-path 2297 | (format-time-string "%Y-%m-%d_%H%M%S") 2298 | (random #xfffff)))) 2299 | (when (not (file-exists-p xah-temp-dir-path)) (make-directory xah-temp-dir-path)) 2300 | (write-region (point-min) (point-max) xnewName) 2301 | (xah-add-to-recently-closed (buffer-name) xnewName) 2302 | (kill-buffer))) 2303 | (t (kill-buffer)))) 2304 | 2305 | (defun xah-open-last-closed () 2306 | "Open the last closed file. 2307 | URL `http://xahlee.info/emacs/emacs/elisp_close_buffer_open_last_closed.html' 2308 | Created: 2016-06-19 2309 | Version: 2022-03-22" 2310 | (interactive) 2311 | (if (> (length xah-recently-closed-buffers) 0) 2312 | (find-file (cdr (pop xah-recently-closed-buffers))) 2313 | (progn (message "No recently close buffer in this session.")))) 2314 | 2315 | (defun xah-open-recently-closed () 2316 | "Open recently closed file. 2317 | Prompt for a choice. 2318 | 2319 | URL `http://xahlee.info/emacs/emacs/elisp_close_buffer_open_last_closed.html' 2320 | Created: 2016-06-19 2321 | Version: 2023-09-19" 2322 | (interactive) 2323 | (find-file 2324 | (let ((completion-ignore-case t)) 2325 | (completing-read 2326 | "Open:" 2327 | (mapcar (lambda (f) (cdr f)) xah-recently-closed-buffers) 2328 | nil t 2329 | )))) 2330 | 2331 | (defun xah-list-recently-closed () 2332 | "List recently closed file. 2333 | 2334 | URL `http://xahlee.info/emacs/emacs/elisp_close_buffer_open_last_closed.html' 2335 | Version: 2016-06-19" 2336 | (interactive) 2337 | (let ((xbuf (generate-new-buffer "*recently closed*"))) 2338 | (switch-to-buffer xbuf) 2339 | (mapc (lambda (xf) (insert (cdr xf) "\n")) 2340 | xah-recently-closed-buffers))) 2341 | 2342 | (defvar xah-open-file-at-cursor-pre-hook nil "Hook for `xah-open-file-at-cursor'. 2343 | Functions in the hook is called in order, each given the raw input text (path) as arg. 2344 | The first return non-nil, its value is given to `xah-open-file-at-cursor' as input. rest functions in hook is ignored. 2345 | This is useful for transforming certain url into file path. e.g. change 2346 | http://xahlee.info/emacs/index.html 2347 | to 2348 | C:/Users/xah/web/xahlee_info/emacs/index.html 2349 | , so instead of opening in browser, it opens in emacs as file.") 2350 | 2351 | (defun xah-open-file-at-cursor () 2352 | "Open the file path under cursor. 2353 | 2354 | • If there is selection, use it for path. 2355 | • Path can be {relative, full path, URL}. 2356 | • If the path starts with 「https*://」, open the URL in browser. 2357 | • Path may have a trailing 「:‹n›」 that indicates line number, or 「:‹n›:‹m›」 with line and column number. If so, jump to that line number. 2358 | 2359 | If path does not have a file extension, automatically try with .el for elisp files. 2360 | 2361 | See also `xah-open-file-at-cursor-pre-hook'. 2362 | 2363 | This command is similar to `find-file-at-point' but without prompting for confirmation. 2364 | 2365 | URL `http://xahlee.info/emacs/emacs/emacs_open_file_path_fast.html' 2366 | Created: 2020-10-17 2367 | Version: 2024-09-25" 2368 | (interactive) 2369 | (let (xinput xinput2 xpath) 2370 | (setq xinput (if (region-active-p) 2371 | (buffer-substring-no-properties (region-beginning) (region-end)) 2372 | (let ((xp0 (point)) xbeg xend 2373 | (xpathStops "^  \t\n\"`'‘’“”|()[]{}「」<>〔〕〈〉《》【】〖〗«»‹›❮❯❬❭〘〙·。\\")) 2374 | (skip-chars-backward xpathStops) 2375 | (setq xbeg (point)) 2376 | (goto-char xp0) 2377 | (skip-chars-forward xpathStops) 2378 | (setq xend (point)) 2379 | (goto-char xp0) 2380 | (buffer-substring-no-properties xbeg xend)))) 2381 | (setq xinput2 (if (> (length xah-open-file-at-cursor-pre-hook) 0) 2382 | (let ((xprehook (run-hook-with-args-until-success 'xah-open-file-at-cursor-pre-hook xinput))) 2383 | (if xprehook xprehook xinput)) 2384 | xinput)) 2385 | 2386 | (setq xpath 2387 | (cond 2388 | ((string-match "\\`file:///[A-Za-z]:/" xinput2) (substring xinput2 8)) 2389 | ((string-match "\\`file://[A-Za-z]:/" xinput2) (substring xinput2 7)) 2390 | (t xinput2))) 2391 | 2392 | (if (string-match-p "\\`https?://" xpath) 2393 | (browse-url xpath) 2394 | (let ((xpathNoQ 2395 | (let ((xHasQuery (string-match "\?[a-z]+=" xpath))) 2396 | (if xHasQuery 2397 | (substring xpath 0 xHasQuery) 2398 | xpath)))) 2399 | (cond 2400 | ((string-match "#" xpathNoQ) 2401 | (let ((xfpath (substring xpathNoQ 0 (match-beginning 0))) 2402 | (xfractPart (substring xpathNoQ (1+ (match-beginning 0))))) 2403 | (if (file-exists-p xfpath) 2404 | (progn 2405 | (find-file xfpath) 2406 | (goto-char (point-min)) 2407 | (search-forward xfractPart)) 2408 | (progn 2409 | (message "File does not exist. Created at\n%s" xfpath) 2410 | (find-file xfpath))))) 2411 | ((string-match "\\`\\(.+?\\):\\([0-9]+\\)\\(:[0-9]+\\)?\\'" xpathNoQ) 2412 | (let ((xfpath (match-string-no-properties 1 xpathNoQ)) 2413 | (xlineNum (string-to-number (match-string-no-properties 2 xpathNoQ)))) 2414 | (if (file-exists-p xfpath) 2415 | (progn 2416 | (find-file xfpath) 2417 | (goto-char (point-min)) 2418 | (forward-line (1- xlineNum))) 2419 | (progn 2420 | (message "File does not exist. Created at\n%s" xfpath) 2421 | (find-file xfpath))))) 2422 | ((file-exists-p xpathNoQ) 2423 | (progn ; open f.ts instead of f.js 2424 | (let ((xext (file-name-extension xpathNoQ)) 2425 | (xfnamecore (file-name-sans-extension xpathNoQ))) 2426 | (if (and (string-equal xext "js") 2427 | (file-exists-p (concat xfnamecore ".ts"))) 2428 | (progn 2429 | (find-file (concat xfnamecore ".ts")) 2430 | (warn "Typescript file .ts exist, opening it")) 2431 | 2432 | (find-file xpathNoQ))))) 2433 | ((file-exists-p (concat xpathNoQ ".el")) 2434 | (find-file (concat xpathNoQ ".el"))) 2435 | (t (progn 2436 | (message "File does not exist. Created at\n%s" xpathNoQ) 2437 | (find-file xpathNoQ)))))))) 2438 | 2439 | ;; HHHH------------------------------ 2440 | 2441 | (defvar xah-run-current-file-dispatch nil 2442 | "A dispatch table used by `xah-run-current-file' to call dedicated function to run code. 2443 | Value is a association list. 2444 | Each item is (EXT . FUNCTION). 2445 | EXT is filename extension (sans the dot), type string. 2446 | FUNCTION is a elisp function name to call, type symbol. 2447 | If file extension match, and FUNCTION is defined, call it, pass current buffer's filepath as arg. 2448 | Else, `xah-run-current-file-map' is looked up." ) 2449 | 2450 | (setq xah-run-current-file-dispatch 2451 | '(("el" . load) 2452 | ("elc" . load) 2453 | ("java" . xah-java-compile-and-run))) 2454 | 2455 | (defvar xah-run-current-file-map 2456 | "A association list that maps file extension to a command for running the file, used by `xah-run-current-file'. 2457 | Each item is (EXT . PROGRAM). 2458 | EXT is filename extension (sans the dot), type string. 2459 | PROGRAM is program name or path, with command options to run a file, type string. 2460 | A filename is appended after the PROGRAM string as external command to call.") 2461 | 2462 | (setq xah-run-current-file-map 2463 | '( 2464 | ;; following are tested as of 2024-12-20 2465 | 2466 | ("fs" . "dotnet fsi") 2467 | ("fsx" . "dotnet fsi") 2468 | ("go" . "go run") 2469 | ("js" . "deno run") 2470 | ("php" . "php") 2471 | ("pl" . "perl") 2472 | ("ps1" . "pwsh") 2473 | ("py" . "python") 2474 | ("py2" . "python2") 2475 | ("py3" . "python3") 2476 | ("rb" . "ruby") 2477 | ("ts" . "deno run") 2478 | ("m" . "wolframscript -print all -file") 2479 | ("wl" . "wolframscript -print all -file") 2480 | ("wls" . "wolframscript -print all -file") 2481 | 2482 | ;; following may be outdated 2483 | 2484 | ("clj" . "clj") 2485 | ("hs" . "runhaskell") 2486 | ("latex" . "pdflatex") 2487 | ("ml" . "ocaml") 2488 | ("rkt" . "racket") 2489 | ("sh" . "bash") 2490 | ("tex" . "pdflatex") 2491 | ("tsx" . "tsc") 2492 | ("vbs" . "cscript") 2493 | ("pov" . "povray +R2 +A0.1 +J1.2 +Am2 +Q9 +H480 +W640"))) 2494 | 2495 | (defun xah-java-compile-and-run (Filename) 2496 | "Compile and run java of current buffer. 2497 | Buffer is saved first if modified. 2498 | 2499 | This command is designed for a simple single java class source file. 2500 | requires the commands 「javac」 and 「java」. 2501 | 2502 | If compile fails, the error is displayed in a buffer. 2503 | 2504 | Created: 2024-12-20 2505 | Version: 2024-12-20" 2506 | (interactive (if buffer-file-name (progn (when (buffer-modified-p) (save-buffer)) (list buffer-file-name)) (user-error "Buffer is not file. Save it first."))) 2507 | (let ((xoutbuf (get-buffer-create "*xah java output*" t)) 2508 | (xjavac-buf (get-buffer-create "*xah java compile output*" t))) 2509 | (with-current-buffer xjavac-buf (erase-buffer)) 2510 | (call-process "javac" nil xjavac-buf nil Filename) 2511 | (if (eq 1 (with-current-buffer xjavac-buf (point-max))) 2512 | (progn 2513 | (save-current-buffer (set-buffer xoutbuf) (erase-buffer)) 2514 | (call-process "java" nil xoutbuf nil (file-name-nondirectory (file-name-sans-extension Filename))) 2515 | (display-buffer xoutbuf)) 2516 | (display-buffer xjavac-buf)))) 2517 | 2518 | (defun xah-run-current-file (Filename) 2519 | "Execute the current file. 2520 | Output is printed to buffer *xah-run output*. 2521 | 2522 | File suffix is used to determine what external command to run, in the variable `xah-run-current-file-map'. 2523 | 2524 | If file is modified, it is auto saved before run. 2525 | 2526 | The variable `xah-run-current-file-dispatch' allows you to customize this command to call other function to run the current file. 2527 | 2528 | URL `http://xahlee.info/emacs/emacs/elisp_run_current_file.html' 2529 | Created: 2020-09-24 2530 | Version: 2024-12-25" 2531 | (interactive (if buffer-file-name (progn (when (buffer-modified-p) (save-buffer)) (list buffer-file-name)) (user-error "Buffer is not file. Save it first."))) 2532 | (let ((xoutbuf (get-buffer-create "*xah-run output*" t)) 2533 | (xext (file-name-extension Filename)) 2534 | xdispatch) 2535 | (setq xdispatch (assoc xext xah-run-current-file-dispatch)) 2536 | (if xdispatch 2537 | (if (fboundp (cdr xdispatch)) 2538 | (progn 2539 | (message "calling %s" (cdr xdispatch)) 2540 | (funcall (cdr xdispatch) Filename)) 2541 | (warn "`xah-run-current-file' found function %s in xah-run-current-file-dispatch but it is unbound. Normal run continues using `xah-run-current-file-map'." xdispatch)) 2542 | (let ((xappCmdStr (cdr (assoc xext xah-run-current-file-map)))) 2543 | (when (not xappCmdStr) (error "%s: Unknown file extension: %s. check `xah-run-current-file-map'" real-this-command xext)) 2544 | (progn 2545 | (save-current-buffer (set-buffer xoutbuf) (erase-buffer)) 2546 | (apply 'start-process (append (list "xah-run" xoutbuf) (split-string xappCmdStr " +" t) (list Filename) nil)) 2547 | ;; (display-buffer xoutbuf) 2548 | (pop-to-buffer xoutbuf) 2549 | ;; (with-current-buffer xoutbuf (goto-char (point-min))) 2550 | ))))) 2551 | 2552 | (defun xah-clean-empty-lines () 2553 | "Replace repeated blank lines to just 1, in whole buffer or selection. 2554 | Respects `narrow-to-region'. 2555 | 2556 | URL `http://xahlee.info/emacs/emacs/elisp_compact_empty_lines.html' 2557 | Created: 2017-09-22 2558 | Version: 2020-09-08" 2559 | (interactive) 2560 | (let (xbegin xend) 2561 | (if (region-active-p) 2562 | (setq xbegin (region-beginning) xend (region-end)) 2563 | (setq xbegin (point-min) xend (point-max))) 2564 | (save-excursion 2565 | (save-restriction 2566 | (narrow-to-region xbegin xend) 2567 | (progn 2568 | (goto-char (point-min)) 2569 | (while (re-search-forward "\n\n\n+" nil 1) 2570 | (replace-match "\n\n"))))))) 2571 | 2572 | (defun xah-clean-whitespace (&optional Begin End) 2573 | "Delete trailing whitespace, and replace repeated blank lines to just 1. 2574 | Only space and tab is considered whitespace here. 2575 | Works on whole buffer or selection, respects `narrow-to-region'. 2576 | 2577 | URL `http://xahlee.info/emacs/emacs/elisp_compact_empty_lines.html' 2578 | Created: 2017-09-22 2579 | Version: 2025-05-04" 2580 | (interactive) 2581 | (let (xbeg xend) 2582 | (seq-setq (xbeg xend) 2583 | (if (and Begin End) 2584 | (list Begin End) 2585 | (if (region-active-p) 2586 | (list (region-beginning) (region-end)) 2587 | (list (point-min) (point-max))))) 2588 | (save-excursion 2589 | (save-restriction 2590 | (narrow-to-region xbeg xend) 2591 | (progn 2592 | (goto-char (point-min)) 2593 | (while (re-search-forward "[ \t]+\n" nil t) (replace-match "\n"))) 2594 | (progn 2595 | (goto-char (point-min)) 2596 | (while (re-search-forward "\n\n\n+" nil t) (replace-match "\n\n"))) 2597 | (progn 2598 | (goto-char (point-max)) 2599 | (while (eq (char-before) 32) (delete-char -1)))))) 2600 | (message "done xah-clean-whitespace")) 2601 | 2602 | (defun xah-make-backup () 2603 | "Make a backup copy of current file or dired marked files. 2604 | If in dired, backup current file or marked files. 2605 | 2606 | The backup file name is the original name with the datetime .yyyymmddhhmmss~ appended. 2607 | 2608 | Overwrite existing file. 2609 | 2610 | If the current buffer is not associated with a file, do nothing. 2611 | 2612 | URL `http://xahlee.info/emacs/emacs/elisp_make-backup.html' 2613 | Created: 2018-06-06 2614 | Version: 2024-10-21" 2615 | (interactive) 2616 | (let ((xfname buffer-file-name) 2617 | (xtimestamp (format-time-string "%Y%m%d%H%M%S"))) 2618 | (if xfname 2619 | (let ((xbackupName 2620 | (concat xfname "." xtimestamp "~"))) 2621 | (copy-file xfname xbackupName t) 2622 | (message (concat "\nBackup saved at: " xbackupName))) 2623 | (if (eq major-mode 'dired-mode) 2624 | (progn 2625 | (mapc (lambda (xx) 2626 | (let ((xbackupName 2627 | (concat xx "." xtimestamp "~"))) 2628 | (copy-file xx xbackupName t))) 2629 | (dired-get-marked-files)) 2630 | (revert-buffer)) 2631 | (user-error "%s: buffer not file nor dired" real-this-command))))) 2632 | 2633 | (defun xah-make-backup-and-save () 2634 | "Backup of current file and save, or backup dired marked files. 2635 | For detail, see `xah-make-backup'. 2636 | If the current buffer is not associated with a file nor dired, nothing's done. 2637 | 2638 | URL `http://xahlee.info/emacs/emacs/elisp_make-backup.html' 2639 | Version: 2015-10-14" 2640 | (interactive) 2641 | (if buffer-file-name 2642 | (progn 2643 | (xah-make-backup) 2644 | (when (buffer-modified-p) 2645 | (save-buffer))) 2646 | (progn 2647 | (xah-make-backup)))) 2648 | 2649 | (defun xah-delete-current-file-make-backup () 2650 | "Makes a backup~, delete current file, close the buffer. 2651 | 2652 | Backup filename is ‹name›.‹dateTimeStamp›~ 2653 | Overwrite existing file. 2654 | 2655 | If buffer is not a file, copy content to `kill-ring', delete buffer. 2656 | 2657 | If buffer is not a file, the backup file name starts with “xx_”. 2658 | 2659 | Call `xah-open-last-closed' to open the backup file. 2660 | 2661 | URL `http://xahlee.info/emacs/emacs/elisp_delete-current-file.html' 2662 | Created: 2018-05-15 2663 | Version: 2024-10-21" 2664 | (interactive) 2665 | (when (eq major-mode 'dired-mode) 2666 | (user-error "%s: In dired. Nothing is done." real-this-command)) 2667 | (let ((xfname buffer-file-name) 2668 | (xbuffname (buffer-name))) 2669 | (if xfname 2670 | (let ((xbackupPath 2671 | (concat 2672 | xfname "." 2673 | (format-time-string "%Y%m%d%H%M%S") "~"))) 2674 | (save-buffer xfname) 2675 | (kill-buffer xbuffname) 2676 | (rename-file xfname xbackupPath t) 2677 | (message "File deleted. 2678 | Backup at 2679 | %s 2680 | Call `xah-open-last-closed' to open." xbackupPath) 2681 | (when (boundp 'xah-recently-closed-buffers) 2682 | (push (cons nil xbackupPath) xah-recently-closed-buffers))) 2683 | (progn 2684 | (widen) 2685 | (kill-new (buffer-string)) 2686 | (kill-buffer xbuffname) 2687 | (message "non-file buffer killed. buffer text copied to `kill-ring'.")))) 2688 | (when (eq major-mode 'dired-mode) (revert-buffer))) 2689 | 2690 | ;; HHHH------------------------------ 2691 | 2692 | (defun xah-search-current-word () 2693 | "Call `isearch' on current word or selection. 2694 | “word” here is A to Z, a to z, and hyphen [-] and lowline [_], independent of syntax table. 2695 | 2696 | URL `http://xahlee.info/emacs/emacs/modernization_isearch.html' 2697 | Created: 2010-05-29 2698 | Version: 2025-02-21" 2699 | (interactive) 2700 | (let (xbeg xend) 2701 | (if (region-active-p) 2702 | (setq xbeg (region-beginning) xend (region-end)) 2703 | (save-excursion 2704 | (skip-chars-backward "-_A-Za-z0-9") 2705 | (setq xbeg (point)) 2706 | (right-char) 2707 | (skip-chars-forward "-_A-Za-z0-9") 2708 | (setq xend (point)))) 2709 | (deactivate-mark) 2710 | (when (< xbeg (point)) (goto-char xbeg)) 2711 | (isearch-mode t) 2712 | (isearch-yank-string (buffer-substring-no-properties xbeg xend)))) 2713 | 2714 | (declare-function w32-shell-execute "w32fns.c" (operation document &optional parameters show-flag)) ; (w32-shell-execute "open" default-directory) 2715 | 2716 | (defun xah-show-in-desktop () 2717 | "Show current file in desktop. 2718 | (Mac Finder, Microsoft Windows File Explorer, Linux file manager) 2719 | This command can be called when in a file buffer or in `dired'. 2720 | 2721 | URL `http://xahlee.info/emacs/emacs/emacs_show_in_desktop.html' 2722 | Created: 2020-11-20 2723 | Version: 2023-09-09" 2724 | (interactive) 2725 | (let ((xpath (if (eq major-mode 'dired-mode) 2726 | (if (eq nil (dired-get-marked-files)) 2727 | default-directory 2728 | (car (dired-get-marked-files))) 2729 | (if buffer-file-name buffer-file-name default-directory)))) 2730 | (cond 2731 | ((eq system-type 'windows-nt) 2732 | (shell-command (format "PowerShell -Command invoke-item '%s'" default-directory )) 2733 | ;; (let ((xcmd (format "Explorer /select,%s" 2734 | ;; (replace-regexp-in-string "/" "\\" xpath t t) 2735 | ;; ;; (shell-quote-argument (replace-regexp-in-string "/" "\\" xpath t t )) 2736 | ;; ))) 2737 | ;; (shell-command xcmd)) 2738 | ) 2739 | ((eq system-type 'darwin) 2740 | (shell-command 2741 | (concat "open -R " (shell-quote-argument xpath)))) 2742 | ((eq system-type 'gnu/linux) 2743 | (call-process shell-file-name nil 0 nil 2744 | shell-command-switch 2745 | (format "%s %s" 2746 | "xdg-open" 2747 | (file-name-directory xpath))) 2748 | ;; (shell-command "xdg-open .") ;; 2013-02-10 this sometimes froze emacs till the folder is closed. eg with nautilus 2749 | )))) 2750 | 2751 | (defun xah-open-in-vscode () 2752 | "Open current file or dir in vscode. 2753 | URL `http://xahlee.info/emacs/emacs/emacs_open_in_vscode.html' 2754 | 2755 | Created: 2020-02-13 2756 | Version: 2024-12-15" 2757 | (interactive) 2758 | (let ((xpath (or buffer-file-name default-directory))) 2759 | (message "path is %s" xpath) 2760 | (cond 2761 | ((eq system-type 'darwin) 2762 | (shell-command (format "open -a Visual\\ Studio\\ Code.app %s" (shell-quote-argument xpath)))) 2763 | ((eq system-type 'windows-nt) 2764 | (shell-command (format "code.cmd %s" (shell-quote-argument xpath)))) 2765 | ((eq system-type 'gnu/linux) 2766 | (shell-command (format "code %s" (shell-quote-argument xpath))))))) 2767 | 2768 | (defun xah-open-in-external-app (&optional Fname) 2769 | "Open the current file or dired marked files in external app. 2770 | When called in emacs lisp, if Fname is given, open that. 2771 | 2772 | URL `http://xahlee.info/emacs/emacs/emacs_dired_open_file_in_ext_apps.html' 2773 | Created: 2019-11-04 2774 | Version: 2025-04-18" 2775 | (interactive) 2776 | (let (xfileList xdoIt) 2777 | (setq xfileList 2778 | (if Fname 2779 | (list Fname) 2780 | (if (eq major-mode 'dired-mode) 2781 | (dired-get-marked-files) 2782 | (list buffer-file-name)))) 2783 | (setq xdoIt (if (<= (length xfileList) 10) t (y-or-n-p "Open more than 10 files? "))) 2784 | (when xdoIt 2785 | (cond 2786 | ((eq system-type 'windows-nt) 2787 | (let ((xoutBuf (get-buffer-create "*xah open in external app*")) 2788 | (xcmdlist (list "PowerShell" "-Command" "Invoke-Item" "-LiteralPath"))) 2789 | (mapc 2790 | (lambda (x) 2791 | (apply 'start-process (append (list "xah open in external app" xoutBuf) xcmdlist (list (format "'%s'" (string-replace "'" "`'" x))) nil))) 2792 | xfileList))) 2793 | ((eq system-type 'darwin) 2794 | (mapc (lambda (xfpath) (shell-command (concat "open " (shell-quote-argument xfpath)))) xfileList)) 2795 | ((eq system-type 'gnu/linux) 2796 | (mapc (lambda (xfpath) 2797 | (call-process shell-file-name nil 0 nil 2798 | shell-command-switch 2799 | (format "%s %s" 2800 | "xdg-open" 2801 | (shell-quote-argument xfpath)))) 2802 | xfileList)) 2803 | ((eq system-type 'berkeley-unix) 2804 | (mapc (lambda (xfpath) (let ((process-connection-type nil)) (start-process "" nil "xdg-open" xfpath))) xfileList)))))) 2805 | 2806 | (defvar xah-fly-mswin-terminal 2807 | "wt" 2808 | "A string. Value should be one of: wt (for Windows Terminal) or pwsh (for PowerShell Core (cross-platform)) or powershell (for Microsoft PowerShell).") 2809 | 2810 | (defun xah-open-in-terminal () 2811 | "Open the current dir in a new terminal window. 2812 | On Microsoft Windows, which terminal it starts depends on `xah-fly-mswin-terminal'. 2813 | 2814 | URL `http://xahlee.info/emacs/emacs/emacs_open_in_terminal.html' 2815 | Created: 2020-11-21 2816 | Version: 2023-06-26" 2817 | (interactive) 2818 | (cond 2819 | ((eq system-type 'windows-nt) 2820 | (cond 2821 | ((string-equal xah-fly-mswin-terminal "wt") 2822 | (shell-command (format "wt -d \"%s\"" default-directory))) 2823 | ((string-equal xah-fly-mswin-terminal "pwsh") 2824 | (shell-command 2825 | (format "pwsh -Command Start-Process pwsh -WorkingDirectory '%s'" (shell-quote-argument default-directory)))) 2826 | ((string-equal xah-fly-mswin-terminal "powershell") 2827 | (shell-command 2828 | (format "powershell -Command Start-Process powershell -WorkingDirectory '%s'" (shell-quote-argument default-directory)))) 2829 | (t (error "Error 702919: value of `xah-fly-mswin-terminal' is not expected. Its value is %s" xah-fly-mswin-terminal)))) 2830 | ((eq system-type 'darwin) 2831 | (shell-command (concat "open -a terminal " (shell-quote-argument default-directory)))) 2832 | ((eq system-type 'gnu/linux) 2833 | (let ((process-connection-type nil)) (start-process "" nil "x-terminal-emulator" (concat "--working-directory=" default-directory)))) 2834 | ((eq system-type 'berkeley-unix) 2835 | (let ((process-connection-type nil)) (start-process "" nil "x-terminal-emulator" (concat "--working-directory=" default-directory)))))) 2836 | 2837 | (defun xah-next-window-or-frame () 2838 | "Switch to next window or frame. 2839 | If current frame has only one window, switch to next frame. 2840 | If `universal-argument' is called first, do switch frame. 2841 | Version: 2017-01-27" 2842 | (interactive) 2843 | (if current-prefix-arg 2844 | (other-frame 1) 2845 | (if (one-window-p) 2846 | (other-frame 1) 2847 | (other-window 1)))) 2848 | 2849 | (defun xah-unsplit-window-or-next-frame () 2850 | "Unsplit window. If current frame has only one window, switch to next frame. 2851 | Version: 2017-01-29" 2852 | (interactive) 2853 | (if (one-window-p) 2854 | (other-frame 1) 2855 | (delete-other-windows))) 2856 | 2857 | ;; HHHH------------------------------ 2858 | ;; layout lookup tables for key conversion 2859 | 2860 | (defvar xah-fly-layout-diagrams (make-hash-table :test 'equal) 2861 | "A hashtable. 2862 | Key is string, of keyboard layout name. 2863 | Value is a string, of text-art (aka ASCII-art) form of the layout. 2864 | The text-art string, each key (sequence of chars) is separated by whitespace. 2865 | The string is split by white space, and each item is considered a key. 2866 | The key is usually a single char, can be unicode, but may also be alt ctrl tab shift return and other. 2867 | It is used to generate key conversion table of a key from layout to layout. 2868 | ") 2869 | 2870 | (progn 2871 | 2872 | (puthash "adnw" " 2873 | ~ ! @ # $ % ^ & * ( ) { } 2874 | ` 1 2 3 4 5 6 7 8 9 0 [ ] 2875 | 2876 | k u ü . ä v g c l j f = \\ 2877 | h i e a o d t r n s ß 2878 | x y ö , q b p w m z 2879 | 2880 | K U Ü > Ä V G C L J F + | 2881 | H I E A O D T R N S ẞ 2882 | X Y Ö < Q B P W M Z 2883 | " xah-fly-layout-diagrams) 2884 | 2885 | (puthash "azerty" 2886 | " 2887 | ~ ! @ # $ % ^ & * ( ) { } 2888 | ² & é \" ' ( - è _ ç à ) = 2889 | 2890 | a z e r t y u i o p ^ $ * 2891 | q s d f g h j k l m ù 2892 | w x c v b n , ; : ! 2893 | 2894 | A Z E R T Y U I O P ? + | 2895 | Q S D F G H J K L M Ù 2896 | W X C V B N ? . / § 2897 | " xah-fly-layout-diagrams) 2898 | 2899 | (puthash "azerty-be" 2900 | " 2901 | ~ ! @ # $ % ^ & * ( ) { } 2902 | ² & é \" ' ( § è ! ç à ) - 2903 | 2904 | a z e r t y u i o p ^ $ µ 2905 | q s d f g h j k l m ù 2906 | w x c v b n , ; : = 2907 | 2908 | A Z E R T Y U I O P ^ $ Μ 2909 | Q S D F G H J K L M Ù 2910 | W X C V B N ? . / + 2911 | " xah-fly-layout-diagrams) 2912 | 2913 | (puthash "bepo" " 2914 | # 1 2 3 4 5 6 7 8 9 0 ° ` 2915 | $ \" « » ( ) @ + - / * = % 2916 | 2917 | b é p o è ^ v d l j z w \\ 2918 | a u i e , c t s r n m 2919 | à y x . k ' q g h f 2920 | 2921 | B É P O È ! V D L J Z W | 2922 | A U I E ; C T S R N M 2923 | À Y X : K ? Q G H F 2924 | " xah-fly-layout-diagrams) 2925 | 2926 | (puthash "colemak" " 2927 | ~ ! @ # $ % ^ & * ( ) _ + 2928 | ` 1 2 3 4 5 6 7 8 9 0 - = 2929 | 2930 | q w f p g j l u y ; [ ] \\ 2931 | a r s t d h n e i o ' 2932 | z x c v b k m , . / 2933 | 2934 | Q W F P G J L U Y : { } | 2935 | A R S T D H N E I O \" 2936 | Z X C V B K M < > ? 2937 | " xah-fly-layout-diagrams) 2938 | 2939 | (puthash "colemak-dh" " 2940 | ~ ! @ # $ % ^ & * ( ) _ + 2941 | ` 1 2 3 4 5 6 7 8 9 0 - = 2942 | 2943 | q w f p b j l u y ; [ ] \\ 2944 | a r s t g m n e i o ' 2945 | z x c d v k h , . / 2946 | 2947 | Q W F P B J L U Y : { } | 2948 | A R S T G M N E I O \" 2949 | Z X C D V K H < > ? 2950 | " xah-fly-layout-diagrams) 2951 | 2952 | (puthash "dvorak" " 2953 | ~ ! @ # $ % ^ & * ( ) { } 2954 | ` 1 2 3 4 5 6 7 8 9 0 [ ] 2955 | 2956 | ' , . p y f g c r l / = \\ 2957 | a o e u i d h t n s - 2958 | ; q j k x b m w v z 2959 | 2960 | \" < > P Y F G C R L ? + | 2961 | A O E U I D H T N S _ 2962 | : Q J K X B M W V Z 2963 | " xah-fly-layout-diagrams) 2964 | 2965 | (puthash "engrammer" " 2966 | ~ ! @ # $ % ^ & * ( ) { } 2967 | ` 1 2 3 4 5 6 7 8 9 0 [ ] 2968 | 2969 | b y o u ' ; l d w v z = \\ 2970 | c i e a , . h t s n q 2971 | g x j k - / r m f p 2972 | 2973 | B Y O U \" : L D W V Z + | 2974 | C I E A < > H T S N Q 2975 | G X J K _ ? R M F P 2976 | " xah-fly-layout-diagrams) 2977 | 2978 | (puthash "koy" " 2979 | ^ ! @ # $ % ^ & * ( ) _ ~ 2980 | ˘ 1 2 3 4 5 6 7 8 9 0 - ` 2981 | 2982 | k . o , y v g c l ß / = \\ 2983 | h a e i u d t r n s f 2984 | x q ä ü ö b p w m j 2985 | 2986 | K > O < Y V G C L ẞ ? + | 2987 | H A E I U D T R N S F 2988 | X Q Ä Ü Ö B P W M J 2989 | " xah-fly-layout-diagrams) 2990 | 2991 | (puthash "halmak" " 2992 | ~ ! @ # $ % ^ & * ( ) _ + 2993 | ` 1 2 3 4 5 6 7 8 9 0 - = 2994 | 2995 | w l r b z ; q u d j [ ] \\ 2996 | s h n t , . a e o i ' 2997 | f m v c / g p x k y 2998 | 2999 | W L R B Z : Q U D J { } | 3000 | S H N T < > A E O I \" 3001 | F M V C ? G P X K Y 3002 | " xah-fly-layout-diagrams) 3003 | 3004 | (puthash "minimak" " 3005 | ~ ! @ # $ % ^ & * ( ) _ + 3006 | ` 1 2 3 4 5 6 7 8 9 0 - = 3007 | 3008 | q w d r k y u i o p [ ] \\ 3009 | a s t f g h j e l ; ' 3010 | z x c v b n m , . / 3011 | 3012 | Q W D R K Y U I O P { } | 3013 | A S T F G H J E L : \" 3014 | Z X C V B N M < > ? 3015 | " xah-fly-layout-diagrams) 3016 | 3017 | ;; todo. need fix 3018 | (puthash "neo2" " 3019 | ^ ! @ # $ % ^ & * ( ) { } 3020 | ` 1 2 3 4 5 6 7 8 9 0 - ] 3021 | 3022 | x v l c w k h g f q ß = \\ 3023 | u i a e o s n r t d y 3024 | ü ö ä p z b m , . j 3025 | 3026 | X V L C W K H G F Q ? + | 3027 | U I A E O S N R T D Y 3028 | Ü Ö Ä P Z B M , . J 3029 | " xah-fly-layout-diagrams) 3030 | 3031 | (puthash "norman" " 3032 | ~ ! @ # $ % ^ & * ( ) _ + 3033 | ` 1 2 3 4 5 6 7 8 9 0 - = 3034 | 3035 | q w d f k j u r l ; [ ] \\ 3036 | a s e t g y n i o h ' 3037 | z x c v b p m , . / 3038 | 3039 | Q W D F K J U R L : { } | 3040 | A S E T G Y N I O H \" 3041 | Z X C V B P M < > ? 3042 | " xah-fly-layout-diagrams) 3043 | 3044 | (puthash "programer-dvorak" " 3045 | ~ % 7 5 3 1 9 0 2 4 6 8 ` 3046 | $ & [ { } ( = * ) + ] ! # 3047 | 3048 | ; , . p y f g c r l / @ \\ 3049 | a o e u i d h t n s - 3050 | ' q j k x b m w v z 3051 | 3052 | : < > P Y F G C R L ? ^ | 3053 | A O E U I D H T N S _ 3054 | \" Q J K X B M W V Z 3055 | " xah-fly-layout-diagrams) 3056 | 3057 | (puthash "pt-nativo" " 3058 | * ! \" # $ % & / ( ) = ª > 3059 | + 1 2 3 4 5 6 7 8 9 0 º < 3060 | 3061 | ' , . h x w l t c p ~ - \\ 3062 | i e a o u m d s r n ´ 3063 | « ç j b k q v g f z 3064 | 3065 | ? ; : H X W L T C P ^ _ | 3066 | I E A O U M D S R N ` 3067 | Y Ç J B K Q V G F Z 3068 | " xah-fly-layout-diagrams) 3069 | 3070 | (puthash "qfmlwy" " 3071 | ~ ! @ # $ % ^ & * ( ) _ + 3072 | ` 1 2 3 4 5 6 7 8 9 0 - = 3073 | 3074 | q f m l w y u o b j [ ] \\ 3075 | d s t n r i a e h ; ' 3076 | z v g c x p k , . / 3077 | 3078 | Q F M L W Y U O B J { } | 3079 | D S T N R I A E H : \" 3080 | Z V G C X P K < > ? 3081 | " xah-fly-layout-diagrams) 3082 | 3083 | (puthash "qgmlwb" " 3084 | ~ ! @ # $ % ^ & * ( ) _ + 3085 | ` 1 2 3 4 5 6 7 8 9 0 - = 3086 | 3087 | q g m l w b y u v ; [ ] \\ 3088 | d s t n r i a e o h ' 3089 | z x c f j k p , . / 3090 | 3091 | Q G M L W B Y U V : { } | 3092 | D S T N R I A E O H \" 3093 | Z X C F J K P < > ? 3094 | " xah-fly-layout-diagrams) 3095 | 3096 | (puthash "qwerty" " 3097 | ~ ! @ # $ % ^ & * ( ) _ + 3098 | ` 1 2 3 4 5 6 7 8 9 0 - = 3099 | 3100 | q w e r t y u i o p [ ] \\ 3101 | a s d f g h j k l ; ' 3102 | z x c v b n m , . / 3103 | 3104 | Q W E R T Y U I O P { } | 3105 | A S D F G H J K L : \" 3106 | Z X C V B N M < > ? 3107 | " xah-fly-layout-diagrams) 3108 | 3109 | (puthash "qwerty-abnt" " 3110 | \" ! @ # $ % ^ & * ( ) _ + 3111 | ' 1 2 3 4 5 6 7 8 9 0 - = 3112 | 3113 | q w e r t y u i o p ´ [ ] 3114 | a s d f g h j k l ç ~ 3115 | z x c v b n m , . ; 3116 | 3117 | Q W E R T Y U I O P ` + | 3118 | A S D F G H J K L Ç ^ 3119 | Z X C V B N M < > : 3120 | " xah-fly-layout-diagrams) 3121 | 3122 | (puthash "qwerty-no" " 3123 | § ! \" # ¤ % & / ( ) = ? ` 3124 | | 1 2 3 4 5 6 7 8 9 0 + \\ 3125 | 3126 | q w e r t y u i o p å ¨ ' 3127 | a s d f g h j k l ø æ 3128 | z x c v b n m , . - 3129 | 3130 | Q W E R T Y U I O P Å ^ * 3131 | A S D F G H J K L Ø Æ 3132 | Z X C V B N M < > _ 3133 | " xah-fly-layout-diagrams) 3134 | 3135 | (puthash "qwerty-se" " 3136 | § ! \" # ¤ % & / ( ) = ? ` 3137 | | 1 2 3 4 5 6 7 8 9 0 + \\ 3138 | 3139 | q w e r t y u i o p å ¨ ' 3140 | a s d f g h j k l ö ä 3141 | z x c v b n m , . - 3142 | 3143 | Q W E R T Y U I O P Å ^ * 3144 | A S D F G H J K L Ö Ä 3145 | Z X C V B N M < > _ 3146 | " xah-fly-layout-diagrams) 3147 | 3148 | (puthash "qwertz" " 3149 | ~ ! @ # $ % ^ & * ( ) _ + 3150 | ` 1 2 3 4 5 6 7 8 9 0 - = 3151 | 3152 | q w e r t z u i o p [ ] \\ 3153 | a s d f g h j k l ; ' 3154 | y x c v b n m , . / 3155 | 3156 | Q W E R T Z U I O P { } | 3157 | A S D F G H J K L : \" 3158 | Y X C V B N M < > ? 3159 | " xah-fly-layout-diagrams) 3160 | 3161 | (puthash "qwpr" " 3162 | ~ ! @ # $ % ^ & * ( ) _ + 3163 | ` 1 2 3 4 5 6 7 8 9 0 - = 3164 | 3165 | q w p r f y u k l ; [ ] \\ 3166 | a s d t g h n i o e ' 3167 | z x c v b j m , . / 3168 | 3169 | Q W P R F Y U K L : { } | 3170 | A S D T G H N I O E \" 3171 | Z X C V B J M < > ? 3172 | " xah-fly-layout-diagrams) 3173 | 3174 | (puthash "russian" " 3175 | Ё ! \" № ; % : ? * ( ) _ + 3176 | ё 1 2 3 4 5 6 7 8 9 0 - = 3177 | 3178 | й ц у к е н г ш щ з х ъ \\ 3179 | ф ы в а п р о л д ж э 3180 | я ч с м и т ь б ю . 3181 | 3182 | Й Ц У К Е Н Г Ш Щ З Х Ъ | 3183 | Ф Ы В А П Р О Л Д Ж Э 3184 | Я Ч С М И Т Ь Б Ю , 3185 | " xah-fly-layout-diagrams) 3186 | 3187 | (puthash "workman" " 3188 | ~ ! @ # $ % ^ & * ( ) _ + 3189 | ` 1 2 3 4 5 6 7 8 9 0 - = 3190 | 3191 | q d r w b j f u p ; [ ] \\ 3192 | a s h t g y n e o i ' 3193 | z x m c v k l , . / 3194 | 3195 | Q D R W B J F U P : { } | 3196 | A S H T G Y N E O I \" 3197 | Z X M C V K L < > ? 3198 | " xah-fly-layout-diagrams)) 3199 | 3200 | (defun xah-fly-create-key-conv-table (Layout1 Layout2) 3201 | "Takes two text diagrams Layout1 Layout2, return a hashtable. 3202 | For remapping key from layout1 to layout2. 3203 | 3204 | example: 3205 | 3206 | Layout1 is a string. e.g. 3207 | 3208 | a b c d 3209 | e f shift 3210 | 3211 | Layout2 is a string. e.g. 3212 | 3213 | a o e i 3214 | m n ctrl 3215 | 3216 | return a hashtable, e.g. 3217 | 3218 | b → o 3219 | c → e 3220 | d → i 3221 | e → m 3222 | f → n 3223 | shift → ctrl 3224 | 3225 | If the keys in layouts are the same, it's not in the table. 3226 | 3227 | Created: 2024-04-19 3228 | Version: 2024-04-23" 3229 | (let (xkeys1 xkeys2 (xtable (make-hash-table :test 'equal))) 3230 | (setq xkeys1 (split-string Layout1 "[ \n]+" t)) 3231 | (setq xkeys2 (split-string Layout2 "[ \n]+" t)) 3232 | (when (not (eq (length xkeys1) (length xkeys2))) 3233 | (error "layout %s and %s lengths not same." (length Layout1) (length Layout2))) 3234 | (seq-mapn 3235 | (lambda (x y) 3236 | (if (string-equal x y) 3237 | nil 3238 | (puthash x y xtable))) 3239 | xkeys1 xkeys2) 3240 | xtable 3241 | )) 3242 | 3243 | ;; (xah-fly-create-key-conv-table 3244 | ;; (gethash "qwerty" xah-fly-layout-diagrams) 3245 | ;; (gethash "dvorak" xah-fly-layout-diagrams)) 3246 | 3247 | (defvar xah-fly-key-current-layout nil 3248 | "The current keyboard layout. 3249 | Value is a key in `xah-fly-layout-diagrams'. 3250 | Do not set this variable manually. 3251 | Use `xah-fly-keys-set-layout' to set it. 3252 | Default to qwerty. 3253 | Version: 2022-10-22") 3254 | 3255 | (if xah-fly-key-current-layout nil (setq xah-fly-key-current-layout "qwerty")) 3256 | 3257 | (defvar xah-fly--key-convert-table nil 3258 | "A alist that's the conversion table from dvorak to current layout. 3259 | Value is a hashtable. 3260 | Created: 2019-02-12 3261 | Version: 2024-04-22" ) 3262 | 3263 | (setq 3264 | xah-fly--key-convert-table 3265 | (xah-fly-create-key-conv-table 3266 | (gethash "dvorak" xah-fly-layout-diagrams) 3267 | (gethash xah-fly-key-current-layout xah-fly-layout-diagrams))) 3268 | 3269 | (defun xah-fly--convert-key (Keystr) 3270 | "Return the corresponding Keystr according to variable `xah-fly--key-convert-table'. 3271 | Keystr must be a string that is the valid argument to `kbd'. 3272 | Keystr may be a single key, or key sequence separated by whitespaces. 3273 | Created: 2022-10-25 3274 | Version: 2024-04-22" 3275 | (interactive) 3276 | (mapconcat 3277 | 'identity 3278 | (mapcar 3279 | (lambda (x) 3280 | (let ((xnew (gethash x xah-fly--key-convert-table))) 3281 | (if xnew xnew x))) 3282 | (split-string Keystr " +")) 3283 | " ")) 3284 | 3285 | (defun xah-fly--define-keys (KeymapName KeyCmdAlist &optional Direct-p) 3286 | "Map `define-key' over a alist KeyCmdAlist, with key layout remap. 3287 | The key is remapped from Dvorak to the current keyboard layout by `xah-fly--convert-key'. 3288 | If Direct-p is t, do not remap key to current keyboard layout. 3289 | Example usage: 3290 | (xah-fly--define-keys 3291 | (define-prefix-command \\='xyz-map) 3292 | \\='( 3293 | (\"h\" . highlight-symbol-at-point) 3294 | (\".\" . isearch-forward-symbol-at-point) 3295 | (\"w\" . isearch-forward-word))) 3296 | Created: 2020-04-18 3297 | Version: 2023-08-21" 3298 | (mapcar 3299 | (lambda (x) 3300 | (define-key 3301 | KeymapName 3302 | (kbd (if Direct-p (car x) (xah-fly--convert-key (car x)))) 3303 | (cdr x))) 3304 | KeyCmdAlist)) 3305 | 3306 | ;; HHHH------------------------------ 3307 | ;; keymaps 3308 | 3309 | (defvar xah-fly-key-map (make-sparse-keymap) 3310 | "If `xah-fly-insert-state-p' is true, point to `xah-fly-insert-map', else, points to `xah-fly-command-map'.") 3311 | 3312 | (defvar xah-fly-command-map (make-sparse-keymap) 3313 | "Keymap when in command mode.") 3314 | 3315 | (defvar xah-fly-insert-map (make-sparse-keymap) 3316 | "Keymap when in insert mode.") 3317 | 3318 | (defvar xah-fly--deactivate-command-mode-func nil) 3319 | 3320 | ;; HHHH------------------------------ 3321 | ;; setting keys 3322 | 3323 | (defun xah-fly-define-keys () 3324 | "Define the keys for xah-fly-keys. 3325 | Created: 2022-10-31 3326 | Version: 2024-04-22" 3327 | (interactive) 3328 | (let () 3329 | 3330 | ;; Movement key integrations with built-in Emacs packages 3331 | (xah-fly--define-keys 3332 | indent-rigidly-map 3333 | '(("h" . indent-rigidly-left) 3334 | ("n" . indent-rigidly-right))) 3335 | 3336 | (xah-fly--define-keys 3337 | xah-fly-command-map 3338 | '(("<escape>" . xah-fly-command-mode-activate) 3339 | ("<home>" . xah-fly-command-mode-activate) 3340 | ("<f8>" . xah-fly-command-mode-activate)) 3341 | :direct) 3342 | 3343 | (xah-fly--define-keys 3344 | xah-fly-insert-map 3345 | '(("<escape>" . xah-fly-command-mode-activate) 3346 | ("<home>" . xah-fly-command-mode-activate) 3347 | ("<f8>" . xah-fly-command-mode-activate)) 3348 | :direct) 3349 | 3350 | (when xah-fly-use-isearch-arrows 3351 | (xah-fly--define-keys 3352 | isearch-mode-map 3353 | '(("<up>" . isearch-ring-retreat) 3354 | ("<down>" . isearch-ring-advance) 3355 | ("<left>" . isearch-repeat-backward) 3356 | ("<right>" . isearch-repeat-forward) 3357 | ("C-v" . isearch-yank-kill)) 3358 | :direct) 3359 | (xah-fly--define-keys 3360 | minibuffer-local-isearch-map 3361 | '(("<left>" . isearch-reverse-exit-minibuffer) 3362 | ("<right>" . isearch-forward-exit-minibuffer)) 3363 | :direct)) 3364 | 3365 | (xah-fly--define-keys 3366 | (define-prefix-command 'xah-fly-leader-key-map) 3367 | '(("SPC" . xah-fly-insert-mode-activate) 3368 | ("RET" . execute-extended-command) 3369 | 3370 | ("TAB" . nil) 3371 | 3372 | ("TAB TAB" . indent-for-tab-command) 3373 | 3374 | ("TAB i" . complete-symbol) 3375 | ("TAB g" . indent-rigidly) 3376 | ("TAB r" . indent-region) 3377 | ("TAB s" . indent-sexp) 3378 | 3379 | (". ." . highlight-symbol-at-point) 3380 | (". g" . unhighlight-regexp) 3381 | (". c" . highlight-lines-matching-regexp) 3382 | (". h" . highlight-regexp) 3383 | (". t" . highlight-phrase) 3384 | (". e" . isearch-forward-symbol-at-point) 3385 | (". u" . isearch-forward-symbol) 3386 | (". p" . isearch-forward-word) 3387 | 3388 | ("'" . xah-fill-or-unfill) 3389 | 3390 | (", t" . xref-find-definitions) 3391 | (", n" . xref-pop-marker-stack) 3392 | 3393 | ;; - / ; = [ 3394 | ("\\" . toggle-input-method) 3395 | ;; ` 3396 | 3397 | ("3" . delete-window) 3398 | ("4" . split-window-right) 3399 | ("5" . balance-windows) 3400 | ("6" . xah-upcase-sentence) 3401 | 3402 | ("9" . ispell-word) 3403 | 3404 | ("a" . mark-whole-buffer) 3405 | ("b" . end-of-buffer) 3406 | 3407 | ("c ," . xah-open-in-external-app) 3408 | ("c ." . find-file) 3409 | ("c c" . bookmark-bmenu-list) 3410 | ("c e" . ibuffer) 3411 | ("c f" . xah-open-recently-closed) 3412 | ("c g" . xah-open-in-terminal) 3413 | ("c h" . recentf-open-files) 3414 | ("c j" . xah-copy-file-path) 3415 | ("c l" . bookmark-set) 3416 | ("c n" . xah-new-empty-buffer) 3417 | ("c o" . xah-show-in-desktop) 3418 | ("c p" . xah-open-last-closed) 3419 | ("c r" . bookmark-jump) 3420 | ("c s" . write-file) 3421 | ("c u" . xah-open-file-at-cursor) 3422 | ("c x" . set-buffer-file-coding-system) 3423 | ("c y" . xah-list-recently-closed) 3424 | ("c z" . revert-buffer-with-coding-system) 3425 | 3426 | ;; set-buffer-process-coding-system 3427 | ;; set-file-name-coding-system 3428 | ;; set-keyboard-coding-system 3429 | ;; set-language-environment 3430 | ;; set-next-selection-coding-system 3431 | ;; set-selection-coding-system 3432 | ;; set-terminal-coding-system 3433 | ;; universal-coding-system-argument 3434 | 3435 | ("d" . beginning-of-buffer) 3436 | 3437 | ("e 6" . xah-insert-white-corner-bracket『』) 3438 | 3439 | ("e a" . xah-insert-double-angle-bracket《》) 3440 | ("e b" . xah-insert-black-lenticular-bracket【】) 3441 | 3442 | ("e c r" . expand-region-abbrevs) 3443 | ("e c t" . edit-abbrevs) 3444 | ("e c u" . expand-abbrev) 3445 | 3446 | ("e c g" . add-mode-abbrev) 3447 | ("e c c" . add-global-abbrev) 3448 | ("e c m" . inverse-add-mode-abbrev) 3449 | ("e c w" . inverse-add-global-abbrev) 3450 | 3451 | ("e c f" . unexpand-abbrev) 3452 | 3453 | ("e c h" . expand-jump-to-previous-slot) 3454 | ("e c n" . expand-jump-to-next-slot) 3455 | ("e c y" . abbrev-prefix-mark) 3456 | 3457 | ("e d" . xah-insert-double-curly-quote“”) 3458 | ("e e" . xah-insert-unicode) 3459 | ("e f" . xah-insert-emacs-quote) 3460 | ("e g" . xah-insert-ascii-double-quote) 3461 | 3462 | ("e h" . xah-insert-brace) 3463 | ("e i" . xah-insert-curly-single-quote‘’) 3464 | ("e j" . insert-char) 3465 | ("e k" . xah-insert-markdown-quote) 3466 | ("e l" . xah-insert-seperator) 3467 | ("e m" . xah-insert-corner-bracket「」) 3468 | ("e n" . xah-insert-square-bracket) 3469 | ("e o" . xah-insert-ascii-single-quote) 3470 | ("e p" . xah-insert-single-angle-quote‹›) 3471 | ("e q" . xah-insert-deco-angle-fat-bracket❰❱) 3472 | ("e r" . xah-insert-tortoise-shell-bracket〔〕) 3473 | ("e s" . xah-insert-ascii-angle-bracket) 3474 | ("e t" . xah-insert-paren) 3475 | ("e u" . xah-insert-date) 3476 | ("e v" . xah-insert-markdown-triple-quote) 3477 | ("e w" . xah-insert-angle-bracket〈〉) 3478 | ("e x" . xah-insert-white-lenticular-bracket〖〗) 3479 | ("e y" . xah-insert-double-angle-quote«») 3480 | ("e z" . xah-insert-deco-angle-bracket❮❯) 3481 | 3482 | ("f" . xah-search-current-word) 3483 | ("g" . xah-close-current-buffer) 3484 | 3485 | ("h a" . apropos-command) 3486 | ("h b" . describe-bindings) 3487 | ("h c" . describe-char) 3488 | ("h d" . apropos-documentation) 3489 | ("h e" . view-echo-area-messages) 3490 | ("h f" . describe-face) 3491 | ("h g" . info-lookup-symbol) 3492 | ;; ("h h" . describe-function) 3493 | ("h i" . info) 3494 | ("h j" . man) 3495 | ("h k" . describe-key) 3496 | ("h l" . view-lossage) 3497 | ("h m" . describe-mode) 3498 | ("h n" . describe-variable) 3499 | ("h o" . describe-language-environment) 3500 | ;; p q 3501 | ("h r" . apropos-variable) 3502 | ("h s" . describe-syntax) 3503 | ("h t" . describe-function) 3504 | ("h u" . elisp-index-search) 3505 | ("h v" . apropos-value) 3506 | ("h x" . describe-command) ; emacs 28 3507 | 3508 | ("h z" . describe-coding-system) 3509 | 3510 | ("i" . kill-line) 3511 | ("j" . xah-copy-all-or-region) 3512 | 3513 | ;; k 3514 | ("l" . recenter-top-bottom) 3515 | 3516 | ("m" . dired-jump) 3517 | 3518 | ;; ("m e" . delete-other-windows) 3519 | ;; ("m u" . split-window-below) 3520 | ;; ("m w" . universal-argument) 3521 | 3522 | ;; commands here are “harmless”, they don't modify text etc. they turn on modes, change display, prompt, start shell, etc. 3523 | ("n SPC" . whitespace-mode) 3524 | ("n ," . abbrev-mode) 3525 | ("n ." . toggle-frame-maximized) 3526 | ("n 1" . set-input-method) 3527 | ("n 2" . global-hl-line-mode) 3528 | ("n 4" . global-display-line-numbers-mode) 3529 | ("n 6" . calendar) 3530 | ("n 7" . calc) 3531 | ("n 9" . shell-command) 3532 | ("n 0" . shell-command-on-region) 3533 | 3534 | ("n <up>" . xah-page-up) 3535 | ("n <down>" . xah-page-down) 3536 | 3537 | ("n a" . text-scale-adjust) 3538 | ("n b" . toggle-debug-on-error) 3539 | ("n c" . toggle-case-fold-search) 3540 | ("n d" . display-line-numbers-mode) 3541 | ("n e" . eshell) 3542 | ;; ("n f" . nil) 3543 | ;; ("n g" . nil) 3544 | ("n h" . widen) 3545 | ("n i" . make-frame-command) 3546 | ("n j" . flyspell-buffer) 3547 | ("n k" . menu-bar-open) 3548 | ("n l" . toggle-word-wrap) 3549 | ("n m" . jump-to-register) 3550 | ("n n" . xah-narrow-to-region) 3551 | ("n o" . variable-pitch-mode) 3552 | ("n p" . read-only-mode) 3553 | ("n q" . enlarge-window) 3554 | ("n r" . count-words) 3555 | ("n s" . count-matches) 3556 | ("n t" . narrow-to-defun) 3557 | ("n u" . shell) 3558 | ("n v" . visual-line-mode) 3559 | ("n w" . eww) 3560 | ("n x" . save-some-buffers) 3561 | ("n y" . toggle-truncate-lines) 3562 | ("n z" . abort-recursive-edit) 3563 | 3564 | ("o" . exchange-point-and-mark) 3565 | ("p" . query-replace) 3566 | ("q" . xah-cut-all-or-region) 3567 | 3568 | ;; roughly text replacement related 3569 | 3570 | ("r ," . apply-macro-to-region-lines) 3571 | ("r ." . kmacro-start-macro) 3572 | ("r /" . xah-slash-to-double-backslash) 3573 | ("r 3" . number-to-register) 3574 | ("r 4" . increment-register) 3575 | ("r RET" . nil) 3576 | ("r SPC" . nil) 3577 | ("r \\" . xah-slash-to-backslash) 3578 | 3579 | ("r a" . nil) 3580 | ("r b" . nil) 3581 | ("r c" . string-rectangle) 3582 | ("r d" . delete-rectangle) 3583 | ("r e" . call-last-kbd-macro) 3584 | ("r f" . nil) 3585 | ("r g" . nil) 3586 | ("r h" . xah-change-bracket-pairs) 3587 | ("r i" . xah-space-to-newline) 3588 | ("r j" . copy-rectangle-to-register) 3589 | ("r k" . yank-rectangle) 3590 | ("r l" . clear-rectangle) 3591 | 3592 | ("r m" . nil) 3593 | ("r n" . rectangle-number-lines) 3594 | ("r o" . open-rectangle) 3595 | ("r p" . kmacro-end-macro) 3596 | ("r q" . kill-rectangle) 3597 | 3598 | ("r r" . rectangle-mark-mode) 3599 | ("r s" . nil) 3600 | ;; kmacro-end-and-call-macro 3601 | ("r t RET" . #'kmacro-edit-macro) 3602 | ("r t SPC" . #'kmacro-step-edit-macro) 3603 | ("r t TAB" . #'kmacro-insert-counter) 3604 | ("r t a" . #'kmacro-add-counter) 3605 | ("r t b" . #'kmacro-bind-to-key) 3606 | ("r t c" . #'kmacro-set-counter) 3607 | ("r t d" . #'kmacro-redisplay) 3608 | ("r t e" . #'edit-kbd-macro) 3609 | ("r t f" . #'kmacro-set-format) 3610 | ("r t g" . #'kmacro-delete-ring-head) 3611 | ("r t h" . #'kmacro-edit-macro-repeat) 3612 | ("r t i" . #'kmacro-call-ring-2nd-repeat) 3613 | ("r t j" . #'kmacro-cycle-ring-next) 3614 | ("r t k" . #'kmacro-end-or-call-macro-repeat) 3615 | ("r t l" . #'kmacro-edit-lossage) 3616 | 3617 | ("r t n" . #'kmacro-name-last-macro) 3618 | ("r t p" . #'kmacro-cycle-ring-previous) 3619 | ("r t q" . #'kbd-macro-query) 3620 | ("r t t" . #'kmacro-swap-ring) 3621 | ("r t v" . #'kmacro-view-macro-repeat) 3622 | ("r t x" . #'kmacro-to-register) 3623 | ("r u" . xah-quote-lines) 3624 | ("r v" . nil) 3625 | ("r w" . nil) 3626 | ("r x" . xah-double-backslash-to-slash) 3627 | ("r y" . delete-whitespace-rectangle) 3628 | ("r z" . nil) 3629 | 3630 | ("s" . save-buffer) 3631 | 3632 | ;; most frequently used 3633 | ("t <up>" . xah-move-block-up) 3634 | ("t <down>" . xah-move-block-down) 3635 | 3636 | ("t ," . sort-numeric-fields) 3637 | ("t ." . xah-sort-lines) 3638 | ("t 1" . xah-append-to-register-1) 3639 | ("t 2" . xah-clear-register-1) 3640 | ("t 3" . xah-copy-to-register-1) 3641 | ("t 4" . xah-paste-from-register-1) 3642 | ("t a" . nil) 3643 | ("t b" . xah-backward-punct) 3644 | ("t c" . copy-matching-lines) 3645 | ("t d" . mark-defun) 3646 | ("t e" . list-matching-lines) 3647 | ("t f" . move-to-column) 3648 | ("t g" . goto-line) 3649 | ("t h" . repeat-complex-command) 3650 | ("t i" . delete-non-matching-lines) 3651 | ("t j" . copy-to-register) 3652 | ("t k" . insert-register) 3653 | ("t l" . nil) 3654 | ("t m" . xah-make-backup-and-save) 3655 | ("t n" . goto-char) 3656 | ("t o" . xah-clean-whitespace) 3657 | ("t p" . query-replace-regexp) 3658 | ("t q" . xah-cut-text-in-quote) 3659 | ("t r" . xah-escape-quotes) 3660 | ("t s" . xah-reformat-to-sentence-lines) 3661 | ("t t" . repeat) 3662 | ("t u" . kill-matching-lines) 3663 | ("t v" . nil) 3664 | ("t w" . xah-next-window-or-frame) 3665 | ("t x" . xah-title-case-region-or-line) 3666 | ("t y" . delete-duplicate-lines) 3667 | ("t z" . nil) 3668 | ("u" . switch-to-buffer) 3669 | ("v" . universal-argument) 3670 | 3671 | ;; dangerous map. run program, delete file, etc 3672 | ("w DEL" . xah-delete-current-file-make-backup) 3673 | ("w ." . eval-buffer) 3674 | ("w e" . eval-defun) 3675 | ("w m" . eval-last-sexp) 3676 | ("w p" . eval-expression) 3677 | ("w u" . eval-region) 3678 | ("w q" . save-buffers-kill-terminal) 3679 | ("w w" . delete-frame) 3680 | ("w j" . xah-run-current-file) 3681 | 3682 | ("x" . xah-toggle-previous-letter-case) 3683 | ("y" . xah-show-kill-ring) 3684 | ;; vc command keys subject to change. need a frequency stat of the commands. 3685 | 3686 | ("z b" . vc-root-diff) ; D 3687 | ("z c" . vc-update) ; git pull, + 3688 | ("z d" . vc-annotate) ; g 3689 | ("z f" . vc-revert) ; u 3690 | ("z g" . vc-push) ; git push, P 3691 | ("z h" . vc-diff) ; git diff, = 3692 | ("z l" . vc-print-root-log) ; L 3693 | ("z m" . vc-dir) ; git status, C-x v d 3694 | ("z n" . vc-print-log) ; git log, l 3695 | ("z r" . vc-merge) ; m 3696 | ("z t" . vc-register) ; git add, i 3697 | ("z z" . vc-next-action) ; v 3698 | 3699 | ("z 1" . vc-create-tag) ; s 3700 | ("z 2" . vc-insert-headers) ; h 3701 | ("z 4" . vc-retrieve-tag) ; r 3702 | ("z 5" . vc-revision-other-window) ; ~ 3703 | ("z 6" . vc-switch-backend) ; b 3704 | ("z 7" . vc-update-change-log) ; a 3705 | 3706 | ;; 3707 | )) 3708 | 3709 | (xah-fly--define-keys 3710 | xah-fly-command-map 3711 | '(("SPC" . xah-fly-leader-key-map) 3712 | ("'" . xah-reformat-lines) 3713 | ("," . xah-shrink-whitespaces) 3714 | ("-" . delete-other-windows) 3715 | ("." . backward-kill-word) 3716 | ("/" . hippie-expand) 3717 | (";" . xah-comment-dwim) 3718 | ("[" . split-window-below) 3719 | ("\\" . xah-cycle-hyphen-lowline-space) 3720 | ("]" . split-window-right) 3721 | ("`" . other-frame) 3722 | 3723 | ;; ("1" . nil) 3724 | ;; ("2" . nil) 3725 | ("3" . delete-other-windows) 3726 | ("4" . split-window-below) 3727 | ("5" . delete-char) 3728 | ("6" . xah-select-block) 3729 | ("7" . xah-select-line) 3730 | ("8" . xah-extend-selection) 3731 | ("9" . xah-select-text-in-quote) 3732 | ("0" . xah-pop-local-mark-ring) 3733 | 3734 | ("a" . execute-extended-command) 3735 | ("b" . isearch-forward) 3736 | ("c" . previous-line) 3737 | ("d" . xah-beginning-of-line-or-block) 3738 | ("e" . xah-smart-delete) 3739 | ("f" . undo) 3740 | ("g" . backward-word) 3741 | ("h" . backward-char) 3742 | ("i" . xah-delete-current-text-block) 3743 | ("j" . xah-copy-line-or-region) 3744 | ("k" . xah-paste-or-paste-previous) 3745 | ("l" . xah-insert-space-before) 3746 | ("m" . xah-backward-left-bracket) 3747 | ("n" . forward-char) 3748 | ("o" . open-line) 3749 | ("p" . kill-word) 3750 | ("q" . xah-cut-line-or-region) 3751 | ("r" . forward-word) 3752 | ("s" . xah-end-of-line-or-block) 3753 | ("t" . next-line) 3754 | ("u" . xah-fly-insert-mode-activate) 3755 | ("v" . xah-forward-right-bracket) 3756 | ("w" . xah-next-window-or-frame) 3757 | ("x" . xah-toggle-letter-case) 3758 | ("y" . set-mark-command) 3759 | ("z" . xah-goto-matching-bracket))) 3760 | 3761 | ;; 3762 | )) 3763 | 3764 | (xah-fly-define-keys) 3765 | 3766 | ;; HHHH------------------------------ 3767 | ;; set control meta, etc keys 3768 | 3769 | (defcustom xah-fly-unset-useless-key t 3770 | "If true, unbind many obsolete or useless or redundant 3771 | keybinding. e.g. <help>, <f1>." 3772 | :type 'boolean) 3773 | 3774 | (when xah-fly-unset-useless-key 3775 | (global-set-key (kbd "<help>") nil) 3776 | (global-set-key (kbd "<f1>") nil)) 3777 | 3778 | (when xah-fly-use-meta-key 3779 | 3780 | (global-set-key (kbd "M-<home>") nil) ; beginning-of-buffer-other-window 3781 | (global-set-key (kbd "M-<end>") nil) ; end-of-buffer-other-window 3782 | 3783 | (global-set-key (kbd "M-SPC") #'xah-fly-command-mode-activate) 3784 | (global-set-key (kbd "M-\\") nil) ; delete-horizontal-space 3785 | (global-set-key (kbd "M-!") nil) ; shell-command 3786 | (global-set-key (kbd "M-$") nil) ; ispell-word 3787 | (global-set-key (kbd "M-%") nil) ; query-replace 3788 | (global-set-key (kbd "M-&") nil) ; async-shell-command 3789 | (global-set-key (kbd "M-'") nil) ; abbrev-prefix-mark 3790 | (global-set-key (kbd "M-(") nil) ; insert-parentheses 3791 | (global-set-key (kbd "M-)") nil) ; move-past-close-and-reindent 3792 | ;; (global-set-key (kbd "M-,") nil) ; xref-pop-marker-stack 3793 | ;; (global-set-key (kbd "M-.") nil) ; xref-find-definitions 3794 | (global-set-key (kbd "M-/") nil) ; dabbrev-expand 3795 | (global-set-key (kbd "M-:") nil) ; eval-expression 3796 | ;; (global-set-key (kbd "M-;") nil) ; comment-dwim 3797 | (global-set-key (kbd "M-<") nil) ; beginning-of-buffer 3798 | (global-set-key (kbd "M-=") nil) ; count-words-region 3799 | (global-set-key (kbd "M->") nil) ; end-of-buffer 3800 | ;; (global-set-key (kbd "M-?") nil) ; xref-find-references 3801 | (global-set-key (kbd "M-@") nil) ; mark-word 3802 | (global-set-key (kbd "M-^") nil) ; delete-indentation 3803 | (global-set-key (kbd "M-`") nil) ; tmm-menubar 3804 | (global-set-key (kbd "M-a") nil) ; backward-sentence 3805 | (global-set-key (kbd "M-b") nil) ; backward-word 3806 | (global-set-key (kbd "M-c") nil) ; capitalize-word 3807 | (global-set-key (kbd "M-d") nil) ; kill-word 3808 | (global-set-key (kbd "M-e") nil) ; forward-sentence 3809 | (global-set-key (kbd "M-f") nil) ; forward-word 3810 | (global-set-key (kbd "M-g") nil) ; Prefix Command 3811 | (global-set-key (kbd "M-h") nil) ; mark-paragraph 3812 | (global-set-key (kbd "M-i") nil) ; tab-to-tab-stop 3813 | (global-set-key (kbd "M-j") nil) ; default-indent-new-line 3814 | (global-set-key (kbd "M-k") nil) ; kill-sentence 3815 | (global-set-key (kbd "M-l") nil) ; downcase-word 3816 | (global-set-key (kbd "M-m") nil) ; back-to-indentation 3817 | (global-set-key (kbd "M-o") nil) ; facemenu-keymap 3818 | (global-set-key (kbd "M-q") nil) ; fill-paragraph 3819 | (global-set-key (kbd "M-r") nil) ; move-to-window-line-top-bottom 3820 | (global-set-key (kbd "M-s") nil) ; Prefix Command 3821 | (global-set-key (kbd "M-t") nil) ; transpose-words 3822 | (global-set-key (kbd "M-u") nil) ; upcase-word 3823 | (global-set-key (kbd "M-v") nil) ; scroll-down-command 3824 | (global-set-key (kbd "M-w") nil) ; kill-ring-save 3825 | ;; (global-set-key (kbd "M-x") nil) ; execute-extended-command 3826 | ;; (global-set-key (kbd "M-y") nil) ; yank-pop 3827 | (global-set-key (kbd "M-z") nil) ; zap-to-char 3828 | (global-set-key (kbd "M-{") nil) ; backward-paragraph 3829 | (global-set-key (kbd "M-|") nil) ; shell-command-on-region 3830 | (global-set-key (kbd "M-}") nil) ; forward-paragraph 3831 | (global-set-key (kbd "M-~") nil) ; not-modified 3832 | (global-set-key (kbd "M-DEL") nil) ; backward-kill-word 3833 | ) 3834 | 3835 | (when xah-fly-use-control-key 3836 | 3837 | (global-set-key (kbd "C-<tab>") #'xah-next-user-buffer) 3838 | (global-set-key (kbd "C-S-<tab>") #'xah-previous-user-buffer) 3839 | 3840 | (global-set-key (kbd "C-S-<prior>") #'xah-previous-emacs-buffer) 3841 | (global-set-key (kbd "C-S-<next>") #'xah-next-emacs-buffer) 3842 | 3843 | (global-set-key (kbd "C-<prior>") #'xah-previous-user-buffer) 3844 | (global-set-key (kbd "C-<next>") #'xah-next-user-buffer) 3845 | 3846 | ;; (global-set-key (kbd "C-1") nil) 3847 | (global-set-key (kbd "C-2") #'pop-global-mark) 3848 | (global-set-key (kbd "C-3") #'previous-error) 3849 | (global-set-key (kbd "C-4") #'next-error) 3850 | (global-set-key (kbd "C-5") #'xah-previous-emacs-buffer) 3851 | (global-set-key (kbd "C-6") #'xah-next-emacs-buffer) 3852 | (global-set-key (kbd "C-7") #'xah-previous-user-buffer) 3853 | (global-set-key (kbd "C-8") #'xah-next-user-buffer) 3854 | (global-set-key (kbd "C-9") #'xah-page-up) 3855 | (global-set-key (kbd "C-0") #'xah-page-down) 3856 | 3857 | (global-set-key (kbd "C--") #'text-scale-decrease) 3858 | (global-set-key (kbd "C-=") #'text-scale-increase) 3859 | 3860 | (global-set-key (kbd "C-SPC") #'xah-fly-command-mode-activate) 3861 | 3862 | (global-set-key (kbd "C-S-n") #'make-frame-command) 3863 | (global-set-key (kbd "C-S-s") #'write-file) 3864 | (global-set-key (kbd "C-S-t") #'xah-open-last-closed) 3865 | 3866 | ;; (global-set-key (kbd "C-@") nil) 3867 | 3868 | (global-set-key (kbd "C-a") #'mark-whole-buffer) 3869 | (global-set-key (kbd "C-b") nil) 3870 | ;; (global-set-key (kbd "C-c") nil) 3871 | (global-set-key (kbd "C-d") nil) 3872 | (global-set-key (kbd "C-e") nil) 3873 | (global-set-key (kbd "C-f") nil) 3874 | ;; (global-set-key (kbd "C-g") nil) ; cancel 3875 | ;; (global-set-key (kbd "C-h") nil) ; help 3876 | ;; (global-set-key (kbd "C-i") nil) ; tab 3877 | ;; (global-set-key (kbd "C-j") nil) ; newline 3878 | (global-set-key (kbd "C-k") nil) 3879 | (global-set-key (kbd "C-l") nil) 3880 | ;; (global-set-key (kbd "C-m") nil) ; newline 3881 | (global-set-key (kbd "C-n") #'xah-new-empty-buffer) 3882 | (global-set-key (kbd "C-o") #'find-file) 3883 | (global-set-key (kbd "C-p") nil) 3884 | ;; (global-set-key (kbd "C-q") nil) ; quoted-insert 3885 | ;; (global-set-key (kbd "C-r") nil) 3886 | (global-set-key (kbd "C-s") #'save-buffer) 3887 | (global-set-key (kbd "C-t") #'hippie-expand) 3888 | ;; (global-set-key (kbd "C-u") nil) ; universal-argument 3889 | (global-set-key (kbd "C-v") #'yank) 3890 | (global-set-key (kbd "C-w") #'xah-close-current-buffer) 3891 | (global-set-key (kbd "C-x") nil) ; c-x map 3892 | (global-set-key (kbd "C-y") #'undo-redo) ; emacs 28 3893 | (global-set-key (kbd "C-z") #'undo) 3894 | ;; 3895 | ) 3896 | 3897 | (global-set-key (kbd "<f7>") 'xah-fly-leader-key-map) 3898 | 3899 | ;; HHHH------------------------------ 3900 | 3901 | (when (< emacs-major-version 28) 3902 | (defalias 'execute-extended-command-for-buffer #'execute-extended-command)) 3903 | 3904 | ;; HHHH------------------------------ 3905 | ;;;; misc 3906 | 3907 | ;; the following have keys in gnu emacs, but i decided not to give them a key, because either they are rarely used (say, 95% of emacs users use them less than once a month ), or there is a more efficient command/workflow with key in xah-fly-keys 3908 | 3909 | ;; C-x $ → set-selective-display 3910 | ;; C-x * → calc-dispatch 3911 | ;; C-x - → shrink-window-if-larger-than-buffer 3912 | ;; C-x . → set-fill-prefix 3913 | ;; C-x 4 . → find-tag-other-window 3914 | ;; C-x 4 0 → kill-buffer-and-window 3915 | ;; C-x 4 C-o → display-buffer 3916 | ;; C-x 4 a → add-change-log-entry-other-window 3917 | ;; C-x 4 b → switch-to-buffer-other-window 3918 | ;; C-x 4 c → clone-indirect-buffer-other-window 3919 | ;; C-x 4 d → dired-other-window 3920 | ;; C-x 4 f → find-file-other-window 3921 | ;; C-x 4 m → compose-mail-other-window 3922 | ;; C-x 4 r → find-file-read-only-other-window 3923 | ;; C-x 5 . xref-find-definitions-other-frame 3924 | ;; C-x 5 1 delete-other-frames 3925 | ;; C-x 5 5 other-frame-prefix 3926 | ;; C-x 5 C-o display-buffer-other-frame 3927 | ;; C-x 5 b switch-to-buffer-other-frame 3928 | ;; C-x 5 c clone-frame 3929 | ;; C-x 5 d dired-other-frame 3930 | ;; C-x 5 f find-file-other-frame 3931 | ;; C-x 5 m compose-mail-other-frame 3932 | ;; C-x 5 p project-other-frame-command 3933 | ;; C-x 5 r find-file-read-only-other-frame 3934 | ;; C-x 5 u undelete-frame 3935 | ;; C-x 6 2 → 2C-two-columns 3936 | ;; C-x 6 b → 2C-associate-buffer 3937 | ;; C-x 6 s → 2C-split 3938 | ;; C-x ; → comment-set-column 3939 | ;; C-x < → scroll-left 3940 | ;; C-x = → what-cursor-position, use describe-char instead 3941 | ;; C-x > → scroll-right 3942 | ;; C-x C-d → list-directory 3943 | ;; C-x C-l → downcase-region 3944 | ;; C-x C-n → set-goal-column 3945 | ;; C-x C-o → delete-blank-lines 3946 | ;; C-x C-p → mark-page 3947 | ;; C-x C-r → find-file-read-only 3948 | ;; C-x C-t → transpose-lines 3949 | ;; C-x C-u → upcase-region 3950 | ;; C-x C-v → find-alternate-file 3951 | ;; C-x C-z → suspend-frame 3952 | ;; C-x DEL → backward-kill-sentence 3953 | ;; C-x ESC → Prefix Command 3954 | ;; C-x [ → backward-page 3955 | ;; C-x ] → forward-page 3956 | ;; C-x f → set-fill-column 3957 | ;; C-x i → insert-file 3958 | ;; C-x k → kill-buffer 3959 | ;; C-x l → count-lines-page 3960 | ;; C-x m → compose-mail 3961 | ;; C-x n g goto-line-relative 3962 | ;; C-x n p narrow-to-page 3963 | ;; C-x r M bookmark-set-no-overwrite 3964 | ;; C-x r M-w copy-rectangle-as-kill 3965 | ;; C-x r SPC point-to-register 3966 | ;; C-x r f frameset-to-register 3967 | ;; C-x r j jump-to-register 3968 | ;; C-x r w window-configuration-to-register 3969 | ;; C-x { → shrink-window-horizontally 3970 | ;; C-x } → enlarge-window-horizontally 3971 | 3972 | ;; HHHH------------------------------ 3973 | 3974 | (defvar xah-fly-insert-state-p t "non-nil means insertion mode is on.") 3975 | 3976 | (defun xah-fly--update-key-map () 3977 | (setq xah-fly-key-map (if xah-fly-insert-state-p 3978 | xah-fly-insert-map 3979 | xah-fly-command-map))) 3980 | 3981 | (defun xah-fly-keys-set-layout (Layout) 3982 | "Set a keyboard layout. 3983 | Argument must be one of the key name in `xah-fly-layout-diagrams' 3984 | Created: 2021-05-19 3985 | Version: 2024-05-23" 3986 | (interactive 3987 | (list 3988 | (completing-read 3989 | "Choose a layout: " 3990 | (seq-sort 'string< (hash-table-keys xah-fly-layout-diagrams)) 3991 | nil t))) 3992 | (let ((xold xah-fly-key-current-layout) 3993 | (xkeydiagram (gethash Layout xah-fly-layout-diagrams))) 3994 | (when (not xkeydiagram) 3995 | (user-error 3996 | "xah-fly-keys-set-layout error: layout must be one of string:\n%s" 3997 | (mapconcat 'identity (hash-table-keys xah-fly-layout-diagrams) "\n"))) 3998 | (setq xah-fly-key-current-layout Layout) 3999 | (setq 4000 | xah-fly--key-convert-table 4001 | (xah-fly-create-key-conv-table 4002 | (gethash "dvorak" xah-fly-layout-diagrams) 4003 | xkeydiagram)) 4004 | (when (not (equal xold Layout)) (xah-fly-define-keys)))) 4005 | 4006 | (defun xah-fly-space-key () 4007 | "Switch to command mode if the char before cursor is a space. 4008 | experimental 4009 | Version: 2018-05-07" 4010 | (interactive) 4011 | (if (eq (char-before ) 32) 4012 | (xah-fly-command-mode-activate) 4013 | (insert " "))) 4014 | 4015 | (defun xah-fly-command-mode-init () 4016 | "Set command mode keys. 4017 | Version: 2022-07-06" 4018 | (interactive) 4019 | (setq xah-fly-insert-state-p nil) 4020 | (xah-fly--update-key-map) 4021 | (when xah-fly--deactivate-command-mode-func 4022 | (funcall xah-fly--deactivate-command-mode-func)) 4023 | (setq xah-fly--deactivate-command-mode-func 4024 | (set-transient-map xah-fly-command-map (lambda () t))) 4025 | (modify-all-frames-parameters (list (cons 'cursor-type 'box))) 4026 | ;; (set-face-background 'cursor "red") 4027 | (setq mode-line-front-space xah-fly-command-mode-indicator) 4028 | (force-mode-line-update)) 4029 | 4030 | (defun xah-fly-insert-mode-init (&optional no-indication) 4031 | "Enter insertion mode." 4032 | (interactive) 4033 | (setq xah-fly-insert-state-p t) 4034 | (xah-fly--update-key-map) 4035 | (funcall xah-fly--deactivate-command-mode-func) 4036 | (unless no-indication 4037 | (modify-all-frames-parameters '((cursor-type . bar))) 4038 | ;; (set-face-background 'cursor "black") 4039 | (setq mode-line-front-space xah-fly-insert-mode-indicator)) 4040 | (force-mode-line-update)) 4041 | 4042 | (defun xah-fly-mode-toggle () 4043 | "Switch between {insertion, command} modes." 4044 | (interactive) 4045 | (if xah-fly-insert-state-p 4046 | (xah-fly-command-mode-activate) 4047 | (xah-fly-insert-mode-activate))) 4048 | 4049 | (defun xah-fly-save-buffer-if-file () 4050 | "Save current buffer if it is a file." 4051 | (interactive) 4052 | (when buffer-file-name 4053 | (save-buffer))) 4054 | 4055 | (defun xah-fly-command-mode-activate () 4056 | "Activate command mode and run `xah-fly-command-mode-activate-hook' 4057 | Version: 2017-07-07" 4058 | (interactive) 4059 | (xah-fly-command-mode-init) 4060 | (run-hooks 'xah-fly-command-mode-activate-hook)) 4061 | 4062 | (defun xah-fly-command-mode-activate-no-hook () 4063 | "Activate command mode. Does not run `xah-fly-command-mode-activate-hook' 4064 | Version: 2017-07-07" 4065 | (interactive) 4066 | (xah-fly-command-mode-init)) 4067 | 4068 | (defun xah-fly-insert-mode-activate () 4069 | "Activate insertion mode. 4070 | Version: 2017-07-07" 4071 | (interactive) 4072 | (xah-fly-insert-mode-init) 4073 | (run-hooks 'xah-fly-insert-mode-activate-hook)) 4074 | 4075 | (defun xah-fly-insert-mode-activate-newline () 4076 | "Activate insertion mode, insert newline below." 4077 | (interactive) 4078 | (xah-fly-insert-mode-activate) 4079 | (open-line 1)) 4080 | 4081 | ;; HHHH------------------------------ 4082 | 4083 | ;;;###autoload 4084 | (define-minor-mode xah-fly-keys 4085 | "A modal keybinding set, like vim, but based on ergonomic 4086 | principles, like Dvorak layout. 4087 | 4088 | URL `http://xahlee.info/emacs/misc/xah-fly-keys.html'" 4089 | :global t 4090 | :lighter "xahflykeys" 4091 | :keymap xah-fly-insert-map 4092 | (delete-selection-mode 1) 4093 | (setq shift-select-mode nil) 4094 | 4095 | (if xah-fly-keys 4096 | ;; Construction: 4097 | (progn 4098 | (add-hook 'minibuffer-setup-hook 'xah-fly-insert-mode-activate) 4099 | (add-hook 'minibuffer-exit-hook 'xah-fly-command-mode-activate) 4100 | (add-hook 'isearch-mode-end-hook 'xah-fly-command-mode-activate) 4101 | (xah-fly-command-mode-activate)) 4102 | (progn 4103 | ;; Teardown: 4104 | (remove-hook 'minibuffer-setup-hook 'xah-fly-insert-mode-activate) 4105 | (remove-hook 'minibuffer-exit-hook 'xah-fly-command-mode-activate) 4106 | (remove-hook 'isearch-mode-end-hook 'xah-fly-command-mode-activate) 4107 | (xah-fly-insert-mode-init :no-indication) 4108 | (setq mode-line-front-space '(:eval (if (display-graphic-p) " " "-"))) 4109 | 4110 | ;; 4111 | ))) 4112 | 4113 | (provide 'xah-fly-keys) 4114 | 4115 | ;;; xah-fly-keys.el ends here 4116 | -------------------------------------------------------------------------------- /xah_fly_keys_qwerty_layout_2024-06-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xahlee/xah-fly-keys/edde947a4f27658c4a0b958461a77512176b8904/xah_fly_keys_qwerty_layout_2024-06-16.png --------------------------------------------------------------------------------