[ \n]?\\|[ \n]?\\| [ \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))))
+ (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 " " nil t) (replace-match "\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]"
+ ""
+ "`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 " \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 "") 'xah-previous-emacs-buffer)
+ (define-key xkmap (kbd "") 'xah-next-emacs-buffer)
+ (define-key xkmap (kbd "") 'xah-previous-user-buffer)
+ (define-key xkmap (kbd "") '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
+ '(("" . xah-fly-command-mode-activate)
+ ("" . xah-fly-command-mode-activate)
+ ("" . xah-fly-command-mode-activate))
+ :direct)
+
+ (xah-fly--define-keys
+ xah-fly-insert-map
+ '(("" . xah-fly-command-mode-activate)
+ ("" . xah-fly-command-mode-activate)
+ ("" . xah-fly-command-mode-activate))
+ :direct)
+
+ (when xah-fly-use-isearch-arrows
+ (xah-fly--define-keys
+ isearch-mode-map
+ '(("" . isearch-ring-retreat)
+ ("" . isearch-ring-advance)
+ ("" . isearch-repeat-backward)
+ ("" . isearch-repeat-forward)
+ ("C-v" . isearch-yank-kill))
+ :direct)
+ (xah-fly--define-keys
+ minibuffer-local-isearch-map
+ '(("" . isearch-reverse-exit-minibuffer)
+ ("" . 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 " . xah-page-up)
+ ("n " . 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 " . xah-move-block-up)
+ ("t " . 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. , ."
+ :type 'boolean)
+
+(when xah-fly-unset-useless-key
+ (global-set-key (kbd "") nil)
+ (global-set-key (kbd "") nil))
+
+(when xah-fly-use-meta-key
+
+ (global-set-key (kbd "M-") nil) ; beginning-of-buffer-other-window
+ (global-set-key (kbd "M-") 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-") #'xah-next-user-buffer)
+ (global-set-key (kbd "C-S-") #'xah-previous-user-buffer)
+
+ (global-set-key (kbd "C-S-") #'xah-previous-emacs-buffer)
+ (global-set-key (kbd "C-S-") #'xah-next-emacs-buffer)
+
+ (global-set-key (kbd "C-") #'xah-previous-user-buffer)
+ (global-set-key (kbd "C-") #'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 "") '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
diff --git a/.emacs.d/lisp/xah-fly-keys.el:Zone.Identifier b/.emacs.d/lisp/xah-fly-keys.el:Zone.Identifier
new file mode 100644
index 0000000..e69de29
|