fix(widget): indent unit shadowing + Tab inserting literal tab - #26
Merged
Conversation
The child editor had two competing indentUnit sources: buildLanguageExtensions
inside the languageCompartment (per-language + setting-aware) and a top-level
indentUnit.of(' ') that silently shadowed it. Auto-indent on Enter always
emitted 4 spaces regardless of language or the user's indentSizeOverride
setting; the JS/TS 'auto' default (2 spaces) was unreachable.
The empty-selection Tab branch additionally called @codemirror/commands'
insertTab, which dispatches a literal "\t" regardless of the indentUnit
facet — combined with the spaces-emitting auto-indent, this produced
"mixed tabs and spaces in the same buffer" (the user-reported symptom).
Fixes:
- Remove the shadowing top-level `indentUnit.of(' ')`; trust the
languageCompartment payload as the single source of truth.
- Replace `insertTab(target)` with `replaceSelection(state.facet(indentUnit))`
so Tab and Enter consult the same facet and cannot diverge.
- Add `WidgetRegistry.applyIndentReconfigure(override)` mirroring `applyDelay`
/ `reconfigureVim` so the dropdown setting applies live to every mounted
widget — pure-effects Compartment.reconfigure, no body change, no
SelfWriteSuppression interaction.
Tests: 22 new tests across two files (real CM6 keymap behavior +
mock-based reactivity assertions). Documents the failure mode as
PITFALLS.md Pitfall 37 and adds an architecture line to CLAUDE.md.
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
User reported "indentation is mixed of spaces and tabs" in the widget editor. Two compounding bugs:
indentUnit.of(' ')in the editable extensions array silently shadowed the per-languageindentUnit.of(...)supplied insidelanguageCompartment.of(buildLanguageExtensions(slug, override)). CM6 facets resolve in extension order; the bare facet won, so auto-indent on Enter always emitted 4 spaces regardless of language (JS/TS 'auto' default of 2 was unreachable) or the user'sindentSizeOverridesetting.@codemirror/commandsinsertTab, which dispatches a literal\tregardless of the indentUnit facet. Combined with the spaces-emitting auto-indent, that produced the mixed-tabs-and-spaces symptom.The setting was also non-reactive — changes only applied to widgets mounted afterward.
Fixes
src/widget/WidgetController.ts: Delete the shadowingindentUnit.of(' '). ReplaceinsertTab(target)withreplaceSelection(state.facet(indentUnit))so Tab and Enter consult the same facet. AddreconfigureIndent(override)method (pure-effectslanguageCompartment.reconfigure(buildLanguageExtensions(currentSlug, override))— nochanges, no SelfWriteSuppression involvement).src/widget/widgetRegistry.ts: AddapplyIndentReconfigure(override)mirroring the existingapplyDelay/reconfigureVimprecedent — walk every mounted controller across every file.src/settings/SettingsTab.ts: Wire the indent-size dropdownonChangeto callapplyIndentReconfigure(val)after persist so the setting applies live without note reload. Reconfigure only swaps the live facet for FUTURE typing; existing buffer content is never rewritten..planning/research/PITFALLS.md: Document as Pitfall 37 with warning signs (canary: JS/TS users onautosee 4-space indents).CLAUDE.md: Architecture sentence on the indent-unit single-source-of-truth and reactivity flow.Test plan
tests/widget/tabHandlerIndent.test.tsusing real CM6 — verify Tab inserts spaces from theindentUnitfacet (not literal\t) for python3/javascript/typescript acrossauto/2/4/8 overrides; Go always inserts\tper D-06; multi-line Tab viaindentMoreconsistent.tests/widget/indentReactivity.test.ts— verifyreconfigureIndentdispatches an effects-only payload built from the livecurrentSlug+ new override; verifyWidgetRegistry.applyIndentReconfigurefans out across every controller; tightened assertions onCompartment.reconfigurecall ledger.npm run build✓npm run lint✓ (0 errors)npx vitest run✓ (2965 passed, 0 failed)~/Documents/interview-prep/.obsidian/plugins/leetcode/).