refactor: DRY up buf/ed/text with shared abstractions#2
Open
nez wants to merge 10 commits into
Open
Conversation
Extract bounded-conj utility to replace 3 instances of capped-vector logic across buf.clj and ed.clj. Unify undo/redo into a single replay helper parameterized by direction. Consolidate mini-history-prev/next into mini-history-move with delta parameter. Extract set-mini-text to eliminate 4 repeated assoc-in patterns. Unify search-forward/backward via shared search* helper. Fix bug: undo was swapping :old/:new when pushing to redo stack, but redo interprets entries with opposite convention — redo never worked correctly. New redo and undo-redo-roundtrip tests verify the fix. Add 19 new tests: redo (3), mini-history navigation (5), search (11). 125 tests, 174 assertions, all passing. Lint clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…sion Port the Elisp evaluator from feat/gap-buffer and extend it with the six primitives that virtually all Elisp packages depend on: - defvar/defcustom: declare variables with docstrings, set-if-unbound - defmacro + backquote reader (`, ,, ,@): full macro system with template-based code generation via expand-backquote - &rest/&optional argument binding for defun, defmacro, and lambda - provide/require/load: module system with load-path search and circular-require guard - save-excursion: save/restore point+mark around body - Symbol property lists: put/get/plist-get/plist-put - let* sequential binding - Additional builtins: apply, funcall, mapcar, boundp, fboundp, 1+, 1-, intern, error, string-to-number, reverse, delete-char Bootstrap subr.el (loaded before init.el) defines standard macros: when, unless, dolist, dotimes, prog1, prog2, push, pop, and utility functions cadr/caddr/caar/cddr/member/assoc/assq/nthcdr. Wire into ed.clj: M-: eval-expression, el-bindings dispatch in handle-key, subr.el + ~/.xmas/init.el loading at startup. Fix: cdr now returns nil (not empty list) for single-element lists, matching Elisp semantics. 320 tests, 386 assertions, all passing. Lint clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
el.clj: - read-delimited: unify read-list/read-vector (differ only in close char) - read-wrapping: unify read-quote/read-backquote (differ only in symbol) - eval-def*: unify eval-defun/eval-defmacro (differ only in target atom) - eval-let* was identical to eval-let — removed, let* now calls eval-let - update-buf!: extract repeated swap!/update-in buffer pattern (9 sites) - expand-backquote-elems: dedup identical seq/vector expansion loops - call-fn: extract resolve+dispatch pattern from apply/funcall/mapcar - self-insert: simplify 3-way cond to single if (str works for both) 320 tests, 386 assertions, all passing. Lint clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
el.clj: - eval-with-env: extract shared bind/persist lifecycle from eval-string and eval-1 - Remove unnecessary wrapper lambdas in el-forward/backward-char (text/next-pos already has the right signature) ed.clj: - mini-bindings: extract 4 inline (and (:mini s) ...) conditions into a data-driven map, matching the pattern used for normal bindings 320 tests, 386 assertions, all passing. Lint clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- eval-let now evaluates all init-forms before binding any variable, matching real Elisp let semantics. (let ((a 1) (b a)) b) now correctly errors with "Void variable: a" instead of returning 1. - eval-let* restored as separate function for sequential binding. - Splice-unquote (,@) on non-iterable values now throws a clear "Attempt to splice non-list" error instead of a raw ClassCastException. 3 new tests verify the fixes. 323 tests, 389 assertions, all passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace imperative patterns with idiomatic alternatives: set-as-predicate for ws?, reduce+reduced for eval-and/eval-or/eval-setq, zipmap for bind-args, mapcat for expand-backquote-elems, partition+some for plist-get, data-driven table for bind-all, select-keys for broadcast!, str/join for log, and str/replace with regex anchoring in dev. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ias dedup - Replace (apply str (interpose ...)) with str/join (5 sites) - Use constantly for zero/constant-returning stubs (8 sites) - Extract div-opt helper for floor/ceiling/round/truncate - Move 5 special forms from runtime cond into compile-time case in eval - Extract named helpers for duplicate lambda builtins (5 pairs) - Hoist loop-invariant branch in el-forward-line Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 3-6 features (uncommitted WIP): - Bootstrap: cl-lib.el, package-stubs.el, custom-stubs.el, use-package.el, init-vars.el - subr.el: with-current-buffer, with-temp-buffer, define-minor-mode, define-derived-mode - buf.clj: :locals and :props (sorted-map text-property intervals + shift on edit) - ed.clj: post-command-hook firing, fire-timers in idle, mini-cmd lookup hoisted - term.clj: neg?/pos? predicates over comparisons - web.clj: io alias instead of fully-qualified namespace - 840 lines of new tests: closures, condition-case, unwind-protect, ignore-errors Refactor + bug fixes: - el-re-search: kill dead branch (if forward (.find m) (.find m)); fix backward bound semantics (Emacs treats backward bound as lower limit, not upper) - el-mapconcat: str/join over (apply str (interpose ...)) - defvar/defconst: shared eval-defvar* helper, drop inline branch - Move 14-line embedded init-vars heredoc out of -main into init-vars.el - Drop redundant double coercion in 'round 475 tests pass, lint clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- el-var! used consistently (setq, defvar*, defvaralias, add-to-list, propertize) - eval-save-current-buffer extracted alongside eval-save-excursion - el-plist-put via (assoc array-map) — one-liner replaces branch+mapcat - el-forward-line: collapse two step lambdas into a single pos-fn via buf/set-point - push-history helper for mini-accept - end-of-buffer: (constantly Integer/MAX_VALUE) + rely on set-point clamp - bufferp: real impl (via el-buffer-live-p) instead of noop stub - add-to-list: honor APPEND arg - mapatoms: enumerate *vars* + *fns* + *macros* - prin1-to-string: escape \r; emit readable ?\\n ?\\t ?\\r ?\\s ?\\\\ for chars 475 tests pass, lint clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- math1 macro: (math1 :sqrt :sin :cos :tan :asin :acos :exp) generates one-arg Math/X wrappers — collapses 7 fn-wrap lines into one - div-int factory: floor/ceiling/truncate share div-opt + (long ...) wrap - Keyword args avoid clj-kondo unresolved-symbol noise Net: ~10 lines saved, similar entries grouped. 475 tests pass, lint clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
bounded-conjutility replacing 3 instances of capped-vector logic in buf.clj and ed.cljundo/redointo a singlereplayhelper parameterized by direction (19 lines → 13)mini-history-prev/mini-history-nextintomini-history-movewith delta parameter (22 lines → 13)set-mini-texthelper eliminating 4 repeatedassoc-inpatternssearch-forward/search-backwardvia sharedsearch*helperundowas swapping:old/:newwhen pushing to redo stack, making redo produce wrong resultsTest plan
clj-kondo— 0 errors from changed files)redo-after-undoandundo-redo-roundtriptests🤖 Generated with Claude Code