Add 20 Emacs features round 13: string-reverse, sort-words, uniq-lines, encode/decode-html-entities, url-decode, camelcase-to-snake, snake-to-camel, kebab-to-camel, wrap-region, unwrap-region, quote-region, strip-comments, insert-file-header, insert-license, insert-shebang, open-in-external, copy-line-number, rename-file-and-buffer, sudo-edit
ober
d1369d947d8f39ababfc66b956e2e7e864f1c993
--- a/docs/jemacs-vs-emacs.md +++ b/docs/jemacs-vs-emacs.md @@ -1366,6 +1366,26 @@ No remaining Tier 1 gaps. All core editing, completion, and navigation features | MD5 hash | :orange_circle: | Compute MD5 hash of text via md5sum | | Word frequency | :orange_circle: | Word frequency analysis with top-50 display | | Text statistics | :orange_circle: | Characters, words, lines, sentences, reading time | +| String reverse | :orange_circle: | Reverse selected text or current line | +| Sort words | :orange_circle: | Alphabetically sort words in selection | +| Unique lines | :orange_circle: | Remove duplicate lines from buffer | +| Encode HTML entities | :orange_circle: | Encode &<>"' as HTML entities | +| Decode HTML entities | :orange_circle: | Decode HTML entities back to characters | +| URL decode | :orange_circle: | Decode URL-encoded text via Python | +| CamelCase to snake_case | :orange_circle: | Case conversion for identifiers | +| snake_case to camelCase | :orange_circle: | Case conversion for identifiers | +| kebab-case to camelCase | :orange_circle: | Case conversion for identifiers | +| Wrap region | :orange_circle: | Wrap selection with user-specified chars | +| Unwrap region | :orange_circle: | Remove outermost wrapping characters | +| Quote region | :orange_circle: | Prefix each line with > | +| Strip comments | :orange_circle: | Remove comment lines (#, //, ;) | +| Insert file header | :orange_circle: | Auto-detect comment style, insert header template | +| Insert license | :orange_circle: | MIT, Apache, GPL, BSD, Unlicense templates | +| Insert shebang | :orange_circle: | Shebang lines for bash, python, ruby, node, etc. | +| Open in external app | :orange_circle: | Open file with xdg-open | +| Copy line number | :orange_circle: | Copy current line number to kill ring | +| Rename file and buffer | :orange_circle: | Rename file on disk and update buffer | +| Sudo edit | :orange_circle: | Re-open file with sudo privileges | --- --- a/src/jerboa-emacs/editor-extra-final.ss +++ b/src/jerboa-emacs/editor-extra-final.ss @@ -5348,3 +5348,239 @@ (editor-set-text ed content) (editor-goto-pos ed 0) (echo-message! echo (str words " words, " lines " lines")))))) + +;; ===== Round 13 Batch 2 ===== + +;; --- Feature 11: Unwrap Region --- + +(def (cmd-unwrap-region app) + "Remove the outermost wrapping characters from selection." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (sel-start (send-message ed SCI_GETSELECTIONSTART 0 0)) + (sel-end (send-message ed SCI_GETSELECTIONEND 0 0))) + (if (or (= sel-start sel-end) (< (- sel-end sel-start) 2)) + (echo-message! echo "No selection or too short") + (let* ((text (editor-get-text-range ed sel-start (- sel-end sel-start))) + (unwrapped (substring text 1 (- (string-length text) 1)))) + (editor-replace-selection ed unwrapped) + (echo-message! echo "Unwrapped"))))) + +;; --- Feature 12: Quote Region --- + +(def (cmd-quote-region app) + "Quote each line in the selection with > prefix." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (sel-start (send-message ed SCI_GETSELECTIONSTART 0 0)) + (sel-end (send-message ed SCI_GETSELECTIONEND 0 0))) + (if (= sel-start sel-end) + (echo-message! echo "No selection") + (let* ((text (editor-get-text-range ed sel-start (- sel-end sel-start))) + (lines (string-split text #\newline)) + (quoted (map (lambda (l) (str "> " l)) lines)) + (result (string-join quoted "\n"))) + (editor-replace-selection ed result) + (echo-message! echo "Region quoted"))))) + +;; --- Feature 13: Strip Comments --- + +(def (cmd-strip-comments app) + "Remove comment lines from the buffer (lines starting with # or //)." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (len (send-message ed SCI_GETLENGTH 0 0)) + (text (editor-get-text ed len)) + (lines (string-split text #\newline)) + (non-comments (filter (lambda (l) + (let ((trimmed (string-trim l))) + (and (> (string-length trimmed) 0) + (not (string-prefix? "#" trimmed)) + (not (string-prefix? "//" trimmed)) + (not (string-prefix? ";" trimmed))))) + lines)) + (removed (- (length lines) (length non-comments)))) + (editor-set-text ed (string-join non-comments "\n")) + (editor-goto-pos ed 0) + (echo-message! echo (str "Stripped " removed " comment lines")))) + +;; --- Feature 14: Insert File Header --- + +(def (cmd-insert-file-header app) + "Insert a file header comment with filename, author, date." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (buf (edit-window-buffer win)) + (file (buffer-file buf)) + (name (if file + (let loop ((i (- (string-length file) 1))) + (cond ((< i 0) file) + ((char=? (string-ref file i) #\/) + (substring file (+ i 1) (string-length file))) + (else (loop (- i 1))))) + "untitled")) + (user (or (getenv "USER") "unknown")) + (date (number->string (time-second (current-time)))) + (ext (if file (path-extension file) "")) + (comment-style (cond + ((member ext '("ss" "scm" "el" "lisp" "clj")) ";;") + ((member ext '("py" "rb" "sh" "bash" "zsh" "yaml" "yml")) "#") + ((member ext '("c" "cpp" "h" "java" "js" "ts" "go" "rs")) "//") + (else "//"))) + (header (string-append + comment-style " " name "\n" + comment-style " Author: " user "\n" + comment-style " Created: " date "\n" + comment-style " Description: \n\n"))) + (send-message ed SCI_GOTOPOS 0 0) + (editor-insert-text ed header) + (echo-message! echo "File header inserted"))) + +;; --- Feature 15: Insert License --- + +(def *license-templates* + '(("MIT" . "MIT License\n\nCopyright (c) [year] [author]\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND.") + ("Apache-2.0" . "Licensed under the Apache License, Version 2.0") + ("GPL-3.0" . "This program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License.") + ("BSD-2" . "Redistribution and use in source and binary forms, with or without\nmodification, are permitted.") + ("Unlicense" . "This is free and unencumbered software released into the public domain."))) + +(def (cmd-insert-license app) + "Insert a license header into the buffer." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (row (tui-rows)) (width (tui-cols)) + (names (map car *license-templates*)) + (choice (echo-read-string-with-completion echo "License: " names row width))) + (when (and choice (not (string-empty? choice))) + (let ((tmpl (assoc choice *license-templates*))) + (if tmpl + (begin + (editor-insert-text ed (cdr tmpl)) + (echo-message! echo (str "Inserted " choice " license"))) + (echo-message! echo "Unknown license")))))) + +;; --- Feature 16: Insert Shebang --- + +(def *shebang-templates* + '(("bash" . "#!/usr/bin/env bash") + ("sh" . "#!/bin/sh") + ("python" . "#!/usr/bin/env python3") + ("python2" . "#!/usr/bin/env python2") + ("ruby" . "#!/usr/bin/env ruby") + ("node" . "#!/usr/bin/env node") + ("perl" . "#!/usr/bin/env perl") + ("scheme" . "#!/usr/bin/env scheme --script"))) + +(def (cmd-insert-shebang app) + "Insert a shebang line at the top of the buffer." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (row (tui-rows)) (width (tui-cols)) + (names (map car *shebang-templates*)) + (choice (echo-read-string-with-completion echo "Shebang: " names row width))) + (when (and choice (not (string-empty? choice))) + (let ((tmpl (assoc choice *shebang-templates*))) + (when tmpl + (send-message ed SCI_GOTOPOS 0 0) + (editor-insert-text ed (str (cdr tmpl) "\n")) + (echo-message! echo (str "Shebang inserted: " (cdr tmpl)))))))) + +;; --- Feature 17: Open in External App --- + +(def (cmd-open-in-external app) + "Open the current file with the system's default application." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (buf (edit-window-buffer win)) + (file (buffer-file buf))) + (if (not file) + (echo-message! echo "No file associated with buffer") + (with-catch + (lambda (e) (echo-message! echo (str "open error: " e))) + (lambda () + (let-values (((si so se pid) + (open-process-ports + (str "xdg-open " (shell-quote file) " 2>/dev/null &") + 'block (native-transcoder)))) + (close-port si) (close-port so) (close-port se) + (echo-message! echo (str "Opened externally: " file)))))))) + +;; --- Feature 18: Copy Line Number --- + +(def (cmd-copy-line-number app) + "Copy the current line number to the kill ring." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (line-num (+ 1 (send-message ed SCI_LINEFROMPOSITION + (send-message ed SCI_GETCURRENTPOS 0 0) 0))) + (line-str (number->string line-num))) + (send-message ed SCI_COPYTEXT (string-length line-str) line-str) + (echo-message! echo (str "Copied line number: " line-num)))) + +;; --- Feature 19: Rename File and Buffer --- + +(def (cmd-rename-file-and-buffer app) + "Rename the current file on disk and update the buffer name." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (buf (edit-window-buffer win)) + (file (buffer-file buf))) + (if (not file) + (echo-message! echo "No file associated with buffer") + (let* ((row (tui-rows)) (width (tui-cols)) + (new-name (echo-read-string echo (str "Rename to (from " file "): ") row width))) + (when (and new-name (not (string-empty? new-name))) + (let ((new-path (string-trim new-name))) + (with-catch + (lambda (e) (echo-message! echo (str "Rename error: " e))) + (lambda () + (rename-file file new-path) + (buffer-file-set! buf new-path) + (echo-message! echo (str "Renamed to " new-path)))))))))) + +;; --- Feature 20: Sudo Edit --- + +(def (cmd-sudo-edit app) + "Re-open the current file with sudo privileges." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (buf (edit-window-buffer win)) + (file (buffer-file buf))) + (if (not file) + (echo-message! echo "No file associated with buffer") + (with-catch + (lambda (e) (echo-message! echo (str "sudo error: " e))) + (lambda () + (let-values (((si so se pid) + (open-process-ports (str "sudo cat " (shell-quote file)) + 'block (native-transcoder)))) + (close-port si) + (let loop ((lines '())) + (let ((line (get-line so))) + (if (eof-object? line) + (begin + (close-port so) (close-port se) + (let ((content (string-join (reverse lines) "\n"))) + (editor-set-text ed content) + (editor-goto-pos ed 0) + (echo-message! echo (str "Opened with sudo: " file)))) + (loop (cons line lines))))))))))) --- a/src/jerboa-emacs/editor-extra-modes.ss +++ b/src/jerboa-emacs/editor-extra-modes.ss @@ -5649,3 +5649,276 @@ (ed (edit-window-editor win))) (send-message ed SCI_COPYTEXT (string-length name) name) (echo-message! echo (str "Copied: " name)))))) + +;; ===== Round 13 Batch 1 ===== + +;; --- Feature 1: String Reverse --- + +(def (cmd-string-reverse app) + "Reverse the selected text or current line." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (sel-start (send-message ed SCI_GETSELECTIONSTART 0 0)) + (sel-end (send-message ed SCI_GETSELECTIONEND 0 0))) + (if (= sel-start sel-end) + ;; Reverse current line + (let* ((line-num (send-message ed SCI_LINEFROMPOSITION + (send-message ed SCI_GETCURRENTPOS 0 0) 0)) + (start (send-message ed SCI_POSITIONFROMLINE line-num 0)) + (end (send-message ed SCI_GETLINEENDPOSITION line-num 0)) + (text (editor-get-text-range ed start (- end start))) + (reversed (list->string (reverse (string->list text))))) + (send-message ed SCI_SETTARGETSTART start 0) + (send-message ed SCI_SETTARGETEND end 0) + (send-message ed SCI_REPLACETARGET -1 reversed) + (echo-message! echo "Line reversed")) + ;; Reverse selection + (let* ((text (editor-get-text-range ed sel-start (- sel-end sel-start))) + (reversed (list->string (reverse (string->list text))))) + (editor-replace-selection ed reversed) + (echo-message! echo "Selection reversed"))))) + +;; --- Feature 2: Sort Words --- + +(def (cmd-sort-words app) + "Sort words in selection or current line alphabetically." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (sel-start (send-message ed SCI_GETSELECTIONSTART 0 0)) + (sel-end (send-message ed SCI_GETSELECTIONEND 0 0))) + (if (= sel-start sel-end) + (echo-message! echo "No selection — select text to sort words") + (let* ((text (editor-get-text-range ed sel-start (- sel-end sel-start))) + (words (filter (lambda (w) (> (string-length w) 0)) + (string-split text #\space))) + (sorted (sort string<? words)) + (result (string-join sorted " "))) + (editor-replace-selection ed result) + (echo-message! echo (str "Sorted " (length sorted) " words")))))) + +;; --- Feature 3: Unique Lines --- + +(def (cmd-uniq-lines app) + "Remove duplicate lines from the buffer." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (len (send-message ed SCI_GETLENGTH 0 0)) + (text (editor-get-text ed len)) + (lines (string-split text #\newline)) + (seen (make-hash-table)) + (unique (filter (lambda (line) + (if (hash-key? seen line) #f + (begin (hash-put! seen line #t) #t))) + lines)) + (removed (- (length lines) (length unique)))) + (editor-set-text ed (string-join unique "\n")) + (editor-goto-pos ed 0) + (echo-message! echo (str "Removed " removed " duplicate lines")))) + +;; --- Feature 4: Encode HTML Entities --- + +(def (cmd-encode-html-entities app) + "Encode special characters as HTML entities in selection." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (sel-start (send-message ed SCI_GETSELECTIONSTART 0 0)) + (sel-end (send-message ed SCI_GETSELECTIONEND 0 0))) + (if (= sel-start sel-end) + (echo-message! echo "No selection") + (let* ((text (editor-get-text-range ed sel-start (- sel-end sel-start))) + (encoded (let loop ((chars (string->list text)) (acc '())) + (if (null? chars) + (list->string (reverse acc)) + (let ((c (car chars))) + (cond + ((char=? c #\&) (loop (cdr chars) (append (reverse (string->list "&")) acc))) + ((char=? c #\<) (loop (cdr chars) (append (reverse (string->list "<")) acc))) + ((char=? c #\>) (loop (cdr chars) (append (reverse (string->list ">")) acc))) + ((char=? c #\") (loop (cdr chars) (append (reverse (string->list """)) acc))) + ((char=? c #\') (loop (cdr chars) (append (reverse (string->list "'")) acc))) + (else (loop (cdr chars) (cons c acc))))))))) + (editor-replace-selection ed encoded) + (echo-message! echo "HTML entities encoded"))))) + +;; --- Feature 5: Decode HTML Entities --- + +(def (cmd-decode-html-entities app) + "Decode HTML entities back to characters in selection." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (sel-start (send-message ed SCI_GETSELECTIONSTART 0 0)) + (sel-end (send-message ed SCI_GETSELECTIONEND 0 0))) + (if (= sel-start sel-end) + (echo-message! echo "No selection") + (let* ((text (editor-get-text-range ed sel-start (- sel-end sel-start))) + (decoded text)) + ;; Simple entity replacements + (let* ((d1 (let loop ((s decoded)) + (let ((pos (string-contains s "&"))) + (if (not pos) s + (loop (string-append (substring s 0 pos) "&" + (substring s (+ pos 5) (string-length s)))))))) + (d2 (let loop ((s d1)) + (let ((pos (string-contains s "<"))) + (if (not pos) s + (loop (string-append (substring s 0 pos) "<" + (substring s (+ pos 4) (string-length s)))))))) + (d3 (let loop ((s d2)) + (let ((pos (string-contains s ">"))) + (if (not pos) s + (loop (string-append (substring s 0 pos) ">" + (substring s (+ pos 4) (string-length s)))))))) + (d4 (let loop ((s d3)) + (let ((pos (string-contains s """))) + (if (not pos) s + (loop (string-append (substring s 0 pos) "\"" + (substring s (+ pos 6) (string-length s))))))))) + (editor-replace-selection ed d4) + (echo-message! echo "HTML entities decoded")))))) + +;; --- Feature 6: URL Decode --- + +(def (cmd-url-decode app) + "Decode URL-encoded text in selection." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (sel-start (send-message ed SCI_GETSELECTIONSTART 0 0)) + (sel-end (send-message ed SCI_GETSELECTIONEND 0 0))) + (if (= sel-start sel-end) + (echo-message! echo "No selection") + (let ((text (editor-get-text-range ed sel-start (- sel-end sel-start)))) + (with-catch + (lambda (e) (echo-message! echo (str "Decode error: " e))) + (lambda () + (let-values (((si so se pid) + (open-process-ports + (str "python3 -c 'import sys,urllib.parse;print(urllib.parse.unquote(sys.stdin.read()),end=\"\")' 2>/dev/null") + 'block (native-transcoder)))) + (display text si) + (close-port si) + (let ((decoded (get-line so))) + (close-port so) (close-port se) + (when (not (eof-object? decoded)) + (editor-replace-selection ed decoded) + (echo-message! echo "URL decoded")))))))))) + +;; --- Feature 7: CamelCase to snake_case --- + +(def (cmd-camelcase-to-snake app) + "Convert camelCase/PascalCase to snake_case in selection." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (sel-start (send-message ed SCI_GETSELECTIONSTART 0 0)) + (sel-end (send-message ed SCI_GETSELECTIONEND 0 0))) + (if (= sel-start sel-end) + (echo-message! echo "No selection") + (let* ((text (editor-get-text-range ed sel-start (- sel-end sel-start))) + (result (let loop ((chars (string->list text)) (acc '()) (prev-lower #f)) + (if (null? chars) + (list->string (reverse acc)) + (let ((c (car chars))) + (if (and prev-lower (char-upper-case? c)) + (loop (cdr chars) (cons (char-downcase c) (cons #\_ acc)) #f) + (loop (cdr chars) (cons (char-downcase c) acc) (char-lower-case? c)))))))) + (editor-replace-selection ed result) + (echo-message! echo "Converted to snake_case"))))) + +;; --- Feature 8: snake_case to camelCase --- + +(def (cmd-snake-to-camel app) + "Convert snake_case to camelCase in selection." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (sel-start (send-message ed SCI_GETSELECTIONSTART 0 0)) + (sel-end (send-message ed SCI_GETSELECTIONEND 0 0))) + (if (= sel-start sel-end) + (echo-message! echo "No selection") + (let* ((text (editor-get-text-range ed sel-start (- sel-end sel-start))) + (parts (string-split text #\_)) + (result (if (null? parts) "" + (string-append + (car parts) + (apply string-append + (map (lambda (p) + (if (> (string-length p) 0) + (string-append + (string (char-upcase (string-ref p 0))) + (substring p 1 (string-length p))) + "")) + (cdr parts))))))) + (editor-replace-selection ed result) + (echo-message! echo "Converted to camelCase"))))) + +;; --- Feature 9: kebab-case to camelCase --- + +(def (cmd-kebab-to-camel app) + "Convert kebab-case to camelCase in selection." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (sel-start (send-message ed SCI_GETSELECTIONSTART 0 0)) + (sel-end (send-message ed SCI_GETSELECTIONEND 0 0))) + (if (= sel-start sel-end) + (echo-message! echo "No selection") + (let* ((text (editor-get-text-range ed sel-start (- sel-end sel-start))) + (parts (string-split text #\-)) + (result (if (null? parts) "" + (string-append + (car parts) + (apply string-append + (map (lambda (p) + (if (> (string-length p) 0) + (string-append + (string (char-upcase (string-ref p 0))) + (substring p 1 (string-length p))) + "")) + (cdr parts))))))) + (editor-replace-selection ed result) + (echo-message! echo "Converted to camelCase"))))) + +;; --- Feature 10: Wrap Region --- + +(def (cmd-wrap-region app) + "Wrap the selection with user-specified characters." + (let* ((echo (app-state-echo app)) + (frame (app-state-frame app)) + (win (current-window frame)) + (ed (edit-window-editor win)) + (sel-start (send-message ed SCI_GETSELECTIONSTART 0 0)) + (sel-end (send-message ed SCI_GETSELECTIONEND 0 0))) + (if (= sel-start sel-end) + (echo-message! echo "No selection") + (let* ((row (tui-rows)) (width (tui-cols)) + (wrapper (echo-read-string echo "Wrap with (e.g. \" or ( or <tag>): " row width))) + (when (and wrapper (not (string-empty? wrapper))) + (let* ((text (editor-get-text-range ed sel-start (- sel-end sel-start))) + (open-char (string-trim wrapper)) + (close-char (cond + ((string=? open-char "(") ")") + ((string=? open-char "[") "]") + ((string=? open-char "{") "}") + ((string=? open-char "<") ">") + ((string=? open-char "\"") "\"") + ((string=? open-char "'") "'") + ((string=? open-char "`") "`") + (else open-char))) + (wrapped (str open-char text close-char))) + (editor-replace-selection ed wrapped) + (echo-message! echo "Region wrapped"))))))) --- a/src/jerboa-emacs/editor-extra-regs2.ss +++ b/src/jerboa-emacs/editor-extra-regs2.ss @@ -1703,4 +1703,26 @@ (register-command! 'md5-hash cmd-md5-hash) (register-command! 'word-frequency cmd-word-frequency) (register-command! 'text-statistics cmd-text-statistics) + ;; Round 13 batch 1: string-reverse, sort-words, uniq-lines, encode-html-entities, decode-html-entities, url-decode, camelcase-to-snake, snake-to-camel, kebab-to-camel, wrap-region + (register-command! 'string-reverse cmd-string-reverse) + (register-command! 'sort-words cmd-sort-words) + (register-command! 'uniq-lines cmd-uniq-lines) + (register-command! 'encode-html-entities cmd-encode-html-entities) + (register-command! 'decode-html-entities cmd-decode-html-entities) + (register-command! 'url-decode cmd-url-decode) + (register-command! 'camelcase-to-snake cmd-camelcase-to-snake) + (register-command! 'snake-to-camel cmd-snake-to-camel) + (register-command! 'kebab-to-camel cmd-kebab-to-camel) + (register-command! 'wrap-region cmd-wrap-region) + ;; Round 13 batch 2: unwrap-region, quote-region, strip-comments, insert-file-header, insert-license, insert-shebang, open-in-external, copy-line-number, rename-file-and-buffer, sudo-edit + (register-command! 'unwrap-region cmd-unwrap-region) + (register-command! 'quote-region cmd-quote-region) + (register-command! 'strip-comments cmd-strip-comments) + (register-command! 'insert-file-header cmd-insert-file-header) + (register-command! 'insert-license cmd-insert-license) + (register-command! 'insert-shebang cmd-insert-shebang) + (register-command! 'open-in-external cmd-open-in-external) + (register-command! 'copy-line-number cmd-copy-line-number) + (register-command! 'rename-file-and-buffer cmd-rename-file-and-buffer) + (register-command! 'sudo-edit cmd-sudo-edit) )