diff --git a/Readme.md b/Readme.md index 18c481175..508054f24 100644 --- a/Readme.md +++ b/Readme.md @@ -117,6 +117,8 @@ Notepad3 uses a portable INI file for all settings. Press **Ctrl+F7** to open it πŸ“– **Full configuration reference:** [readme/config/Configuration.md](readme/config/Configuration.md) +🎨 **Schemas, styles & themes:** [readme/schema/CustomSchema.md](readme/schema/CustomSchema.md) β€” the layered override model, the style mini-language, `View β†’ Customize Schemes` (**F12**), and how to export / import / collect custom themes. + ## Contributing Contributions are welcome! Here's how to help: diff --git a/plans/schema_enhancements.md b/plans/schema_enhancements.md new file mode 100644 index 000000000..5a107f1e7 --- /dev/null +++ b/plans/schema_enhancements.md @@ -0,0 +1,424 @@ +# Schema / Style / Theme System β€” Enhancements & Fixes + +> Companion plan to the user-facing reference at +> [`readme/schema/CustomSchema.md`](../readme/schema/CustomSchema.md). +> Use this document to scope a future Claude Code session focused on the +> schema/style subsystem. + +## 1. Background + +Notepad3's schema system (the `EDITLEXER` array, per-schema `EDITSTYLE` slots, +the *Common Base* / *2nd Common Base* super-schemas, and the `themes\*.ini` +collection) is stable and user-visible, but has accumulated small rough +edges over time. This plan consolidates: + +- issues surfaced while writing the user documentation, +- follow-up ideas marked as `//~` in the code, +- UX gaps reported during the review, +- plus a few additional findings that emerged from cross-checking the + code paths. + +Nothing here is a correctness bug blocking release. All items are +low-to-moderate effort; several are trivial single-call-site fixes. + +## 2. Key source locations + +| File | What lives there | +|------|------------------| +| `src/Styles.c` | `g_pLexArray[]`, load/save, mini-DSL parsers, Customize / Select / Themes dialogs, Import / Export | +| `src/StyleLexers/EditLexer.h` | `EDITLEXER`, `EDITSTYLE`, `KEYWORDLIST`, `BUFSIZE_STYLE_VALUE = 256` | +| `src/TypeDefs.h` | `STYLE_EXTENTIONS_BUFFER = 512`, `EXTENTIONS_FILTER_BUFFER = 1024` | +| `src/StyleLexers/*.c` | 60+ per-language lexer definitions | +| `src/Config/Config.cpp` | Settings β†’ theme name persistence (`Settings.CurrentThemeName`) | +| `language/common_res.h` | Menu IDs: `IDM_VIEW_SCHEME=41001`, `IDM_VIEW_USE2NDDEFAULT=41002`, `IDM_VIEW_SCHEMECONFIG=41003`, `IDM_THEMES_FACTORY_RESET=37001`, `IDM_THEMES_STD_CFG=37002` | +| `Build/themes/Dark.ini`, `Obsidian.ini`, `Sombra.ini` | Shipped themes | + +## 3. Inventory of issues + +| # | Severity | Area | Issue | +|---|----------|------|-------| +| I-01 | Medium | Buffers | 255-char style-string cap; silent truncation on load | +| I-02 | Low | Buffers | 511-char `FileNameExtensions` cap; silent truncation | +| I-03 | Medium | Themes | 24-theme hard cap (fixed-size `Theme_Files[25]`); silently drops extras | +| I-04 | Medium | UX | No "Reset this whole schema" button β€” user must edit INI by hand or factory-reset everything | +| I-05 | Medium | UX | Import overwrites `FileNameExtensions` β€” users lose extension tweaks when switching themes | +| I-06 | Low | UX | No mid-session revert: `pStylesBackup` is only taken on dialog open | +| I-07 | Low | UX | No "import from URL" and no way to distribute themes via anything but a file copy | +| I-08 | Low | Round-trip | `smoothing:` token parsed on load but never written back by the dialog | +| I-09 | Low | Round-trip | `//~` dead code around `Style_StrGetAlpha` for Whitespace background layer (alpha2 never exposed) | +| I-10 | Low | Round-trip | `SelectDlgSizeX/Y` travels inside `[Styles]` and leaks into every exported theme | +| I-11 | Medium | Matching | First-in-array wins for extension conflicts β€” no per-user priority mechanism | +| I-12 | Low | Matching | `\regex` extension syntax is entirely undocumented in-UI | +| I-13 | Low | Validation | Silent load of malformed theme files β€” no minimum-viable-theme check | +| I-14 | Low | Validation | Stale `Settings.CurrentThemeName` silently falls back to Standard Config | +| I-15 | Low | Consistency | Dialog-cancel restores styles but *not* extension-list edits (they hit the array directly via `_ApplyDialogItemText`) β€” verify & fix if confirmed | +| I-16 | Low | Dead code | `//~bool ChooseFontDirectWrite(...)` (`Styles.c:~50`) β€” decide delete vs enable | +| I-17 | Low | Feature | Dark-mode 16-colour defaults (`s_colorLightDefault[]`, `s_colorDarkDefault[]`) are hard-coded with no load/save path | +| I-18 | Low | Feature | Style names are compile-time fixed β€” no user-defined styles, no extension lists on custom detection beyond extension/regex | +| I-19 | Low | Performance | Schema auto-detect is O(n) per file open across ~60 schemas Γ— multi-token extension lists | +| I-20 | Low | Performance | All `themes\*.ini` are stat-scanned at startup even if user never switches themes | + +## 4. Enhancement proposals + +Each proposal below lists the motivation, concrete change, files to touch, +and likely risk level. They are grouped into tiers so a session can pick +up an easy batch without stepping on the harder ones. + +--- + +### Tier 1 β€” Quick wins (half-day each) + +#### E-01 β€” Warn on style-string truncation (closes I-01) + +**Motivation.** When a user hand-edits a style in `Notepad3.ini` and the +result exceeds 255 chars (easy with long font names plus many +attributes), the tail is silently dropped on load. No signal is given. + +**Change.** +- In `_ReadFromIniCache()` (`src/Styles.c`), after each + `IniSectionGetString(..., dst, BUFSIZE_STYLE_VALUE)`, compare the + returned length against `BUFSIZE_STYLE_VALUE - 1`. If equal, the value + was almost certainly clipped. +- Accumulate a list of offending `
/ ` pairs in a static + buffer. +- After `_ReadFromIniCache()` completes, if any clipped values were + found, show a single `InfoBoxLng(MB_ICONWARNING, ...)` listing the + first few (e.g. up to 5) and hinting that the cap is 255 chars. +- Add string IDs `IDS_MUI_STYLE_TRUNCATED` to `language/common_res.h` + and all 26 `strings_*.rc` files with an English placeholder (per + memory note about locale files). + +**Risk.** Low. Read-only addition. + +#### E-02 β€” Reset-whole-schema button (closes I-04) + +**Motivation.** Customize Schemes has a *Default* button that resets the +currently-selected style. Users who want to revert an entire schema +(say, C++) to defaults currently have no one-click path. + +**Change.** +- Add a new button to `IDD_MUI_STYLECONFIG` next to the existing + *Default* button, e.g. `IDC_STYLERESETSCHEMA`, labelled + "Reset &Scheme". +- When the selected tree node is a schema (not a style), enable the + button; otherwise disable. Current wiring in + `Style_CustomizeSchemesDlgProc` already distinguishes the two cases. +- On click: for every `EDITSTYLE` in `pLexCurrent->Styles[]`, copy + `pszDefault` into `szValue`; also reset `szExtensions` to + `pszDefExt`. Refresh the preview. +- No backup/undo required β€” the existing `pStylesBackup` captured on + dialog open already covers Cancel. + +**Risk.** Low. UI addition only. + +#### E-03 β€” Drop `SelectDlgSize*` from theme exports (closes I-10) + +**Motivation.** `SelectDlgSizeX` / `SelectDlgSizeY` are remembered +ListView dimensions for the *Select Scheme* dialog. They are unrelated +to styling and pollute every exported theme. + +**Change.** +Two reasonable variants β€” pick one: + +1. **Move them out of `[Styles]`** into `[Settings]` where other UI + remembered-size values already live. Keeps the INI cleaner. +2. **Skip writing them** inside `Style_ToIniSection()` when invoked by + the *Export* path. Easiest: add a boolean flag to + `Style_ToIniSection(bool bForceAll, bool bIncludeUIState)` and pass + `false` from `Style_Export()`. + +Variant 2 is minimally invasive; Variant 1 is cleaner long-term. A +future session should pick one and stick with it. + +**Risk.** Low. Either variant is easy to revert if feedback is +negative. + +#### E-04 β€” Tooltip documenting `\regex` extension syntax (closes I-12) + +**Motivation.** `\^CMakeLists$` style regex entries in `FileNameExtensions` +are a real feature, implemented in `Style_RegExMatchLexer()` +(`src/Styles.c:2543`), but the UI never mentions it. + +**Change.** +- Add a tooltip (`TOOLTIPS_CLASS`) to the `IDC_STYLEEDIT_ROOT` control + in `Style_CustomizeSchemesDlgProc`. +- Text (new localised string `IDS_MUI_EXTLIST_TOOLTIP`): "Semicolon- + separated. Prefix with `\` for a regex matched against the full file + name, e.g. `\^CMakeLists$`." +- Add the string to all 26 locale files. + +**Risk.** Trivial. + +#### E-05 β€” Theme-file header comment (closes part of I-07) + +**Motivation.** Shared theme files currently have no authorship or +version metadata. A single leading comment line is cheap. + +**Change.** +- In `Style_ExportToFile()` (when `bForceAll=true` path), before calling + `SaveIniFileCache()`, prepend a single comment line to the cache via + the existing IniSection helpers β€” e.g. via a new helper + `IniFileSetHeaderComment()` if one doesn't exist, otherwise write the + line as a key in a throwaway `[; meta]` section ordering hack. +- Line format: `; Notepad3 theme β€” exported YYYY-MM-DD from Notepad3 + `. +- Check that existing `LoadIniFileCache()` tolerates a leading comment + before the first section header (it should β€” standard INI). + +**Risk.** Low; verify that Windows INI APIs (if used anywhere β€” the code +uses a custom parser) don't choke. + +#### E-06 β€” Remove documented dead code (closes I-16, part of I-09) + +**Motivation.** `//~`-prefixed lines accumulate without a decision. + +**Change.** +- Delete the commented `ChooseFontDirectWrite` declaration near + `Styles.c:50` if the MUI/ChooseFont path is the intended permanent + solution. +- Either implement the commented `Style_StrGetAlpha` alpha2 branch for + the Whitespace style (`src/Styles.c:~1626`, `~4489`) or delete it + along with the matching "expose alpha2 for Whitespace" TODO. A + comment in the source explaining *why* it's not supported (e.g. + "Scintilla does not honour alpha2 on SCE_STANDARDWHITESPACE") would + also close the loop. + +**Risk.** Low. Cleanup only. + +--- + +### Tier 2 β€” Medium features (1–2 days each) + +#### E-07 β€” Decouple `FileNameExtensions` from theme Import (closes I-05) + +**Motivation.** The current Import behaviour forces users to choose +between colours and their accumulated extension tweaks. Most real-world +"themes" shared online are colour-only. + +**Change.** +- In `Style_Import()` (`src/Styles.c:714`), before the + `FileOpenDlg` call, show a small confirmation dialog or use + `FOS_` custom-place extension with a checkbox: *"Also import file-name + extensions"*, default **off**. +- Propagate the flag into `Style_ImportFromFile(HPATHL, bool + bImportExtensions)`. +- Inside `_ReadFromIniCache()`, gate the call to + `Style_FileExtFromIniSection()` on this flag. +- Keep the old signature as a `static` inline caller that passes the + legacy `true` for startup / theme-switch paths where the extension + set must follow. +- **Symmetry:** add the mirror "include extensions" checkbox to Export + too β€” see E-09. + +**Risk.** Medium. The Import path also runs on theme switch; ensure +that Factory Reset and Theme switching still reset extensions. Default +`true` on those paths, `false` only on explicit *Import…* button. + +#### E-08 β€” Dynamic `Theme_Files[]` (closes I-03) + +**Motivation.** 24-theme cap is arbitrary and not even documented. +Some users have 40+ curated themes. + +**Change.** +- Replace the fixed `THEMEFILES Theme_Files[25]` (`src/Styles.c:374-400`) + with a heap-allocated grow-on-demand array. Suggested: a local helper + `ThemeList` wrapping a `THEMEFILES*` pointer and a size_t count. +- Update every reference: `ThemeItems_CountOf()`, `ThemesItems_Init()`, + `ThemesItems_Release()`, `ThemesItems_MaxIndex()`, the loop in + `_FillThemesMenuTable()`, and `Style_InsertThemesMenu()`. +- Keep `extern "C" THEMEFILES Theme_Files[]` compatibility by exposing + a `Theme_Files_Get(unsigned i)` accessor, or by turning the external + reference (`src/Config/Config.cpp:77`) into a function call. +- Bound at something sane (e.g. 256) to avoid accidental DoS on a + poisoned `themes\` directory. + +**Risk.** Medium. Touches many call sites; needs care with menu +command ID ranges (`IDM_THEMES_STD_CFG + i`) β€” decide whether to keep +consecutive IDs (cap at a known range) or switch to a single +reserved range checked in the WM_COMMAND handler. + +#### E-09 β€” Export filter: styles vs styles+extensions (pairs with E-07) + +**Motivation.** Symmetry with the Import change; authors can now +publish *colours only* themes cleanly. + +**Change.** +- Add a checkbox "Include file-name extensions" to the Export dialog + (use `CustomizeSaveFileDialog` or bundle it into a small pre-dialog + that then launches `FileSaveDlg`). +- Propagate into `Style_ExportToFile(HPATHL, bool bForceAll, bool + bIncludeExtensions)`. +- Inside that function, gate the `Style_FileExtToIniSection()` call. + +**Risk.** Low. + +#### E-10 β€” Import validation (closes I-13) + +**Motivation.** Loading a random INI as a theme silently succeeds, +overwriting in-memory styles with partial or unrelated data. + +**Change.** +- Before calling `_ReadFromIniCache()`, peek at the cache to confirm + the imported file contains at least one of: `[Common Base]`, + `[Styles]`, or any per-schema section that matches a known + `g_pLexArray[]` name. +- If none found, show `InfoBoxLng(MB_ICONWARNING, ..., + IDS_MUI_IMPORT_NOT_A_THEME)` and abort without touching in-memory + state. +- Add the string to all 26 locale files. + +**Risk.** Low. + +#### E-11 β€” Handle missing theme file gracefully (closes I-14) + +**Motivation.** If a theme file referenced by `Settings.CurrentThemeName` +was deleted between sessions, Notepad3 silently falls back to Standard +Config without informing the user β€” changes made "to the active theme" +after that point land in `Notepad3.ini`, not the expected file. + +**Change.** +- In `_FillThemesMenuTable()` (`src/Styles.c:438`), remember whether + the stored `Settings.CurrentThemeName` was actually matched. +- If not matched and non-empty, show a one-shot info message at + startup: *"Theme '' was not found in `themes\`. Reverting to + Standard Config."* +- Gate under a `Settings2.NoThemeMissingWarning` flag so power users + can suppress it. + +**Risk.** Low. Add a new `Settings2` key β€” remember to document it per +the project's memory note on new parameters. + +--- + +### Tier 3 β€” Larger features (multi-day) + +#### E-12 β€” Per-schema extension priority (closes I-11) + +**Motivation.** Today, if two schemas claim `.h`, whichever appears +first in `g_pLexArray[]` wins. Users who prefer C over C++ for `.h` +(or vice versa) can't express it. + +**Change.** +- Add an optional integer key `Priority=` inside each per-schema + INI section. Default 0 when absent. +- In `Style_MatchLexer()` (`src/Styles.c:2517`), when searching by + extension with `bCheckNames=false`, collect all matches and return + the one with highest priority; tie-break by array index as before. +- Surface in Customize Schemes: a numeric spinner on the schema node + page. + +**Risk.** Medium. Touches the hot detection path; add a short-circuit +fast path for "exactly one match" (today's common case) to avoid a +double scan. + +#### E-13 β€” Named custom-colour references (closes I-17 partially) + +**Motivation.** The 16-slot custom-colour palette is currently isolated +from style strings; it's only used by the colour-picker dialog. Letting +users reference slots from style strings (`fore:@3`) would enable +trivial "palette-swap" themes: change 16 lines, re-theme everything. + +**Change.** +- Extend `Style_StrGetColor()` (`src/Styles.c:~3476`) to accept `@N` + (1-16) as an alternative to `#RRGGBB`. Resolve via + `Globals.crCustom[N-1]`. +- When writing styles via the dialog, continue to emit `#RRGGBB` (so + pre-existing themes stay pre-existing). Only users who opt in by + hand-editing get the `@N` form. +- Add documentation (both in `CustomSchema.md` and an in-dialog + tooltip) that `@N` resolves at draw time and follows changes to the + custom palette. + +**Risk.** Medium. Watch for cases where a style is evaluated before +`Globals.crCustom` is populated (early startup). Add a guard that +falls back to the compiled-in default colour in that window. + +#### E-14 β€” Schema-detection cache (closes I-19) + +**Motivation.** `Style_MatchLexer()` is O(n Γ— tokens) per file open. +For a batch open of 1000 files, this adds up. + +**Change.** +- Maintain a `WCHAR *ext β†’ EDITLEXER*` hash map built once per session + from the schema extension lists, rebuilt when Customize Schemes + commits edits. +- `Style_MatchLexer()` first checks the hash, only falling through to + the existing regex / shebang / sniff pipeline when the hash misses. +- Use `uthash` (`src/uthash/`) to stay consistent with the project's + existing dependencies. + +**Risk.** Medium. Must invalidate on every `_ApplyDialogItemText()` +for the extension field and after every theme switch. + +--- + +### Tier 4 β€” Nice-to-have / discussion + +- **E-15 β€” Custom-colour load/save symmetry for dark vs light defaults + (I-17).** Today `s_colorLightDefault[]` / `s_colorDarkDefault[]` are + compile-time constants; the palette is reset on factory reset. Allow + persisting user-tweaked defaults per dark/light mode to match + Windows' adaptive behaviour. + +- **E-16 β€” User-defined highlight classes (I-18).** Huge undertaking β€” + would require extending `EDITSTYLE` to be dynamically allocated + per-lexer, teaching the INI parser about user-added keys, and + wiring Scintilla indicator slots. Out of scope for a normal + session; noted here only for completeness. + +- **E-17 β€” Lazy theme scanning (I-20).** Defer the `FindFirstFile` + sweep of `themes\` to the first time the user opens the *View β†’ + Themes* submenu. Negligible savings for most users; probably not + worth it. + +- **E-18 β€” Cancel-revert for extension edits (I-15).** Unverified + claim; needs a quick read of `Style_CustomizeSchemesDlgProc`'s + Cancel path. If the Cancel handler only restores `szValue` and not + `szExtensions`, restore both. Two-liner fix if the issue is real. + +## 5. Verification plan (shared across items) + +For any change that touches style I/O: + +1. **Round-trip test.** Save a reference file list with heavy theming + (e.g. `Dark.ini`), load it, save again β€” the second save must be + byte-identical except for the known `SelectDlgSize*` / timestamp + differences. +2. **Locale coverage.** For every new `IDS_MUI_*` string, run + `Build/rc_to_utf8.cmd` and confirm all 26 locale `strings_*.rc` + files still load (no BOM, CRLF preserved β€” see project memory + note). +3. **Theme-switch smoke test.** Switch Standard Config β†’ Dark β†’ + Obsidian β†’ Standard Config; confirm `Settings.CurrentThemeName` + is persisted and the menu radio follows. +4. **Customize Schemes smoke test.** Open the dialog, edit several + styles, click Cancel β€” verify that the edits are reverted (nothing + leaks to INI on a subsequent Save). +5. **Missing-theme test.** Rename `themes\Dark.ini` to something else + between sessions; ensure E-11's warning fires exactly once. +6. **ARM64 smoke.** Theme switching invokes a lot of redraws β€” verify + no flicker regression on ARM64 (relevant because of + `RenderingTechnology=2` default there β€” see + `readme/config/Configuration.md`). + +## 6. Documentation hooks + +Whenever any of the enhancements above lands, update: + +- `readme/schema/CustomSchema.md` β€” user-facing behaviour. +- `readme/config/Configuration.md` β€” new `Settings2` keys (E-11, and + anything else that introduces one). +- `CLAUDE.md` and `.github/copilot-instructions.md` β€” if the change + introduces a new pattern worth guiding future contributions toward. +- `Build/Notepad3.ini` β€” commented-out entries for any new INI keys, + per the project's "document new parameters" convention. + +## 7. Out of scope (explicitly) + +- Renaming schemas or reordering `g_pLexArray[]`. The array order is + load-bearing for current-session INI compatibility. +- Switching away from Scintilla / Lexilla lexers. +- Adding a full-blown theme editor GUI separate from Customize + Schemes. +- Making style keys user-extensible (E-16 above is documented but + deliberately parked). diff --git a/readme/config/Configuration.md b/readme/config/Configuration.md index 1f7044a3d..d42689039 100644 --- a/readme/config/Configuration.md +++ b/readme/config/Configuration.md @@ -78,6 +78,13 @@ Available languages: #### `IMEInteraction=0` +Scintilla IME composition mode: +- `-1` β€” Auto-detect (one-shot at startup): Korean code pages (`949`, `1361`) β†’ inline (`1`); all other languages β†’ windowed (`0`). +- `0` β€” `SC_IME_WINDOWED`: IME composition shown in a separate floating window. +- `1` β€” `SC_IME_INLINE`: composition happens inline at the caret. Recommended when working with Far East languages that emit long composition strings. + +Range: `-1`–`1`. + ### Date & Time #### `DateTimeFormat=` @@ -115,6 +122,8 @@ Default extension for saved files (omit the leading dot). #### `DenyVirtualSpaceAccess=0` +Set to `1` to disable virtual-space access (the area beyond each line's end). With access denied, rectangular (column) selections cannot extend past the shortest line end. Default (`0`) allows rectangular selection to span virtual space β€” useful for aligning columns across lines of varying length. + #### `filebrowser.exe=minipath.exe` External program launched by the Browse toolbar button. Defaults to MiniPath. @@ -269,6 +278,14 @@ Whether to prompt for save when closing an untitled document that contains only #### `SciFontQuality=3` +Scintilla font rendering quality: +- `0` β€” Default (system-chosen) +- `1` β€” Non-antialiased +- `2` β€” Antialiased +- `3` β€” LCD-optimized (ClearType; default) + +Range: `0`–`3`. A per-resolution override is supported in the `[Window]` section via a key named `x SciFontQuality=` (e.g. `1920x1080 SciFontQuality=2`), so different monitor layouts can use different font quality settings. + #### `SimpleIndentGuides=0` Set to `1` to prevent indentation guides from jumping across empty lines. @@ -293,10 +310,29 @@ Set to `true` for old selection behavior: single click selects the visible sub-l #### `LaunchInstanceWndPosOffset=28` +Pixel offset cascade applied to each new Notepad3 window launched as an additional instance. Each successive window is shifted by this many pixels (both X and Y) relative to the previous one, so overlapping windows remain distinguishable. Range: `-10000`–`10000`. Set to `0` to spawn new windows at the exact same position. + #### `LaunchInstanceFullVisible=true` +When `true` (default), newly launched windows are clamped so they remain fully visible on the target monitor, even if the `LaunchInstanceWndPosOffset` cascade would push them off-screen. Set to `false` to allow partially off-screen positions (e.g. if you rely on the OS to handle multi-monitor placement). + ### Appearance +#### `RenderingTechnology=1` + +Scintilla rendering back-end: + +| Value | Name | Description | +|-------|------|-------------| +| `0` | GDI | Classic Win32 rendering | +| `1` | DirectWrite | Direct2D (default on x86/x64) | +| `2` | DirectWriteRetain | Direct2D preserving the back buffer between frames (default on ARM64 to prevent flicker on Qualcomm Adreno GPUs and the Win11 25H2 DWM compositor) | +| `3` | DirectWriteDC | DC-based Direct2D | + +Also configurable via **Menu β†’ View β†’ Rendering Technology**. A per-resolution override is supported in the `[Window]` section, e.g. `1920x1080 RenderingTechnology=0`, so a docked laptop and an external 4K monitor can use different back-ends. + +**Tip:** On ARM64 systems showing rendering glitches, try `0` (GDI) or `3` (DirectWriteDC) as a workaround. + #### `UseOldStyleBraceMatching=0` Set to `1` for legacy brace-matching style. @@ -307,10 +343,16 @@ Display scale percent threshold to switch to larger file-type icons. #### `DarkModeBkgColor=0x1F1F1F` +Dark-mode window background color. The three `DarkMode*Color` entries use Win32 `COLORREF` byte order β€” `0xBBGGRR` (blue, green, red), **not** the usual HTML `0xRRGGBB`. So `0x1F1F1F` is a near-black neutral, and e.g. `0x0000FF` is pure red, not pure blue. + #### `DarkModeBtnFaceColor=0x333333` +Dark-mode button face color. Same `0xBBGGRR` byte order as above. + #### `DarkModeTxtColor=0xEFEFEF` +Dark-mode text color. Same `0xBBGGRR` byte order as above. + ### Web Actions #### `WebTemplate1=https://google.com/search?q=%s` @@ -385,10 +427,16 @@ Font priority list for the "Text Files" scheme. #### `UpdateDelayMarkAllOccurrences=50` +Debounce interval (milliseconds) before the "mark all occurrences" highlight is recomputed after a document change. Lower values feel more responsive but cost more CPU on large documents; higher values reduce flicker while typing. Range: ~`20`–`10000` ms (the lower bound is clamped to `USER_TIMER_MINIMUM * 2`, typically around 20 ms). + #### `CurrentLineHorizontalSlop=40` +Horizontal caret slop in columns, passed to Scintilla's `SCI_SETXCARETPOLICY`. Defines the minimum horizontal gap (in character cells) the caret keeps from the left/right edge of the view before the view starts to scroll horizontally. Larger values keep more surrounding context visible; `0` disables the slop. Range: `0`–`240`. + #### `CurrentLineVerticalSlop=5` +Vertical caret slop in lines, passed to Scintilla's `SCI_SETYCARETPOLICY`. Defines the minimum vertical gap (in whole lines) the caret keeps from the top/bottom of the view before the view scrolls. `0` forces the "even caret" policy (caret always centered-ish); positive values set a fixed top/bottom margin. Range: `0`–`100`. + #### `UndoTransactionTimeout=0` Timeout in milliseconds. Set to `1` (clamped to 10 ms minimum) to make nearly every keystroke a separate undo action. `0` disables the timer. @@ -424,7 +472,9 @@ Bias added to confidence when the system's ANSI code page matches the detected e Enable `#` as a line-comment character in MySQL-dialect SQL. Set to `0` to disable. -#### `AtomicFileSave=true;` +#### `AtomicFileSave=true` + +When `true` (default), `Save` writes to a temporary file alongside the target and then atomically replaces the original via `ReplaceFileW`. This preserves the original file's ACLs, alternate data streams, and attributes on failure (nothing is lost if the write is interrupted). Set to `false` to write in-place, which is faster on very large files but offers no rollback if the write fails mid-way. During Windows session end (`WM_ENDSESSION`) the atomic path is bypassed regardless of this setting. #### `ExitOnESCSkipLevel=2` diff --git a/readme/schema/CustomSchema.md b/readme/schema/CustomSchema.md new file mode 100644 index 000000000..fb8547c73 --- /dev/null +++ b/readme/schema/CustomSchema.md @@ -0,0 +1,411 @@ +# Notepad3 Schemas, Styles & Themes + +This document describes how Notepad3's appearance customization works: what a *schema* (a.k.a. *lexer style set*) is, how styles are layered, how the style mini-language reads, how to export / import / switch themes, and where the user-editable files live. + +Audience: advanced users who want to tweak colours and fonts beyond the built-in dialog, share a theme with others, or understand why a particular setting isn't sticking. + +--- + +## Table of contents + +1. [Core concepts](#1-core-concepts) +2. [Layered override model](#2-layered-override-model) +3. [The style mini-language](#3-the-style-mini-language) +4. [The default schemas (`Common Base` & `2nd Common Base`)](#4-the-default-schemas-common-base--2nd-common-base) +5. [File β†’ schema matching](#5-file--schema-matching) +6. [The Customize Schemes dialog](#6-the-customize-schemes-dialog) +7. [Themes collection (`View β†’ Themes`)](#7-themes-collection-view--themes) +8. [Export / Import](#8-export--import) +9. [Anatomy of a style INI file](#9-anatomy-of-a-style-ini-file) +10. [Toolbar themes (separate from colour schemes)](#10-toolbar-themes-separate-from-colour-schemes) +11. [References in the code](#references-in-the-code) + +--- + +## 1. Core concepts + +| Term | Meaning | +|------|---------| +| **Schema** / **Scheme** | A complete set of styles for one language (e.g. *C/C++ Source Code*, *Python Script*). Backed in code by an `EDITLEXER` record plus an array of `EDITSTYLE` entries. | +| **Style** | A single styleable element inside a schema (e.g. *Default Style*, *Keywords*, *Comments*, *Strings*). Each style has a plain-text *style string* describing its font, size, colours, etc. | +| **Theme** | A complete snapshot of all custom colours, schemas and per-schema extensions, stored as a standalone `.ini` file (e.g. `Dark.ini`). Switching themes replaces every schema's style strings at once. | +| **Lexilla lexer** | The underlying syntax-highlighting engine for a schema. Notepad3 ships with ~60 lexers (C++, Python, Markdown, Rust, YAML, …). | +| **Custom colours** | A 16-slot colour palette stored per-installation. Used by the *Customize Schemes* colour-picker and exported with every theme. | + +Notepad3 ships with roughly 60 schemas; the exact list appears in the *Select Scheme* dialog (**Ctrl+F11**) and in the left-hand tree of *Customize Schemes* (**F12**). + +--- + +## 2. Layered override model + +Every style string you see in the editor is the result of a four-level resolution chain, applied each time a file is opened or a theme is switched: + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ 4. Runtime edits (Customize Schemes dialog) β”‚ ← highest priority, in-memory only +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ 3. User INI β€” either Notepad3.ini [SchemaSection] β”‚ +β”‚ or the currently loaded theme file (themes/.ini) β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ 2. 'Common Base' / '2nd Common Base' defaults β”‚ +β”‚ (font, size, fg/bg for everything unstyled) β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ 1. Hard-coded lexer defaults (compiled into Notepad3.exe) β”‚ ← lowest priority +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +- **Lower levels fill gaps only.** If a user INI defines `fore:#ff0000` for a style but omits `font:`, the font comes from the Common Base, and ultimately from the hard-coded default. +- **Only deltas are saved.** When Notepad3 writes your styles back, it stores *only* values that differ from the merged defaults. An exported theme, by contrast, can force-save everything β€” see [Β§8 Export/Import](#8-export--import). +- **Empty lexer sections are removed.** After saving, any `[Python Script]`-style section that ended up empty (because the user reset everything to default) is stripped so the file stays readable. +- **Dark mode is its own level 2.** When Windows is in dark mode, the *Common Base* defaults come from a dark-mode palette instead of the light one. Your custom deltas stack on top of whichever palette is active. + +### Where do my custom changes land on disk? + +| Active theme | Changes written to | +|---|---| +| *Standard Config* (index 0) | `Notepad3.ini`, sections `[Styles]`, `[Custom Colors]`, and per-schema sections like `[C/C++ Source Code]`, `[Python Script]`, … | +| A named theme (e.g. *Dark*) | `\themes\Dark.ini` (full dump β€” all styles, not just deltas) | + +Saving occurs only if `Settings.SaveSettings = 1` (Menu **Settings β†’ Save Settings On Exit**) or you choose **Settings β†’ Save Settings Now** (**F7**). + +--- + +## 3. The style mini-language + +Every `EDITSTYLE::szValue` is a **semicolon-delimited list of `attribute:value` tokens**. Order is irrelevant, keys are case-insensitive, and spacing around `:` and `;` is tolerated: + +``` +font:Consolas; size:11; fore:#D7BA7D; back:#1E1E1E; bold +``` + +Buffer limit: **255 characters** per style string. Longer strings are silently truncated β€” see [Β§11 Limitations](#11-known-limitations--quirks). + +### 3.1 Attributes + +| Token | Purpose | Examples | +|-------|---------|----------| +| `font:` | Font face. Literal face names or the aliases `Default`, `$Code0…$Code9`, `$Text0…$Text9` which resolve to entries of `CodeFontPrefPrioList` / `TextFontPrefPrioList` (`Settings2`). | `font:Consolas`, `font:$Code0` | +| `size:` or `size:Β±` | Absolute (`11`) or relative (`+2`, `-1`) font size in points. Relative sizes cumulate across the inheritance chain. | `size:10`, `size:+1` | +| `fore:#RRGGBB` | Foreground (text) colour, hex. `#AARRGGBB` also accepted on styles that respect alpha. | `fore:#569CD6` | +| `back:#RRGGBB` | Background colour, hex. `#AARRGGBB` also accepted. | `back:#1E1E1E` | +| `bold` / `italic` / `underline` / `strikeout` | Plain flags, no value. | `italic` | +| `eolfilled` | Extends the background colour to the end of the line. | `eolfilled` | +| `case:u` \| `case:l` \| `case:m` | Force upper / lower / mixed case (visual only). | `case:u` | +| `charset:` | LOGFONT charset id (0 = ANSI, 128 = Shift-JIS, 186 = Baltic, 204 = Russian, …). Clamped to β‰₯ 0. | `charset:204` | +| `smoothing:` | Font quality. Tokens: `default`, `standard`, `aliased`, `antialiased`, `cleartype`. | `smoothing:cleartype` | +| `alpha:<0-255>` | Primary alpha (indicator / overlay opacity). | `alpha:40` | +| `alpha2:<0-255>` | Secondary alpha (outline for box-type indicators). | `alpha2:220` | +| `weight:` | Font weight. Accepted words: `thin`, `extralight`, `light`, `book`, `regular`, `medium`, `semibold`, `bold`, `extrabold`, `heavy`, `extrablack`, plus a handful of synonyms. | `weight:semibold` | +| `stretch:` | Font stretch: `ultracondensed`, `extracondensed`, `condensed`, `semicondensed`, `normal`, `semiexpanded`, `expanded`, `extraexpanded`, `ultraexpanded`. | `stretch:condensed` | +| `block` | Draw caret as a block. | `block` | +| `hotspot` | Mark the style as a hotspot (clickable region). | `hotspot` | +| `indic_` | Indicator drawing style (used by hotspot / brace / occurrence markers). Common values: `indic_plain`, `indic_squiggle`, `indic_tt`, `indic_diagonal`, `indic_strike`, `indic_box`, `indic_roundbox`, `indic_straightbox`, `indic_fullbox`, `indic_dash`, `indic_dots`, `indic_squigglelow`, `indic_squigglepixmap`, `indic_compositionthick`, `indic_compositionthin`, `indic_textfore`, `indic_point`, `indic_pointcharacter`, `indic_gradient`, `indic_gradientcentre`. | `indic_roundbox` | + +### 3.2 Colour syntax + +- Always `#` followed by six hex digits (`#RRGGBB`) β€” **not** `0xBBGGRR` as in the dark-mode INI keys. +- Named colours are **not** supported; use hex. +- Colours stored in the *Custom Colors* palette (the 16 slots at the top of any theme file) are written as `#RRGGBB` strings with keys `01`…`16` and are just a convenience shared with the Windows colour-picker dialog β€” they are not referenced from style strings. + +### 3.3 Example β€” styling comments in C++ + +``` +Comment=italic; fore:#608B4E; back:#1E1E1E +``` + +- Italic, no explicit font (inherits Common Base), explicit fg and bg. +- Key (`Comment`) matches a fixed slot inside `styleLexCPP.c`. You cannot invent new style names; you edit the ones the lexer defines. + +--- + +## 4. The default schemas (`Common Base` & `2nd Common Base`) + +Two "super-schemas" act as the global fallback for *every* language-specific schema: + +| Schema | INI section | Role | +|--------|-------------|------| +| `Common Base` | `[Common Base]` | Primary defaults β€” applied to every style that does not explicitly set an attribute. | +| `2nd Common Base` | `[2nd Common Base]` | Alternate set, activated by **Menu β†’ View β†’ Use 2nd Default Style** (`IDM_VIEW_USE2NDDEFAULT = 41002`). | + +Both schemas also contain the universal editor chrome styles (they are not language specific): + +- `Default Style` β€” base font, size, fg, bg. +- `Margins and Line Numbers` β€” line-number gutter. +- `Matching Braces (Indicator)` / `Matching Braces Error (Indicator)` β€” brace highlight. +- `Control Characters` β€” the `[NUL]`, `[BS]` etc. glyphs. +- `Indent Guide` β€” vertical dotted lines. +- `Selection` β€” selection highlight (foreground/background, with alpha). +- `Whitespace` β€” dot/arrow/EOL glyphs. +- `Current Line` β€” background highlight of the line containing the caret. +- `Caret` β€” colour + width, `block` modifier. +- `Long Line Marker` β€” visual for the right-margin line. +- `Bookmark` / `Folding` colours. +- `Mark Occurrences` β€” the highlight colour of *Mark All Occurrences*. +- `Hotspot Style` / `Unicode Hotspot` / `Multi-Edit` β€” interactive overlays. +- `Change History` markers (modified/saved/unsaved). +- `IME Input` colour. +- `Invisible` / `Read-only` overlays. + +### Switching with `Use 2nd Default Style` + +The status bar shows **STD** or **2ND** in the INS/OVR field. Double-click there (or use the menu) to toggle. The active state is saved as: + +```ini +[Styles] +Use2ndDefaultStyle=1 +``` + +Both base schemas coexist in the INI: toggling only flips which one Notepad3 reads. + +--- + +## 5. File β†’ schema matching + +When you open a file, Notepad3 runs a pipeline to pick the right schema. It stops at the first match. + +1. **File variables** (`vim:` / `emacs:` modelines inside the file). If the file declares `mode: python;` or `-*- mode: cpp -*-`, the declared mode name is matched against schema names (case-insensitive prefix) and then against extension lists. Disabled by `Settings2.NoFileVariables=1`. +2. **Shebang detection** for `.cgi` / `.fcgi` files or files flagged as CGI by mode. Recognised interpreters: `python`, `ruby`, `bash`/`sh`, `perl`, `tcl`, `node`/`js`, `php`. Disabled by `Settings2.NoCGIGuess=1`. +3. **Regex match on the file name.** Any entry in a schema's extension list that starts with a backslash is treated as a regex β€” e.g. `\^CMakeLists$` matches the file name `CMakeLists.txt` once combined with the bare `txt` entry below. +4. **Plain extension match.** First lexer whose extension list contains the file's extension wins. Extension comparison is case-insensitive; separator is `;` (semicolon or space both work in practice). +5. **HTML/XML sniff** β€” if the first bytes start with `<`, classify as HTML or XML. Disabled by `Settings2.NoHTMLGuess=1`. +6. **Shebang fallback** for extension-less files (same recognisers as step 2). +7. **ANSI-art fallback** β€” DOS/OEM-encoded files fall back to the ANSI Art lexer. +8. **Default schema.** Whatever is set as *Default Scheme* in the *Select Scheme* dialog (default: *Pure Text Files*). + +### The two toggles in the *Select Scheme* dialog + +| Checkbox | INI key | Meaning | +|---|---|---| +| **Set as default** | `[Styles] DefaultScheme=` | The schema used at startup and when nothing else matches. | +| **Automatic detection** | `[Styles] AutoSelect=0\|1` | When off, steps 3–7 above are skipped. File β†’ schema association is based solely on extension. | + +### Extension lists + +Each schema has a hard-coded default extension list (compiled in), and a user-editable override stored in the schema's INI section as: + +```ini +[Python Script] +FileNameExtensions=py;pyw;pyi;\^setup\.py$ +``` + +The override replaces the default completely (not merged). Clearing the field in *Customize Schemes* restores the default. Buffer limit: **~512 characters per schema**. + +> If two schemas both claim the same extension, the one that appears first in the internal schema array wins. There is currently no UI to reorder schemas. + +--- + +## 6. The Customize Schemes dialog + +- Launch: **Menu β†’ View β†’ Customize Schemes…** (`IDM_VIEW_SCHEMECONFIG = 41003`) or **F12**. +- Modeless β€” you can keep it open while editing files and see live preview. + +### Tree + +Every schema appears in the left-hand tree. Expand a schema to see its style slots. Selecting: + +- **a schema node** lets you edit the *FileNameExtensions* field for that schema. +- **a style node** lets you edit the raw style string plus helpers for font, foreground, and background. + +### Editing aids + +| Control | Role | +|---|---| +| *Style* text field | Free-form style string β€” full mini-language from [Β§3](#3-the-style-mini-language). | +| *Font…* button | Opens the common ChooseFont dialog and writes `font:`, `size:`, `weight:`, `italic` back into the text field. | +| *Foreground* / *Background* | Colour pickers; write `fore:#…` / `back:#…` tokens. | +| *Default* | Resets the current style to its compiled-in default (discards the INI override for that style only). | +| *Preview* | Toggles live preview of your edits in the main editor. | +| *< Prev / Next >* | Step through the styles of the current schema. | +| *Import…* | See [Β§8](#8-export--import). | +| *Export…* | See [Β§8](#8-export--import). | +| *Dark Mode Highlight Contrast* (slider/field) | Adjusts how strongly foreground colours are re-mapped for legibility against the dark background. Stored as `Settings.DarkModeHiglightContrast`. | + +### Cancel vs OK + +Clicking **Cancel** reverts every style string that was modified during this dialog session (a backup is taken on open). Closing the window via the title-bar X behaves the same as Cancel. + +### Drag-and-drop + +Dragging a *style* node onto another *style* node copies the dragged style's string into the target. Dragging a *schema* node is blocked (the cursor changes to the *no-drop* glyph). Drag-and-drop is therefore a quick "use Comments' look for Documentation-Comments" shortcut β€” not a reordering tool. + +--- + +## 7. Themes collection (`View β†’ Themes`) + +A theme is a standalone INI file holding a snapshot of every schema's styles. Themes live in: + +``` +\themes\*.ini +``` + +where `` is the directory that contains the active `Notepad3.ini`. + +### Shipped themes + +The release package includes three reference themes under `Build/themes/`: + +- `Dark.ini` +- `Obsidian.ini` +- `Sombra.ini` + +Copy any of them next to your `Notepad3.ini` (inside a `themes` folder) and they will appear in **View β†’ Themes** on next launch. + +### Menu layout (`View β†’ Themes`) + +``` + Factory Reset ← reloads compiled-in defaults, discards INI/theme + -------------- + ● Standard Config ← Notepad3.ini itself (always present, always first) + Dark + Obsidian + Sombra + + … +``` + +- **Factory Reset** (`IDM_THEMES_FACTORY_RESET = 37001`) discards the current session's style overrides and reloads the compiled-in defaults. It does *not* delete your INI file, but if you save afterwards, the deltas will be rewritten. +- **Standard Config** (`IDM_THEMES_STD_CFG = 37002`) is the internal name for "styles in Notepad3.ini". +- Each theme file adds a menu item with consecutive IDs (`STD_CFG + 1`, `+2`, …). + +### Capacity + +The theme table is allocated with **25 slots** total β€” slot 0 for *Standard Config*, slots 1-24 for theme files. If more than 24 `.ini` files exist in `themes\`, the extras are silently ignored (the ones scanned first by `FindFirstFile` win). + +### Switching themes + +Selecting a theme from the menu triggers this sequence: + +1. If **Save Settings On Exit** is on, save the *current* theme first (to `Notepad3.ini` for Standard Config, to the theme file otherwise β€” with `bForceAll = true`, i.e. every style written in full). +2. Reset the INI file cache. +3. Load the new theme's `.ini` (or the compiled-in defaults for **Factory Reset**). +4. Update `Settings.CurrentThemeName` and tick the chosen menu radio. + +`Settings.CurrentThemeName` persists the choice across restarts. If the file disappears before the next launch, Notepad3 falls back to *Standard Config*. + +### Creating a new theme + +Easiest flow: + +1. Get your styles looking the way you want (Customize Schemes, regular save). +2. **View β†’ Customize Schemes… β†’ Export…**, save as `\themes\MyTheme.ini` (see [Β§8](#8-export--import)). +3. Restart Notepad3. **View β†’ Themes β†’ MyTheme** now appears. + +--- + +## 8. Export / Import + +Both actions are in the *Customize Schemes* dialog (**F12**), lower left. + +### Export + +- File dialog filter: `*.ini` only. +- Written with `bForceAll = true` β€” **every** style is written out in full, regardless of whether it equals the default. Exported files are therefore self-contained and can be shared with anyone. +- Sections written: + - `[Custom Colors]` β€” 16 palette slots. + - `[Styles]` β€” global settings: `Use2ndDefaultStyle`, `DefaultScheme`, `AutoSelect`, `SelectDlgSizeX`, `SelectDlgSizeY`. + - `[Common Base]`, `[2nd Common Base]` β€” super-schemas. + - One section per schema (~60 sections). + - `FileNameExtensions` keys inside each schema section. + +### Import + +- File dialog filter: `*.ini`. +- Loads the target file into the cache and re-reads style strings out of it. Existing in-memory styles are replaced slot-by-slot for keys present in the imported file; keys missing from the imported file keep their current value. +- Extensions are imported too β€” **your previous per-schema extension customisations will be overwritten** by whatever is in the imported file. +- Import does *not* persist by itself. Until you save settings (or a later auto-save fires), re-starting Notepad3 reverts to the previously persisted state. Hence the usual recipe: *Import β†’ verify β†’ Save Settings Now* (**F7**). + +### Comparison table + +| Operation | Scope | What it writes | Uses `bForceAll` | +|---|---|---|---| +| *Save Settings Now* (F7), theme = Standard Config | `Notepad3.ini` | Only deltas from defaults | ✘ | +| *Save Settings Now* (F7), theme = named theme | `themes/.ini` | Only deltas from defaults | ✘ | +| **View β†’ Themes β†’ ** with SaveSettings on, leaving a *named* theme | `themes/.ini` | Every style | βœ” | +| **View β†’ Themes β†’ ** with SaveSettings on, leaving *Standard Config* | `Notepad3.ini` | Only deltas | ✘ | +| *Customize Schemes β†’ Export…* | user-chosen `.ini` | Every style (self-contained) | βœ” | + +--- + +## 9. Anatomy of a style INI file + +Whether it is your `Notepad3.ini` or a shipped theme like `Dark.ini`, the schema-related sections follow the same layout: + +```ini +[Custom Colors] +01=#252526 +02=#1346CE +03=#A2C5D4 +; … up to 16 slots + +[Styles] +Use2ndDefaultStyle=0 +DefaultScheme=2 +AutoSelect=1 +SelectDlgSizeX=0 +SelectDlgSizeY=0 + +[Common Base] +Default Style=font:$Code0; size:11; fore:#DEDEDE; back:#1E1E1E +Margins and Line Numbers=font:Consolas; size:-2; fore:#A4FFFF; back:#2B2B2B +Matching Braces (Indicator)=fore:#00FF00; alpha:40; alpha2:220; indic_roundbox +Selection=fore:#000000; back:#3F84DB; alpha:50; alpha2:150; eolfilled +Current Line=back:#323232; alpha:60; alpha2:255 +Caret=fore:#FFFFFF; block +; … + +[2nd Common Base] +2nd Default Style=font:$Code1; size:10; fore:#DCDCDC; back:#000000 +; … + +[Text Files] +Default Style=font:$Text0; size:11; fore:#E0E0E0 +FileNameExtensions=txt;text;log;asc;doc;diz;nfo + +[C/C++ Source Code] +Default=fore:#D7D7D7 +Comment=italic; fore:#608B4E +Keyword=bold; fore:#569CD6 +Type Keyword=fore:#4EC9B0 +String=fore:#CE9178 +Number=fore:#B5CEA8 +Operator=fore:#B4B4B4 +Preprocessor=fore:#C586C0 +FileNameExtensions=c;cc;cpp;cxx;h;hh;hpp;hxx;inl + +; … one section per schema … +``` + +Observations: + +- **Section names** (`[C/C++ Source Code]`, `[Python Script]`, …) are the schema display names as used internally; they are stable across versions but are localised copies of the names shown in the UI. +- **Style keys** inside a section (`Default`, `Comment`, `Keyword`, `Preprocessor`, …) are fixed per schema β€” you cannot add new ones. +- **Missing keys** fall through to the *Common Base* β†’ hard-coded default chain. +- **`FileNameExtensions`** appears at most once per schema. The list is semicolon-separated; entries beginning with `\` are regexes matched against the full filename. +- **Custom Colors entries** are just hex colours; they populate the 16-slot picker, nothing more. + +> **Encoding.** INI files are written in UTF-8. A UTF-8 BOM is tolerated on read. Do not hand-save as UTF-16 β€” Notepad3's INI parser is 8-bit. + +--- + +## 10. Toolbar themes (separate from colour schemes) + +The directories under the project's `themes/` folder (`Flat`, `b&w`, `professional`, `std_scaled`, `c6248`) are **toolbar bitmap themes**, not style themes. They contain `.bmp` files (`Toolbar.bmp`, `ToolbarDisabled.bmp`, `ToolbarHot.bmp`, plus 24- and 48-pixel variants). + +The toolbar bitmap in use is chosen via the `[Toolbar Images]` section in `Notepad3.ini` β€” see [Configuration.md](../config/Configuration.md) for details. This mechanism is entirely independent of the schema/theme system described above. + +--- + +## References in the code + +For developers wanting to look up behaviour described above: + +| Topic | Primary file(s) | +|---|---| +| Data structures (`EDITLEXER`, `EDITSTYLE`, `KEYWORDLIST`) | `src/StyleLexers/EditLexer.h` | +| Schema array, loading, saving, layering | `src/Styles.c` β€” `g_pLexArray[]`, `_ReadFromIniCache()`, `Style_ToIniSection()`, `Style_CanonicalSectionToIniCache()`, `Style_ExportToFile()` | +| Auto-detection pipeline | `src/Styles.c` β€” `Style_SetLexerFromFile()`, `Style_MatchLexer()`, `Style_RegExMatchLexer()`, `Style_SniffShebang()` | +| Customize Schemes dialog | `src/Styles.c` β€” `Style_CustomizeSchemesDlg()`, `Style_CustomizeSchemesDlgProc()` | +| Select Scheme dialog | `src/Styles.c` β€” `Style_SelectLexerDlg()`, `Style_SelectLexerDlgProc()` | +| Theme menu & switching | `src/Styles.c` β€” `_FillThemesMenuTable()`, `Style_InsertThemesMenu()`, `Style_DynamicThemesMenuCmd()`, `Style_ImportTheme()` | +| Menu command IDs | `language/common_res.h` β€” `IDM_VIEW_SCHEME`, `IDM_VIEW_USE2NDDEFAULT`, `IDM_VIEW_SCHEMECONFIG`, `IDM_THEMES_STD_CFG`, `IDM_THEMES_FACTORY_RESET` | +| Shipped themes | `Build/themes/Dark.ini`, `Obsidian.ini`, `Sombra.ini` |