diff --git a/.emacs.d/init.el b/.emacs.d/init.el index 758d137..fe9ec38 100644 --- a/.emacs.d/init.el +++ b/.emacs.d/init.el @@ -74,16 +74,10 @@ (nyan-mode)) ;; Requires the JetBrains Mono font be installed on the system. (add-to-list 'default-frame-alist '(font . "JetBrains Mono-12")) -;; Import external elisp programs -(add-to-list 'load-path "~/.emacs.d/lisp/") -;; Xah fly keys -(require 'xah-fly-keys) -(xah-fly-keys-set-layout "qwerty") -;; (xah-fly-keys) ;; I've been using Neovim a lot lately, so I'm using Evil mode for the ;; time being. I think XFK is technically better, but it messes with ;; my muscle memory a bit, and I need to be able to get stuff done, -;; frankly. I'm already wasting time configuring emacs (*  ̄︿ ̄) +;; frankly. I'm already wasting time configuring Emacs (*  ̄︿ ̄) (use-package evil :init ;; tweak evil's configuration before loading it (setq evil-want-keybinding nil) diff --git a/.emacs.d/lisp/keyfreq.el b/.emacs.d/lisp/keyfreq.el deleted file mode 100644 index 722c047..0000000 --- a/.emacs.d/lisp/keyfreq.el +++ /dev/null @@ -1,603 +0,0 @@ -;;; keyfreq.el --- track command frequencies -;; -*- coding: utf-8 -*- -;; -;; Copyright 2009-2010, 2015 by David Capello -;; Copyright 2008 by Xah Lee -;; Copyright 2006 by Michal Nazarewicz -;; Copyright 2006 by Ryan Yeske -;; -;; Author: Ryan Yeske, Michal Nazarewicz (mina86/AT/mina86.com) -;; Maintainer: David Capello, Xah lee -;; Created: 2006 -;; -;; Package-Requires: ((cl-lib "0.5")) -;; -;; -;; Keyfreq is free software; you can redistribute it and/or modify it -;; under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3 of the License, or -;; (at your option) any later version. -;; -;; Version 1.7 - 2015-09 - David Capello -;; * Avoid accumulating key frequency if keyfreq-file-release-lock fails -;; -;; Version 1.6 - 2015-09 - David Capello -;; * Added keyfreq-reset thanks to @w-vi -;; * Fixed issue running multiple instances of Emacs 24.5 -;; -;; Version 1.5 - 2014-11 - David Capello -;; * Support cl-lib or cl -;; * Minor doc fixes -;; -;; Version 1.4 - 2010-09 - David Capello -;; * Renamed from command-frequency to keyfreq -;; * Now keyfreq-table holds "deltas" -;; * Removed a lot of "facility" functions to keep the code simple. -;; * Rename keyfreq-summarize-all-modes -> keyfreq-groups-major-modes. -;; * Added keyfreq-filter-major-mode -;; * Added lock file to avoid overwrite the file by two processes at -;; the same time. -;; -;; Version 1.3 - 2009-09 - David Capello -;; * Added keyfreq-summarize-all-modes -;; -;; Version 1.2 - 2009-09 - David Capello -;; * Now each hash hash-key is a (major-mode . command) cons. Now only -;; symbols are recorded. -;; -;; Version 1.1 - 2008-09 -;; - Replaced the use of this-command var by real-last-command, so -;; that the commands backward-kill-word, kill-word, kill-line, -;; kill-region, do not all get counted as kill-region. Changed -;; post-command-hook to pre-command-hook -;; -;; Version 1.0 - 2007 -;; - Made into a full featured minor mode. Added full doc -;; strings. Added feature to save and read to disk the frequency -;; hash table. Added ability to set user preference using emacs's -;; customization system. Code is ~400 lines. This version is made by -;; Michal Nazarewicz in 2007. -;; -;; Version 0.1 - 2006 -;; - First version by Ryan Yeske. A quick hack of about 40 lines. -;; - -;;; Commentary: -;; -;; HOW TO USE IT? -;; -;; Include the following lines in your .emacs file: -;; -;; (require 'keyfreq) -;; (keyfreq-mode 1) -;; (keyfreq-autosave-mode 1) -;; -;; And use `keyfreq-show' to see how many times you used a command. -;; -;;; Code: - -(if (not (featurep 'cl-lib)) - (progn - (require 'cl) - ;; fix conflict name - (defalias 'cl-reduce 'reduce)) - (require 'cl-lib)) -;; (require 'json)? - -(defgroup keyfreq nil - "Customization group for Keyfreq mode. -This mode stores number of times each command was called and -provides it as a statistical data." - :package-version '(keyfreq . "1.7") - :group 'local - :prefix "keyfreq") - -;;;###autoload -(define-minor-mode keyfreq-mode - "Keyfreq mode records number of times each command was -called making it possible to access usage statistics through -various keyfreq-* functions." - :global t - :init-value nil - :lighter nil - :keymap nil - :group 'keyfreq - - (if keyfreq-mode - (add-hook 'pre-command-hook 'keyfreq-pre-command-hook) - (remove-hook 'pre-command-hook 'keyfreq-pre-command-hook))) - - -(defcustom keyfreq-buffer "*frequencies*" - "Buffer where frequencies are displayed." - :group 'keyfreq - :type 'string) - - -(defcustom keyfreq-file "~/.emacs.keyfreq" - "File `keyfreq-table' is saved to/loaded from by -`keyfreq-table-save' and `keyfreq-table-load' functions -by default." - :group 'keyfreq - :type 'file) - - -(defcustom keyfreq-file-lock "~/.emacs.keyfreq.lock" - "Lock file to update the `keyfreq-file'." - :group 'keyfreq - :type 'file) - -(defcustom keyfreq-excluded-regexp '() - "List of commands in REGEXP-FORM excluded by keyfreq." - :group 'keyfreq - :type 'file) - -(defvar keyfreq-table (make-hash-table :test 'equal :size 128) - "Hash table storing number of times each command was called in each major mode -since the last time the frequencies were saved in `keyfreq-file'.") - -(defvar keyfreq-excluded-commands '() - "List of commands excluded by keyfreq.") - -(defun keyfreq-match-p (cmd) - "Return t if CMD in `keyfreq-excluded-commands' or match `keyfreq-excluded-regexp'." - ;; HACK Inspire by `consult--regexp-filter' - (let ((filter (mapconcat (lambda (x) (concat "\\(?:" x "\\)")) - keyfreq-excluded-regexp "\\|"))) - (or (memq cmd keyfreq-excluded-commands) - (when keyfreq-excluded-regexp - (string-match-p filter (symbol-name cmd)))))) - - -(defun keyfreq-pre-command-hook () - "Record command execution in `keyfreq-table' hash." - (let ((command real-last-command) count) - (when (and command (symbolp command)) - (setq count (gethash (cons major-mode command) keyfreq-table)) - (unless (keyfreq-match-p command) - (puthash (cons major-mode command) (if count (1+ count) 1) - keyfreq-table) - )))) - -(defun keyfreq-groups-major-modes (table) - "Group major modes in TABLE by command. -Return a hash table where each entry has COMMAND as key and -COUNTER as value." - (let ((new-table (make-hash-table :test 'equal :size 128))) - (maphash (lambda (k v) - (puthash (cdr k) (+ (gethash (cdr k) new-table 0) v) new-table)) - table) - new-table)) - - -(defun keyfreq-filter-major-mode (table major-mode) - "Leave the frequencies of the specified major mode. -Return a hash table where each entry has COMMAND as key and -COUNTER as value." - - (let ((new-table (make-hash-table :test 'equal :size 128))) - (maphash (lambda (k v) - (when (eq (car k) major-mode) - (puthash (cdr k) (+ (gethash (cdr k) new-table 0) v) new-table))) - table) - new-table)) - - -(defun keyfreq-used-major-modes (table) - "Return a list with the used major modes (major modes -contained in the TABLE)." - (let ((list)) - (maphash (lambda (k v) - (add-to-list 'list (car k))) - table) - list)) - - -(defun keyfreq-list (table &optional reverse limit) - "Return a cons which car is sum of times any command was used -and cdr is a list of (command . count) pairs. - -If REVERSE is nil, sort it starting from the most used command; -if it is `no-sort' the list is not sorted; if it is non-nil and -not `no-sort', sort it from the least used commands. If LIMIT is -a positive number, only commands which were used more then LIMIT -times will be added. If it is a negative number, only commands -which were used less then -LIMIT times will be added." - - (let (l (sum 0)) - (maphash - (cond - ((or (not (numberp limit)) (= limit 0)) - (lambda (k v) (setq l (cons (cons k v) l) sum (+ sum v)))) - ((= limit -1) (lambda (k v) (setq sum (+ sum v)))) - ((< limit 0) - (setq limit (- limit)) - (lambda (k v) (setq sum (+ sum v)) - (if (< v limit) (setq l (cons (cons k v) l))))) - (t - (lambda (k v) (setq sum (+ sum v)) - (if (> v limit) (setq l (cons (cons k v) l)))))) - table) - (cons sum - (cond - ((equal reverse 'no-sort) l) - (reverse (sort l (lambda (a b) (< (cdr a) (cdr b))))) - (t (sort l (lambda (a b) (> (cdr a) (cdr b))))))))) - - -(defun keyfreq-format-list (list &optional func) - "Return formatted string with command usage statistics. - -The LIST is the `keyfreq-table' converted to a list using the `keyfreq-list'. - -If FUNC is nil each line contains number of times command was -called and the command; if it is t percentage usage is added in -the middle; if it is 'raw each line will contain number an -command separated by single line (with no formatting) otherwise -FUNC must be a function returning a string which will be called -for each entry with three arguments: number of times command was -called, percentage usage and the command." - (let* ((sum (car list)) - (max-len - (cl-reduce (lambda (a b) (max a (length (symbol-name (car b))))) - (cdr list) - :initial-value 0))) - (mapconcat - (cond - ((not func) (lambda (e) (format "%7d %s\n" (cdr e) (car e)))) - ((equal func t) - (lambda (e) (format (concat "%7d %6.2f%% %- " - (format "%d" max-len) - "s %s\n") - (cdr e) (/ (* 1e2 (cdr e)) sum) (car e) - (ignore-errors (keyfreq-where-is (car e)))))) - ((equal func 'raw) (lambda (e) (format "%d %s\n" (cdr e) (car e)))) - (t (lambda (e) (funcall func (cdr e) (/ (* 1e2 (cdr e)) sum) (car e))))) - (cdr list) ""))) - -(defun keyfreq-where-is (command) - (mapconcat 'key-description - (where-is-internal command) - ", ")) - -(defun keyfreq-show (&optional major-mode-symbol) - "Show command usage statistics in `keyfreq-buffer'. - -If MAJOR-MODE-SYMBOL is given, the function shows the statistics -for that particular major mode only. - -With a universal argument, the major-mode of the current buffer -is used as MAJOR-MODE-SYMBOL argument." - (interactive (list (cond (current-prefix-arg major-mode) - (t nil)))) - - (let ((table (copy-hash-table keyfreq-table))) - ;; Merge with the values in `keyfreq-file' - (keyfreq-table-load table) - - (let* ((list (keyfreq-list - (cond - (major-mode-symbol (keyfreq-filter-major-mode table major-mode-symbol)) - (t (keyfreq-groups-major-modes table))))) - (formatted-list (keyfreq-format-list list t))) - - ;; Display the table - (display-message-or-buffer (concat (if major-mode-symbol - (concat "For " (symbol-name major-mode)) - (concat "For all major modes")) - ":\n\n" - formatted-list) - keyfreq-buffer) - ))) - - -(defun keyfreq-html (filename &optional confirm) - "Save an HTML file as FILENAME with all the statistics of each mode." - - (interactive - (list (if buffer-file-name - (read-file-name "Write HTML file: " - nil nil nil nil) - (read-file-name "Write HTML file: " default-directory - (expand-file-name - (file-name-nondirectory (buffer-name)) - default-directory) - nil nil)) - (not current-prefix-arg))) - - (and confirm - (file-exists-p filename) - (or (y-or-n-p (format "File `%s' exists; overwrite? " filename)) - (error "Canceled"))) - - (let ((table (copy-hash-table keyfreq-table)) - (htmltable (lambda (list) - (insert "\n") - (insert "\n") - (insert "\n") - (keyfreq-format-list list - (lambda (count perc command) - (insert (format "\n" count perc command)))) - (insert "\n") - (insert "
TimesPercetageCommand
%d%.2f%%%s
\n")))) - - ;; Merge with the values in `keyfreq-file' - (keyfreq-table-load table) - - (with-temp-file filename - (insert "\n\n") - (insert "

Keyfreq Report

\n") - (insert "\n") - - (insert "

All major modes

\n") - (funcall htmltable (keyfreq-list (keyfreq-groups-major-modes table))) - - (mapc - (lambda (major-mode-symbol) - (insert (format "

%s

\n" - (symbol-name major-mode-symbol) - (symbol-name major-mode-symbol))) - (funcall htmltable (keyfreq-list (keyfreq-filter-major-mode table major-mode-symbol)))) - (keyfreq-used-major-modes table)) - - (insert "\n\n") - ) - )) - - -(defun keyfreq-json-encode (table) - "Return a JSON representation of the table of frequencies." - (require 'json) - (let ((commands-indexes (make-hash-table :test 'equal :size 128)) - commands-list frequencies-matrix i) - - ;; Build the "commands" property of the JSON object. - (setq i 0) - (maphash - (lambda (command-symbol counter) - (add-to-list 'commands-list command-symbol t) - (puthash command-symbol i commands-indexes) - (setq i (+ i 1)) - ) - (keyfreq-groups-major-modes table)) - - ;; Build the "frequencies" property. - (mapc - (lambda (major-mode-symbol) - (let ((cmd-count-pairs '())) - (keyfreq-format-list (keyfreq-list (keyfreq-filter-major-mode table major-mode-symbol)) - (lambda (count perc command) - (add-to-list 'cmd-count-pairs (gethash command commands-indexes) t) - (add-to-list 'cmd-count-pairs count t))) - (add-to-list 'frequencies-matrix major-mode-symbol t) - (add-to-list 'frequencies-matrix (append cmd-count-pairs nil) t))) - (keyfreq-used-major-modes table)) - - (json-encode `((:format . 1) - (:commands . ,commands-list) - (:frequencies . ,frequencies-matrix))))) - - -(defun keyfreq-json (filename &optional confirm) - "Save a file (as FILENAME) with a JSON structure of the data." - - (interactive - (list (if buffer-file-name - (read-file-name "Write JSON file: " - nil nil nil nil) - (read-file-name "Write JSON file: " default-directory - (expand-file-name - (file-name-nondirectory (buffer-name)) - default-directory) - nil nil)) - (not current-prefix-arg))) - - (and confirm - (file-exists-p filename) - (or (y-or-n-p (format "File `%s' exists; overwrite? " filename)) - (error "Canceled"))) - - (let ((table (copy-hash-table keyfreq-table))) - - ;; Merge with the values in `keyfreq-file' - (keyfreq-table-load table) - - (with-temp-file filename - (insert (keyfreq-json-encode table))))) - - -(defun keyfreq-reset () - "Reset all statistics including those in the file." - (interactive) - (when (yes-or-no-p (concat "Delete keyfreq file? You will lose all your stats. ")) - ;; clear the hash table - (clrhash keyfreq-table) - ;; Deal with the file - (when (keyfreq-file-is-unlocked) - ;; Lock the file - (keyfreq-file-claim-lock) - ;; Check that we have the lock - (if (eq (keyfreq-file-owner) (emacs-pid)) - (unwind-protect - ;; if the file exists just delete it - (if (file-exists-p keyfreq-file) - (delete-file keyfreq-file)) - ;; Release the lock. - (keyfreq-file-release-lock)))))) - - -(defun keyfreq-file-owner () - "Return the PID of the Emacs process that owns the table file lock file." - (let ((owner)) - (and (file-exists-p keyfreq-file-lock) - (ignore-errors - (with-temp-buffer - (insert-file-contents-literally keyfreq-file-lock) - (goto-char (point-min)) - (setq owner (read (current-buffer))) - (integerp owner))) - owner))) - - -(defun keyfreq-file-claim-lock () - (ignore-errors - (write-region (number-to-string (emacs-pid)) nil - keyfreq-file-lock nil 'nomessage nil 'excl))) - - -(defun keyfreq-file-release-lock () - (when (file-exists-p keyfreq-file-lock) - (delete-file keyfreq-file-lock))) - - -(defun keyfreq-file-is-unlocked () - ;; If the lock file exists.... - (if (file-exists-p keyfreq-file-lock) - ;; If the process which has the lock does not exist - (if (not (memql (keyfreq-file-owner) (list-system-processes))) - ;; Delete the lock - (delete-file keyfreq-file-lock))) - ;; Check again the lock existence (just in case...) - (not (file-exists-p keyfreq-file-lock))) - - -(defun keyfreq-table-save (table &optional mustsave) - "Append all values from the specified TABLE into the -`keyfreq-file' as a sexp of an alist. Then resets the TABLE -if it was successfully merged. - -If MUSTSAVE is t, this function tries to save the table until it -gets the lock and successfully saves it. If MUSTSAVE is nil, it -does nothing if the table cannot be saved." - - ;; Avoid adding nothing to the file - (if (> (hash-table-count table) 0) - (let ((done)) - ;; Check that the lock file doesn't exist - (while (not done) - (when (keyfreq-file-is-unlocked) - ;; Lock the file - (keyfreq-file-claim-lock) - - ;; Check that we have the lock - (if (eq (keyfreq-file-owner) (emacs-pid)) - (unwind-protect - (progn - ;; Load values and merge them with the current keyfreq-table - (keyfreq-table-load table) - - ;; Write the new frequencies - (with-temp-file keyfreq-file - (let ((l (cdr (keyfreq-list table 'no-sort)))) - (insert "(") - (dolist (item l) - (prin1 item (current-buffer)) - ;; Easy for git to track if every command is - ;; one line - (insert "\n")) - (insert ")")))) - - ;; Reset the hash table, enable the 'done' flag, and - ;; release the lock. - (clrhash table) - (setq done t) - (keyfreq-file-release-lock)))) - - (if (and (not done) mustsave) - ;; If we must save the file right now, we'll just keep - ;; trying until we can get the lock. So we can sleep some - ;; milliseconds for the next while-loop cycle. - (sleep-for 0.1) - ;; If we can wait to the next timer's timeout, just enable - ;; the 'done' flag to break the while-loop. - (setq done t)) - - )))) - - -(defun keyfreq-table-load (table) - "Load all values from the `keyfreq-file' and add them in the TABLE. -The table is not reset, so the values are appended to the table." - - ;; Does `keyfreq-file' exist? - (if (file-exists-p keyfreq-file) - ;; Load sexp - (let ((l (with-temp-buffer - (insert-file-contents keyfreq-file) - (goto-char (point-min)) - (read (current-buffer))))) - - ;; Add the values in the table - (while (and (listp l) l) - (if (listp (car l)) - (unless (keyfreq-match-p (cdr (caar l))) - (puthash (caar l) (+ (gethash (caar l) table 0) (cdar l)) table))) - (setq l (cdr l))) - ))) - - -;;;###autoload -(define-minor-mode keyfreq-autosave-mode - "Keyfreq Autosave mode automatically saves -`keyfreq-table' every `keyfreq-autosave-timeout' seconds -and when emacs is killed." - :global t - :init-value nil - :lighter nil - :keymap nil - :group 'keyfreq - - (when keyfreq-autosave--timer - (cancel-timer keyfreq-autosave--timer) - (setq keyfreq-autosave--timer nil)) - - (if keyfreq-autosave-mode - (progn - (setq keyfreq-autosave--timer - (run-at-time t keyfreq-autosave-timeout - 'keyfreq-autosave--do)) - (add-hook 'kill-emacs-hook 'keyfreq-mustsave--do)) - (keyfreq-mustsave--do) - (remove-hook 'kill-emacs-hook 'keyfreq-mustsave--do))) - - -(defcustom keyfreq-autosave-timeout 600 - "How often in seconds `keyfreq-table' should be saved -when `keyfreq-autosave-mode' is enabled. Setting this -value will take effect only after (re)enabling -`keyfreq-autosave-mode'." - :group 'keyfreq - :type 'number) - - -(defvar keyfreq-autosave--timer nil) - - -(defun keyfreq-autosave--do () - "Function executed periodically to save the `keyfreq-table' in `keyfreq-file'." - (keyfreq-table-save keyfreq-table)) - - -(defun keyfreq-mustsave--do () - "Function executed when we close Emacs." - (keyfreq-table-save keyfreq-table t)) - - -;;;###autoload -(defun keyfreq-save-now () - "Save keyfreq data now." - (interactive) - (keyfreq-mustsave--do) - (message "keyfreq data saved into %s" keyfreq-file)) - -(provide 'keyfreq) - -;;; keyfreq.el ends here diff --git a/.emacs.d/lisp/xah-fly-keys.el b/.emacs.d/lisp/xah-fly-keys.el deleted file mode 100644 index 0b7299e..0000000 --- a/.emacs.d/lisp/xah-fly-keys.el +++ /dev/null @@ -1,4163 +0,0 @@ -;;; xah-fly-keys.el --- ergonomic modal keybinding minor mode. -*- coding: utf-8; lexical-binding: t; -*- - -;; Copyright © 2013, 2024 by Xah Lee - -;; Author: Xah Lee ( http://xahlee.info/ ) -;; Maintainer: Xah Lee -;; Version: 26.11.20250325184849 -;; Created: 2013-09-10 -;; Package-Requires: ((emacs "27")) -;; Keywords: convenience, vi, vim, ergoemacs, keybinding -;; License: GPL v3. -;; Homepage: http://xahlee.info/emacs/misc/xah-fly-keys.html - -;; This file is not part of GNU Emacs. - -;;; Commentary: - -;; xah-fly-keys is a efficient keybinding for emacs. It is modal like -;; vi, but key choices are based on statistics of command call -;; frequency. - -;;; Usage: - -;; M-x xah-fly-keys to toggle the mode on/off. - -;; Important command/insert mode switch keys: - -;; M-x `xah-fly-command-mode-activate' -;; or press F8 or Alt+Space or Ctrl+Space or . -;; Note: if using emacs 28 or before, escape key only works when in emacs is running in graphical user interface mode. - -;; M-x `xah-fly-insert-mode-activate' -;; when in command mode, press qwerty letter key f. - -;; When in command mode: - -;; "f" calls `xah-fly-insert-mode-activate'. - -;; Space is a leader key. For example, "SPC r" calls `query-replace'. -;; Press "SPC C-h" to see the full list. - -;; "SPC SPC" also activates insertion mode. - -;; "SPC RET" calls `execute-extended-command'. - -;; "a" calls `execute-extended-command'. - -;; The leader key sequence basically supplant ALL emacs commands that -;; starts with C-x key. - -;; When using xah-fly-keys, you don't need to press Control or Meta, -;; with the following exceptions: - -;; "C-c" for major mode commands. -;; "C-g" for cancel. -;; "C-q" for quoted-insert. -;; "C-h" for getting a list of keys following a prefix/leader key. - -;; Leader key - -;; You NEVER need to press "C-x" - -;; Any emacs command that has a keybinding starting with C-x, has also -;; a key sequence binding in xah-fly-keys. For example, - -;; "C-x b" for `switch-to-buffer' is "SPC f" -;; "C-x C-f" for `find-file' is "SPC i e" -;; "C-x n n" for `narrow-to-region' is "SPC l l" - -;; The first key we call it leader key. In the above examples, the SPC -;; is the leader key. - -;; When in command mode, the "SPC" is a leader key. - -;; if you want the following standard keys with Control -;; "C-TAB" `xah-next-user-buffer' -;; "C-S-TAB" `xah-previous-user-buffer' -;; "C-v" paste -;; "C-w" close -;; "C-z" undo -;; "C-n" new -;; "C-o" open -;; "C-s" save -;; "C-S-s" save as -;; "C-S-t" open last closed -;; "C-+" `text-scale-increase' -;; "C--" `text-scale-decrease' - -;; add -;; (setq xah-fly-use-control-key t) - -;; To disable any change to Control keybinding -;; add this to emacs init -;; (setq xah-fly-use-control-key nil) -;; before loading xah-fly-keys - -;; To disable any change to meta keybinding -;; add this to emacs init -;; (setq xah-fly-use-meta-key nil) -;; before loading xah-fly-keys - -;; If you have a bug, post on github. - -;; For detail about design and other info, see home page at -;; http://xahlee.info/emacs/misc/xah-fly-keys.html - -;; If you like this project, paypal me $30 to Xah@XahLee.org - -;;; Installation: -;; here's how to manual install -;; -;; put the file xah-fly-keys.el in ~/.emacs.d/lisp/ -;; create the dir if doesn't exist. -;; -;; put the following in your emacs init file: -;; (add-to-list 'load-path "~/.emacs.d/lisp/") -;; (require 'xah-fly-keys) -;; (xah-fly-keys-set-layout "qwerty") ; optional -;; (xah-fly-keys 1) -;; -;; possible layout values: - -;; adnw (German) -;; azerty -;; azerty-be -;; bepo (French) -;; colemak -;; colemak-dh -;; dvorak -;; engrammer -;; halmak -;; koy (German) -;; minimak -;; neo2 (German) -;; norman -;; programer-dvorak -;; pt-nativo (Brazil) -;; qfmlwy -;; qgmlwb -;; qwerty -;; qwerty-abnt (Brazil) -;; qwerty-no (Norwegian) -;; qwerty-se (Swedish) -;; qwertz -;; qwpr -;; russian -;; workman - -;; supported layouts are stored in the variable xah-fly-layout-diagrams - -;; HHHH------------------------------ -;;; Code: - -(require 'dired) -(require 'dired-x) -(require 'seq) - -;; HHHH------------------------------ - -(defgroup xah-fly-keys nil - "Ergonomic modal keybinding minor mode." - :group 'keyboard) - -(defvar xah-fly-command-mode-activate-hook nil "Hook for `xah-fly-command-mode-activate'") -(defvar xah-fly-insert-mode-activate-hook nil "Hook for `xah-fly-insert-mode-activate'") - -(defvar xah-fly-command-mode-indicator "c" - "Character in mode line indicating command mode is active.") -(defvar xah-fly-insert-mode-indicator "i" - "Character in mode line indicating insert mode is active.") - -(defcustom xah-fly-use-control-key nil - "If true, change many emacs keybinding involving control key. -Keys changed: -Standard shortcut for open, close, copy, paste etc. -Remove many redundant emacs default keys -Must be set before loading xah-fly-keys." - :type 'boolean) - -(defcustom xah-fly-use-meta-key t - "If true, change some emacs keybinding involving meta key. -Remove many redundant emacs default keys. -Must be set before loading xah-fly-keys." - :type 'boolean) - -(defcustom xah-fly-use-isearch-arrows t - "If true, set arrow keys for moving between occurrences, and C-v is paste, in isearch (`isearch-forward'). -Must be set before loading xah-fly-keys." - :type 'boolean) - -;; HHHH------------------------------ -;; cursor movement - -(defun xah-pop-local-mark-ring () - "Move cursor to last mark position of current buffer. -Repeat call cycles all positions in `mark-ring'. - -URL `http://xahlee.info/emacs/emacs/emacs_cycle_local_mark_ring.html' -Created: 2016-04-04 -Version: 2023-09-03" - (interactive) - (set-mark-command t)) - -(defun xah-beginning-of-line-or-block () - "Move cursor to beginning of indent or line, end of previous block, in that order. - -If `visual-line-mode' is on, beginning of line means visual line. - -URL `http://xahlee.info/emacs/emacs/emacs_move_by_paragraph.html' -Created: 2018-06-04 -Version: 2024-10-30" - (interactive) - (let ((xp (point))) - (if (or (eq (point) (line-beginning-position)) - (eq last-command this-command)) - (when (re-search-backward "\n[\t\n ]*\n+" nil :move) - (skip-chars-backward "\n\t ") - ;; (forward-char) - ) - (if visual-line-mode - (beginning-of-visual-line) - (if (eq major-mode 'eshell-mode) - (beginning-of-line) - (back-to-indentation) - (when (eq xp (point)) - (beginning-of-line))))))) - -(defun xah-end-of-line-or-block () - "Move cursor to end of line or next block. - -• When called first time, move cursor to end of line. -• When called again, move cursor forward by jumping over any sequence of whitespaces containing 2 blank lines. -• if `visual-line-mode' is on, end of line means visual line. - -URL `http://xahlee.info/emacs/emacs/emacs_move_by_paragraph.html' -Created: 2018-06-04 -Version: 2024-10-30" - (interactive) - (if (or (eq (point) (line-end-position)) - (eq last-command this-command)) - (re-search-forward "\n[\t\n ]*\n+" nil :move) - (if visual-line-mode - (end-of-visual-line) - (end-of-line)))) - -(defun xah-page-up () - "Call `scroll-down-command'. (page up key.) -Created: 2024-10-09 -Version: 2024-10-09" - (interactive) - (progn - (scroll-down-command) - (set-transient-map - (let ((xkmap (make-sparse-keymap))) - (define-key xkmap (kbd "") #'xah-page-up) - (define-key xkmap (kbd "") #'xah-page-down) - xkmap)))) - -(defun xah-page-down () - "Call `scroll-up-command'. (page down key.) -Created: 2024-10-09 -Version: 2024-10-09" - (interactive) - (progn - (scroll-up-command) - (set-transient-map - (let ((xkmap (make-sparse-keymap))) - (define-key xkmap (kbd "") #'xah-page-up) - (define-key xkmap (kbd "") #'xah-page-down) - xkmap)))) - -(defvar xah-brackets '( "“”" "()" "[]" "{}" "<>" "<>" "()" "[]" "{}" "⦅⦆" "〚〛" "⦃⦄" "‹›" "«»" "「」" "〈〉" "《》" "【】" "〔〕" "⦗⦘" "『』" "〖〗" "〘〙" "「」" "⟦⟧" "⟨⟩" "⟪⟫" "⟮⟯" "⟬⟭" "⌈⌉" "⌊⌋" "⦇⦈" "⦉⦊" "❛❜" "❝❞" "❨❩" "❪❫" "❴❵" "❬❭" "❮❯" "❰❱" "❲❳" "〈〉" "⦑⦒" "⧼⧽" "﹙﹚" "﹛﹜" "﹝﹞" "⁽⁾" "₍₎" "⦋⦌" "⦍⦎" "⦏⦐" "⁅⁆" "⸢⸣" "⸤⸥" "⟅⟆" "⦓⦔" "⦕⦖" "⸦⸧" "⸨⸩" "⦅⦆") - "A list of strings, each element is a string of 2 chars, the left bracket and a matching right bracket. -Used by `xah-select-text-in-quote' and others.") - -(defconst xah-left-brackets - (mapcar (lambda (x) (substring x 0 1)) xah-brackets) - "List of left bracket chars. Each element is a string.") - -(defconst xah-right-brackets - (mapcar (lambda (x) (substring x 1 2)) xah-brackets) - "List of right bracket chars. Each element is a string.") - -(defun xah-backward-left-bracket () - "Move cursor to the previous occurrence of left bracket. -The list of brackets to jump to is defined by `xah-left-brackets'. - -URL `http://xahlee.info/emacs/emacs/emacs_navigating_keys_for_brackets.html' -Version: 2015-10-01" - (interactive) - (re-search-backward (regexp-opt xah-left-brackets) nil t)) - -(defun xah-forward-right-bracket () - "Move cursor to the next occurrence of right bracket. -The list of brackets to jump to is defined by `xah-right-brackets'. - -URL `http://xahlee.info/emacs/emacs/emacs_navigating_keys_for_brackets.html' -Version: 2015-10-01" - (interactive) - (re-search-forward (regexp-opt xah-right-brackets) nil t)) - -(defun xah-goto-matching-bracket () - "Move cursor to the matching bracket. -If cursor is not on a bracket, call `backward-up-list'. -The list of brackets to jump to is defined by `xah-left-brackets' and `xah-right-brackets'. - -URL `http://xahlee.info/emacs/emacs/emacs_navigating_keys_for_brackets.html' -Created: 2016-11-22 -Version: 2024-06-15" - (interactive) - (if (nth 3 (syntax-ppss)) - (backward-up-list 1 'ESCAPE-STRINGS 'NO-SYNTAX-CROSSING) - (cond - ((eq (char-after) ?\") (forward-sexp)) - ((eq (char-before) ?\") (backward-sexp)) - ((looking-at (regexp-opt xah-left-brackets)) - (forward-sexp)) - ((if (eq (point-min) (point)) - nil - (prog2 - (backward-char) - (looking-at (regexp-opt xah-right-brackets)) - (forward-char))) - (backward-sexp) - (while (looking-at "\\s'") (forward-char))) - (t (backward-up-list 1 'ESCAPE-STRINGS 'NO-SYNTAX-CROSSING))))) - -(defvar xah-punctuation-regex nil "A regex string for the purpose of moving cursor to a punctuation.") -(setq xah-punctuation-regex "\"") - -(defun xah-forward-punct () - "Move cursor to the next occurrence of punctuation. -Punctuations is defined by `xah-punctuation-regex' - -URL `http://xahlee.info/emacs/emacs/emacs_jump_to_punctuations.html' -Created: 2017-06-26 -Version: 2025-03-21" - (interactive) - (set-transient-map - (let ((xkmap (make-sparse-keymap))) - (define-key xkmap (kbd "") #'xah-backward-punct) - (define-key xkmap (kbd "") #'xah-forward-punct) - (define-key xkmap (kbd (if (boundp 'xah-repeat-key) xah-repeat-key "m")) real-this-command) - xkmap)) - (re-search-forward xah-punctuation-regex nil t)) - -(defun xah-backward-punct () - "Move cursor to the previous occurrence of punctuation. -See `xah-forward-punct' - -URL `http://xahlee.info/emacs/emacs/emacs_jump_to_punctuations.html' -Created: 2017-06-26 -Version: 2025-03-21" - (interactive) - (set-transient-map - (let ((xkmap (make-sparse-keymap))) - (define-key xkmap (kbd "") #'xah-backward-punct) - (define-key xkmap (kbd "") #'xah-forward-punct) - (define-key xkmap (kbd (if (boundp 'xah-repeat-key) xah-repeat-key "m")) real-this-command) - xkmap)) - (re-search-backward xah-punctuation-regex nil t)) - -(defun xah-sort-lines () - "Like `sort-lines' but if no region, do the current block. -Created: 2022-01-22 -Version: 2025-03-25" - (interactive) - (let (xbeg xend) - (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)))))) - (sort-lines current-prefix-arg xbeg xend))) - -(defun xah-narrow-to-region () - "Same as `narrow-to-region', but if no selection, narrow to the current block. -Created: 2022-01-22 -Version: 2025-03-25" - (interactive) - (let (xbeg xend) - (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)))))) - (narrow-to-region xbeg xend))) - -;; HHHH------------------------------ -;; editing commands - -(defun xah-copy-line-or-region () - "Copy current line or selection. - -Copy current line. When called repeatedly, append copy subsequent lines. -Except: - -If `universal-argument' is called first, copy whole buffer (respects `narrow-to-region'). -If `rectangle-mark-mode' is on, copy the rectangle. -If `region-active-p', copy the region. - -URL `http://xahlee.info/emacs/emacs/emacs_copy_cut_current_line.html' -Created: 2010-05-21 -Version: 2024-06-19" - (interactive) - (cond - (current-prefix-arg (copy-region-as-kill (point-min) (point-max))) - ((and (boundp 'rectangle-mark-mode) rectangle-mark-mode) - (copy-region-as-kill (region-beginning) (region-end) t)) - ((region-active-p) (copy-region-as-kill (region-beginning) (region-end))) - ((eq last-command this-command) - (if (eobp) - nil - (progn - (kill-append "\n" nil) - (kill-append (buffer-substring (line-beginning-position) (line-end-position)) nil) - (end-of-line) - (forward-char)))) - ((eobp) - (if (eq (char-before) 10) - (progn) - (progn - (copy-region-as-kill (line-beginning-position) (line-end-position)) - (end-of-line)))) - (t - (copy-region-as-kill (line-beginning-position) (line-end-position)) - (end-of-line) - (forward-char)))) - -(defun xah-cut-line-or-region () - "Cut current line or selection. -If `universal-argument' is called first, cut whole buffer (respects `narrow-to-region'). - -URL `http://xahlee.info/emacs/emacs/emacs_copy_cut_current_line.html' -Created: 2010-05-21 -Version: 2015-06-10" - (interactive) - (if current-prefix-arg - (progn ; not using kill-region because we don't want to include previous kill - (kill-new (buffer-string)) - (delete-region (point-min) (point-max))) - (progn (if (region-active-p) - (kill-region (region-beginning) (region-end) t) - (kill-region (line-beginning-position) (line-beginning-position 2)))))) - -(defun xah-copy-all-or-region () - "Copy buffer or selection content to `kill-ring'. -Respects `narrow-to-region'. - -URL `http://xahlee.info/emacs/emacs/emacs_copy_cut_all_or_region.html' -Version: 2015-08-22" - (interactive) - (if (region-active-p) - (progn - (kill-new (buffer-substring (region-beginning) (region-end))) - (message "Text selection copied.")) - (progn - (kill-new (buffer-string)) - (message "Buffer content copied.")))) - -(defun xah-cut-all-or-region () - "Cut buffer or selection content to `kill-ring'. -Respects `narrow-to-region'. - -URL `http://xahlee.info/emacs/emacs/emacs_copy_cut_all_or_region.html' -Version: 2015-08-22" - (interactive) - (if (region-active-p) - (progn - (kill-new (buffer-substring (region-beginning) (region-end))) - (delete-region (region-beginning) (region-end))) - (progn - (kill-new (buffer-string)) - (delete-region (point-min) (point-max))))) - -(defun xah-copy-all () - "Put the whole buffer content into the `kill-ring'. -(respects `narrow-to-region') -Version: 2016-10-06" - (interactive) - (kill-new (buffer-string)) - (message "Buffer content copied.")) - -(defun xah-cut-all () - "Cut the whole buffer content into the `kill-ring'. -Respects `narrow-to-region'. -Version: 2017-01-03" - (interactive) - (kill-new (buffer-string)) - (delete-region (point-min) (point-max))) - -(defun xah-paste-or-paste-previous () - "Paste. When called repeatedly, paste previous. -This command calls `yank', and if repeated, call `yank-pop'. - -If `universal-argument' is called first with a number arg, paste that many times. - -URL `http://xahlee.info/emacs/emacs/emacs_paste_or_paste_previous.html' -Created: 2017-07-25 -Version: 2020-09-08" - (interactive) - (progn - (when (and delete-selection-mode (region-active-p)) - (delete-region (region-beginning) (region-end))) - (if current-prefix-arg - (progn - (dotimes (_ (prefix-numeric-value current-prefix-arg)) - (yank))) - (if (eq real-last-command this-command) - (yank-pop 1) - (yank))))) - -(defun xah-show-kill-ring () - "Insert all `kill-ring' content in a new buffer named *copy history*. - -URL `http://xahlee.info/emacs/emacs/emacs_show_kill_ring.html' -Created: 2019-12-02 -Version: 2024-05-07" - (interactive) - (let ((xbuf (generate-new-buffer "*copy history*")) - (inhibit-read-only t)) - (progn - (switch-to-buffer xbuf) - (funcall 'fundamental-mode) - (mapc - (lambda (x) - (insert x "\n\nsss97707------------------------------------------------\n\n" )) - kill-ring)) - (goto-char (point-min)))) - -(defun xah-move-block-up () - "Swap the current text block with the previous. -After this command is called, press or to move. Any other key to exit. -Version: 2022-03-04" - (interactive) - (let ((xp0 (point)) - xc1 ; current block begin - xc2 ; current Block End - xbeg ; prev Block Begin - xend ; prev Block end - ) - (if (re-search-forward "\n[ \t]*\n+" nil "move") - (setq xc2 (match-beginning 0)) - (setq xc2 (point))) - (goto-char xp0) - (if (re-search-backward "\n[ \t]*\n+" nil "move") - (progn - (skip-chars-backward "\n \t") - (setq xend (point)) - (skip-chars-forward "\n \t") - (setq xc1 (point))) - (error "No previous block.")) - (goto-char xend) - (if (re-search-backward "\n[ \t]*\n+" nil "move") - (progn - (setq xbeg (match-end 0))) - (setq xbeg (point))) - (transpose-regions xbeg xend xc1 xc2) - (goto-char xbeg) - (set-transient-map - (let ((xkmap (make-sparse-keymap))) - (define-key xkmap (kbd "") #'xah-move-block-up) - (define-key xkmap (kbd "") #'xah-move-block-down) - xkmap)))) - -(defun xah-move-block-down () - "Swap the current text block with the next. -After this command is called, press or to move. Any other key to exit. -Version: 2022-03-04" - (interactive) - (let ((xp0 (point)) - xc1 ; current block begin - xc2 ; current Block End - xn1 ; next Block Begin - xn2 ; next Block end - ) - (if (eq (point-min) (point)) - (setq xc1 (point)) - (if (re-search-backward "\n\n+" nil "move") - (progn - (setq xc1 (match-end 0))) - (setq xc1 (point)))) - (goto-char xp0) - (if (re-search-forward "\n[ \t]*\n+" nil "move") - (progn - (setq xc2 (match-beginning 0)) - (setq xn1 (match-end 0))) - (error "No next block.")) - (if (re-search-forward "\n[ \t]*\n+" nil "move") - (progn - (setq xn2 (match-beginning 0))) - (setq xn2 (point))) - (transpose-regions xc1 xc2 xn1 xn2) - (goto-char xn2)) - (set-transient-map - (let ((xkmap (make-sparse-keymap))) - (define-key xkmap (kbd "") #'xah-move-block-up) - (define-key xkmap (kbd "") #'xah-move-block-down) - xkmap))) - -(defun xah-shrink-whitespaces () - "Remove whitespaces around cursor . - -Shrink neighboring spaces, then newlines, then spaces again, leaving one space or newline at each step, till no more white space. - -URL `http://xahlee.info/emacs/emacs/emacs_shrink_whitespace.html' -Created: 2014-10-21 -Version: 2023-07-12" - (interactive) - (let ((xeol-count 0) - (xp0 (point)) - xbeg ; whitespace begin - xend ; whitespace end - (xcharBefore (char-before)) - (xcharAfter (char-after)) - xspace-neighbor-p) - (setq xspace-neighbor-p (or (eq xcharBefore 32) (eq xcharBefore 9) (eq xcharAfter 32) (eq xcharAfter 9))) - (skip-chars-backward " \n\t ") - (setq xbeg (point)) - (goto-char xp0) - (skip-chars-forward " \n\t ") - (setq xend (point)) - (goto-char xbeg) - (while (search-forward "\n" xend t) - (setq xeol-count (1+ xeol-count))) - (goto-char xp0) - (cond - ((eq xeol-count 0) - (if (> (- xend xbeg) 1) - (progn - (delete-horizontal-space) (insert " ")) - (progn (delete-horizontal-space)))) - ((eq xeol-count 1) - (if xspace-neighbor-p - (delete-horizontal-space) - (progn (delete-space--internal "\n" nil) (insert " ")))) - ((eq xeol-count 2) - (if xspace-neighbor-p - (delete-horizontal-space) - (progn - (delete-space--internal "\n" nil) - (insert "\n")))) - ((> xeol-count 2) - (if xspace-neighbor-p - (delete-horizontal-space) - (progn - (goto-char xend) - (search-backward "\n") - (delete-region xbeg (point)) - (insert "\n")))) - (t (progn - (message "nothing done. logic error 40873. shouldn't reach here")))))) - -(defun xah-delete-string-backward (&optional DeleteJustQuote) - "Delete string to the left of cursor. - -Cursor must be on the right of a string delimiter. -e.g. \"▮some\" or \"some\"▮ -Else, do nothing. - -String delimiter is determined by current syntax table. (see `describe-syntax') - -If DeleteJustQuote is true, delete only the quotation marks. - -Created: 2023-11-12 -Version: 2024-06-06" - (when (prog2 (backward-char) (looking-at "\\s\"") (forward-char)) - (let ((xp0 (point)) xbeg xend) - ;; xbeg xend are the begin and end pos of the string - (if (nth 3 (syntax-ppss)) - (setq xbeg (1- xp0) - xend - (progn - (backward-char) - (forward-sexp) - (point))) - (setq xend (point) - xbeg - (progn (forward-sexp -1) (point)))) - (if DeleteJustQuote - (progn (goto-char xend) - (delete-char -1) - (goto-char xbeg) - (delete-char 1)) - (if (eq real-this-command real-last-command) - (kill-append (delete-and-extract-region xbeg xend) t) - (kill-region xbeg xend)))))) - -(defvar xah-smart-delete-dispatch - nil - "Used by `xah-smart-delete'. -This makes that function behavior dependent on current major-mode. -Value is Alist of pairs, each is of the form -(‹major-mode-name› . ‹function-name›) -If ‹major-mode-name› match current var `major-mode', the paired function is called. -If no major mode matches, `xah-smart-delete' default behavior is used. - -Version: 2024-06-05") - -(setq xah-smart-delete-dispatch - '((xah-wolfram-mode . xah-wolfram-smart-delete-backward) - (xah-html-mode . xah-html-smart-delete-backward))) - -(defun xah-smart-delete (&optional BracketOnly SkipDispatch) - "Smart backward delete. -Typically, delete to the left 1 char or entire bracketed text. -Behavior depends on what's left char, and current `major-mode'. - -If `xah-smart-delete-dispatch' match, call the matched function instead. -If region active, delete region. -If cursor left is space tab newline, delete them. -If cursor left is bracket, delete the whole bracket block. -If cursor left is string quote, delete the string. -Else just delete one char to the left. - -If `universal-argument' is called first, do not delete bracket's innertext. - -In elisp code, arg BracketOnly if true, do not delete innertext. SkipDispatch if true, skip checking `xah-smart-delete-dispatch'. - -Created: 2023-07-22 -Version: 2025-02-05" - (interactive (list current-prefix-arg nil)) - (let (xfun) - (cond - ((and (not SkipDispatch) (setq xfun (assq major-mode xah-smart-delete-dispatch))) - (message "calling cdr of %s" xfun) - (funcall (cdr xfun))) - ((region-active-p) (delete-region (region-beginning) (region-end))) - ((or - ;; 32 is space, 9 is tab, 10 is newline - (eq (char-before) 32) - (eq (char-before) 10) - (eq (char-before) 9)) - (if (minibufferp (current-buffer)) - (while (or (eq (char-before) 32) (eq (char-before) 10) (eq (char-before) 9)) - (delete-char -1)) - (let ((xp0 (point)) xbeg xend) - (skip-chars-backward " \t\n") - (setq xbeg (point) xend xp0) - (if (eq real-this-command real-last-command) - (kill-append (delete-and-extract-region xbeg xend) t) - (kill-region xbeg xend))))) - ((prog2 (backward-char) (looking-at "\\s)") (forward-char)) - ;; (message "cursor left is closing bracket") - (cond - ;; unmatched bracket, just delete it - ((not (condition-case nil (scan-sexps (point) -1) (scan-error nil))) - (warn "There was unmatched bracket: no paired opening bracket on left of cursor") - (delete-char -1)) - ;; delete just the brackets - (BracketOnly - (let ((xp0 (point)) xbeg) - (forward-sexp -1) - (while (looking-at "\\s'") (forward-char)) - (setq xbeg (point)) - (goto-char xp0) - (delete-char -1) - (goto-char xbeg) - (delete-char 1) - (goto-char (- xp0 2)))) - ;; delete the bracket block - (t - (let ((xp0 (point)) xbeg xend) - (forward-sexp -1) - (while (looking-at "\\s'") (forward-char)) - (setq xbeg (point) xend xp0) - (if (eq real-this-command real-last-command) - (kill-append (delete-and-extract-region xbeg xend) t) - (kill-region xbeg xend)))))) - ((prog2 (backward-char) (looking-at "\\s(") (forward-char)) - ;; (message "cursor left is opening bracket") - (cond - ;; unmatched bracket, just delete it - ((save-excursion - (backward-char) - (not (condition-case nil (scan-sexps (point) 1) (scan-error nil)))) - (warn "There was unmatched bracket: no paired closing bracket on right of cursor") - (delete-char -1)) - ;; delete just the brackets - (BracketOnly - (let (xbeg) - (backward-char) - (setq xbeg (point)) - (forward-sexp 1) - (delete-char -1) - (goto-char xbeg) - (delete-char 1))) - ;; delete the bracket block - (t - (let (xbeg xend) - (backward-char) - (setq xbeg (point)) - (forward-sexp 1) - (setq xend (point)) - (if (eq real-this-command real-last-command) - (kill-append (delete-and-extract-region xbeg xend) t) - (kill-region xbeg xend)))))) - ((prog2 (backward-char) (looking-at "\\s\"") (forward-char)) - (message "calling xah-delete-string-backward") - (xah-delete-string-backward BracketOnly)) - (t (delete-char -1))))) - -(defun xah-change-bracket-pairs (FromChars ToChars) - "Change bracket pairs to another type or none. -For example, change all parenthesis () to square brackets []. -Works on current block or selection. - -In lisp code, FromChars is a string with at least 2 spaces. -e.g. -paren ( ) -french angle ‹ › -double bracket [[ ]] -etc. -It is split by space, and last 2 items are taken as left and right brackets. - -ToChars is similar, with a special value of -none -followed by 2 spaces. -,it means replace by empty string. - -URL `http://xahlee.info/emacs/emacs/elisp_change_brackets.html' -Created: 2020-11-01 -Version: 2025-03-25" - (interactive - (let ((xbrackets - '( - "square [ ]" - "brace { }" - "paren ( )" - "less greater than < >" - "QUOTATION MARK \" \"" - "APOSTROPHE ' '" - "emacs ` '" - "GRAVE ACCENT ` `" - "double square [[ ]]" - "tilde ~ ~" - "equal = =" - "double curly quote “ ”" - "single curly quote ‘ ’" - "french angle quote ‹ ›" - "french double angle « »" - "corner 「 」" - "white corner 『 』" - "lenticular 【 】" - "white lenticular 〖 〗" - "title angle 〈 〉" - "double angle 《 》" - "tortoise 〔 〕" - "white tortoise 〘 〙" - "white square 〚 〛" - "white paren ⦅ ⦆" - "white curly bracket ⦃ ⦄" - "pointing angle 〈 〉" - "angle with dot ⦑ ⦒" - "curved angle ⧼ ⧽" - "math square ⟦ ⟧" - "math angle ⟨ ⟩" - "math double angle ⟪ ⟫" - "math flattened parenthesis ⟮ ⟯" - "math white tortoise shell ⟬ ⟭" - "heavy single quotation mark ornament ❛ ❜" - "heavy double turned comma quotation mark ornament ❝ ❞" - "medium parenthesis ornament ❨ ❩" - "medium flattened parenthesis ornament ❪ ❫" - "medium curly ornament ❴ ❵" - "medium pointing angle ornament ❬ ❭" - "heavy pointing angle quotation mark ornament ❮ ❯" - "heavy pointing angle ornament ❰ ❱" - "none " - ))) - (let ((completion-ignore-case t)) - (list - (completing-read "Replace this:" xbrackets nil t nil nil (car xbrackets)) - (completing-read "To:" xbrackets nil t nil nil (car (last xbrackets))))))) - (let (xbeg xend xleft xright xtoL xtoR) - (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)))))) - (let ((xsFrom (last (split-string FromChars " ") 2)) - (xsTo (last (split-string ToChars " ") 2))) - - ;; (when (< (length xsFrom) 3) - ;; (error "cannot find input brackets %s" xsFrom)) - - ;; (when (< (length xsTo) 3) - ;; (message "replace blacket is empty string") - ;; (setq xsTo (list "" "" ""))) - - (setq xleft (car xsFrom) xright (car (cdr xsFrom)) - xtoL (car xsTo) xtoR (car (cdr xsTo))) - - (save-excursion - (save-restriction - (narrow-to-region xbeg xend) - (let ((case-fold-search nil)) - (if (string-equal xleft xright) - (let ((xx (regexp-quote xleft))) - (goto-char (point-min)) - (while - (re-search-forward - (format "%s\\([^%s]+?\\)%s" xx xx xx) - nil t) - (overlay-put (make-overlay (match-beginning 0) (match-end 0)) 'face 'highlight) - (replace-match (concat xtoL "\\1" xtoR) t))) - (progn - (progn - (goto-char (point-min)) - (while (search-forward xleft nil t) - (overlay-put (make-overlay (match-beginning 0) (match-end 0)) 'face 'highlight) - (replace-match xtoL t t))) - (progn - (goto-char (point-min)) - (while (search-forward xright nil t) - (overlay-put (make-overlay (match-beginning 0) (match-end 0)) 'face 'highlight) - (replace-match xtoR t t))))))))))) - -(defun xah-toggle-letter-case () - "Toggle the letter case of current word or selection. -Always cycle in this order: Init Caps, ALL CAPS, all lower. - -URL `http://xahlee.info/emacs/emacs/emacs_toggle_letter_case.html' -Created: 2020-06-26 -Version: 2024-06-17" - (interactive) - (let ((deactivate-mark nil) xbeg xend) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (save-excursion - (skip-chars-backward "[:alnum:]") - (setq xbeg (point)) - (skip-chars-forward "[:alnum:]") - (setq xend (point)))) - (when (not (eq last-command this-command)) - (put this-command 'state 0)) - (cond - ((equal 0 (get this-command 'state)) - (upcase-initials-region xbeg xend) - (put this-command 'state 1)) - ((equal 1 (get this-command 'state)) - (upcase-region xbeg xend) - (put this-command 'state 2)) - ((equal 2 (get this-command 'state)) - (downcase-region xbeg xend) - (put this-command 'state 0))))) - -;; test case -;; test_case some -;; test-case -;; tes▮t-case - -(defun xah-toggle-previous-letter-case () - "Toggle the letter case of the letter to the left of cursor. - -URL `http://xahlee.info/emacs/emacs/emacs_toggle_letter_case.html' -Created: 2015-12-22 -Version: 2023-11-14" - (interactive) - (let ((case-fold-search nil)) - (left-char 1) - (cond - ((looking-at "[[:lower:]]") (upcase-region (point) (1+ (point)))) - ((looking-at "[[:upper:]]") (downcase-region (point) (1+ (point))))) - (right-char))) - -(defun xah-upcase-sentence () - "Upcase first letters of sentences of current block or selection. - -URL `http://xahlee.info/emacs/emacs/emacs_upcase_sentence.html' -Created: 2020-12-08 -Version: 2025-03-25" - (interactive) - (let (xbeg xend) - (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)))))) - (save-restriction - (narrow-to-region xbeg xend) - (let ((case-fold-search nil)) - ;; after period or question mark or exclamation - (goto-char (point-min)) - (while (re-search-forward "\\(\\.\\|\\?\\|!\\)[ \n]+ *\\([a-z]\\)" nil :move) - (upcase-region (match-beginning 2) (match-end 2)) - (overlay-put (make-overlay (match-beginning 2) (match-end 2)) 'face 'highlight)) - ;; after a blank line, after a bullet, or beginning of buffer - (goto-char (point-min)) - (while (re-search-forward "\\(\\`\\|• \\|\n\n\\)\\([a-z]\\)" nil :move) - (upcase-region (match-beginning 2) (match-end 2)) - (overlay-put (make-overlay (match-beginning 2) (match-end 2)) 'face 'highlight)) - ;; for HTML. first letter after tag - (when - (or - (eq major-mode 'xah-html-mode) - (eq major-mode 'html-mode) - (eq major-mode 'sgml-mode) - (eq major-mode 'nxml-mode) - (eq major-mode 'xml-mode) - (eq major-mode 'mhtml-mode)) - (goto-char (point-min)) - (while - (re-search-forward "\\([ \n]?\\|<h[1-6]>[ \n]?\\|<p>[ \n]?\\|<li>[ \n]?\\|<dd>[ \n]?\\|<td>[ \n]?\\|<br ?/?>[ \n]?\\|<figcaption>[ \n]?\\)\\([a-z]\\)" nil :move) - (upcase-region (match-beginning 2) (match-end 2)) - (overlay-put (make-overlay (match-beginning 2) (match-end 2)) 'face 'highlight)))) - (goto-char (point-max))) - (skip-chars-forward " \n\t"))) - -(defun xah-title-case-region-or-line (&optional Begin End) - "Title case text between nearest brackets, or current line or selection. -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. - -When called in a elisp program, Begin End are region boundaries. - -URL `http://xahlee.info/emacs/emacs/elisp_title_case_text.html' -Created: 2017-01-11 -Version: 2021-09-19" - (interactive) - (let* ((xskipChars "^\"<>(){}[]“”‘’‹›«»「」『』【】〖〗《》〈〉〔〕") - (xp0 (point)) - (xbeg (if Begin - Begin - (if (region-active-p) - (region-beginning) - (progn - (skip-chars-backward xskipChars (line-beginning-position)) (point))))) - (xend (if End - End - (if (region-active-p) - (region-end) - (progn (goto-char xp0) - (skip-chars-forward xskipChars (line-end-position)) (point))))) - (xstrPairs [ - [" A " " a "] - [" An " " an "] - [" And " " and "] - [" At " " at "] - [" As " " as "] - [" By " " by "] - [" Be " " be "] - [" Into " " into "] - [" In " " in "] - [" Is " " is "] - [" It " " it "] - [" For " " for "] - [" Of " " of "] - [" Or " " or "] - [" On " " on "] - [" Via " " via "] - [" The " " the "] - [" That " " that "] - [" To " " to "] - [" Vs " " vs "] - [" With " " with "] - [" From " " from "] - ["'S " "'s "] - ["'T " "'t "] - ])) - (save-excursion - (save-restriction - (narrow-to-region xbeg xend) - (upcase-initials-region (point-min) (point-max)) - (let ((case-fold-search nil)) - (mapc - (lambda (xx) - (goto-char (point-min)) - (while - (search-forward (aref xx 0) nil t) - (replace-match (aref xx 1) t t))) - xstrPairs)))))) - -(defun xah-add-space-after-comma () - "Add a space after comma of current block or selection. -and highlight changes made. -Created: 2022-01-20 -Version: 2025-03-25" - (interactive) - (let (xbeg xend) - (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)))))) - (save-restriction - (narrow-to-region xbeg xend) - (goto-char (point-min)) - (while - (re-search-forward ",\\b" nil t) - (replace-match ", ") - (overlay-put - (make-overlay - (match-beginning 0) - (match-end 0)) 'face 'highlight))))) - -(defun xah-toggle-read-novel-mode () - "Setup current frame to be suitable for reading long novel/article text. -• Set frame width to 70 -• Line wrap at word boundaries. -• Line spacing is increased. -• Proportional width font is used. -Call again to toggle back. - -URL `http://xahlee.info/emacs/emacs/emacs_novel_reading_mode.html' -Created: 2019-01-30 -Version: 2021-01-16" - (interactive) - (if (eq (frame-parameter (selected-frame) 'width) 70) - (progn - (set-frame-parameter (selected-frame) 'width 106) - (variable-pitch-mode 0) - (setq line-spacing nil) - (setq word-wrap nil)) - (progn - (set-frame-parameter (selected-frame) 'width 70) - (variable-pitch-mode 1) - (setq line-spacing 0.5) - (setq word-wrap t))) - (redraw-frame (selected-frame))) - -(defun xah-fill-or-unfill () - "Reformat current block or selection to short/long line. -First call will break into multiple short lines. Repeated call toggles between short and long lines. -This commands calls `fill-region' to do its work. Set `fill-column' for short line length. - -URL `http://xahlee.info/emacs/emacs/modernization_fill-paragraph.html' -Created: 2020-11-22 -Version: 2025-03-25" - (interactive) - ;; 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 - (let ( (xisLongline (if (eq last-command this-command) (get this-command 'longline-p) t)) - (deactivate-mark nil) - xbeg xend ) - (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)))))) - (if xisLongline - (fill-region xbeg xend) - (let ((fill-column 99999 )) - (fill-region xbeg xend))) - (put this-command 'longline-p (not xisLongline)))) - -(defun xah-unfill-paragraph () - "Replace newline chars in current paragraph by single spaces. -This command does the inverse of `fill-paragraph'. - -URL `http://xahlee.info/emacs/emacs/emacs_unfill-paragraph.html' -Created: 2010-05-12 -Version: 2022-05-20" - (interactive) - (let ((fill-column 90002000)) - (fill-paragraph))) - -(defun xah-unfill-region (Begin End) - "Replace newline chars in region by single spaces. -This command does the inverse of `fill-region'. - -URL `http://xahlee.info/emacs/emacs/emacs_unfill-paragraph.html' -Created: 2010-05-12 -Version: 2022-05-20" - (interactive "r") - (let ((fill-column 90002000)) - (fill-region Begin End))) - -(defun xah-change-newline-chars-to-one (Begin End) - "Replace newline char sequence by just one. - -URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html' -Version: 2021-07-06" - (interactive "r") - (save-excursion - (save-restriction - (narrow-to-region Begin End) - (goto-char (point-min)) - (while (re-search-forward "\n\n+" nil :move) (replace-match "\n"))))) - -(defun xah-reformat-whitespaces-to-one-space (Begin End) - "Replace whitespaces by one space. - -URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html' -Created: 2017-01-11 -Version: 2022-01-08" - (interactive "r") - (save-restriction - (narrow-to-region Begin End) - (goto-char (point-min)) - (while (search-forward "\n" nil :move) (replace-match " ")) - (goto-char (point-min)) - (while (search-forward "\t" nil :move) (replace-match " ")) - (goto-char (point-min)) - (while (re-search-forward " +" nil :move) (replace-match " ")) - (goto-char (point-max)))) - -(defun xah-reformat-to-multi-lines ( &optional Begin End MinLength) - "Replace spaces by a newline at ~70 chars, on current block or selection. -If `universal-argument' is called first, ask user for max width. - -URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html' -Created: 2018-12-16 -Version: 2025-03-25" - (interactive) - (let ( xbeg xend xminlen ) - (setq xminlen (if MinLength MinLength (if current-prefix-arg (prefix-numeric-value current-prefix-arg) fill-column))) - (if (and Begin End) - (setq xbeg Begin xend End) - (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))))))) - (save-excursion - (save-restriction - (narrow-to-region xbeg xend) - (goto-char (point-min)) - (while (re-search-forward " +" nil :move) - (when (> (- (point) (line-beginning-position)) xminlen) - (replace-match "\n" ))))))) - -(defun xah-reformat-lines (&optional Width) - "Reformat current block or selection into short lines or 1 long line. -When called for the first time, change to one line. Second call change it to multi-lines. Repeated call toggles. -If `universal-argument' is called first, ask user to type max length of line. By default, it is 66. - -Note: this command is different from emacs `fill-region' or `fill-paragraph'. -This command never adds or delete non-whitespace chars. It only exchange whitespace sequence. - -URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html' -Created: 2016 -Version: 2025-03-25" - (interactive) - ;; 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 - (let (xisLong xwidth xbeg xend) - (setq xwidth (if Width Width (if current-prefix-arg (prefix-numeric-value current-prefix-arg) 66))) - (setq xisLong (if (eq last-command this-command) (get this-command 'is-long-p) nil)) - (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)))))) - (if current-prefix-arg - (xah-reformat-to-multi-lines xbeg xend xwidth) - (if xisLong - (xah-reformat-to-multi-lines xbeg xend xwidth) - (progn - (xah-reformat-whitespaces-to-one-space xbeg xend)))) - (put this-command 'is-long-p (not xisLong)))) - -(defun xah-reformat-to-sentence-lines () - "Reformat current block or selection into multiple lines by ending period. -Move cursor to the beginning of next text block. -After this command is called, press `xah-repeat-key' to repeat it. - -URL `http://xahlee.info/emacs/emacs/elisp_reformat_to_sentence_lines.html' -Created: 2020-12-02 -Version: 2025-03-25" - (interactive) - (let (xbeg xend) - (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)))))) - (save-restriction - (narrow-to-region xbeg xend) - (goto-char (point-min)) (while (search-forward "。" nil t) (replace-match "。\n")) - ;; (goto-char (point-min)) (while (search-forward " <a " nil t) (replace-match "\n<a ")) - ;; (goto-char (point-min)) (while (search-forward "</a> " nil t) (replace-match "</a>\n")) - (goto-char (point-min)) - (while (re-search-forward "\\([A-Za-z0-9]+\\)[ \t]*\n[ \t]*\\([A-Za-z0-9]+\\)" nil t) - (replace-match "\\1 \\2")) - (goto-char (point-min)) - (while (re-search-forward "\\([,]\\)[ \t]*\n[ \t]*\\([A-Za-z0-9]+\\)" nil t) - (replace-match "\\1 \\2")) - (goto-char (point-min)) - (while (re-search-forward " +" nil t) (replace-match " ")) - (goto-char (point-min)) - (while (re-search-forward "\\([.?!]\\) +\\([(0-9A-Za-z]+\\)" nil t) (replace-match "\\1\n\\2")) - (goto-char (point-max)) - (while (eq (char-before) 32) (delete-char -1)))) - (re-search-forward "\n+" nil :move) - (set-transient-map (let ((xkmap (make-sparse-keymap))) (define-key xkmap (kbd (if (boundp 'xah-repeat-key) xah-repeat-key "m")) this-command) xkmap)) - (set-transient-map (let ((xkmap (make-sparse-keymap))) (define-key xkmap (kbd "DEL") this-command) xkmap))) - -(defun xah-space-to-newline () - "Replace space sequence to a newline char in current block or selection. - -URL `http://xahlee.info/emacs/emacs/emacs_space_to_newline.html' -Created: 2017-08-19 -Version: 2025-03-25" - (interactive) - (let (xbeg xend) - (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)))))) - (save-restriction - (narrow-to-region xbeg xend) - (goto-char (point-min)) - (while (re-search-forward " +" nil t) - (replace-match "\n"))))) - -(defun xah-slash-to-backslash (&optional Begin End) - "Replace slash by backslash on current line or region. -Created: 2021-07-14 -Version: 2021-09-12" - (interactive) - (let (xbeg xend) - (if (and Begin End) - (setq xbeg Begin xend End) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (setq xbeg (line-beginning-position) xend (line-end-position)))) - (save-restriction - (narrow-to-region xbeg xend) - (let ((case-fold-search nil)) - (goto-char (point-min)) - (while (search-forward "/" nil t) - (replace-match "\\\\")))))) - -(defun xah-backslash-to-slash (&optional Begin End) - "Replace backslash by slash on current line or region. -Version: 2021-09-11" - (interactive) - (let (xbeg xend) - (if (and Begin End) - (setq xbeg Begin xend End) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (setq xbeg (line-beginning-position) xend (line-end-position)))) - (save-restriction - (narrow-to-region xbeg xend) - (let ((case-fold-search nil)) - (goto-char (point-min)) - (while (search-forward "\\" nil t) - (replace-match "/")))))) - -(defun xah-double-backslash (&optional Begin End) - "Replace backslash by two backslash on current line or region. -Version: 2021-11-09" - (interactive) - (let (xbeg xend) - (if (and Begin End) - (setq xbeg Begin xend End) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (setq xbeg (line-beginning-position) xend (line-end-position)))) - (save-restriction - (narrow-to-region xbeg xend) - (let ((case-fold-search nil)) - (goto-char (point-min)) - (while (search-forward "\\" nil t) - (replace-match "\\\\\\\\")))))) - -(defun xah-double-backslash-to-single (&optional Begin End) - "Replace double backslash by single backslash on current line or region. -Version: 2021-11-09" - (interactive) - (let (xbeg xend) - (if (and Begin End) - (setq xbeg Begin xend End) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (setq xbeg (line-beginning-position) xend (line-end-position)))) - (save-restriction - (narrow-to-region xbeg xend) - (let ((case-fold-search nil)) - (goto-char (point-min)) - (while (search-forward "\\\\" nil t) - (replace-match "\\\\")))))) - -(defun xah-slash-to-double-backslash (&optional Begin End) - "Replace slash by double backslash on current line or region. -Version: 2021-07-14" - (interactive) - (let (xbeg xend) - (if (and Begin End) - (setq xbeg Begin xend End) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (setq xbeg (line-beginning-position) xend (line-end-position)))) - (save-restriction - (narrow-to-region xbeg xend) - (let ((case-fold-search nil)) - (goto-char (point-min)) - (while (search-forward "/" nil t) - (replace-match "\\\\\\\\")))))) - -(defun xah-double-backslash-to-slash (&optional Begin End) - "Replace double backslash by slash on current line or region. -Version: 2021-07-14" - (interactive) - (let (xbeg xend) - (if (and Begin End) - (setq xbeg Begin xend End) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (setq xbeg (line-beginning-position) xend (line-end-position)))) - (save-restriction - (narrow-to-region xbeg xend) - (let ((case-fold-search nil)) - (goto-char (point-min)) - (while (search-forward "\\\\" nil t) - (replace-match "/")))))) - -(defun xah-comment-dwim () - "Toggle comment in programing language code. - -Like `comment-dwim', but toggle comment if cursor is not at end of line. -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. - -URL `http://xahlee.info/emacs/emacs/emacs_toggle_comment_by_line.html' -Created: 2016-10-25 -Version: 2023-07-10" - (interactive) - (if (region-active-p) - (comment-dwim nil) - (let ((xbegin (line-beginning-position)) - (xend (line-end-position))) - (if (eq xbegin xend) - (progn - (comment-dwim nil)) - (if (eq (point) xend) - (progn - (comment-dwim nil)) - (progn - (comment-or-uncomment-region xbegin xend) - (forward-line ))))))) - -(defun xah-quote-lines (QuoteL QuoteR Sep) - "Add quotes/brackets and separator (comma) to lines. -Act on current block or selection. - -For example, - - cat - dog - cow - -becomes - - \"cat\", - \"dog\", - \"cow\", - -or - - (cat) - (dog) - (cow) - -In lisp code, QuoteL QuoteR Sep are strings. - -URL `http://xahlee.info/emacs/emacs/emacs_quote_lines.html' -Created: 2020-06-26 -Version: 2025-03-25" - (interactive - (let ((xbrackets - '( - "\"QUOTATION MARK\"" - "'APOSTROPHE'" - "(paren)" - "{brace}" - "[square]" - "<greater>" - "`emacs'" - "`markdown`" - "~tilde~" - "=equal=" - "“curly double”" - "‘curly single’" - "‹french angle›" - "«french double angle»" - "「corner」" - "none" - "other" - )) - (xcomma '("comma ," "semicolon ;" "none" "other")) - xbktChoice xsep xsepChoice xquoteL xquoteR) - (let ((completion-ignore-case t)) - (setq xbktChoice (completing-read "Quote to use:" xbrackets nil t nil nil (car xbrackets))) - (setq xsepChoice (completing-read "line separator:" xcomma nil t nil nil (car xcomma)))) - (cond - ((string-equal xbktChoice "none") - (setq xquoteL "" xquoteR "")) - ((string-equal xbktChoice "other") - (let ((xx (read-string "Enter 2 chars, for begin/end quote:"))) - (setq xquoteL (substring xx 0 1) - xquoteR (substring xx 1 2)))) - (t (setq xquoteL (substring xbktChoice 0 1) - xquoteR (substring xbktChoice -1)))) - (setq xsep - (cond - ((string-equal xsepChoice "comma ,") ",") - ((string-equal xsepChoice "semicolon ;") ";") - ((string-equal xsepChoice "none") "") - ((string-equal xsepChoice "other") (read-string "Enter separator:")) - (t xsepChoice))) - (list xquoteL xquoteR xsep))) - (let (xbeg xend (xquoteL QuoteL) (xquoteR QuoteR) (xsep Sep)) - (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)))))) - (save-excursion - (save-restriction - (narrow-to-region xbeg xend) - (goto-char (point-min)) - (catch 'EndReached - (while t - (skip-chars-forward "\t ") - (insert xquoteL) - (end-of-line) - (insert xquoteR xsep) - (if (eq (point) (point-max)) - (throw 'EndReached t) - (forward-char)))))))) - -(defun xah-escape-quotes (Begin End) - "Add slash before double quote in current line or selection. -Double quote is codepoint 34. -See also: `xah-unescape-quotes' -URL `http://xahlee.info/emacs/emacs/elisp_escape_quotes.html' -Version: 2017-01-11" - (interactive - (if (region-active-p) - (list (region-beginning) (region-end)) - (list (line-beginning-position) (line-end-position)))) - (save-excursion - (save-restriction - (narrow-to-region Begin End) - (goto-char (point-min)) - (while (search-forward "\"" nil t) - (replace-match "\\\"" t t))))) - -(defun xah-unescape-quotes (&optional Begin End) - "Replace 「\\\"」 by 「\"」 in current line or selection. -See also: `xah-escape-quotes' - -URL `http://xahlee.info/emacs/emacs/elisp_escape_quotes.html' -Created: 2017-01-11 -Version: 2023-11-02" - (interactive) - (let (xbeg xend) - (if (and Begin End) - (setq xbeg Begin xend End) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (setq xbeg (line-beginning-position) xend (line-end-position)))) - (save-excursion - (save-restriction - (narrow-to-region xbeg xend) - (goto-char (point-min)) - (while (search-forward "\\\"" nil t) - (replace-match "\"" t t)))))) - -(defun xah-cycle-hyphen-lowline-space (&optional Begin End) - "Cycle {hyphen lowline space} chars. - -The region to work on is by this order: - 1. if there is a selection, use that. - 2. If cursor is in a string quote or any type of bracket, and is within current line, work on that region. - 3. else, work on current line. - -After this command is called, press `xah-repeat-key' to repeat it. - -URL `http://xahlee.info/emacs/emacs/elisp_change_space-hyphen_underscore.html' -Created: 2019-02-12 -Version: 2025-01-24" - (interactive) - ;; this function sets a property 'state. Possible values are 0 to length of xcharArray. - (let (xbeg xend xlen - (xcharArray ["-" "_" " "]) - (xregionWasActive-p (region-active-p)) - (xnowState (if (eq last-command this-command) (get 'xah-cycle-hyphen-lowline-space 'state) 0)) - xchangeTo) - (setq - xlen (length xcharArray) - xchangeTo (elt xcharArray xnowState)) - (if (and Begin End) - (setq xbeg Begin xend End) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (let ((xskipChars "^\"<>(){}[]“”‘’‹›«»「」『』【】〖〗《》〈〉〔〕()")) - (skip-chars-backward xskipChars (line-beginning-position)) - (setq xbeg (point)) - (skip-chars-forward xskipChars (line-end-position)) - (setq xend (point)) - (push-mark xbeg)))) - (save-excursion - (save-restriction - (narrow-to-region xbeg xend) - (goto-char (point-min)) - (while (re-search-forward (elt xcharArray (% (+ xnowState 2) xlen)) (point-max) 1) - (replace-match xchangeTo t t)))) - (when (or (string-equal xchangeTo " ") xregionWasActive-p) - (goto-char xend) - (push-mark xbeg) - (setq deactivate-mark nil)) - (put 'xah-cycle-hyphen-lowline-space 'state (% (+ xnowState 1) xlen))) - (set-transient-map (let ((xkmap (make-sparse-keymap))) (define-key xkmap (kbd (if (boundp 'xah-repeat-key) xah-repeat-key "m")) this-command) xkmap))) - -(defun xah-copy-file-path (&optional DirPathOnlyQ) - "Copy current buffer file path or dired path. -Result is full path. -If `universal-argument' is called first, copy only the dir path. - -If in dired, copy the current or marked files. - -If a buffer is not file and not dired, copy value of `default-directory'. - -URL `http://xahlee.info/emacs/emacs/emacs_copy_file_path.html' -Created: 2018-06-18 -Version: 2024-12-15" - (interactive "P") - (let ((xfpath - (if (eq major-mode 'dired-mode) - (progn - (let ((xresult (mapconcat #'identity - (dired-get-marked-files) "\n"))) - (if (equal (length xresult) 0) - (progn default-directory) - (progn xresult)))) - (or buffer-file-name default-directory)))) - (kill-new - (if DirPathOnlyQ - (progn - (message "Directory copied: %s" (file-name-directory xfpath)) - (file-name-directory xfpath)) - (progn - (message "File path copied: %s" xfpath) - xfpath))))) - -(defun xah-delete-current-text-block () - "Delete the current text block plus blank lines, or selection, and copy to `kill-ring'. - -If cursor is between blank lines, delete following blank lines. - -URL `http://xahlee.info/emacs/emacs/emacs_delete_block.html' -Created: 2017-07-09 -Version: 2024-10-07" - (interactive) - (let (xbeg xend (xp (point))) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (progn - (setq xbeg - (if (re-search-backward "\n[ \t]*\n+" nil :move) - (match-end 0) - (point))) - (goto-char xp) - (setq xend (if (re-search-forward "\n[ \t]*\n+" nil :move) - (match-end 0) - (point-max))))) - (kill-region xbeg xend))) - -(defun xah-copy-to-register-1 () - "Copy current line or selection to register 1. - -See also: -`xah-copy-to-register-1' -`xah-append-to-register-1' -`xah-paste-from-register-1' -`xah-clear-register-1' - -URL `http://xahlee.info/emacs/emacs/elisp_copy-paste_register_1.html' -Created: 2012-07-17 -Version: 2023-08-05" - (interactive) - (let (xbeg xend) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (setq xbeg (line-beginning-position) xend (line-end-position))) - (copy-to-register ?1 xbeg xend) - (message "Copied to register 1: [%s]." (buffer-substring xbeg xend)))) - -(defun xah-append-to-register-1 () - "Append current line or selection to register 1. -When no selection, append current line, with newline char. - -See also: -`xah-copy-to-register-1' -`xah-append-to-register-1' -`xah-paste-from-register-1' -`xah-clear-register-1' - -URL `http://xahlee.info/emacs/emacs/emacs_copy_append.html' -Created: 2015-12-08 -Version: 2023-08-05" - (interactive) - (let (xbeg xend) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (setq xbeg (line-beginning-position) xend (line-end-position))) - (append-to-register ?1 xbeg xend) - (with-temp-buffer (insert "\n") - (append-to-register ?1 (point-min) (point-max))) - (message "Appended to register 1: [%s]." (buffer-substring xbeg xend)))) - -(defun xah-paste-from-register-1 () - "Paste text from register 1. - -See also: -`xah-copy-to-register-1' -`xah-append-to-register-1' -`xah-paste-from-register-1' -`xah-clear-register-1' - -URL `http://xahlee.info/emacs/emacs/elisp_copy-paste_register_1.html' -Created: 2015-12-08 -Version: 2023-04-07" - (interactive) - (when (region-active-p) - (delete-region (region-beginning) (region-end))) - (insert-register ?1 t)) - -(defun xah-clear-register-1 () - "Clear register 1. - -See also: -`xah-copy-to-register-1' -`xah-append-to-register-1' -`xah-paste-from-register-1' -`xah-clear-register-1' - -URL `http://xahlee.info/emacs/emacs/elisp_copy-paste_register_1.html' -Created: 2015-12-08 -Version: 2023-04-07" - (interactive) - (progn - (copy-to-register ?1 (point-min) (point-min)) - (message "Cleared register 1."))) - -;; HHHH------------------------------ -;; insertion commands - -(defun xah-insert-date () - "Insert current date time. -Insert date in this format: yyyy-mm-dd. -If `universal-argument' is called first, prompt for a format to use. -If there is selection, delete it first. - -URL `http://xahlee.info/emacs/emacs/elisp_insert-date-time.html' -Created: 2013-05-10 -Version: 2025-03-07" - (interactive) - (let (xmenu xstyle) - (setq - xmenu - '(("ISO date • 2018-04-12" . (format-time-string "%Y-%m-%d")) - ("all digits datetime • 20180412224611" . (format-time-string "%Y%m%d%H%M%S")) - ("date _ time digits • 2018-04-12_224611" . (format-time-string "%Y-%m-%d_%H%M%S")) - ("ISO datetime full • 2018-04-12T22:46:11-07:00" . - (concat - (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")))) - ("ISO datetime w space • 2018-04-12 22:46:11-07:00" . - (concat - (format-time-string "%Y-%m-%d %T") - (funcall - (lambda (xx) (format "%s:%s" (substring xx 0 3) (substring xx 3 5))) - (format-time-string "%z")))) - ("ISO date + weekday • 2018-04-12 Thursday" . (format-time-string "%Y-%m-%d %A")) - ("USA date + weekday • Thursday, April 12, 2018" . (format-time-string "%A, %B %d, %Y")) - ("USA date + weekday abbrev • Thu, Apr 12, 2018" . (format-time-string "%a, %b %d, %Y")) - ("USA date • April 12, 2018" . (format-time-string "%B %d, %Y")) - ("USA date abbrev • Apr 12, 2018" . (format-time-string "%b %d, %Y"))) - - xstyle - (if current-prefix-arg - (let ((completion-ignore-case t)) - (completing-read "Style:" xmenu nil t nil nil (caar xmenu))) - (caar xmenu))) - (when (region-active-p) (delete-region (region-beginning) (region-end))) - (insert (eval (cdr (assoc xstyle xmenu)))))) - -(defun xah-insert-bracket-pair (LBracket RBracket &optional WrapMethod) - "Insert brackets around selection, word, at point, and maybe move cursor in between. - - LBracket and RBracket are strings. WrapMethod must be either `line' or `block'. `block' means between empty lines. - -• If there is a active region, wrap around region. -Else -• If WrapMethod is `line', wrap around line. -• If WrapMethod is `block', wrap around block. -Else -• If cursor is at beginning of line and its not empty line and contain at least 1 space, wrap around the line. -• If cursor is at end of a word or buffer, one of the following will happen: - xyz▮ → xyz(▮) - xyz▮ → (xyz▮) if in one of the lisp modes. -• wrap brackets around word if any. e.g. xy▮z → (xyz▮). Or just (▮) - -URL `http://xahlee.info/emacs/emacs/elisp_insert_brackets_by_pair.html' -Created: 2017-01-17 -Version: 2025-03-25" - (if (region-active-p) - (progn - (let ((xbeg (region-beginning)) (xend (region-end))) - (goto-char xend) (insert RBracket) - (goto-char xbeg) (insert LBracket) - (goto-char (+ xend 2)))) - (let (xbeg xend) - (cond - ((eq WrapMethod 'line) - (setq xbeg (line-beginning-position) xend (line-end-position)) - (goto-char xend) - (insert RBracket) - (goto-char xbeg) - (insert LBracket) - (goto-char (+ xend (length LBracket)))) - ((eq WrapMethod 'block) - (save-excursion - (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)))))) - (goto-char xend) - (insert RBracket) - (goto-char xbeg) - (insert LBracket) - (goto-char (+ xend (length LBracket))))) - ( ; do line. line must contain space - (and - (eq (point) (line-beginning-position)) - (not (eq (line-beginning-position) (line-end-position)))) - (insert LBracket) - (end-of-line) - (insert RBracket)) - ((and - (or ; cursor is at end of word or buffer. i.e. xyz▮ - (looking-at "[^-_[:alnum:]]") - (eq (point) (point-max))) - (not (or - (eq major-mode 'xah-elisp-mode) - (eq major-mode 'emacs-lisp-mode) - (eq major-mode 'lisp-mode) - (eq major-mode 'lisp-interaction-mode) - (eq major-mode 'common-lisp-mode) - (eq major-mode 'clojure-mode) - (eq major-mode 'xah-clojure-mode) - (eq major-mode 'scheme-mode)))) - (progn - (setq xbeg (point) xend (point)) - (insert LBracket RBracket) - (search-backward RBracket))) - (t (progn - ;; wrap around “word”. basically, want all alphanumeric, plus hyphen and underscore, but don't want space or punctuations. Also want chinese chars - ;; 我有一帘幽梦,不知与谁能共。多少秘密在其中,欲诉无人能懂。 - (skip-chars-backward "-_[:alnum:]") - (setq xbeg (point)) - (skip-chars-forward "-_[:alnum:]") - (setq xend (point)) - (goto-char xend) - (insert RBracket) - (goto-char xbeg) - (insert LBracket) - (goto-char (+ xend (length LBracket))))))))) - -(defun xah-insert-paren () (interactive) (xah-insert-bracket-pair "(" ")")) -(defun xah-insert-square-bracket () (interactive) (xah-insert-bracket-pair "[" "]")) -(defun xah-insert-brace () (interactive) (xah-insert-bracket-pair "{" "}")) - -(defun xah-insert-ascii-double-quote () (interactive) (xah-insert-bracket-pair "\"" "\"")) -(defun xah-insert-ascii-single-quote () (interactive) (xah-insert-bracket-pair "'" "'")) -(defun xah-insert-ascii-angle-bracket () (interactive) (xah-insert-bracket-pair "<" ">")) - -(defun xah-insert-emacs-quote () (interactive) (xah-insert-bracket-pair "`" "'")) -(defun xah-insert-markdown-quote () (interactive) (xah-insert-bracket-pair "`" "`")) -(defun xah-insert-markdown-triple-quote () (interactive) (xah-insert-bracket-pair "```\n" "\n```")) - -(defun xah-insert-double-curly-quote“” () (interactive) (xah-insert-bracket-pair "“" "”")) -(defun xah-insert-curly-single-quote‘’ () (interactive) (xah-insert-bracket-pair "‘" "’")) -(defun xah-insert-single-angle-quote‹› () (interactive) (xah-insert-bracket-pair "‹" "›")) -(defun xah-insert-double-angle-quote«» () (interactive) (xah-insert-bracket-pair "«" "»")) - -(defun xah-insert-corner-bracket「」 () (interactive) (xah-insert-bracket-pair "「" "」")) -(defun xah-insert-white-corner-bracket『』 () (interactive) (xah-insert-bracket-pair "『" "』")) -(defun xah-insert-angle-bracket〈〉 () (interactive) (xah-insert-bracket-pair "〈" "〉")) -(defun xah-insert-double-angle-bracket《》 () (interactive) (xah-insert-bracket-pair "《" "》")) -(defun xah-insert-white-lenticular-bracket〖〗 () (interactive) (xah-insert-bracket-pair "〖" "〗")) -(defun xah-insert-black-lenticular-bracket【】 () (interactive) (xah-insert-bracket-pair "【" "】")) -(defun xah-insert-tortoise-shell-bracket〔〕 () (interactive) (xah-insert-bracket-pair "〔" "〕")) -(defun xah-insert-deco-angle-bracket❮❯ () (interactive) (xah-insert-bracket-pair "❮" "❯")) -(defun xah-insert-deco-angle-fat-bracket❰❱ () (interactive) (xah-insert-bracket-pair "❰" "❱")) - -(defun xah-insert-hyphen () - "Insert a HYPHEN-MINUS character." - (interactive) - (insert "-")) - -(defun xah-insert-low-line () - "Insert a LOW LINE character." - (interactive) - (insert "_")) - -(defun xah-insert-string-assignment () - "Insert =\"\"" - (interactive) - (progn (insert "=\"\"") - (left-char))) - -(defun xah-insert-space-before () - "Insert space before cursor." - (interactive) - (insert " ")) - -(defun xah-insert-space-after () - "Insert space after cursor" - (interactive) - (insert " ") - (left-char)) - -(defun xah-insert-seperator () - "Insert a visual seperator line." - (interactive) - (cond - ((and buffer-file-name (string-equal "html" (file-name-extension buffer-file-name))) (insert "<hr />\n")) - ((not comment-start) - (insert "\nHHHH------------------------------\n")) - (t (insert "\nHHHH------------------------------\n") - (backward-char) - (comment-line 1)))) - -(defvar xah-unicode-list nil - "A alist. -Each item is (prompStr . xString). Used by `xah-insert-unicode'. -prompStr is used for prompt. -xString is is the char to insert. -xString can be multiple chars or any string. -") - -(setq - xah-unicode-list - '( - ;; - ("smile beaming 😊" . "😊") - ("tears of joy" . "😂") - ("hug 🤗" . "🤗") - ("heart eyes 😍" . "😍") - ("heart face 🥰" . "🥰") - ("angry 😠" . "😠") - ("vomit 🤮" . "🤮") - ("thumb up 👍" . "👍") - ("thumb down 👎" . "👎") - ("tv 📺" . "📺") - ("checkmark ✅" . "✅") - ("new 🆕" . "🆕") - ("glowing star 🌟" . "🌟") - ("star ⭐" . "⭐") - ("sparkles ✨" . "✨") - ("rocket 🚀" . "🚀") - ("sun 🌞" . "🌞") - ("heart 🧡" . "🧡") - ("clown 🤡" . "🤡") - ("large circle" . "⭕") - ("cross ❌" . "❌") - ("red triangle 🔺" . "🔺") - ("diamond 💠" . "💠") - ("square ⬛" . "⬛") - ("script 📜" . "📜") - ("package 📦" . "📦") - ("cursor ▮" . "▮") - ("music 🎵" . "🎵") - - ("dagger †" . "†") - ("double dagger ‡" . "‡") - - ("double angle bracket" . "《》") - ("black lenticular bracket" . "【】") - ("corner-bracket" . "「」") - ("tortoise shell bracket" . "〔〕") - ("angle bracket" . "〈〉") - ("double angle quote" . "«»") - - ("bullet •" . "•") - ("diamond ◆" . "◆") - ("...ellipsis …" . "…") - ("nbsp non breaking space" . " ") - ("chinese comma 、" . "、") - ("emdash —" . "—") - ("fullwidth ampersand &" . "&") - ("left arrow ←" . "←") - ("right arrow →" . "→") - ("up arrow ↑" . "↑") - ("down arrow ↓" . "↓") - ("f hook ƒ" . "ƒ") - ("chinese space" . " ") - - ;; - )) - -(defun xah-insert-unicode () - "Insert a unicode from a custom list `xah-unicode-list'. -URL `http://xahlee.info/emacs/emacs/emacs_insert_unicode.html' -Created: 2021-01-05 -Version: 2023-09-19" - (interactive) - (let ((xkey - (let ((completion-ignore-case t)) - (completing-read "Insert:" xah-unicode-list nil t)))) - (insert (cdr (assoc xkey xah-unicode-list))))) - -;; HHHH------------------------------ -;; text selection - -(defun xah-select-block () - "Select the current/next block plus 1 blankline. -If region is active, extend selection downward by block. - -URL `http://xahlee.info/emacs/emacs/emacs_select_text_block.html' -Created: 2019-12-26 -Version: 2023-11-14" - (interactive) - (if (region-active-p) - (re-search-forward "\n[ \t]*\n[ \t]*\n*" nil :move) - (progn - (skip-chars-forward " \n\t") - (when (re-search-backward "\n[ \t]*\n" nil :move) - (goto-char (match-end 0))) - (push-mark (point) t t) - (re-search-forward "\n[ \t]*\n" nil :move)))) - -(defun xah-select-line () - "Select current line. If region is active, extend selection downward by line. -If `visual-line-mode' is on, consider line as visual line. - -URL `http://xahlee.info/emacs/emacs/emacs_select_line.html' -Created: 2017-11-01 -Version: 2023-11-14" - (interactive) - (if (region-active-p) - (if visual-line-mode - (let ((xbeg (point))) - (end-of-visual-line 1) - (when (eq xbeg (point)) - (end-of-visual-line 2))) - (progn - (forward-line 1) - (end-of-line))) - (if visual-line-mode - (progn (beginning-of-visual-line) - (push-mark (point) t t) - (end-of-visual-line)) - (progn - (push-mark (line-beginning-position) t t) - (end-of-line))))) - -(defun xah-extend-selection () - "Select the current word, bracket/quote expression, or expand selection. -Subsequent calls expands the selection. - -when there is no selection, -• If cursor is on any type of bracket (including parenthesis, quotation mark), select whole bracketed thing including bracket -• else, select current word. - -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. - -URL `http://xahlee.info/emacs/emacs/emacs_extend_selection.html' -Created: 2020-02-04 -Version: 2023-11-14" - (interactive) - - (cond - ((region-active-p) - (let ((xbeg (region-beginning)) (xend (region-end))) - (goto-char xbeg) - (cond - ((looking-at "\\s(") - (if (eq (nth 0 (syntax-ppss)) 0) - (progn - ;; (message "debug: left bracket, depth 0.") - (end-of-line) ; select current line - (push-mark (line-beginning-position) t t)) - (progn - ;; (message "debug: left bracket, depth not 0") - (up-list -1 t t) - (mark-sexp)))) - ((eq xbeg (line-beginning-position)) - (progn - (goto-char xbeg) - (let ((xfirstLineEndPos (line-end-position))) - (cond - ((eq xend xfirstLineEndPos) - (progn - ;; (message "debug: exactly 1 line. extend to next whole line." ) - (forward-line 1) - (end-of-line))) - ((< xend xfirstLineEndPos) - (progn - ;; (message "debug: less than 1 line. complete the line." ) - (end-of-line))) - ((> xend xfirstLineEndPos) - (progn - ;; (message "debug: beginning of line, but end is greater than 1st end of line" ) - (goto-char xend) - (if (eq (point) (line-end-position)) - (progn - ;; (message "debug: exactly multiple lines" ) - (forward-line 1) - (end-of-line)) - (progn - ;; (message "debug: multiple lines but end is not eol. make it so" ) - (goto-char xend) - (end-of-line))))) - (t (error "%s: logic error 42946" real-this-command)))))) - ((and (> (point) (line-beginning-position)) (<= (point) (line-end-position))) - (progn - ;; (message "debug: less than 1 line" ) - (end-of-line) ; select current line - (push-mark (line-beginning-position) t t))) - (t - ;; (message "debug: last resort" ) - nil)))) - - ((looking-at "\\s(") - ;; (message "debug: left bracket") - (mark-sexp)) - - ((looking-at "\\s)") - ;; (message "debug: right bracket") - (backward-up-list) (mark-sexp)) - - ((looking-at "\\s\"") - ;; (message "debug: string quote") - (mark-sexp)) - - ((looking-at "[ \t\n]") - ;; (message "debug: is white space") - (skip-chars-backward " \t\n") - (push-mark) - (skip-chars-forward " \t\n") - (setq mark-active t)) - - ((looking-at "[-_a-zA-Z0-9]") - ;; (message "debug: left is word or symbol") - (skip-chars-backward "-_a-zA-Z0-9") - (push-mark) - (skip-chars-forward "-_a-zA-Z0-9") - (setq mark-active t)) - - ;; ((and (looking-at "[[:blank:]]") - ;; (prog2 (backward-char) (looking-at "[[:blank:]]") (forward-char))) - ;; ;; (message "debug: left and right both space" ) - ;; (skip-chars-backward "[[:blank:]]") (push-mark (point) t t) - ;; (skip-chars-forward "[[:blank:]]")) - - ((and (looking-at "\n") - (eq (char-before) 10)) - ;; (message "debug: left and right both newline") - (skip-chars-forward "\n") - (push-mark (point) t t) - (re-search-forward "\n[ \t]*\n")) - - (t - ;; (message "debug: just mark sexp" ) - (mark-sexp) - (exchange-point-and-mark)))) - -(defun xah-select-text-in-quote () - "Select text between the nearest left and right delimiters. -Delimiters here includes QUOTATION MARK, GRAVE ACCENT, and anything in variable `xah-brackets'. -This command ignores nesting. For example, if text is -「(a(b)c▮)」 -the selected char is 「c」, not 「a(b)c」. - -URL `http://xahlee.info/emacs/emacs/emacs_select_quote_text.html' -Created: 2020-11-24 -Version: 2023-11-14" - (interactive) - (let ((xskipChars (concat "^\"`" (mapconcat #'identity xah-brackets "")))) - (skip-chars-backward xskipChars) - (push-mark (point) t t) - (skip-chars-forward xskipChars))) - -(defun xah-cut-text-in-quote () - "Cut text between the nearest left and right delimiters. -See `xah-select-text-in-quote' - -Created: 2023-07-23 -Version: 2024-10-02" - (interactive) - (let (xbeg xend - (xskipChars (concat "^\"`" (mapconcat #'identity xah-brackets "")))) - (skip-chars-backward xskipChars) - (setq xbeg (point)) - (skip-chars-forward xskipChars) - (setq xend (point)) - (kill-region xbeg xend))) - -;; HHHH------------------------------ -;; misc - -(defvar xah-fly-switch-buffer-map nil "repeat key map for `xah-next-user-buffer' etc.") -(setq xah-fly-switch-buffer-map - (let ((xkmap (make-sparse-keymap))) - (define-key xkmap (kbd "<up>") 'xah-previous-emacs-buffer) - (define-key xkmap (kbd "<down>") 'xah-next-emacs-buffer) - (define-key xkmap (kbd "<left>") 'xah-previous-user-buffer) - (define-key xkmap (kbd "<right>") 'xah-next-user-buffer) - xkmap)) - -(defun xah-user-buffer-p () - "Return t if current buffer is a user buffer, else nil. -A user buffer has buffer name NOT starts with * or space, and is not dired mode, help mode, etc. -This function is used by buffer switching command and close buffer command, so that next buffer shown is a user buffer. -You can override this function to get your idea of “user buffer”. -Created: 2016-06-18 -Version: 2024-09-23" - (interactive) - (cond - ((string-match "^\*" (buffer-name)) nil) - ((eq major-mode 'dired-mode) nil) - ((eq major-mode 'eww-mode) nil) - ((eq major-mode 'help-mode) nil) - (t t))) - -(defun xah-next-user-buffer () - "Switch to the next user buffer. -User Buffer here is determined by `xah-user-buffer-p'. - -Press left or right arrow key to switch to prev next user. -Press up or down arrow to switch to prev next emacs buffer. -Any other key to exit. - -URL `http://xahlee.info/emacs/emacs/elisp_next_prev_user_buffer.html' -Created: 2016-06-19 -Version: 2024-09-23" - (interactive) - (next-buffer) - (let ((i 0)) - (while (< i 30) - (if (not (xah-user-buffer-p)) - (progn (next-buffer) - (setq i (1+ i))) - (progn (setq i 100))))) - (set-transient-map xah-fly-switch-buffer-map)) - -(defun xah-previous-user-buffer () - "Switch to the previous user buffer. -User Buffer here is determined by `xah-user-buffer-p'. - -Press left or right arrow key to switch to prev next user. -Press up or down arrow to switch to prev next emacs buffer. -Any other key to exit. - -URL `http://xahlee.info/emacs/emacs/elisp_next_prev_user_buffer.html' -Created: 2016-06-19 -Version: 2024-05-01" - (interactive) - (previous-buffer) - (let ((i 0)) - (while (< i 29) - (if (not (xah-user-buffer-p)) - (progn (previous-buffer) - (setq i (1+ i))) - (progn (setq i 100))))) - (set-transient-map xah-fly-switch-buffer-map)) - -(defun xah-next-emacs-buffer () - "Switch to the next emacs buffer. -Emacs buffer here means `xah-user-buffer-p' return nil. - -Press left or right arrow key to switch to prev next user. -Press up or down arrow to switch to prev next emacs buffer. -Any other key to exit. - -URL `http://xahlee.info/emacs/emacs/elisp_next_prev_user_buffer.html' -Created: 2013-05-22 -Version: 2024-09-16" - (interactive) - (next-buffer) - (let ((i 0)) - (while (and (xah-user-buffer-p) (< i 20)) - (setq i (1+ i)) (next-buffer))) - (set-transient-map xah-fly-switch-buffer-map)) - -(defun xah-previous-emacs-buffer () - "Switch to the previous emacs buffer. -Emacs buffer here means `xah-user-buffer-p' return nil. - -Press left or right arrow key to switch to prev next user. -Press up or down arrow to switch to prev next emacs buffer. -Any other key to exit. - -URL `http://xahlee.info/emacs/emacs/elisp_next_prev_user_buffer.html' -Created: 2013-05-22 -Version: 2024-09-16" - (interactive) - (previous-buffer) - (let ((i 0)) - (while (and (xah-user-buffer-p) (< i 20)) - (setq i (1+ i)) (previous-buffer))) - (set-transient-map xah-fly-switch-buffer-map)) - -(defun xah-new-empty-buffer () - "Create a new empty buffer. -Returns the buffer object. -New buffer is named untitled, untitled<2>, etc. - -Warning: new buffer is not prompted for save when killed, see `kill-buffer'. -Or manually `save-buffer' - -URL `http://xahlee.info/emacs/emacs/emacs_new_empty_buffer.html' -Created: 2017-11-01 -Version: 2022-04-05" - (interactive) - (let ((xbuf (generate-new-buffer "untitled"))) - (switch-to-buffer xbuf) - (funcall initial-major-mode) - xbuf - )) - -(declare-function minibuffer-keyboard-quit "delsel" ()) -(declare-function org-edit-src-save "org-src" ()) - -(defcustom xah-recently-closed-buffers-max 40 "The maximum length for `xah-recently-closed-buffers'." - :type 'integer) - -(defvar xah-recently-closed-buffers nil "A Alist of recently closed buffers. -Each element is (bufferName . filePath). -The max number to track is controlled by the variable `xah-recently-closed-buffers-max'.") - -(defun xah-add-to-recently-closed (&optional BufferName BufferFileName) - "Add to `xah-recently-closed-buffers'. -Version: 2023-03-02" - (let ((xbn (if BufferName BufferName (buffer-name))) - (xbfn (if BufferFileName BufferFileName buffer-file-name))) - (setq xah-recently-closed-buffers (cons (cons xbn xbfn) xah-recently-closed-buffers))) - (when (> (length xah-recently-closed-buffers) xah-recently-closed-buffers-max) - (setq xah-recently-closed-buffers (butlast xah-recently-closed-buffers 1)))) - -(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") - -(setq xah-create-buffer-backup t) - -(defvar xah-temp-dir-path nil "Path to temp dir used by xah commands. -by default, the value is dir named temp at `user-emacs-directory'. -Version: 2023-03-21") - -(setq xah-temp-dir-path (concat user-emacs-directory "temp/")) - -(defun xah-close-current-buffer () - "Close the current buffer with possible backup. - -• If the buffer is a file and not modified, kill it. If is modified, do nothing. Print a message. -• 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. - -If `universal-argument' is called first, call `kill-buffer'. (this is useful to force kill.) - -If the buffer is a file, add the path to the list `xah-recently-closed-buffers'. - -URL `http://xahlee.info/emacs/emacs/elisp_close_buffer_open_last_closed.html' -Created: 2016-06-19 -Version: 2024-11-09" - (interactive) - (widen) - (cond - (current-prefix-arg (kill-buffer)) - ;; ((eq major-mode 'minibuffer-inactive-mode) (minibuffer-keyboard-quit)) - ;; ((active-minibuffer-window) (minibuffer-keyboard-quit)) - ((minibufferp (current-buffer)) (minibuffer-keyboard-quit)) - - ((eq major-mode 'dired-mode) - (xah-add-to-recently-closed (buffer-name) default-directory) - (kill-buffer)) - - ((and buffer-file-name (not (buffer-modified-p))) - (xah-add-to-recently-closed (buffer-name) buffer-file-name) - (kill-buffer)) - - ((and buffer-file-name (buffer-modified-p)) - (message "buffer file modified. Save it first.\n%s" buffer-file-name)) - ((and xah-create-buffer-backup (not buffer-file-name) (xah-user-buffer-p) (not (eq (point-max) 1))) - (let ((xnewName (format "%suntitled_%s_%x.txt" - xah-temp-dir-path - (format-time-string "%Y%m%d_%H%M%S") - (random #xfffff)))) - (when (not (file-exists-p xah-temp-dir-path)) (make-directory xah-temp-dir-path)) - (write-region (point-min) (point-max) xnewName) - (xah-add-to-recently-closed (buffer-name) xnewName) - (kill-buffer))) - (t (kill-buffer)))) - -(defun xah-open-last-closed () - "Open the last closed file. -URL `http://xahlee.info/emacs/emacs/elisp_close_buffer_open_last_closed.html' -Created: 2016-06-19 -Version: 2022-03-22" - (interactive) - (if (> (length xah-recently-closed-buffers) 0) - (find-file (cdr (pop xah-recently-closed-buffers))) - (progn (message "No recently close buffer in this session.")))) - -(defun xah-open-recently-closed () - "Open recently closed file. -Prompt for a choice. - -URL `http://xahlee.info/emacs/emacs/elisp_close_buffer_open_last_closed.html' -Created: 2016-06-19 -Version: 2023-09-19" - (interactive) - (find-file - (let ((completion-ignore-case t)) - (completing-read - "Open:" - (mapcar (lambda (f) (cdr f)) xah-recently-closed-buffers) - nil t - )))) - -(defun xah-list-recently-closed () - "List recently closed file. - -URL `http://xahlee.info/emacs/emacs/elisp_close_buffer_open_last_closed.html' -Version: 2016-06-19" - (interactive) - (let ((xbuf (generate-new-buffer "*recently closed*"))) - (switch-to-buffer xbuf) - (mapc (lambda (xf) (insert (cdr xf) "\n")) - xah-recently-closed-buffers))) - -(defvar xah-open-file-at-cursor-pre-hook nil "Hook for `xah-open-file-at-cursor'. -Functions in the hook is called in order, each given the raw input text (path) as arg. -The first return non-nil, its value is given to `xah-open-file-at-cursor' as input. rest functions in hook is ignored. -This is useful for transforming certain url into file path. e.g. change -http://xahlee.info/emacs/index.html -to -C:/Users/xah/web/xahlee_info/emacs/index.html -, so instead of opening in browser, it opens in emacs as file.") - -(defun xah-open-file-at-cursor () - "Open the file path under cursor. - -• If there is selection, use it for path. -• Path can be {relative, full path, URL}. -• If the path starts with 「https*://」, open the URL in browser. -• 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. - -If path does not have a file extension, automatically try with .el for elisp files. - -See also `xah-open-file-at-cursor-pre-hook'. - -This command is similar to `find-file-at-point' but without prompting for confirmation. - -URL `http://xahlee.info/emacs/emacs/emacs_open_file_path_fast.html' -Created: 2020-10-17 -Version: 2024-09-25" - (interactive) - (let (xinput xinput2 xpath) - (setq xinput (if (region-active-p) - (buffer-substring-no-properties (region-beginning) (region-end)) - (let ((xp0 (point)) xbeg xend - (xpathStops "^  \t\n\"`'‘’“”|()[]{}「」<>〔〕〈〉《》【】〖〗«»‹›❮❯❬❭〘〙·。\\")) - (skip-chars-backward xpathStops) - (setq xbeg (point)) - (goto-char xp0) - (skip-chars-forward xpathStops) - (setq xend (point)) - (goto-char xp0) - (buffer-substring-no-properties xbeg xend)))) - (setq xinput2 (if (> (length xah-open-file-at-cursor-pre-hook) 0) - (let ((xprehook (run-hook-with-args-until-success 'xah-open-file-at-cursor-pre-hook xinput))) - (if xprehook xprehook xinput)) - xinput)) - - (setq xpath - (cond - ((string-match "\\`file:///[A-Za-z]:/" xinput2) (substring xinput2 8)) - ((string-match "\\`file://[A-Za-z]:/" xinput2) (substring xinput2 7)) - (t xinput2))) - - (if (string-match-p "\\`https?://" xpath) - (browse-url xpath) - (let ((xpathNoQ - (let ((xHasQuery (string-match "\?[a-z]+=" xpath))) - (if xHasQuery - (substring xpath 0 xHasQuery) - xpath)))) - (cond - ((string-match "#" xpathNoQ) - (let ((xfpath (substring xpathNoQ 0 (match-beginning 0))) - (xfractPart (substring xpathNoQ (1+ (match-beginning 0))))) - (if (file-exists-p xfpath) - (progn - (find-file xfpath) - (goto-char (point-min)) - (search-forward xfractPart)) - (progn - (message "File does not exist. Created at\n%s" xfpath) - (find-file xfpath))))) - ((string-match "\\`\\(.+?\\):\\([0-9]+\\)\\(:[0-9]+\\)?\\'" xpathNoQ) - (let ((xfpath (match-string-no-properties 1 xpathNoQ)) - (xlineNum (string-to-number (match-string-no-properties 2 xpathNoQ)))) - (if (file-exists-p xfpath) - (progn - (find-file xfpath) - (goto-char (point-min)) - (forward-line (1- xlineNum))) - (progn - (message "File does not exist. Created at\n%s" xfpath) - (find-file xfpath))))) - ((file-exists-p xpathNoQ) - (progn ; open f.ts instead of f.js - (let ((xext (file-name-extension xpathNoQ)) - (xfnamecore (file-name-sans-extension xpathNoQ))) - (if (and (string-equal xext "js") - (file-exists-p (concat xfnamecore ".ts"))) - (progn - (find-file (concat xfnamecore ".ts")) - (warn "Typescript file .ts exist, opening it")) - - (find-file xpathNoQ))))) - ((file-exists-p (concat xpathNoQ ".el")) - (find-file (concat xpathNoQ ".el"))) - (t (progn - (message "File does not exist. Created at\n%s" xpathNoQ) - (find-file xpathNoQ)))))))) - -;; HHHH------------------------------ - -(defvar xah-run-current-file-dispatch nil -"A dispatch table used by `xah-run-current-file' to call dedicated function to run code. -Value is a association list. -Each item is (EXT . FUNCTION). -EXT is filename extension (sans the dot), type string. -FUNCTION is a elisp function name to call, type symbol. -If file extension match, and FUNCTION is defined, call it, pass current buffer's filepath as arg. -Else, `xah-run-current-file-map' is looked up." ) - -(setq xah-run-current-file-dispatch - '(("el" . load) - ("elc" . load) - ("java" . xah-java-compile-and-run))) - -(defvar xah-run-current-file-map - "A association list that maps file extension to a command for running the file, used by `xah-run-current-file'. -Each item is (EXT . PROGRAM). -EXT is filename extension (sans the dot), type string. -PROGRAM is program name or path, with command options to run a file, type string. -A filename is appended after the PROGRAM string as external command to call.") - -(setq xah-run-current-file-map - '( - ;; following are tested as of 2024-12-20 - - ("fs" . "dotnet fsi") - ("fsx" . "dotnet fsi") - ("go" . "go run") - ("js" . "deno run") - ("php" . "php") - ("pl" . "perl") - ("ps1" . "pwsh") - ("py" . "python") - ("py2" . "python2") - ("py3" . "python3") - ("rb" . "ruby") - ("ts" . "deno run") - ("m" . "wolframscript -print all -file") - ("wl" . "wolframscript -print all -file") - ("wls" . "wolframscript -print all -file") - - ;; following may be outdated - - ("clj" . "clj") - ("hs" . "runhaskell") - ("latex" . "pdflatex") - ("ml" . "ocaml") - ("rkt" . "racket") - ("sh" . "bash") - ("tex" . "pdflatex") - ("tsx" . "tsc") - ("vbs" . "cscript") - ("pov" . "povray +R2 +A0.1 +J1.2 +Am2 +Q9 +H480 +W640"))) - -(defun xah-java-compile-and-run (Filename) - "Compile and run java of current buffer. -Buffer is saved first if modified. - -This command is designed for a simple single java class source file. -requires the commands 「javac」 and 「java」. - -If compile fails, the error is displayed in a buffer. - -Created: 2024-12-20 -Version: 2024-12-20" - (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."))) - (let ((xoutbuf (get-buffer-create "*xah java output*" t)) - (xjavac-buf (get-buffer-create "*xah java compile output*" t))) - (with-current-buffer xjavac-buf (erase-buffer)) - (call-process "javac" nil xjavac-buf nil Filename) - (if (eq 1 (with-current-buffer xjavac-buf (point-max))) - (progn - (save-current-buffer (set-buffer xoutbuf) (erase-buffer)) - (call-process "java" nil xoutbuf nil (file-name-nondirectory (file-name-sans-extension Filename))) - (display-buffer xoutbuf)) - (display-buffer xjavac-buf)))) - -(defun xah-run-current-file (Filename) - "Execute the current file. -Output is printed to buffer *xah-run output*. - -File suffix is used to determine what external command to run, in the variable `xah-run-current-file-map'. - -If file is modified, it is auto saved before run. - -The variable `xah-run-current-file-dispatch' allows you to customize this command to call other function to run the current file. - -URL `http://xahlee.info/emacs/emacs/elisp_run_current_file.html' -Created: 2020-09-24 -Version: 2024-12-25" - (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."))) - (let ((xoutbuf (get-buffer-create "*xah-run output*" t)) - (xext (file-name-extension Filename)) - xdispatch) - (setq xdispatch (assoc xext xah-run-current-file-dispatch)) - (if xdispatch - (if (fboundp (cdr xdispatch)) - (progn - (message "calling %s" (cdr xdispatch)) - (funcall (cdr xdispatch) Filename)) - (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)) - (let ((xappCmdStr (cdr (assoc xext xah-run-current-file-map)))) - (when (not xappCmdStr) (error "%s: Unknown file extension: %s. check `xah-run-current-file-map'" real-this-command xext)) - (progn - (save-current-buffer (set-buffer xoutbuf) (erase-buffer)) - (apply 'start-process (append (list "xah-run" xoutbuf) (split-string xappCmdStr " +" t) (list Filename) nil)) - ;; (display-buffer xoutbuf) - (pop-to-buffer xoutbuf) - ;; (with-current-buffer xoutbuf (goto-char (point-min))) - ))))) - -(defun xah-clean-empty-lines () - "Replace repeated blank lines to just 1, in whole buffer or selection. -Respects `narrow-to-region'. - -URL `http://xahlee.info/emacs/emacs/elisp_compact_empty_lines.html' -Created: 2017-09-22 -Version: 2020-09-08" - (interactive) - (let (xbegin xend) - (if (region-active-p) - (setq xbegin (region-beginning) xend (region-end)) - (setq xbegin (point-min) xend (point-max))) - (save-excursion - (save-restriction - (narrow-to-region xbegin xend) - (progn - (goto-char (point-min)) - (while (re-search-forward "\n\n\n+" nil :move) - (replace-match "\n\n"))))))) - -(defun xah-clean-whitespace () - "Delete trailing whitespace, and replace repeated blank lines to just 1. -Only space and tab is considered whitespace here. -Works on whole buffer or selection, respects `narrow-to-region'. - -URL `http://xahlee.info/emacs/emacs/elisp_compact_empty_lines.html' -Created: 2017-09-22 -Version: 2022-08-06" - (interactive) - (let (xbegin xend) - (if (region-active-p) - (setq xbegin (region-beginning) xend (region-end)) - (setq xbegin (point-min) xend (point-max))) - (save-excursion - (save-restriction - (narrow-to-region xbegin xend) - (goto-char (point-min)) - (while (re-search-forward "[ \t]+\n" nil :move) (replace-match "\n")) - (goto-char (point-min)) - (while (re-search-forward "\n\n\n+" nil :move) (replace-match "\n\n")) - (goto-char (point-max)) - (while (eq (char-before) 32) (delete-char -1))))) - (message "%s done" real-this-command)) - -(defun xah-make-backup () - "Make a backup copy of current file or dired marked files. -If in dired, backup current file or marked files. - -The backup file name is the original name with the datetime .yyyymmddhhmmss~ appended. - -Overwrite existing file. - -If the current buffer is not associated with a file, do nothing. - -URL `http://xahlee.info/emacs/emacs/elisp_make-backup.html' -Created: 2018-06-06 -Version: 2024-10-21" - (interactive) - (let ((xfname buffer-file-name) - (xtimestamp (format-time-string "%Y%m%d%H%M%S"))) - (if xfname - (let ((xbackupName - (concat xfname "." xtimestamp "~"))) - (copy-file xfname xbackupName t) - (message (concat "\nBackup saved at: " xbackupName))) - (if (eq major-mode 'dired-mode) - (progn - (mapc (lambda (xx) - (let ((xbackupName - (concat xx "." xtimestamp "~"))) - (copy-file xx xbackupName t))) - (dired-get-marked-files)) - (revert-buffer)) - (user-error "%s: buffer not file nor dired" real-this-command))))) - -(defun xah-make-backup-and-save () - "Backup of current file and save, or backup dired marked files. -For detail, see `xah-make-backup'. -If the current buffer is not associated with a file nor dired, nothing's done. - -URL `http://xahlee.info/emacs/emacs/elisp_make-backup.html' -Version: 2015-10-14" - (interactive) - (if buffer-file-name - (progn - (xah-make-backup) - (when (buffer-modified-p) - (save-buffer))) - (progn - (xah-make-backup)))) - -(defun xah-delete-current-file-make-backup () - "Makes a backup~, delete current file, close the buffer. - -Backup filename is ‹name›.‹dateTimeStamp›~ -Overwrite existing file. - -If buffer is not a file, copy content to `kill-ring', delete buffer. - -If buffer is not a file, the backup file name starts with “xx_”. - -Call `xah-open-last-closed' to open the backup file. - -URL `http://xahlee.info/emacs/emacs/elisp_delete-current-file.html' -Created: 2018-05-15 -Version: 2024-10-21" - (interactive) - (when (eq major-mode 'dired-mode) - (user-error "%s: In dired. Nothing is done." real-this-command)) - (let ((xfname buffer-file-name) - (xbuffname (buffer-name))) - (if xfname - (let ((xbackupPath - (concat - xfname "." - (format-time-string "%Y%m%d%H%M%S") "~"))) - (save-buffer xfname) - (kill-buffer xbuffname) - (rename-file xfname xbackupPath t) - (message "File deleted. -Backup at -%s -Call `xah-open-last-closed' to open." xbackupPath) - (when (boundp 'xah-recently-closed-buffers) - (push (cons nil xbackupPath) xah-recently-closed-buffers))) - (progn - (widen) - (kill-new (buffer-string)) - (kill-buffer xbuffname) - (message "non-file buffer killed. buffer text copied to `kill-ring'.")))) - (when (eq major-mode 'dired-mode) (revert-buffer))) - -;; HHHH------------------------------ - -(defun xah-search-current-word () - "Call `isearch' on current word or selection. -“word” here is A to Z, a to z, and hyphen [-] and lowline [_], independent of syntax table. - -URL `http://xahlee.info/emacs/emacs/modernization_isearch.html' -Created: 2010-05-29 -Version: 2025-02-21" - (interactive) - (let (xbeg xend) - (if (region-active-p) - (setq xbeg (region-beginning) xend (region-end)) - (save-excursion - (skip-chars-backward "-_A-Za-z0-9") - (setq xbeg (point)) - (right-char) - (skip-chars-forward "-_A-Za-z0-9") - (setq xend (point)))) - (deactivate-mark) - (when (< xbeg (point)) (goto-char xbeg)) - (isearch-mode t) - (isearch-yank-string (buffer-substring-no-properties xbeg xend)))) - -(declare-function w32-shell-execute "w32fns.c" (operation document &optional parameters show-flag)) ; (w32-shell-execute "open" default-directory) - -(defun xah-show-in-desktop () - "Show current file in desktop. - (Mac Finder, Microsoft Windows File Explorer, Linux file manager) -This command can be called when in a file buffer or in `dired'. - -URL `http://xahlee.info/emacs/emacs/emacs_show_in_desktop.html' -Created: 2020-11-20 -Version: 2023-09-09" - (interactive) - (let ((xpath (if (eq major-mode 'dired-mode) - (if (eq nil (dired-get-marked-files)) - default-directory - (car (dired-get-marked-files))) - (if buffer-file-name buffer-file-name default-directory)))) - (cond - ((eq system-type 'windows-nt) - (shell-command (format "PowerShell -Command invoke-item '%s'" default-directory )) - ;; (let ((xcmd (format "Explorer /select,%s" - ;; (replace-regexp-in-string "/" "\\" xpath t t) - ;; ;; (shell-quote-argument (replace-regexp-in-string "/" "\\" xpath t t )) - ;; ))) - ;; (shell-command xcmd)) - ) - ((eq system-type 'darwin) - (shell-command - (concat "open -R " (shell-quote-argument xpath)))) - ((eq system-type 'gnu/linux) - (call-process shell-file-name nil 0 nil - shell-command-switch - (format "%s %s" - "xdg-open" - (file-name-directory xpath))) - ;; (shell-command "xdg-open .") ;; 2013-02-10 this sometimes froze emacs till the folder is closed. eg with nautilus - )))) - -(defun xah-open-in-vscode () - "Open current file or dir in vscode. -URL `http://xahlee.info/emacs/emacs/emacs_open_in_vscode.html' - -Created: 2020-02-13 -Version: 2024-12-15" - (interactive) - (let ((xpath (or buffer-file-name default-directory))) - (message "path is %s" xpath) - (cond - ((eq system-type 'darwin) - (shell-command (format "open -a Visual\\ Studio\\ Code.app %s" (shell-quote-argument xpath)))) - ((eq system-type 'windows-nt) - (shell-command (format "code.cmd %s" (shell-quote-argument xpath)))) - ((eq system-type 'gnu/linux) - (shell-command (format "code %s" (shell-quote-argument xpath))))))) - -(defun xah-open-in-external-app (&optional Fname) - "Open the current file or dired marked files in external app. -When called in emacs lisp, if Fname is given, open that. - -URL `http://xahlee.info/emacs/emacs/emacs_dired_open_file_in_ext_apps.html' -Created: 2019-11-04 -Version: 2023-06-26" - (interactive) - (let (xfileList xdoIt) - (setq xfileList - (if Fname - (list Fname) - (if (eq major-mode 'dired-mode) - (dired-get-marked-files) - (list buffer-file-name)))) - (setq xdoIt (if (<= (length xfileList) 10) t (y-or-n-p "Open more than 10 files? "))) - (when xdoIt - (cond - ((eq system-type 'windows-nt) - (let ((xoutBuf (get-buffer-create "*xah open in external app*")) - (xcmdlist (list "PowerShell" "-Command" "Invoke-Item" "-LiteralPath"))) - (mapc - (lambda (x) - (message "%s" x) - (apply 'start-process (append (list "xah open in external app" xoutBuf) xcmdlist (list (format "'%s'" (if (string-match "'" x) (replace-match "`'" t t x) x))) nil))) - xfileList))) - ((eq system-type 'darwin) - (mapc (lambda (xfpath) (shell-command (concat "open " (shell-quote-argument xfpath)))) xfileList)) - ((eq system-type 'gnu/linux) - (mapc (lambda (xfpath) - (call-process shell-file-name nil 0 nil - shell-command-switch - (format "%s %s" - "xdg-open" - (shell-quote-argument xfpath)))) - xfileList)) - ((eq system-type 'berkeley-unix) - (mapc (lambda (xfpath) (let ((process-connection-type nil)) (start-process "" nil "xdg-open" xfpath))) xfileList)))))) - -(defvar xah-fly-mswin-terminal - "wt" - "A string. Value should be one of: wt (for Windows Terminal) or pwsh (for PowerShell Core (cross-platform)) or powershell (for Microsoft PowerShell).") - -(defun xah-open-in-terminal () - "Open the current dir in a new terminal window. -On Microsoft Windows, which terminal it starts depends on `xah-fly-mswin-terminal'. - -URL `http://xahlee.info/emacs/emacs/emacs_open_in_terminal.html' -Created: 2020-11-21 -Version: 2023-06-26" - (interactive) - (cond - ((eq system-type 'windows-nt) - (cond - ((string-equal xah-fly-mswin-terminal "wt") - (shell-command (format "wt -d \"%s\"" default-directory))) - ((string-equal xah-fly-mswin-terminal "pwsh") - (shell-command - (format "pwsh -Command Start-Process pwsh -WorkingDirectory '%s'" (shell-quote-argument default-directory)))) - ((string-equal xah-fly-mswin-terminal "powershell") - (shell-command - (format "powershell -Command Start-Process powershell -WorkingDirectory '%s'" (shell-quote-argument default-directory)))) - (t (error "Error 702919: value of `xah-fly-mswin-terminal' is not expected. Its value is %s" xah-fly-mswin-terminal)))) - ((eq system-type 'darwin) - (shell-command (concat "open -a terminal " (shell-quote-argument default-directory)))) - ((eq system-type 'gnu/linux) - (let ((process-connection-type nil)) (start-process "" nil "x-terminal-emulator" (concat "--working-directory=" default-directory)))) - ((eq system-type 'berkeley-unix) - (let ((process-connection-type nil)) (start-process "" nil "x-terminal-emulator" (concat "--working-directory=" default-directory)))))) - -(defun xah-next-window-or-frame () - "Switch to next window or frame. -If current frame has only one window, switch to next frame. -If `universal-argument' is called first, do switch frame. -Version: 2017-01-27" - (interactive) - (if current-prefix-arg - (other-frame 1) - (if (one-window-p) - (other-frame 1) - (other-window 1)))) - -(defun xah-unsplit-window-or-next-frame () - "Unsplit window. If current frame has only one window, switch to next frame. -Version: 2017-01-29" - (interactive) - (if (one-window-p) - (other-frame 1) - (delete-other-windows))) - -;; HHHH------------------------------ -;; layout lookup tables for key conversion - -(defvar xah-fly-layout-diagrams (make-hash-table :test 'equal) - "A hashtable. -Key is string, of keyboard layout name. -Value is a string, of text-art (aka ASCII-art) form of the layout. -The text-art string, each key (sequence of chars) is separated by whitespace. -The string is split by white space, and each item is considered a key. -The key is usually a single char, can be unicode, but may also be alt ctrl tab shift return and other. -It is used to generate key conversion table of a key from layout to layout. -") - -(progn - - (puthash "adnw" " -~ ! @ # $ % ^ & * ( ) { } -` 1 2 3 4 5 6 7 8 9 0 [ ] - -k u ü . ä v g c l j f = \\ -h i e a o d t r n s ß -x y ö , q b p w m z - -K U Ü > Ä V G C L J F + | -H I E A O D T R N S ẞ -X Y Ö < Q B P W M Z -" xah-fly-layout-diagrams) - - (puthash "azerty" - " -~ ! @ # $ % ^ & * ( ) { } -² & é \" ' ( - è _ ç à ) = - -a z e r t y u i o p ^ $ * -q s d f g h j k l m ù -w x c v b n , ; : ! - -A Z E R T Y U I O P ? + | -Q S D F G H J K L M Ù -W X C V B N ? . / § -" xah-fly-layout-diagrams) - - (puthash "azerty-be" - " -~ ! @ # $ % ^ & * ( ) { } -² & é \" ' ( § è ! ç à ) - - -a z e r t y u i o p ^ $ µ -q s d f g h j k l m ù -w x c v b n , ; : = - -A Z E R T Y U I O P ^ $ Μ -Q S D F G H J K L M Ù -W X C V B N ? . / + -" xah-fly-layout-diagrams) - - (puthash "bepo" " -# 1 2 3 4 5 6 7 8 9 0 ° ` -$ \" « » ( ) @ + - / * = % - -b é p o è ^ v d l j z w \\ -a u i e , c t s r n m -à y x . k ' q g h f - -B É P O È ! V D L J Z W | -A U I E ; C T S R N M -À Y X : K ? Q G H F -" xah-fly-layout-diagrams) - - (puthash "colemak" " -~ ! @ # $ % ^ & * ( ) _ + -` 1 2 3 4 5 6 7 8 9 0 - = - -q w f p g j l u y ; [ ] \\ -a r s t d h n e i o ' -z x c v b k m , . / - -Q W F P G J L U Y : { } | -A R S T D H N E I O \" -Z X C V B K M < > ? -" xah-fly-layout-diagrams) - - (puthash "colemak-dh" " -~ ! @ # $ % ^ & * ( ) _ + -` 1 2 3 4 5 6 7 8 9 0 - = - -q w f p b j l u y ; [ ] \\ -a r s t g m n e i o ' -z x c d v k h , . / - -Q W F P B J L U Y : { } | -A R S T G M N E I O \" -Z X C D V K H < > ? -" xah-fly-layout-diagrams) - - (puthash "dvorak" " -~ ! @ # $ % ^ & * ( ) { } -` 1 2 3 4 5 6 7 8 9 0 [ ] - -' , . p y f g c r l / = \\ -a o e u i d h t n s - -; q j k x b m w v z - -\" < > P Y F G C R L ? + | -A O E U I D H T N S _ -: Q J K X B M W V Z -" xah-fly-layout-diagrams) - - (puthash "engrammer" " -~ ! @ # $ % ^ & * ( ) { } -` 1 2 3 4 5 6 7 8 9 0 [ ] - -b y o u ' ; l d w v z = \\ -c i e a , . h t s n q -g x j k - / r m f p - -B Y O U \" : L D W V Z + | -C I E A < > H T S N Q -G X J K _ ? R M F P -" xah-fly-layout-diagrams) - - (puthash "koy" " -^ ! @ # $ % ^ & * ( ) _ ~ -˘ 1 2 3 4 5 6 7 8 9 0 - ` - -k . o , y v g c l ß / = \\ -h a e i u d t r n s f -x q ä ü ö b p w m j - -K > O < Y V G C L ẞ ? + | -H A E I U D T R N S F -X Q Ä Ü Ö B P W M J -" xah-fly-layout-diagrams) - - (puthash "halmak" " -~ ! @ # $ % ^ & * ( ) _ + -` 1 2 3 4 5 6 7 8 9 0 - = - -w l r b z ; q u d j [ ] \\ -s h n t , . a e o i ' -f m v c / g p x k y - -W L R B Z : Q U D J { } | -S H N T < > A E O I \" -F M V C ? G P X K Y -" xah-fly-layout-diagrams) - - (puthash "minimak" " -~ ! @ # $ % ^ & * ( ) _ + -` 1 2 3 4 5 6 7 8 9 0 - = - -q w d r k y u i o p [ ] \\ -a s t f g h j e l ; ' -z x c v b n m , . / - -Q W D R K Y U I O P { } | -A S T F G H J E L : \" -Z X C V B N M < > ? -" xah-fly-layout-diagrams) - - ;; todo. need fix - (puthash "neo2" " -^ ! @ # $ % ^ & * ( ) { } -` 1 2 3 4 5 6 7 8 9 0 - ] - -x v l c w k h g f q ß = \\ -u i a e o s n r t d y -ü ö ä p z b m , . j - -X V L C W K H G F Q ? + | -U I A E O S N R T D Y -Ü Ö Ä P Z B M , . J -" xah-fly-layout-diagrams) - - (puthash "norman" " -~ ! @ # $ % ^ & * ( ) _ + -` 1 2 3 4 5 6 7 8 9 0 - = - -q w d f k j u r l ; [ ] \\ -a s e t g y n i o h ' -z x c v b p m , . / - -Q W D F K J U R L : { } | -A S E T G Y N I O H \" -Z X C V B P M < > ? -" xah-fly-layout-diagrams) - - (puthash "programer-dvorak" " -~ % 7 5 3 1 9 0 2 4 6 8 ` -$ & [ { } ( = * ) + ] ! # - -; , . p y f g c r l / @ \\ -a o e u i d h t n s - -' q j k x b m w v z - -: < > P Y F G C R L ? ^ | -A O E U I D H T N S _ -\" Q J K X B M W V Z -" xah-fly-layout-diagrams) - - (puthash "pt-nativo" " -* ! \" # $ % & / ( ) = ª > -+ 1 2 3 4 5 6 7 8 9 0 º < - -' , . h x w l t c p ~ - \\ -i e a o u m d s r n ´ -« ç j b k q v g f z - -? ; : H X W L T C P ^ _ | -I E A O U M D S R N ` -Y Ç J B K Q V G F Z -" xah-fly-layout-diagrams) - - (puthash "qfmlwy" " -~ ! @ # $ % ^ & * ( ) _ + -` 1 2 3 4 5 6 7 8 9 0 - = - -q f m l w y u o b j [ ] \\ -d s t n r i a e h ; ' -z v g c x p k , . / - -Q F M L W Y U O B J { } | -D S T N R I A E H : \" -Z V G C X P K < > ? -" xah-fly-layout-diagrams) - -(puthash "qgmlwb" " -~ ! @ # $ % ^ & * ( ) _ + -` 1 2 3 4 5 6 7 8 9 0 - = - -q g m l w b y u v ; [ ] \\ -d s t n r i a e o h ' -z x c f j k p , . / - -Q G M L W B Y U V : { } | -D S T N R I A E O H \" -Z X C F J K P < > ? -" xah-fly-layout-diagrams) - - (puthash "qwerty" " -~ ! @ # $ % ^ & * ( ) _ + -` 1 2 3 4 5 6 7 8 9 0 - = - -q w e r t y u i o p [ ] \\ -a s d f g h j k l ; ' -z x c v b n m , . / - -Q W E R T Y U I O P { } | -A S D F G H J K L : \" -Z X C V B N M < > ? -" xah-fly-layout-diagrams) - - (puthash "qwerty-abnt" " -\" ! @ # $ % ^ & * ( ) _ + - ' 1 2 3 4 5 6 7 8 9 0 - = - -q w e r t y u i o p ´ [ ] -a s d f g h j k l ç ~ -z x c v b n m , . ; - -Q W E R T Y U I O P ` + | -A S D F G H J K L Ç ^ -Z X C V B N M < > : -" xah-fly-layout-diagrams) - - (puthash "qwerty-no" " -§ ! \" # ¤ % & / ( ) = ? ` -| 1 2 3 4 5 6 7 8 9 0 + \\ - -q w e r t y u i o p å ¨ ' -a s d f g h j k l ø æ -z x c v b n m , . - - -Q W E R T Y U I O P Å ^ * -A S D F G H J K L Ø Æ -Z X C V B N M < > _ -" xah-fly-layout-diagrams) - - (puthash "qwerty-se" " -§ ! \" # ¤ % & / ( ) = ? ` -| 1 2 3 4 5 6 7 8 9 0 + \\ - -q w e r t y u i o p å ¨ ' -a s d f g h j k l ö ä -z x c v b n m , . - - -Q W E R T Y U I O P Å ^ * -A S D F G H J K L Ö Ä -Z X C V B N M < > _ -" xah-fly-layout-diagrams) - - (puthash "qwertz" " -~ ! @ # $ % ^ & * ( ) _ + -` 1 2 3 4 5 6 7 8 9 0 - = - -q w e r t z u i o p [ ] \\ -a s d f g h j k l ; ' -y x c v b n m , . / - -Q W E R T Z U I O P { } | -A S D F G H J K L : \" -Y X C V B N M < > ? -" xah-fly-layout-diagrams) - - (puthash "qwpr" " -~ ! @ # $ % ^ & * ( ) _ + -` 1 2 3 4 5 6 7 8 9 0 - = - -q w p r f y u k l ; [ ] \\ -a s d t g h n i o e ' -z x c v b j m , . / - -Q W P R F Y U K L : { } | -A S D T G H N I O E \" -Z X C V B J M < > ? -" xah-fly-layout-diagrams) - - (puthash "russian" " -Ё ! \" № ; % : ? * ( ) _ + -ё 1 2 3 4 5 6 7 8 9 0 - = - -й ц у к е н г ш щ з х ъ \\ -ф ы в а п р о л д ж э -я ч с м и т ь б ю . - -Й Ц У К Е Н Г Ш Щ З Х Ъ | -Ф Ы В А П Р О Л Д Ж Э -Я Ч С М И Т Ь Б Ю , -" xah-fly-layout-diagrams) - - (puthash "workman" " -~ ! @ # $ % ^ & * ( ) _ + -` 1 2 3 4 5 6 7 8 9 0 - = - -q d r w b j f u p ; [ ] \\ -a s h t g y n e o i ' -z x m c v k l , . / - -Q D R W B J F U P : { } | -A S H T G Y N E O I \" -Z X M C V K L < > ? -" xah-fly-layout-diagrams)) - -(defun xah-fly-create-key-conv-table (Layout1 Layout2) - "Takes two text diagrams Layout1 Layout2, return a hashtable. -For remapping key from layout1 to layout2. - -example: - -Layout1 is a string. e.g. - -a b c d -e f shift - -Layout2 is a string. e.g. - -a o e i -m n ctrl - -return a hashtable, e.g. - -b → o -c → e -d → i -e → m -f → n -shift → ctrl - -If the keys in layouts are the same, it's not in the table. - -Created: 2024-04-19 -Version: 2024-04-23" - (let (xkeys1 xkeys2 (xtable (make-hash-table :test 'equal))) - (setq xkeys1 (split-string Layout1 "[ \n]+" t)) - (setq xkeys2 (split-string Layout2 "[ \n]+" t)) - (when (not (eq (length xkeys1) (length xkeys2))) - (error "layout %s and %s lengths not same." (length Layout1) (length Layout2))) - (seq-mapn - (lambda (x y) - (if (string-equal x y) - nil - (puthash x y xtable))) - xkeys1 xkeys2) - xtable - )) - -;; (xah-fly-create-key-conv-table -;; (gethash "qwerty" xah-fly-layout-diagrams) -;; (gethash "dvorak" xah-fly-layout-diagrams)) - -(defvar xah-fly-key-current-layout nil - "The current keyboard layout. -Value is a key in `xah-fly-layout-diagrams'. -Do not set this variable manually. -Use `xah-fly-keys-set-layout' to set it. -Default to qwerty. -Version: 2022-10-22") - -(if xah-fly-key-current-layout nil (setq xah-fly-key-current-layout "qwerty")) - -(defvar xah-fly--key-convert-table nil - "A alist that's the conversion table from dvorak to current layout. -Value is a hashtable. -Created: 2019-02-12 -Version: 2024-04-22" ) - -(setq - xah-fly--key-convert-table - (xah-fly-create-key-conv-table - (gethash "dvorak" xah-fly-layout-diagrams) - (gethash xah-fly-key-current-layout xah-fly-layout-diagrams))) - -(defun xah-fly--convert-key (Keystr) - "Return the corresponding Keystr according to variable `xah-fly--key-convert-table'. -Keystr must be a string that is the valid argument to `kbd'. -Keystr may be a single key, or key sequence separated by whitespaces. -Created: 2022-10-25 -Version: 2024-04-22" - (interactive) - (mapconcat - 'identity - (mapcar - (lambda (x) - (let ((xnew (gethash x xah-fly--key-convert-table))) - (if xnew xnew x))) - (split-string Keystr " +")) - " ")) - -(defun xah-fly--define-keys (KeymapName KeyCmdAlist &optional Direct-p) - "Map `define-key' over a alist KeyCmdAlist, with key layout remap. -The key is remapped from Dvorak to the current keyboard layout by `xah-fly--convert-key'. -If Direct-p is t, do not remap key to current keyboard layout. -Example usage: - (xah-fly--define-keys - (define-prefix-command \\='xyz-map) - \\='( - (\"h\" . highlight-symbol-at-point) - (\".\" . isearch-forward-symbol-at-point) - (\"w\" . isearch-forward-word))) -Created: 2020-04-18 -Version: 2023-08-21" - (mapcar - (lambda (x) - (define-key - KeymapName - (kbd (if Direct-p (car x) (xah-fly--convert-key (car x)))) - (cdr x))) - KeyCmdAlist)) - -;; HHHH------------------------------ -;; keymaps - -(defvar xah-fly-key-map (make-sparse-keymap) - "If `xah-fly-insert-state-p' is true, point to `xah-fly-insert-map', else, points to `xah-fly-command-map'.") - -(defvar xah-fly-command-map (make-sparse-keymap) - "Keymap when in command mode.") - -(defvar xah-fly-insert-map (make-sparse-keymap) - "Keymap when in insert mode.") - -(defvar xah-fly--deactivate-command-mode-func nil) - -;; HHHH------------------------------ -;; setting keys - -(defun xah-fly-define-keys () - "Define the keys for xah-fly-keys. -Created: 2022-10-31 -Version: 2024-04-22" - (interactive) - (let () - - ;; Movement key integrations with built-in Emacs packages - (xah-fly--define-keys - indent-rigidly-map - '(("h" . indent-rigidly-left) - ("n" . indent-rigidly-right))) - - (xah-fly--define-keys - xah-fly-command-map - '(("<escape>" . xah-fly-command-mode-activate) - ("<home>" . xah-fly-command-mode-activate) - ("<f8>" . xah-fly-command-mode-activate)) - :direct) - - (xah-fly--define-keys - xah-fly-insert-map - '(("<escape>" . xah-fly-command-mode-activate) - ("<home>" . xah-fly-command-mode-activate) - ("<f8>" . xah-fly-command-mode-activate)) - :direct) - - (when xah-fly-use-isearch-arrows - (xah-fly--define-keys - isearch-mode-map - '(("<up>" . isearch-ring-retreat) - ("<down>" . isearch-ring-advance) - ("<left>" . isearch-repeat-backward) - ("<right>" . isearch-repeat-forward) - ("C-v" . isearch-yank-kill)) - :direct) - (xah-fly--define-keys - minibuffer-local-isearch-map - '(("<left>" . isearch-reverse-exit-minibuffer) - ("<right>" . isearch-forward-exit-minibuffer)) - :direct)) - - (xah-fly--define-keys - (define-prefix-command 'xah-fly-leader-key-map) - '(("SPC" . xah-fly-insert-mode-activate) - ("RET" . execute-extended-command) - - ("TAB" . nil) - - ("TAB TAB" . indent-for-tab-command) - - ("TAB i" . complete-symbol) - ("TAB g" . indent-rigidly) - ("TAB r" . indent-region) - ("TAB s" . indent-sexp) - - (". ." . highlight-symbol-at-point) - (". g" . unhighlight-regexp) - (". c" . highlight-lines-matching-regexp) - (". h" . highlight-regexp) - (". t" . highlight-phrase) - (". e" . isearch-forward-symbol-at-point) - (". u" . isearch-forward-symbol) - (". p" . isearch-forward-word) - - ("'" . xah-fill-or-unfill) - - (", t" . xref-find-definitions) - (", n" . xref-pop-marker-stack) - - ;; - / ; = [ - ("\\" . toggle-input-method) - ;; ` - - ("3" . delete-window) - ("4" . split-window-right) - ("5" . balance-windows) - ("6" . xah-upcase-sentence) - - ("9" . ispell-word) - - ("a" . mark-whole-buffer) - ("b" . end-of-buffer) - - ("c ," . xah-open-in-external-app) - ("c ." . find-file) - ("c c" . bookmark-bmenu-list) - ("c e" . ibuffer) - ("c f" . xah-open-recently-closed) - ("c g" . xah-open-in-terminal) - ("c h" . recentf-open-files) - ("c j" . xah-copy-file-path) - ("c l" . bookmark-set) - ("c n" . xah-new-empty-buffer) - ("c o" . xah-show-in-desktop) - ("c p" . xah-open-last-closed) - ("c r" . bookmark-jump) - ("c s" . write-file) - ("c u" . xah-open-file-at-cursor) - ("c x" . set-buffer-file-coding-system) - ("c y" . xah-list-recently-closed) - ("c z" . revert-buffer-with-coding-system) - - ;; set-buffer-process-coding-system - ;; set-file-name-coding-system - ;; set-keyboard-coding-system - ;; set-language-environment - ;; set-next-selection-coding-system - ;; set-selection-coding-system - ;; set-terminal-coding-system - ;; universal-coding-system-argument - - ("d" . beginning-of-buffer) - - ("e 6" . xah-insert-white-corner-bracket『』) - - ("e a" . xah-insert-double-angle-bracket《》) - ("e b" . xah-insert-black-lenticular-bracket【】) - - ("e c r" . expand-region-abbrevs) - ("e c t" . edit-abbrevs) - ("e c u" . expand-abbrev) - - ("e c g" . add-mode-abbrev) - ("e c c" . add-global-abbrev) - ("e c m" . inverse-add-mode-abbrev) - ("e c w" . inverse-add-global-abbrev) - - ("e c f" . unexpand-abbrev) - - ("e c h" . expand-jump-to-previous-slot) - ("e c n" . expand-jump-to-next-slot) - ("e c y" . abbrev-prefix-mark) - - ("e d" . xah-insert-double-curly-quote“”) - ("e e" . xah-insert-unicode) - ("e f" . xah-insert-emacs-quote) - ("e g" . xah-insert-ascii-double-quote) - - ("e h" . xah-insert-brace) - ("e i" . xah-insert-curly-single-quote‘’) - ("e j" . insert-char) - ("e k" . xah-insert-markdown-quote) - ("e l" . xah-insert-seperator) - ("e m" . xah-insert-corner-bracket「」) - ("e n" . xah-insert-square-bracket) - ("e o" . xah-insert-ascii-single-quote) - ("e p" . xah-insert-single-angle-quote‹›) - ("e q" . xah-insert-deco-angle-fat-bracket❰❱) - ("e r" . xah-insert-tortoise-shell-bracket〔〕) - ("e s" . xah-insert-ascii-angle-bracket) - ("e t" . xah-insert-paren) - ("e u" . xah-insert-date) - ("e v" . xah-insert-markdown-triple-quote) - ("e w" . xah-insert-angle-bracket〈〉) - ("e x" . xah-insert-white-lenticular-bracket〖〗) - ("e y" . xah-insert-double-angle-quote«») - ("e z" . xah-insert-deco-angle-bracket❮❯) - - ("f" . xah-search-current-word) - ("g" . xah-close-current-buffer) - - ("h a" . apropos-command) - ("h b" . describe-bindings) - ("h c" . describe-char) - ("h d" . apropos-documentation) - ("h e" . view-echo-area-messages) - ("h f" . describe-face) - ("h g" . info-lookup-symbol) - ;; ("h h" . describe-function) - ("h i" . info) - ("h j" . man) - ("h k" . describe-key) - ("h l" . view-lossage) - ("h m" . describe-mode) - ("h n" . describe-variable) - ("h o" . describe-language-environment) - ;; p q - ("h r" . apropos-variable) - ("h s" . describe-syntax) - ("h t" . describe-function) - ("h u" . elisp-index-search) - ("h v" . apropos-value) - ("h x" . describe-command) ; emacs 28 - ;; y - ("h z" . describe-coding-system) - - ("i" . kill-line) - ("j" . xah-copy-all-or-region) - ("k" . xah-show-kill-ring) - - ("l" . recenter-top-bottom) - - ("m" . dired-jump) - - ;; ("m e" . delete-other-windows) - ;; ("m u" . split-window-below) - ;; ("m w" . universal-argument) - - ;; commands here are “harmless”, they don't modify text etc. they turn on modes, change display, prompt, start shell, etc. - ("n SPC" . whitespace-mode) - ("n ," . abbrev-mode) - ("n ." . toggle-frame-maximized) - ("n 1" . set-input-method) - ("n 2" . global-hl-line-mode) - ("n 4" . global-display-line-numbers-mode) - ("n 6" . calendar) - ("n 7" . calc) - ("n 9" . shell-command) - ("n 0" . shell-command-on-region) - - ("n <up>" . xah-page-up) - ("n <down>" . xah-page-down) - - ("n a" . text-scale-adjust) - ("n b" . toggle-debug-on-error) - ("n c" . toggle-case-fold-search) - ("n d" . display-line-numbers-mode) - ("n e" . eshell) - ("n f" . nil) - ("n g" . xah-toggle-read-novel-mode) - ("n h" . widen) - ("n i" . make-frame-command) - ("n j" . flyspell-buffer) - ("n k" . menu-bar-open) - ("n l" . toggle-word-wrap) - ("n m" . jump-to-register) - ("n n" . xah-narrow-to-region) - ("n o" . variable-pitch-mode) - ("n p" . read-only-mode) - ("n q" . enlarge-window) - ("n r" . count-words) - ("n s" . count-matches) - ("n t" . narrow-to-defun) - ("n u" . shell) - ("n v" . visual-line-mode) - ("n w" . eww) - ("n x" . save-some-buffers) - ("n y" . toggle-truncate-lines) - ("n z" . abort-recursive-edit) - - ("o" . exchange-point-and-mark) - ("p" . query-replace) - ("q" . xah-cut-all-or-region) - - ;; roughly text replacement related - - ("r ," . apply-macro-to-region-lines) - ("r ." . kmacro-start-macro) - ("r /" . xah-slash-to-double-backslash) - ("r 3" . number-to-register) - ("r 4" . increment-register) - ("r RET" . nil) - ("r SPC" . nil) - ("r \\" . xah-slash-to-backslash) - - ("r a" . nil) - ("r b" . nil) - ("r c" . string-rectangle) - ("r d" . delete-rectangle) - ("r e" . call-last-kbd-macro) - ("r f" . nil) - ("r g" . nil) - ("r h" . xah-change-bracket-pairs) - ("r i" . xah-space-to-newline) - ("r j" . copy-rectangle-to-register) - ("r k" . yank-rectangle) - ("r l" . clear-rectangle) - - ("r m" . nil) - ("r n" . rectangle-number-lines) - ("r o" . open-rectangle) - ("r p" . kmacro-end-macro) - ("r q" . kill-rectangle) - - ("r r" . rectangle-mark-mode) - ("r s" . nil) - ;; kmacro-end-and-call-macro - ("r t RET" . #'kmacro-edit-macro) - ("r t SPC" . #'kmacro-step-edit-macro) - ("r t TAB" . #'kmacro-insert-counter) - ("r t a" . #'kmacro-add-counter) - ("r t b" . #'kmacro-bind-to-key) - ("r t c" . #'kmacro-set-counter) - ("r t d" . #'kmacro-redisplay) - ("r t e" . #'edit-kbd-macro) - ("r t f" . #'kmacro-set-format) - ("r t g" . #'kmacro-delete-ring-head) - ("r t h" . #'kmacro-edit-macro-repeat) - ("r t i" . #'kmacro-call-ring-2nd-repeat) - ("r t j" . #'kmacro-cycle-ring-next) - ("r t k" . #'kmacro-end-or-call-macro-repeat) - ("r t l" . #'kmacro-edit-lossage) - - ("r t n" . #'kmacro-name-last-macro) - ("r t p" . #'kmacro-cycle-ring-previous) - ("r t q" . #'kbd-macro-query) - ("r t t" . #'kmacro-swap-ring) - ("r t v" . #'kmacro-view-macro-repeat) - ("r t x" . #'kmacro-to-register) - ("r u" . xah-quote-lines) - ("r v" . nil) - ("r w" . nil) - ("r x" . xah-double-backslash-to-slash) - ("r y" . delete-whitespace-rectangle) - ("r z" . nil) - - ("s" . save-buffer) - - ;; most frequently used - ("t <up>" . xah-move-block-up) - ("t <down>" . xah-move-block-down) - - ("t ," . sort-numeric-fields) - ("t ." . xah-sort-lines) - ("t 1" . xah-append-to-register-1) - ("t 2" . xah-clear-register-1) - ("t 3" . xah-copy-to-register-1) - ("t 4" . xah-paste-from-register-1) - ("t 7" . xah-append-to-register-1) - ("t 8" . xah-clear-register-1) - ("t a" . nil) - ("t b" . xah-backward-punct) - ("t c" . copy-matching-lines) - ("t d" . mark-defun) - ("t e" . list-matching-lines) - ("t f" . move-to-column) - ("t g" . goto-line) - ("t h" . repeat-complex-command) - ("t i" . delete-non-matching-lines) - ("t j" . copy-to-register) - ("t k" . insert-register) - ("t l" . nil) - ("t m" . xah-make-backup-and-save) - ("t n" . goto-char) - ("t o" . xah-clean-whitespace) - ("t p" . query-replace-regexp) - ("t q" . xah-cut-text-in-quote) - ("t r" . xah-escape-quotes) - ("t s" . xah-reformat-to-sentence-lines) - ("t t" . repeat) - ("t u" . kill-matching-lines) - ("t v" . nil) - ("t w" . xah-next-window-or-frame) - ("t x" . xah-title-case-region-or-line) - ("t y" . delete-duplicate-lines) - ("t z" . nil) - ("u" . switch-to-buffer) - ("v" . universal-argument) - - ;; dangerous map. run program, delete file, etc - ("w DEL" . xah-delete-current-file-make-backup) - ("w ." . eval-buffer) - ("w e" . eval-defun) - ("w m" . eval-last-sexp) - ("w p" . eval-expression) - ("w u" . eval-region) - ("w q" . save-buffers-kill-terminal) - ("w w" . delete-frame) - ("w j" . xah-run-current-file) - - ("x" . xah-toggle-previous-letter-case) - ;; y - - ;; vc command keys subject to change. need a frequency stat of the commands. - - ("z b" . vc-root-diff) ; D - ("z c" . vc-update) ; git pull, + - ("z d" . vc-annotate) ; g - ("z f" . vc-revert) ; u - ("z g" . vc-push) ; git push, P - ("z h" . vc-diff) ; git diff, = - ("z l" . vc-print-root-log) ; L - ("z m" . vc-dir) ; git status, C-x v d - ("z n" . vc-print-log) ; git log, l - ("z r" . vc-merge) ; m - ("z t" . vc-register) ; git add, i - ("z z" . vc-next-action) ; v - - ("z 1" . vc-create-tag) ; s - ("z 2" . vc-insert-headers) ; h - ("z 4" . vc-retrieve-tag) ; r - ("z 5" . vc-revision-other-window) ; ~ - ("z 6" . vc-switch-backend) ; b - ("z 7" . vc-update-change-log) ; a - - ;; - )) - - (xah-fly--define-keys - xah-fly-command-map - '(("SPC" . xah-fly-leader-key-map) - ("'" . xah-reformat-lines) - ("," . xah-shrink-whitespaces) - ("-" . delete-other-windows) - ("." . backward-kill-word) - ("/" . hippie-expand) - (";" . xah-comment-dwim) - ("[" . split-window-below) - ("\\" . xah-cycle-hyphen-lowline-space) - ("]" . split-window-right) - ("`" . other-frame) - - ;; ("1" . nil) - ;; ("2" . nil) - ("3" . delete-other-windows) - ("4" . split-window-below) - ("5" . delete-char) - ("6" . xah-select-block) - ("7" . xah-select-line) - ("8" . xah-extend-selection) - ("9" . xah-select-text-in-quote) - ("0" . xah-pop-local-mark-ring) - - ("a" . execute-extended-command) - ("b" . isearch-forward) - ("c" . previous-line) - ("d" . xah-beginning-of-line-or-block) - ("e" . xah-smart-delete) - ("f" . undo) - ("g" . backward-word) - ("h" . backward-char) - ("i" . xah-delete-current-text-block) - ("j" . xah-copy-line-or-region) - ("k" . xah-paste-or-paste-previous) - ;; ("l" . xah-fly-insert-mode-activate-space-before) - ("l" . xah-insert-space-before) - ("m" . xah-backward-left-bracket) - ("n" . forward-char) - ("o" . open-line) - ("p" . kill-word) - ("q" . xah-cut-line-or-region) - ("r" . forward-word) - ("s" . xah-end-of-line-or-block) - ("t" . next-line) - ("u" . xah-fly-insert-mode-activate) - ("v" . xah-forward-right-bracket) - ("w" . xah-next-window-or-frame) - ("x" . xah-toggle-letter-case) - ("y" . set-mark-command) - ("z" . xah-goto-matching-bracket))) - - ;; - )) - -(xah-fly-define-keys) - -;; HHHH------------------------------ -;; set control meta, etc keys - -(defcustom xah-fly-unset-useless-key t - "If true, unbind many obsolete or useless or redundant - keybinding. e.g. <help>, <f1>." - :type 'boolean) - -(when xah-fly-unset-useless-key - (global-set-key (kbd "<help>") nil) - (global-set-key (kbd "<f1>") nil)) - -(when xah-fly-use-meta-key - - (global-set-key (kbd "M-<home>") nil) ; beginning-of-buffer-other-window - (global-set-key (kbd "M-<end>") nil) ; end-of-buffer-other-window - - (global-set-key (kbd "M-SPC") #'xah-fly-command-mode-activate) - (global-set-key (kbd "M-\\") nil) ; delete-horizontal-space - (global-set-key (kbd "M-!") nil) ; shell-command - (global-set-key (kbd "M-$") nil) ; ispell-word - (global-set-key (kbd "M-%") nil) ; query-replace - (global-set-key (kbd "M-&") nil) ; async-shell-command - (global-set-key (kbd "M-'") nil) ; abbrev-prefix-mark - (global-set-key (kbd "M-(") nil) ; insert-parentheses - (global-set-key (kbd "M-)") nil) ; move-past-close-and-reindent - ;; (global-set-key (kbd "M-,") nil) ; xref-pop-marker-stack - ;; (global-set-key (kbd "M-.") nil) ; xref-find-definitions - (global-set-key (kbd "M-/") nil) ; dabbrev-expand - (global-set-key (kbd "M-:") nil) ; eval-expression - ;; (global-set-key (kbd "M-;") nil) ; comment-dwim - (global-set-key (kbd "M-<") nil) ; beginning-of-buffer - (global-set-key (kbd "M-=") nil) ; count-words-region - (global-set-key (kbd "M->") nil) ; end-of-buffer - ;; (global-set-key (kbd "M-?") nil) ; xref-find-references - (global-set-key (kbd "M-@") nil) ; mark-word - (global-set-key (kbd "M-^") nil) ; delete-indentation - (global-set-key (kbd "M-`") nil) ; tmm-menubar - (global-set-key (kbd "M-a") nil) ; backward-sentence - (global-set-key (kbd "M-b") nil) ; backward-word - (global-set-key (kbd "M-c") nil) ; capitalize-word - (global-set-key (kbd "M-d") nil) ; kill-word - (global-set-key (kbd "M-e") nil) ; forward-sentence - (global-set-key (kbd "M-f") nil) ; forward-word - (global-set-key (kbd "M-g") nil) ; Prefix Command - (global-set-key (kbd "M-h") nil) ; mark-paragraph - (global-set-key (kbd "M-i") nil) ; tab-to-tab-stop - (global-set-key (kbd "M-j") nil) ; default-indent-new-line - (global-set-key (kbd "M-k") nil) ; kill-sentence - (global-set-key (kbd "M-l") nil) ; downcase-word - (global-set-key (kbd "M-m") nil) ; back-to-indentation - (global-set-key (kbd "M-o") nil) ; facemenu-keymap - (global-set-key (kbd "M-q") nil) ; fill-paragraph - (global-set-key (kbd "M-r") nil) ; move-to-window-line-top-bottom - (global-set-key (kbd "M-s") nil) ; Prefix Command - (global-set-key (kbd "M-t") nil) ; transpose-words - (global-set-key (kbd "M-u") nil) ; upcase-word - (global-set-key (kbd "M-v") nil) ; scroll-down-command - (global-set-key (kbd "M-w") nil) ; kill-ring-save - ;; (global-set-key (kbd "M-x") nil) ; execute-extended-command - ;; (global-set-key (kbd "M-y") nil) ; yank-pop - (global-set-key (kbd "M-z") nil) ; zap-to-char - (global-set-key (kbd "M-{") nil) ; backward-paragraph - (global-set-key (kbd "M-|") nil) ; shell-command-on-region - (global-set-key (kbd "M-}") nil) ; forward-paragraph - (global-set-key (kbd "M-~") nil) ; not-modified - (global-set-key (kbd "M-DEL") nil) ; backward-kill-word - ) - -(when xah-fly-use-control-key - - (global-set-key (kbd "C-<tab>") #'xah-next-user-buffer) - (global-set-key (kbd "C-S-<tab>") #'xah-previous-user-buffer) - - (global-set-key (kbd "C-S-<prior>") #'xah-previous-emacs-buffer) - (global-set-key (kbd "C-S-<next>") #'xah-next-emacs-buffer) - - (global-set-key (kbd "C-<prior>") #'xah-previous-user-buffer) - (global-set-key (kbd "C-<next>") #'xah-next-user-buffer) - - ;; (global-set-key (kbd "C-1") nil) - (global-set-key (kbd "C-2") #'pop-global-mark) - (global-set-key (kbd "C-3") #'previous-error) - (global-set-key (kbd "C-4") #'next-error) - (global-set-key (kbd "C-5") #'xah-previous-emacs-buffer) - (global-set-key (kbd "C-6") #'xah-next-emacs-buffer) - (global-set-key (kbd "C-7") #'xah-previous-user-buffer) - (global-set-key (kbd "C-8") #'xah-next-user-buffer) - (global-set-key (kbd "C-9") #'xah-page-up) - (global-set-key (kbd "C-0") #'xah-page-down) - - (global-set-key (kbd "C--") #'text-scale-decrease) - (global-set-key (kbd "C-=") #'text-scale-increase) - - (global-set-key (kbd "C-SPC") #'xah-fly-command-mode-activate) - - (global-set-key (kbd "C-S-n") #'make-frame-command) - (global-set-key (kbd "C-S-s") #'write-file) - (global-set-key (kbd "C-S-t") #'xah-open-last-closed) - - ;; (global-set-key (kbd "C-@") nil) - - (global-set-key (kbd "C-a") #'mark-whole-buffer) - (global-set-key (kbd "C-b") nil) - ;; (global-set-key (kbd "C-c") nil) - (global-set-key (kbd "C-d") nil) - (global-set-key (kbd "C-e") nil) - (global-set-key (kbd "C-f") nil) - ;; (global-set-key (kbd "C-g") nil) ; cancel - ;; (global-set-key (kbd "C-h") nil) ; help - ;; (global-set-key (kbd "C-i") nil) ; tab - ;; (global-set-key (kbd "C-j") nil) ; newline - (global-set-key (kbd "C-k") nil) - (global-set-key (kbd "C-l") nil) - ;; (global-set-key (kbd "C-m") nil) ; newline - (global-set-key (kbd "C-n") #'xah-new-empty-buffer) - (global-set-key (kbd "C-o") #'find-file) - (global-set-key (kbd "C-p") nil) - ;; (global-set-key (kbd "C-q") nil) ; quoted-insert - ;; (global-set-key (kbd "C-r") nil) - (global-set-key (kbd "C-s") #'save-buffer) - (global-set-key (kbd "C-t") #'hippie-expand) - ;; (global-set-key (kbd "C-u") nil) ; universal-argument - (global-set-key (kbd "C-v") #'yank) - (global-set-key (kbd "C-w") #'xah-close-current-buffer) - (global-set-key (kbd "C-x") nil) ; c-x map - (global-set-key (kbd "C-y") #'undo-redo) ; emacs 28 - (global-set-key (kbd "C-z") #'undo) - ;; - ) - - (global-set-key (kbd "<f7>") 'xah-fly-leader-key-map) - -;; HHHH------------------------------ - -(when (< emacs-major-version 28) - (defalias 'execute-extended-command-for-buffer #'execute-extended-command)) - -;; HHHH------------------------------ -;;;; misc - -;; 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 - -;; C-x $ → set-selective-display -;; C-x * → calc-dispatch -;; C-x - → shrink-window-if-larger-than-buffer -;; C-x . → set-fill-prefix -;; C-x 4 . → find-tag-other-window -;; C-x 4 0 → kill-buffer-and-window -;; C-x 4 C-o → display-buffer -;; C-x 4 a → add-change-log-entry-other-window -;; C-x 4 b → switch-to-buffer-other-window -;; C-x 4 c → clone-indirect-buffer-other-window -;; C-x 4 d → dired-other-window -;; C-x 4 f → find-file-other-window -;; C-x 4 m → compose-mail-other-window -;; C-x 4 r → find-file-read-only-other-window -;; C-x 5 . xref-find-definitions-other-frame -;; C-x 5 1 delete-other-frames -;; C-x 5 5 other-frame-prefix -;; C-x 5 C-o display-buffer-other-frame -;; C-x 5 b switch-to-buffer-other-frame -;; C-x 5 c clone-frame -;; C-x 5 d dired-other-frame -;; C-x 5 f find-file-other-frame -;; C-x 5 m compose-mail-other-frame -;; C-x 5 p project-other-frame-command -;; C-x 5 r find-file-read-only-other-frame -;; C-x 5 u undelete-frame -;; C-x 6 2 → 2C-two-columns -;; C-x 6 b → 2C-associate-buffer -;; C-x 6 s → 2C-split -;; C-x ; → comment-set-column -;; C-x < → scroll-left -;; C-x = → what-cursor-position, use describe-char instead -;; C-x > → scroll-right -;; C-x C-d → list-directory -;; C-x C-l → downcase-region -;; C-x C-n → set-goal-column -;; C-x C-o → delete-blank-lines -;; C-x C-p → mark-page -;; C-x C-r → find-file-read-only -;; C-x C-t → transpose-lines -;; C-x C-u → upcase-region -;; C-x C-v → find-alternate-file -;; C-x C-z → suspend-frame -;; C-x DEL → backward-kill-sentence -;; C-x ESC → Prefix Command -;; C-x [ → backward-page -;; C-x ] → forward-page -;; C-x f → set-fill-column -;; C-x i → insert-file -;; C-x k → kill-buffer -;; C-x l → count-lines-page -;; C-x m → compose-mail -;; C-x n g goto-line-relative -;; C-x n p narrow-to-page -;; C-x r M bookmark-set-no-overwrite -;; C-x r M-w copy-rectangle-as-kill -;; C-x r SPC point-to-register -;; C-x r f frameset-to-register -;; C-x r j jump-to-register -;; C-x r w window-configuration-to-register -;; C-x { → shrink-window-horizontally -;; C-x } → enlarge-window-horizontally - -;; HHHH------------------------------ - -(defvar xah-fly-insert-state-p t "non-nil means insertion mode is on.") - -(defun xah-fly--update-key-map () - (setq xah-fly-key-map (if xah-fly-insert-state-p - xah-fly-insert-map - xah-fly-command-map))) - -(defun xah-fly-keys-set-layout (Layout) - "Set a keyboard layout. -Argument must be one of the key name in `xah-fly-layout-diagrams' -Created: 2021-05-19 -Version: 2024-05-23" - (interactive - (list - (completing-read - "Choose a layout: " - (seq-sort 'string< (hash-table-keys xah-fly-layout-diagrams)) - nil t))) - (let ((xold xah-fly-key-current-layout) - (xkeydiagram (gethash Layout xah-fly-layout-diagrams))) - (when (not xkeydiagram) - (user-error - "xah-fly-keys-set-layout error: layout must be one of string:\n%s" - (mapconcat 'identity (hash-table-keys xah-fly-layout-diagrams) "\n"))) - (setq xah-fly-key-current-layout Layout) - (setq - xah-fly--key-convert-table - (xah-fly-create-key-conv-table - (gethash "dvorak" xah-fly-layout-diagrams) - xkeydiagram)) - (when (not (equal xold Layout)) (xah-fly-define-keys)))) - -(defun xah-fly-space-key () - "Switch to command mode if the char before cursor is a space. -experimental -Version: 2018-05-07" - (interactive) - (if (eq (char-before ) 32) - (xah-fly-command-mode-activate) - (insert " "))) - -(defun xah-fly-command-mode-init () - "Set command mode keys. -Version: 2022-07-06" - (interactive) - (setq xah-fly-insert-state-p nil) - (xah-fly--update-key-map) - (when xah-fly--deactivate-command-mode-func - (funcall xah-fly--deactivate-command-mode-func)) - (setq xah-fly--deactivate-command-mode-func - (set-transient-map xah-fly-command-map (lambda () t))) - (modify-all-frames-parameters (list (cons 'cursor-type 'box))) - ;; (set-face-background 'cursor "red") - (setq mode-line-front-space xah-fly-command-mode-indicator) - (force-mode-line-update)) - -(defun xah-fly-insert-mode-init (&optional no-indication) - "Enter insertion mode." - (interactive) - (setq xah-fly-insert-state-p t) - (xah-fly--update-key-map) - (funcall xah-fly--deactivate-command-mode-func) - (unless no-indication - (modify-all-frames-parameters '((cursor-type . bar))) - ;; (set-face-background 'cursor "black") - (setq mode-line-front-space xah-fly-insert-mode-indicator)) - (force-mode-line-update)) - -(defun xah-fly-mode-toggle () - "Switch between {insertion, command} modes." - (interactive) - (if xah-fly-insert-state-p - (xah-fly-command-mode-activate) - (xah-fly-insert-mode-activate))) - -(defun xah-fly-save-buffer-if-file () - "Save current buffer if it is a file." - (interactive) - (when buffer-file-name - (save-buffer))) - -(defun xah-fly-command-mode-activate () - "Activate command mode and run `xah-fly-command-mode-activate-hook' -Version: 2017-07-07" - (interactive) - (xah-fly-command-mode-init) - (run-hooks 'xah-fly-command-mode-activate-hook)) - -(defun xah-fly-command-mode-activate-no-hook () - "Activate command mode. Does not run `xah-fly-command-mode-activate-hook' -Version: 2017-07-07" - (interactive) - (xah-fly-command-mode-init)) - -(defun xah-fly-insert-mode-activate () - "Activate insertion mode. -Version: 2017-07-07" - (interactive) - (xah-fly-insert-mode-init) - (run-hooks 'xah-fly-insert-mode-activate-hook)) - -(defun xah-fly-insert-mode-activate-newline () - "Activate insertion mode, insert newline below." - (interactive) - (xah-fly-insert-mode-activate) - (open-line 1)) - -(defun xah-fly-insert-mode-activate-space-before () - "Insert a space, then activate insertion mode." - (interactive) - (insert " ") - (xah-fly-insert-mode-activate)) - -(defun xah-fly-insert-mode-activate-space-after () - "Insert a space, then activate insertion mode." - (interactive) - (insert " ") - (xah-fly-insert-mode-activate) - (left-char)) - -;; HHHH------------------------------ - -;;;###autoload -(define-minor-mode xah-fly-keys - "A modal keybinding set, like vim, but based on ergonomic - principles, like Dvorak layout. - -URL `http://xahlee.info/emacs/misc/xah-fly-keys.html'" - :global t - :lighter " xflykeys" - :keymap xah-fly-insert-map - (delete-selection-mode 1) - (setq shift-select-mode nil) - - (if xah-fly-keys - ;; Construction: - (progn - (add-hook 'minibuffer-setup-hook 'xah-fly-insert-mode-activate) - (add-hook 'minibuffer-exit-hook 'xah-fly-command-mode-activate) - (add-hook 'isearch-mode-end-hook 'xah-fly-command-mode-activate) - (xah-fly-command-mode-activate)) - (progn - ;; Teardown: - (remove-hook 'minibuffer-setup-hook 'xah-fly-insert-mode-activate) - (remove-hook 'minibuffer-exit-hook 'xah-fly-command-mode-activate) - (remove-hook 'isearch-mode-end-hook 'xah-fly-command-mode-activate) - (xah-fly-insert-mode-init :no-indication) - (setq mode-line-front-space '(:eval (if (display-graphic-p) " " "-"))) - - ;; - ))) - -(provide 'xah-fly-keys) - -;;; xah-fly-keys.el ends here