Notepad++ plugin for Elastic Tabstops — tab-separated text aligns into columns because adjacent lines auto-stretch to match the widest item in each column. Port of the macOS port of the ElasticTabstops plugin by Justin Dailey and Marius Vasiliu (GPL v2).
With the plugin disabled, tabs render at fixed tab-width intervals — so
Al<tab>30 and Catherine<tab>30 land the "30" in different screen
columns. Enable the plugin and tabs stretch so that same-index items line
up. No spaces are inserted — the file still contains literal \t
bytes; only the visual rendering changes (via per-line Scintilla tab
stops).
Menu (Plugins ▸ Elastic Tabstops):
- Enable — toggle elastic tabstops for the current file
- Selection only — only process the current selection
- Global tabs — uniform column widths across the whole file (off = block-local alignment)
- Convert Elastic Tabs to Spaces — bake the visual alignment into literal spaces
- Convert Spaces to Elastic Tabs — collapse runs of 2+ spaces into a tab (asks first above 1000 lines)
- Remember enabled files — persist each enabled file's per-file state across restarts
- Save and edit default settings… — opens the JSON config; the
extensionsfield controls which file types auto-enable (*= all,!*= none,.c .h= specific,!-prefix negates)
Config: ElasticTabstops.json in the plugin config folder
(~/.local/share/nextpad++/plugins/Config/) — same keys and shape as the
macOS port.
Single-file plugin; the grid computation, per-file state model, range-aware
recompute and conversions are line-for-line from the macOS port. The
platform seams: NSJSONSerialization → json-glib, NSAlert → GTK4 modals,
NSFileManager → GLib (incl. the legacy ~/.nextpad++/~/.notepad++
config migration).
Two behavioral notes:
- The macOS layout-refresh workaround (a no-op
SCI_SETTABWIDTHafter changing tab stops, because the vendored Scintilla's ChangeTabStops branch doesn't invalidate the line-layout cache) is needed and works on this host's vendored Scintilla too. - Host-timing adaptation: this host can surface a buffer to the plugin before the document's file path is stamped, so a tracked record can be created with an empty name. The remembered per-file state ("Remember enabled files") is therefore applied at the moment the real name is first learned — without this, remembered files silently failed to re-enable after a restart. (macOS loads plugins after session restore, so the first sight always carries the path.)
NPPN_WORDSTYLESUPDATEDis not emitted by this host today; the handler is kept for parity and lights up automatically if that changes. Zoom invalidation (SCN_ZOOM) works.
Set NPP_ET_DEBUG=1 for stderr traces of the tracking/compute decisions.
cmake -B build -S .
cmake --build build -j"$(nproc)"
cmake --install build # -> ~/.local/share/nextpad++/plugins/ElasticTabstops/Requires libgtk-4-dev, libjson-glib-dev and the Nextpad++ GTK4 tree
checked out alongside this folder.
ctest --test-dir build --output-on-failureet_core (24 assertions, no display needed) covers the extension rules,
the file registry (fgIdx patching, remembered-state priority, the
empty-name-first host-timing regression), and the json-glib config round
trip incl. tolerant parsing of hand-edited/malformed files. plugin_loader
checks the dlopen surface, the 12-item menu with its two separators and
the FuncItem ABI.
GNU General Public License v2, as upstream.