aboutsummaryrefslogtreecommitdiff
path: root/docs/design/flycheck-modeline-customization.org
diff options
context:
space:
mode:
Diffstat (limited to 'docs/design/flycheck-modeline-customization.org')
-rw-r--r--docs/design/flycheck-modeline-customization.org315
1 files changed, 0 insertions, 315 deletions
diff --git a/docs/design/flycheck-modeline-customization.org b/docs/design/flycheck-modeline-customization.org
deleted file mode 100644
index 25e2c7854..000000000
--- a/docs/design/flycheck-modeline-customization.org
+++ /dev/null
@@ -1,315 +0,0 @@
-#+TITLE: Design: Flycheck modeline customization
-#+AUTHOR: Craig Jennings
-#+DATE: 2026-05-15
-#+OPTIONS: toc:nil num:nil
-
-* Status
-
-Draft. Supersedes the earlier =flycheck-modeline-customization-spec.org=
-draft in =.ai/= (2025-11-14), which used stale line numbers and conflated
-Option 3's risky-local-variable requirement with Option 4.
-
-* Problem
-
-Flycheck's status (error / warning counts, "checking" indicator) is not
-visible in the custom modeline. The cause is a deliberate choice in
-=modules/modeline-config.el=: =mode-line-format= is built from explicit
-segments (=cj/modeline-buffer-name=, =cj/modeline-position=,
-=cj/modeline-vc-branch=, etc.) and does not include =minor-mode-alist=
-or =mode-line-modes=. Flycheck publishes its lighter into
-=minor-mode-alist=, so the custom modeline never picks it up.
-
-The fix is to add a flycheck-aware segment to =mode-line-format=.
-
-* Goals
-
-1. Flycheck status appears in the custom modeline when =flycheck-mode= is on.
-2. The display picks up flycheck's existing color logic (error count in =error= face, warning count in =warning= face).
-3. The display gates on active window, matching the convention used by =cj/modeline-vc-branch= and =cj/modeline-misc-info=.
-4. The customization is small enough that swapping prefix / success indicator is a one-line edit.
-
-* Non-Goals
-
-- A "minor modes" segment that surfaces every lighter from =minor-mode-alist=. Flycheck is the specific case we care about; the rest stay invisible.
-- Reworking =flycheck-config.el= beyond the two =:custom= additions.
-- Adding flycheck-side checkers or changing what gets checked.
-
-* Current State
-
-** =modules/flycheck-config.el:47-97=
-
-#+begin_src emacs-lisp
-(use-package flycheck
- :defer t
- :commands (flycheck-list-errors cj/flycheck-list-errors)
- :hook ((sh-mode emacs-lisp-mode) . flycheck-mode)
- :bind (:map cj/custom-keymap ("?" . cj/flycheck-list-errors))
- :custom
- (checkdoc-arguments
- '(("sentence-end-double-space" nil)
- ("warn-escape" nil)))
- :config
- ...)
-#+end_src
-
-No flycheck-modeline customization. Defaults are in force:
-
-| Variable | Default |
-|-------------------------------------+-------------------------------------------|
-| =flycheck-mode-line-prefix= | ="FlyC"= |
-| =flycheck-mode-success-indicator= | =":0"= |
-| =flycheck-mode-line-color= | =t= (apply error / warning faces) |
-| =flycheck-mode-line= | ='(:eval (flycheck-mode-line-status-text))= |
-
-** =modules/modeline-config.el:220-237=
-
-=mode-line-format= layout (left → right, with right-align edge):
-
-#+begin_src emacs-lisp
-(setq-default mode-line-format
- '("%e"
- " "
- cj/modeline-major-mode
- " "
- cj/modeline-buffer-name
- " "
- cj/modeline-position
- mode-line-format-right-align
- (:eval (when (fboundp 'cj/recording-modeline-indicator)
- (cj/recording-modeline-indicator)))
- cj/modeline-vc-branch
- " "
- cj/modeline-misc-info
- " "))
-#+end_src
-
-Risky-local-variable list (=modeline-config.el:240-246=):
-
-#+begin_src emacs-lisp
-(dolist (construct '(cj/modeline-buffer-name
- cj/modeline-position
- cj/modeline-vc-branch
- cj/modeline-vc-faces
- cj/modeline-major-mode
- cj/modeline-misc-info))
- (put construct 'risky-local-variable t))
-#+end_src
-
-Note: =cj/modeline-vc-branch= and =cj/modeline-misc-info= both gate on
-=(mode-line-window-selected-p)= so they appear only in the active window.
-
-** Flycheck lighter outputs (for reference)
-
-Flycheck status text values that =flycheck-mode-line-status-text=
-returns, depending on =flycheck-last-status-change= and current errors:
-
-| Status | Display (with default prefix / indicator) |
-|------------------------------+----------------------------------------------------|
-| Not yet checked | =FlyC= |
-| Currently checking | =FlyC*= |
-| Finished, no errors | =FlyC:0= |
-| Finished, 3 errors, 5 warns | =FlyC:3|5= |
-| Checker errored | =FlyC!= |
-| Interrupted | =FlyC.= |
-| Suspicious | =FlyC?= |
-| No checker available | =FlyC-= |
-
-With =flycheck-mode-line-color= = =t= (the default), the count portion
-is colored: error count in the =error= face, warning count in =warning=.
-
-* Approaches Considered
-
-** Option 1 (Reject): customize prefix / indicator only
-
-Setting =flycheck-mode-line-prefix= and =flycheck-mode-success-indicator=
-in =:custom= changes the lighter content, but the lighter still publishes
-to =minor-mode-alist=, which the custom modeline doesn't read. The lighter
-becomes prettier wherever it does show (e.g. doom-modeline if reinstated)
-but not here. Doesn't solve the visibility problem.
-
-** Option 2 (Reject): add the raw =flycheck-mode-line= variable
-
-Inserting =flycheck-mode-line= into =mode-line-format= directly works,
-but the form has no =flycheck-mode= guard. In a buffer where flycheck
-isn't loaded or not enabled, the =:eval (flycheck-mode-line-status-text)=
-call still fires and either errors or returns junk. Needs a wrapping
-guard, which is what Option 4 does.
-
-** Option 3 (Reject for now): custom segment with full control
-
-Define =cj/modeline-flycheck= as a =defvar-local= holding a =(:eval ...)=
-form that pulls error / warning counts directly from
-=flycheck-current-errors=, builds a per-status string, propertizes it
-with =error= / =warning= faces, and returns it. Reimplements what
-=flycheck-mode-line-status-text= already does, with bespoke formatting.
-
-Pros: full control over format. Cons: maintenance burden, drifts from
-flycheck's status model if flycheck changes it.
-
-If the Option 4 result ever stops being good enough -- e.g. you want a
-different layout (=E:3 W:5= instead of =:3|5=) -- come back to this.
-Until then, more code than the problem deserves.
-
-** Option 4 (Recommended): hybrid -- customize variables + add guarded segment
-
-Two changes:
-
-1. =modules/flycheck-config.el= =:custom= block gets prefix and success-indicator overrides. (Optional: also =flycheck-mode-line-color=.)
-
-2. =modules/modeline-config.el= adds a small =(:eval ...)= form inline in =mode-line-format= that guards on =flycheck-mode= and calls =(flycheck-mode-line-status-text)= directly.
-
-Pros: minimal code, uses flycheck's logic verbatim, prefix / indicator
-swappable with a one-line edit, picks up flycheck's face colors
-automatically.
-
-Cons: layout fixed to flycheck's =PREFIX[indicator|counts]= shape.
-Acceptable.
-
-* Recommended Implementation (Option 4)
-
-** Step 1: =modules/flycheck-config.el=
-
-Add to the =:custom= block (currently lines 55-59 in the file):
-
-#+begin_src emacs-lisp
-;; Modeline customization (rendered via mode-line-format in modeline-config.el).
-(flycheck-mode-line-prefix "🐛")
-(flycheck-mode-success-indicator " ✓")
-;; flycheck-mode-line-color stays t (default) so counts keep their face coloring.
-#+end_src
-
-Prefix and success indicator are taste; the **Emoji Reference** section
-below catalogs the candidates. Note that the prefix emoji itself does
-not inherit the =error= / =warning= face -- only the count portion does
-(via =flycheck-mode-line-color=). That trade-off is fine for a static
-prefix; an emoji prefix gives a recognizable shape that you scan for,
-and the colored count carries the alert signal.
-
-** Step 2: =modules/modeline-config.el=
-
-Insert a =(:eval ...)= form into =mode-line-format= (currently lines
-220-237). Recommended placement: between the recording indicator and
-=cj/modeline-vc-branch= so flycheck status sits with the other
-right-aligned status segments.
-
-After the change, the right-side block reads:
-
-#+begin_src emacs-lisp
-;; RIGHT SIDE
-mode-line-format-right-align
-(:eval (when (fboundp 'cj/recording-modeline-indicator)
- (cj/recording-modeline-indicator)))
-(:eval (when (and (mode-line-window-selected-p)
- (bound-and-true-p flycheck-mode))
- (flycheck-mode-line-status-text)))
-" "
-cj/modeline-vc-branch
-" "
-cj/modeline-misc-info
-" ")
-#+end_src
-
-Two design choices baked in:
-
-- =(mode-line-window-selected-p)= gates the segment to the active window, matching the convention used by =cj/modeline-vc-branch= and =cj/modeline-misc-info=.
-- =(bound-and-true-p flycheck-mode)= prevents the function call in buffers where flycheck never loaded; safer than asking =flycheck-mode= directly.
-
-** Risky-local-variable: not needed here
-
-This implementation places =(:eval ...)= inline inside =mode-line-format=
-rather than wrapping it in a =defvar-local=. Inline forms are evaluated
-by mode-line processing without a risky-local-variable marker. The
-existing risky list (=modeline-config.el:240-246=) does not need to
-grow.
-
-(If you ever refactor this to a named segment -- =defvar-local cj/modeline-flycheck= -- then add it to the risky list. Option 3 above is the path that needs that step.)
-
-* Emoji Reference
-
-** Prefix candidates (=flycheck-mode-line-prefix=)
-
-| Glyph | Codepoint | Name |
-|-------+-----------+---------------------------------------|
-| 🪰 | U+1FAB0 | FLY (literal "fly" for flycheck) |
-| 🐛 | U+1F41B | BUG (recommended -- broadest font support) |
-| 🐞 | U+1F41E | LADY BEETLE |
-| ⚠ | U+26A0 | WARNING SIGN |
-| 🔍 | U+1F50D | MAGNIFYING GLASS |
-| 📝 | U+1F4DD | MEMO |
-| ✓ | U+2713 | CHECK MARK (text) |
-
-🪰 (U+1FAB0) is from Unicode 13.0 (2020) and needs an up-to-date emoji
-font. 🐛 (U+1F41B) is older and renders everywhere. Default to 🐛 unless
-the fly is a strong preference and the GUI fonts are known to cover it.
-
-** Success indicator candidates (=flycheck-mode-success-indicator=)
-
-| Glyph | Codepoint | Name |
-|-------+-----------+---------------------------------------|
-| ✓ | U+2713 | CHECK MARK (text) |
-| ✔ | U+2714 | HEAVY CHECK MARK |
-| ✅ | U+2705 | WHITE HEAVY CHECK MARK (green box) |
-| 🟢 | U+1F7E2 | GREEN CIRCLE |
-| ⭐ | U+2B50 | WHITE MEDIUM STAR |
-
-Note the leading space in the recommended setting (=" ✓"=): flycheck
-joins the prefix and the success indicator with no separator, so a
-leading space in the indicator gives breathing room between the emoji
-prefix and the check mark.
-
-** Suggested combinations
-
-| Mood | Prefix | Success indicator | Result example |
-|---------------------+--------+-------------------+----------------|
-| Recommended default | 🐛 | " ✓" | =🐛 ✓= / =🐛:3|5= |
-| Literal Flycheck | 🪰 | " ✓" | =🪰 ✓= / =🪰:3|5= |
-| Minimal | "" | " ✓" | = ✓= / =:3|5= |
-| Status light | "" | " 🟢" | = 🟢= / =:3|5= |
-
-* Testing
-
-** Manual
-
-1. Open =modules/flycheck-config.el= (an =emacs-lisp-mode= buffer with =flycheck-mode= auto-enabled per the existing =:hook=). The right side of the modeline shows the prefix + success indicator when there are no errors.
-2. Introduce a deliberate parse error (drop a paren). Save. The modeline updates to show =:1|0= (or whatever count) in the =error= face.
-3. Trigger =M-x flycheck-buffer= in a fresh =sh-mode= buffer. The "currently checking" state (=PREFIX*=) flashes briefly before settling on success or counts.
-4. Open a second window onto the same buffer (=C-x 2=). The flycheck segment appears in the active window only; the inactive copy drops it. Confirms the active-window gate.
-5. Open a buffer where flycheck never engages (e.g. =*scratch*= in fundamental-mode, or a =dired= buffer). No segment, no errors.
-6. Run =cj/flycheck-prose-on-demand= in an org buffer (=C-; ?= in org-mode). The LanguageTool checker engages and the segment appears with prose-error counts.
-
-** Regression watch
-
-- The custom-modeline width should not jump distractingly as flycheck cycles "checking → finished". The status text is short (one to seven chars), so this should be invisible -- worth a glance.
-- Inactive-window display: confirm the segment disappears, not just greys out. The current pattern is "hide entirely" via the =mode-line-window-selected-p= guard.
-- =cj/modeline-misc-info= keeps showing chime / notification text. The flycheck segment sits to its left; verify the visual order matches the spec.
-
-* Files to Modify
-
-- =modules/flycheck-config.el= -- add two =:custom= lines.
-- =modules/modeline-config.el= -- insert one =(:eval ...)= form into =mode-line-format=.
-
-Two-line / one-form change. No new tests required (the existing tests
-don't lock the modeline content; they exercise behavior elsewhere). If
-you want a smoke test, add one assertion in =tests/test-modeline-config.el=
-(if that file exists or you create it) that =mode-line-format='s sexp
-contains a form mentioning =flycheck-mode-line-status-text=. Optional.
-
-* Risks
-
-| Risk | Mitigation |
-|-----------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------|
-| Emoji renders as a tofu square in terminal Emacs | The user runs GUI Emacs primarily; if terminal use matters, set the prefix to a text glyph (=""= or =":"=) instead. |
-| Modeline width thrash when flycheck transitions running → finished | Status text is one to seven chars; jitter is negligible. Confirm during manual testing. |
-| Prefix emoji doesn't pick up =error= / =warning= face | Expected: =flycheck-mode-line-color= colors the count portion only. The static prefix is intentionally unstyled. If you want a colored prefix, switch to Option 3. |
-| Flycheck not yet loaded when modeline first evaluates | The =(bound-and-true-p flycheck-mode)= guard returns nil in that case, the =(:eval ...)= returns nil, mode-line skips the slot. |
-| Active-window gate is wrong for some workflow (e.g. multi-window comparison) | Drop =(mode-line-window-selected-p)=. One-line change. Decide after living with the default. |
-
-* Rollback
-
-Revert the commit. Two-file change, no schema impact. Idempotent.
-
-* Effort estimate
-
-S (under 1 hour). Two lines in =flycheck-config.el=, one form in
-=modeline-config.el=, plus the manual verification walk-through. The
-emoji selection is the time sink, not the code.