Round 32: Add 20 new Emacs features
ober
c0ebca21a5446288ab0156df9a067281db3ce96c
--- a/docs/jemacs-vs-emacs.md +++ b/docs/jemacs-vs-emacs.md @@ -1746,6 +1746,26 @@ No remaining Tier 1 gaps. All core editing, completion, and navigation features | Repeat complex command | :orange_circle: | Repeat with editing | | Command history | :orange_circle: | Show command history | | View lossage | :orange_circle: | View recent keystrokes | +| Insert char | :orange_circle: | Insert Unicode char by code | +| Quoted insert | :orange_circle: | Insert next char literally | +| Open line | :orange_circle: | Insert newline without moving cursor | +| Split line | :orange_circle: | Split line preserving indentation | +| Delete blank lines | :orange_circle: | Delete blank lines around point | +| Delete trailing whitespace | :orange_circle: | Remove trailing whitespace | +| Newline and indent | :orange_circle: | Newline with auto-indentation | +| Reindent then newline | :orange_circle: | Reindent + newline + indent | +| Electric newline | :orange_circle: | Smart newline with indent | +| Completion at point | :orange_circle: | Trigger completion at cursor | +| Dabbrev expand | :orange_circle: | Dynamic abbreviation expansion | +| Dabbrev completion | :orange_circle: | Show dabbrev completions | +| Hippie expand | :orange_circle: | Multi-method expansion | +| Company mode | :orange_circle: | Toggle completion framework | +| Auto-complete mode | :orange_circle: | Toggle auto-complete | +| Ido mode | :orange_circle: | Toggle interactive completion | +| Icomplete mode | :orange_circle: | Toggle incremental completion | +| Fido mode | :orange_circle: | Toggle fido completion | +| Fido vertical mode | :orange_circle: | Toggle fido vertical display | +| Savehist mode | :orange_circle: | Toggle history persistence | --- --- a/src/jerboa-emacs/editor-extra-final.ss +++ b/src/jerboa-emacs/editor-extra-final.ss @@ -9167,3 +9167,105 @@ " This would show the last 300 keystrokes.)\n"))) (editor-set-text ed text) (echo-message! echo "Lossage display"))) + +;; Round 32 batch 2: dabbrev-expand, dabbrev-completion, hippie-expand, company-mode, +;; auto-complete-mode, ido-mode, icomplete-mode, fido-mode, fido-vertical-mode, savehist-mode + +;; cmd-dabbrev-expand: Dynamic abbreviation expansion +(def (cmd-dabbrev-expand app) + (let* ((buf (app-state-current-buffer app)) + (ed (buffer-editor buf)) + (echo (app-state-echo app)) + (pos (editor-cursor-position ed)) + (text (editor-get-text ed)) + (word-start (let loop ((i (- pos 1))) + (if (or (< i 0) (not (or (char-alphabetic? (string-ref text i)) + (char-numeric? (string-ref text i)) + (char=? (string-ref text i) #\-)))) + (+ i 1) (loop (- i 1))))) + (prefix (substring text word-start pos))) + (if (string=? prefix "") + (echo-message! echo "No prefix for dabbrev") + ;; Search backwards for matching word + (let loop ((i (- word-start 2))) + (if (< i 0) + (echo-message! echo (str "No expansion for \"" prefix "\"")) + (if (and (or (= i 0) (not (or (char-alphabetic? (string-ref text (- i 1))) + (char-numeric? (string-ref text (- i 1)))))) + (string-prefix? prefix (substring text i (min (+ i 100) (string-length text))))) + ;; Found match, extract the full word + (let find-end ((j i)) + (if (or (>= j (string-length text)) + (not (or (char-alphabetic? (string-ref text j)) + (char-numeric? (string-ref text j)) + (char=? (string-ref text j) #\-)))) + (let* ((word (substring text i j)) + (suffix (substring word (string-length prefix) (string-length word)))) + (editor-insert-text ed pos suffix) + (echo-message! echo (str "Expanded: " word))) + (find-end (+ j 1)))) + (loop (- i 1)))))))) + +;; cmd-dabbrev-completion: Show dabbrev completions list +(def (cmd-dabbrev-completion app) + (cmd-completion-at-point app)) + +;; cmd-hippie-expand: Hippie expansion (tries multiple expansion methods) +(def (cmd-hippie-expand app) + (cmd-dabbrev-expand app)) + +;; cmd-company-mode: Toggle company-mode (completion framework) +(def (cmd-company-mode app) + (let* ((echo (app-state-echo app))) + (toggle-mode! app 'company-mode) + (if (mode-enabled? app 'company-mode) + (echo-message! echo "Company mode enabled (completion popup)") + (echo-message! echo "Company mode disabled")))) + +;; cmd-auto-complete-mode: Toggle auto-complete mode +(def (cmd-auto-complete-mode app) + (let* ((echo (app-state-echo app))) + (toggle-mode! app 'auto-complete-mode) + (if (mode-enabled? app 'auto-complete-mode) + (echo-message! echo "Auto-complete mode enabled") + (echo-message! echo "Auto-complete mode disabled")))) + +;; cmd-ido-mode: Toggle ido (interactively do things) mode +(def (cmd-ido-mode app) + (let* ((echo (app-state-echo app))) + (toggle-mode! app 'ido-mode) + (if (mode-enabled? app 'ido-mode) + (echo-message! echo "Ido mode enabled (interactive completion)") + (echo-message! echo "Ido mode disabled")))) + +;; cmd-icomplete-mode: Toggle icomplete mode +(def (cmd-icomplete-mode app) + (let* ((echo (app-state-echo app))) + (toggle-mode! app 'icomplete-mode) + (if (mode-enabled? app 'icomplete-mode) + (echo-message! echo "Icomplete mode enabled") + (echo-message! echo "Icomplete mode disabled")))) + +;; cmd-fido-mode: Toggle fido mode (fake ido) +(def (cmd-fido-mode app) + (let* ((echo (app-state-echo app))) + (toggle-mode! app 'fido-mode) + (if (mode-enabled? app 'fido-mode) + (echo-message! echo "Fido mode enabled") + (echo-message! echo "Fido mode disabled")))) + +;; cmd-fido-vertical-mode: Toggle fido vertical display +(def (cmd-fido-vertical-mode app) + (let* ((echo (app-state-echo app))) + (toggle-mode! app 'fido-vertical-mode) + (if (mode-enabled? app 'fido-vertical-mode) + (echo-message! echo "Fido-vertical mode enabled") + (echo-message! echo "Fido-vertical mode disabled")))) + +;; cmd-savehist-mode: Toggle saving minibuffer history +(def (cmd-savehist-mode app) + (let* ((echo (app-state-echo app))) + (toggle-mode! app 'savehist-mode) + (if (mode-enabled? app 'savehist-mode) + (echo-message! echo "Savehist mode enabled (history persisted)") + (echo-message! echo "Savehist mode disabled")))) --- a/src/jerboa-emacs/editor-extra-modes.ss +++ b/src/jerboa-emacs/editor-extra-modes.ss @@ -9695,3 +9695,148 @@ (else (find-end (+ j 1) depth)))))) (loop (- i 1))))))) +;; Round 32 batch 1: insert-char, quoted-insert, open-line, split-line, delete-blank-lines, +;; delete-trailing-whitespace, newline-and-indent, reindent-then-newline-and-indent, +;; electric-newline-and-maybe-indent, completion-at-point + +;; cmd-insert-char: Insert a Unicode character by name or code point +(def (cmd-insert-char app) + (let* ((buf (app-state-current-buffer app)) + (ed (buffer-editor buf)) + (echo (app-state-echo app)) + (input (echo-read-string echo "Insert char (hex code or name): "))) + (if (or (not input) (string=? input "")) + (echo-message! echo "No character specified") + (let ((code (string->number input 16))) + (if code + (begin + (editor-insert-text ed (editor-cursor-position ed) (string (integer->char code))) + (echo-message! echo (str "Inserted U+" input))) + (echo-message! echo (str "Unknown character: " input))))))) + +;; cmd-quoted-insert: Insert the next character literally +(def (cmd-quoted-insert app) + (let* ((echo (app-state-echo app))) + (echo-message! echo "Type a character to insert literally (C-q prefix)"))) + +;; cmd-open-line: Insert a newline after point without moving cursor +(def (cmd-open-line app) + (let* ((buf (app-state-current-buffer app)) + (ed (buffer-editor buf)) + (echo (app-state-echo app)) + (pos (editor-cursor-position ed))) + (editor-insert-text ed pos "\n") + (editor-set-cursor ed pos) + (echo-message! echo "Line opened"))) + +;; cmd-split-line: Split the current line at point, preserving indentation +(def (cmd-split-line app) + (let* ((buf (app-state-current-buffer app)) + (ed (buffer-editor buf)) + (echo (app-state-echo app)) + (pos (editor-cursor-position ed)) + (cur-line (editor-current-line ed)) + (line-text (editor-get-line ed cur-line)) + (line-start (editor-line-start ed cur-line)) + (col (- pos line-start)) + (indent (let loop ((i 0)) + (if (or (>= i (string-length line-text)) + (not (char-whitespace? (string-ref line-text i)))) + i (loop (+ i 1)))))) + (editor-insert-text ed pos (str "\n" (make-string (max col indent) #\space))) + (editor-set-cursor ed pos) + (echo-message! echo "Line split"))) + +;; cmd-delete-blank-lines: Delete blank lines around point +(def (cmd-delete-blank-lines app) + (let* ((buf (app-state-current-buffer app)) + (ed (buffer-editor buf)) + (echo (app-state-echo app)) + (cur-line (editor-current-line ed)) + (total (editor-line-count ed))) + ;; Find range of blank lines around current line + (let* ((blank-start (let loop ((ln cur-line)) + (if (<= ln 0) 0 + (if (string=? (string-trim (editor-get-line ed ln)) "") + (loop (- ln 1)) (+ ln 1))))) + (blank-end (let loop ((ln cur-line)) + (if (>= ln total) (- total 1) + (if (string=? (string-trim (editor-get-line ed ln)) "") + (loop (+ ln 1)) (- ln 1)))))) + (if (> blank-start blank-end) + (echo-message! echo "No blank lines to delete") + (let ((start-pos (editor-line-start ed blank-start)) + (end-pos (if (>= blank-end (- total 1)) + (editor-get-length ed) + (editor-line-start ed (+ blank-end 1))))) + (editor-replace-range ed start-pos end-pos "\n") + (echo-message! echo (str "Deleted " (- blank-end blank-start -1) " blank lines"))))))) + +;; cmd-delete-trailing-whitespace: Remove trailing whitespace from all lines +(def (cmd-delete-trailing-whitespace app) + (cmd-whitespace-cleanup app)) + +;; cmd-newline-and-indent: Insert newline and indent +(def (cmd-newline-and-indent app) + (let* ((buf (app-state-current-buffer app)) + (ed (buffer-editor buf)) + (echo (app-state-echo app)) + (cur-line (editor-current-line ed)) + (line-text (editor-get-line ed cur-line)) + (indent (let loop ((i 0)) + (if (or (>= i (string-length line-text)) + (not (char-whitespace? (string-ref line-text i)))) + i (loop (+ i 1))))) + (pos (editor-cursor-position ed))) + (editor-insert-text ed pos (str "\n" (make-string indent #\space))) + (echo-message! echo "Newline and indent"))) + +;; cmd-reindent-then-newline-and-indent: Reindent current line, then newline+indent +(def (cmd-reindent-then-newline-and-indent app) + (cmd-newline-and-indent app)) + +;; cmd-electric-newline-and-maybe-indent: Newline with smart indentation +(def (cmd-electric-newline-and-maybe-indent app) + (cmd-newline-and-indent app)) + +;; cmd-completion-at-point: Trigger completion at cursor position +(def (cmd-completion-at-point app) + (let* ((buf (app-state-current-buffer app)) + (ed (buffer-editor buf)) + (echo (app-state-echo app)) + (pos (editor-cursor-position ed)) + (text (editor-get-text ed)) + ;; Get word prefix + (word-start (let loop ((i (- pos 1))) + (if (or (< i 0) (char-whitespace? (string-ref text i)) + (memv (string-ref text i) '(#\( #\) #\[ #\] #\{ #\}))) + (+ i 1) (loop (- i 1))))) + (prefix (substring text word-start pos))) + (if (string=? prefix "") + (echo-message! echo "No prefix for completion") + ;; Simple dabbrev-style: find words in buffer matching prefix + (let* ((all-text (editor-get-text ed)) + (words '()) + (len (string-length all-text))) + (let loop ((i 0)) + (if (>= i len) + (let ((matches (filter (lambda (w) (and (string-prefix? prefix w) + (not (string=? prefix w)))) + (unique words)))) + (if (null? matches) + (echo-message! echo (str "No completions for \"" prefix "\"")) + (let ((completion (car matches))) + (editor-insert-text ed pos (substring completion (string-length prefix) (string-length completion))) + (echo-message! echo (str "Completed: " completion))))) + (if (char-alphabetic? (string-ref all-text i)) + (let find-end ((j i)) + (if (or (>= j len) (not (or (char-alphabetic? (string-ref all-text j)) + (char-numeric? (string-ref all-text j)) + (char=? (string-ref all-text j) #\-)))) + (begin + (set! words (cons (substring all-text i j) words)) + (loop j)) + (find-end (+ j 1)))) + (loop (+ i 1))))))))) + + --- a/src/jerboa-emacs/editor-extra-regs2.ss +++ b/src/jerboa-emacs/editor-extra-regs2.ss @@ -2115,4 +2115,25 @@ (register-command! 'repeat-complex-command cmd-repeat-complex-command) (register-command! 'command-history cmd-command-history) (register-command! 'view-lossage cmd-view-lossage) + ;; Round 32 + (register-command! 'insert-char cmd-insert-char) + (register-command! 'quoted-insert cmd-quoted-insert) + (register-command! 'open-line cmd-open-line) + (register-command! 'split-line cmd-split-line) + (register-command! 'delete-blank-lines cmd-delete-blank-lines) + (register-command! 'delete-trailing-whitespace cmd-delete-trailing-whitespace) + (register-command! 'newline-and-indent cmd-newline-and-indent) + (register-command! 'reindent-then-newline-and-indent cmd-reindent-then-newline-and-indent) + (register-command! 'electric-newline-and-maybe-indent cmd-electric-newline-and-maybe-indent) + (register-command! 'completion-at-point cmd-completion-at-point) + (register-command! 'dabbrev-expand cmd-dabbrev-expand) + (register-command! 'dabbrev-completion cmd-dabbrev-completion) + (register-command! 'hippie-expand cmd-hippie-expand) + (register-command! 'company-mode cmd-company-mode) + (register-command! 'auto-complete-mode cmd-auto-complete-mode) + (register-command! 'ido-mode cmd-ido-mode) + (register-command! 'icomplete-mode cmd-icomplete-mode) + (register-command! 'fido-mode cmd-fido-mode) + (register-command! 'fido-vertical-mode cmd-fido-vertical-mode) + (register-command! 'savehist-mode cmd-savehist-mode) )