Commit dicipline fail

This commit is contained in:
Madeleine Sydney
2025-02-11 11:36:20 -07:00
parent fc14c41edd
commit f53f58df5a
11 changed files with 412 additions and 136 deletions

View File

@@ -5,4 +5,106 @@
(require 'general)
(defvar syd-leader-key "SPC"
"A prefix key akin to Vim's <Leader>.")
(defvar syd-localleader-key "SPC m"
"A prefix key akin to Vim's <LocalLeader>.")
(defvar syd-alt-leader-key "M-SPC"
"`syd-leader', but for the states specified in `syd-alt-leader-key-states'.
Often, your \"usual\" leader key will be something unavailable in the Insert
state. This key exists as a fallback for when you need your Leader, but must
remain in the Insert state. Substitute \"Insert state\" for your states of
choice with `syd-alt-leader-key-states'.")
(defvar syd-alt-localleader-key "M-SPC m"
"`syd-localleader', but for the states specified in `syd-alt-leader-key-states'.
See `syd-alt-leader-key' for rationale.")
(defvar syd-leader-key-states '(normal visual motion)
"States for which the Leader keys (`syd-leader-key', `syd-localleader-key')
are active.")
(defvar syd-alt-leader-key-states '(emacs insert)
"States for which the alternative Leader keys are active. See
`syd-alt-leader-key' and `syd-alt-localleader-key'.")
(defvar-keymap syd-leader-map
:doc "Leader-prefixed commands")
(defun syd-initialise-leader ()
"Set up the (empty) keymap associated with `syd-leader-key',
`syd-localleader-key', `syd-alt-leader-key', and `syd-alt-localleader-key'."
(require 'evil)
;; Define `syd/leader' as a command corresponding to the prefix map
;; `syd-leader-map'.
(define-prefix-command 'syd/leader 'syd-leader-map)
;; This should help make the Leader key close to universally available.
;; Ideally, *nothing* takes precedence over Leader — it's an incredibly
;; important key!
;; https://github.com/noctuid/evil-guide?tab=readme-ov-file#undoprevent-overridingintercept-maps
;; See `evil-make-overriding-map'.
(define-key syd-leader-map [override-state] 'all)
;; Finally, we shall bind the damned keys. }:)
(let ((map general-override-mode-map))
(evil-define-key* syd-leader-key-states map (kbd syd-leader-key) 'syd/leader)
(evil-define-key* syd-alt-leader-key-states map (kbd syd-alt-leader-key) 'syd/leader))
(general-override-mode 1))
(defvar syd-escape-hook nil
"A hook run when C-g is pressed (or ESC in Evil's normal state).
More specifically, when `syd/escape' is pressed. If any hook returns non-nil,
all hooks after it are ignored.")
;;
;;; Universal, non-nuclear escape
;; `keyboard-quit' is too much of a nuclear option. I want ESC/C-g to
;; do-what-I-mean. It serves four purposes (in order):
;;
;; 1. Quit active states; e.g. highlights, searches, snippets, iedit,
;; multiple-cursors, recording macros, etc.
;; 2. Close popup windows remotely (if it is allowed to)
;; 3. Refresh buffer indicators, like diff-hl and flycheck
;; 4. Or fall back to `keyboard-quit'
;;
;; And it should do these things incrementally, rather than all at once. And it
;; shouldn't interfere with recording macros or the minibuffer. This may
;; require you press ESC/C-g two or three times on some occasions to reach
;; `keyboard-quit', but this is much more intuitive.
(defun syd/escape (&optional interactive)
"Run `syd-escape-hook'."
(interactive (list 'interactive))
(let ((inhibit-quit t))
(cond ((minibuffer-window-active-p (minibuffer-window))
;; quit the minibuffer if open.
(when interactive
(setq this-command 'abort-recursive-edit))
(abort-recursive-edit))
;; Run all escape hooks. If any returns non-nil, then stop there.
((run-hook-with-args-until-success 'syd-escape-hook))
;; don't abort macros
((or defining-kbd-macro executing-kbd-macro) nil)
;; Back to the default
((unwind-protect (keyboard-quit)
(when interactive
(setq this-command 'keyboard-quit)))))))
(with-eval-after-load 'eldoc
(eldoc-add-command 'syd/escape))
;; In normal state, pressing escape should run `syd-escape-hook'.
(with-eval-after-load 'evil
(defun evil-syd/escape-a (&rest _)
"Call `syd/escape' if `evil-force-normal-state' is called interactively."
(when (called-interactively-p 'any)
(call-interactively #'syd/escape)))
(advice-add #'evil-force-normal-state
:after #'evil-syd/escape-a))
(provide 'syd-general)

View File

@@ -124,6 +124,21 @@ comment, if there is one. Returns nil or a pair (BEG . END)."
"When `syd-evil-a-defun' is used in combination with one of these operators,
some cleanup will be performed.")
;; FIXME(#12): Comments should only attach to the *immediately* following sexp.
;; Consider the following snippet:
;;
;; ;; Call the continuation if non-nil. Wraps the return value in a singleton
;; ;; list for "affine" use with unquote-splicing.
;; (let ((call-cont (lambda (cont arg)
;; (if cont
;; (list (funcall cont arg))
;; nil)))
;; names)
;; ...)
;;
;; The curreny behaviour of `syd-sexp--backward-attached-comment' considers the
;; comment to be attached to both the (let ...) form, as well as the ((call-cont
;; ...)) form and the (call-cont ...) form. Not good!
(defun syd-sexp--backward-attached-comment ()
"Assuming point is on the opening delimiter of a sexp, move point backward to
the beginning of the \"attached\" comment."
@@ -155,7 +170,7 @@ to clean up whitespace following certain operators."
(list beg-0 :end)))))
;; IDEA: How about the inner-defun text object selects the defun /without/ the
;; comment? Is that more useful, or less? I can't think of the last time I've
;; comment? Is that more useful, or less? I can't think of the last time Ive
;; needed the top-level sexp without the brackets.
;;;###autoload
@@ -189,6 +204,7 @@ delimiters."
(sexp (syd-get-enclosing-sexp)))
(if cleanup-p
(save-excursion
(goto-char (sp-get sexp :beg))
(if (syd-sexp--looking-at-last-p)
(progn (syd-sexp--backward-leading-whitespace sexp)
(list (point) (sp-get sexp :end)))
@@ -208,9 +224,9 @@ sexp-wise analogue to Evil's line-wise `evil-open-below'."
:suppress-operator t
(evil-with-single-undo
;; We want to add an additional blank line when operating at the top level.
;; Instead of parsing upward until we can no longer find an enclosing sexp, we
;; simply check if the opening bracket is on the first column. This is not
;; very correct, but it's way less work (for myself and the CPU). If we
;; Instead of parsing upward until we can no longer find an enclosing sexp,
;; we simply check if the opening bracket is on the first column. This is
;; not very correct, but it's way less work (for myself and the CPU). If we
;; switch to a tree-sitterbased parser, I'd love to switch to the correct
;; algorithm.
(-let* (((beg . end) (sp-get (syd-get-enclosing-sexp) (cons :beg :end)))

View File

@@ -12,40 +12,70 @@
,todo
(error ,todo))))
(cl-defun syd--lift-lambdas (forms &key with-each with-all)
;; FIXME: When `arg-list' contains nils, things break.
(cl-defun syd-parse-rest-and-keys (arg-list)
"The default behaviour of `cl-defun' makes combining &rest with &keys pretty
useless. This function will partition ARG-LIST by returning a pair (REST
. KEYS), where REST is the list of ARGS that belong to no key-value pair, and
KEYS is an alist of the parsed keywords."
;; Ugh.
(let (parsed-rest parsed-keys)
(cl-loop for (lead lag) on arg-list by (lambda (x) (-drop 2 x))
do (if (keywordp lead)
(push (cons lead lag) parsed-keys)
;; Push in reverse order; we reverse the whole list as a
;; post-processing step.
(push lead parsed-rest)
(when lag
(push lag parsed-rest))))
(cons (reverse parsed-rest) parsed-keys)))
(cl-defun syd-lift-lambdas (&rest args)
;; Call the continuation if non-nil. Wraps the return value in a singleton
;; list for "affine" use with unquote-splicing.
(let ((call-cont (lambda (cont)
(if cont
(lambda (name) (list (funcall with-each name)))
(lambda (_) nil))))
names)
`(progn ,@(mapconcat (lambda (form)
(cond ((and (symbolp form) (functionp form))
(push form names)
(call-cont with-each form))
((eq (car-safe form) 'defun)
(let ((name (nth 1 form)))
(push name names)
`(,form
,@(call-cont with-each))))
((eq (car-safe form) 'lambda)
(let ((name (gensym "lifted-lambda")))
(push name names)
`((defun ,name (&rest args)
(,form args))
,@(call-cont with-each))))))
(ensure-list forms))
,@(call-cont with-all names))))
(-let (((forms . (&alist :with-each with-each
:with-all with-all))
(syd-parse-rest-and-keys args))
(call-cont (lambda (cont arg)
(if cont
(list (funcall cont arg))
nil)))
names)
`(progn
,@(cl-loop
for form in forms
appending (cond ((and (symbolp form) (functionp form))
(push form names)
(funcall call-cont with-each form))
((syd-hform-defun form)
(let ((name (nth 1 form)))
(push name names)
`(,form
,@(funcall call-cont with-each name))))
((syd-hform-lambda form)
(let ((name (gensym "lifted-lambda")))
(push name names)
`((defun ,name (&rest args)
(,form args))
,@(funcall call-cont with-each name))))
(t (error "IDK!"))))
,@(funcall call-cont with-all names))))
;; (defun syd-hform-defun (hform)
;; "If HFORM is a defun form, return the defun's name. Otherwise, return nil"
;; (and (listp hform)
;; (<= 2 (length hform))
;; (nth 1 hform)))
(defun syd-hform-defun (hform)
"If HFORM is a defun form, return the defun's name. Otherwise, return nil"
(when-let* ((sym (car-safe hform)))
(and (symbolp sym)
(eq sym 'defun)
(nth 1 hform))))
(defun syd-hform-lambda (hform)
"If HFORM is a lambda, return non-nil."
(when-let* ((sym (car-safe hform)))
(and (symbolp sym)
(eq sym 'lambda))))
(defmacro comment (&rest _)
"Completely every argument, and expand to nil."
"Ignore each argument, and expand to nil."
nil)
(defmacro with-transient-after (hook-or-function &rest forms)
@@ -74,4 +104,30 @@ not mutated; a new plist is returned."
(list prop* new-val)
(list prop* old-val))))
;; TODO: Support (syd-add-hook 'hook (defun my-hook () ...))
(defun syd-add-hook (hooks &rest functions)
(declare (indent defun))
(dolist (hook (ensure-list hooks))
(dolist (fn functions)
(add-hook hook fn))))
(defmacro syd-silently (&rest body)
`(error "TODO: syd-silently"))
(defmacro syd-quietly (&rest body)
"Evaluate BODY without generating any output.
This silences calls to `message', `load', `write-region' and anything that
writes to `standard-output'. In interactive sessions this inhibits output to
the echo-area, but not to *Messages*. Return value is that of BODY's final
form."
`(if init-file-debug
(progn ,@body)
,(if noninteractive
`(syd-silently ,@body)
`(let ((inhibit-message t)
(save-silently t))
(prog1 (progn ,@body)
(message ""))))))
(provide 'syd-prelude)

View File

@@ -0,0 +1,27 @@
;;; syd-prose.el -*- lexical-binding: t; -*-
(use-package visual-fill-column
:defer t)
(defun syd-prose-disable-display-line-numbers-mode-h ()
"Disable `display-line-numbers-mode'."
(display-line-numbers-mode -1))
(defun syd-prose-set-visual-fill-column-center ()
"Sets the buffer-local value for `visual-fill-column-center-text'."
(setq-local visual-fill-column-center-text t))
(defvar syd-prose-mode-hook
(list #'syd-prose-set-visual-fill-column-center
#'visual-fill-column-mode
#'visual-line-mode
#'variable-pitch-mode
#'syd-prose-disable-display-line-numbers-mode-h)
"Hooks run for `syd-prose-mode'.")
;;;###autoload
(define-minor-mode syd-prose-mode
"A minor mode for writing prose."
:lighter nil)
(provide 'syd-prose)