Changed
- Sign column migrated to dedicated gutter column — vim mark indicators (
a–z,A–Z) now render in a proper CM6gutter()column instead of usingDecoration.line()+ CSS::afteroverlays. Fixes marks cascading vertically into the wrong line, overlapping on multi-mark lines, and inheriting heading font sizes. The gutter layout from left to right is: sign column → line numbers → fold column → content, matching Neovim's default arrangement. UsesCompartment-based runtime reconfiguration —:set signcolumn=yes/auto/notakes effect without full feature reload.signcolumn=autonow causes layout shift when marks appear/disappear (matching Neovim behavior);signcolumn=yesalways reserves gutter space. (#59)- Plugin:
src/vim/sign-column.ts(rewritten:gutter()+GutterMarker+Compartment),src/vim/mark-gutter.ts(updated re-exports),src/main.ts(unconditional registration,reconfigureSignColumnGutter()),src/settings.ts(removed fromRELOAD_KEYS, dedicated handlers in both settings panels,enableMarkGutterdeprecated),src/vimrc/loader.ts(markguttermapped tosigncolumnviasideEffect),styles.css(gutter element styles replacing::afteroverlay)
- Plugin:
enableMarkGuttersetting deprecated — the booleanenableMarkGutterproperty is now optional with@deprecatedJSDoc. Existing settings are auto-migrated tosigncolumnviasettings-migration.ts. The property is kept in the interface for migration type safety only.
Added
- Dual line number display — new
linenumbermodeoption (hybrid/dual/dual-rel-abs) shows absolute and relative line numbers in separate side-by-side gutter columns.set number relativenumber linenumbermode=dualrenders absolute on the left, relative on the right (configurable viadual-rel-absfor the reverse). The defaulthybridmode is unchanged — existing configs are fully backward compatible. Auto-disabled on mobile viewports (≤600px, falls back to hybrid). Configurable via Settings UI dropdown,set linenumbermode=dual(aliaslnm) in vimrc, orvim.opt.linenumbermode = "dual"in Lua.- Plugin:
src/vim/line-number-gutter.ts(dual compartments,resolveGutters()),src/vim/options.ts(linenumbermodeoption),src/settings.ts(setting + UI dropdown),src/vimrc/loader.ts(linenumbermode/lnm),src/main.ts(passlinenumbermodeto extension creation/reconfiguration)
- Plugin:
- Global vs local mark color differentiation — global marks (
A–Z) render in--text-mutedcolor, local marks (a–z) render in--text-accent. The first character of the label determines the CSS class (.vim-motions-sign-marker-globalor.vim-motions-sign-marker-local).- Plugin:
src/vim/sign-column.ts(SignMarker.toDOM()case detection),styles.css
- Plugin:
- Click-to-navigate on mark labels — clicking a mark label in the sign column gutter moves the cursor to that line. Uses
domEventHandlers.clickon the gutter, querying thesignColumnFieldRangeSet for markers at the clicked line.- Plugin:
src/vim/sign-column.ts(domEventHandlers.click)
- Plugin:
signcolumnwidth modes —signcolumnnow acceptsauto:Nandyes:Nsyntax (N = 1–4) to control sign column character width.set signcolumn=auto:3reserves 3 character slots when marks exist.set signcolumn=yes:2always reserves 2 character slots. Validation via regex; invalid values silently rejected.- Plugin:
src/vim/sign-column.ts(parseSignColumnMode(),isValidSignColumnValue()),src/vim/options.ts(validation),src/settings.ts(type widened tostring),src/vimrc/loader.ts(removedvalidValues)
- Plugin:
statuscolumnAPI — Neovim-compatiblestatuscolumnoption for user-configurable gutter layout. A format string controls which gutter segments appear and in what order:%l(line number),%r(relative number),%s(sign column marks),%C(fold indicators),%=(separator), and literal text. When set, the unified gutter replaces all individual gutter columns. When empty (default), individualsigncolumn/number/relativenumber/foldcolumnsettings manage gutters independently. Configurable viavim.opt.statuscolumn = "%s %l %r %C"in Lua orset statuscolumn(aliasstc) in vimrc. Click handlers on sign and fold segments preserved.linenumbermodedeprecated —dualmaps tostatuscolumn = "%l %r". (#59)- Plugin:
src/vim/statuscolumn.ts(new: parser, compositeStatusColumnMarker, unified gutter,StatusColumnSpacer),src/vim/sign-column.ts(signColumnFieldextracted as standalone extension,SignMarker.labelpublic),src/vim/line-number-gutter.ts(computeLineNumber+getNumberwidthexported),src/vim/mark-gutter.ts(re-exportssignColumnFieldExtension),src/vim/options.ts(statuscolumnoption),src/settings.ts(statuscolumnsetting),src/vimrc/loader.ts(statuscolumn/stc),src/main.ts(standalonesignColumnFieldregistration,statusColumnCompartment,reconfigureStatusColumnGutter()),styles.css(statuscolumn segment styles)
- Plugin:
Fixed
- Status bar vim mode duplication — the vim mode indicator and chord display were duplicated in the status bar when the plugin had a non-default
clipboardortextwidthsetting saved. The settings restoration loop duringonload()(lines 717-739 ofmain.ts, added in v0.53.0 by commit947c6a7) calledapplySettingOverride→reloadFeatures()beforeonload()had created its ownVimModeTrackerandGlobalKeyHandler.reloadFeatures()created these resources, thenonload()overwrotethis.modeTrackerandthis.globalKeyHandlerwith new instances — without destroying the first ones. The orphanedVimModeTrackerleft duplicateaddStatusBarItem()DOM elements in the status bar, and the orphanedGlobalKeyHandlerleft a duplicatekeydownlistener on the document (causing intermittent leader key failures). Fixed by adding aninitializingphase guard toapplySettingOverride— all 6 side-effect branches (5 gutter reconfigurations +reloadFeatures()) are suppressed duringonload(), matching the existingvimrcLoading/luaLoadingguard pattern. Settings mutations tothis.settingsstill apply immediately; only premature side effects are blocked. (#63)- Plugin:
src/main.ts(initializingflag, 6 guard site updates inapplySettingOverride)
- Plugin:
iterateEditorViewscrash on non-editor leaves —reconfigureLineNumberGutter()and other gutter reconfigure methods could throwview.dispatch is not a functionon leaves where the CM6 EditorView chain resolved to a non-EditorView object. Fixed by adding atypeof cm.dispatch === 'function'guard initerateEditorViews.- Plugin:
src/main.ts(iterateEditorViewstype guard)
- Plugin:
Tests
- 10 e2e tests in
test/specs/marks-gutter.e2e.ts(4 new, 6 updated): mark in gutter, mark move, multiple marks, delmarks removal, no marks = empty, dedicated gutter column, no line overlays, ellipsis truncation (4+ marks), consistent font size on headings, nodata-vim-marksattribute - 4 e2e tests in
test/specs/statuscolumn.e2e.ts: no statuscolumn by default, option registered in vim engine, sign column gutter present, mark gutter functionality preserved
Documentation
docs/features/marks.md: rewritten gutter indicators section — dedicated gutter column, fixed font size, truncation, three-mode table, gutter layout orderdocs/configuration/settings.md: updated sign column description, added gutter layout tip with ASCII art exampledocs/configuration/lua-config.md: expanded hybrid line numbers tip with gutter layout exampleKNOWN_LIMITATIONS.md: updatedsigncolumnsection — removed zero-width overlay note, updated behavior descriptionREADME.md: updated marks feature description
Full Changelog: 0.54.0...0.55.0