mirror of
https://github.com/rizonesoft/Notepad3.git
synced 2026-06-14 21:09:05 +08:00
feat: Schema documentation and enhancement plans
This commit is contained in:
parent
2e187641c7
commit
aa0ce545c5
@ -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:
|
||||
|
||||
424
plans/schema_enhancements.md
Normal file
424
plans/schema_enhancements.md
Normal file
@ -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 `<section> / <key>` 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
|
||||
<version>`.
|
||||
- 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 '<name>' 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=<n>` 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).
|
||||
@ -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 `<ResX>x<ResY> SciFontQuality=<n>` (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`
|
||||
|
||||
|
||||
411
readme/schema/CustomSchema.md
Normal file
411
readme/schema/CustomSchema.md
Normal file
@ -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/<Name>.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*) | `<IniFileDir>\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:<name>` | 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:<pt>` or `size:±<pt>` | 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:<n>` | LOGFONT charset id (0 = ANSI, 128 = Shift-JIS, 186 = Baltic, 204 = Russian, …). Clamped to ≥ 0. | `charset:204` |
|
||||
| `smoothing:<q>` | 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:<name>` | Font weight. Accepted words: `thin`, `extralight`, `light`, `book`, `regular`, `medium`, `semibold`, `bold`, `extrabold`, `heavy`, `extrablack`, plus a handful of synonyms. | `weight:semibold` |
|
||||
| `stretch:<name>` | 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_<shape>` | 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=<n>` | 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:
|
||||
|
||||
```
|
||||
<IniFileDir>\themes\*.ini
|
||||
```
|
||||
|
||||
where `<IniFileDir>` 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
|
||||
<your-theme>
|
||||
…
|
||||
```
|
||||
|
||||
- **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 `<IniFileDir>\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/<Name>.ini` | Only deltas from defaults | ✘ |
|
||||
| **View → Themes → <switch>** with SaveSettings on, leaving a *named* theme | `themes/<oldName>.ini` | Every style | ✔ |
|
||||
| **View → Themes → <switch>** 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` |
|
||||
Loading…
Reference in New Issue
Block a user