This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Notepad3 is a Windows-only Win32 desktop text editor (C/C++) built on Scintilla (editing component) and Lexilla (syntax highlighting). It ships with companion tools MiniPath (file browser, Ctrl+M) and grepWinNP3 (file search/grep, Ctrl+Shift+F). Licensed under BSD 3-Clause.
# NuGet restore (required before first build)
nuget restore Notepad3.sln
# Single platform builds
Build\Build_x64.cmd [Release|Debug]
Build\Build_Win32.cmd [Release|Debug]
Build\Build_ARM64.cmd [Release|Debug]
Build\Build_x64_AVX2.cmd [Release|Debug]
# All platforms at once
Build\BuildAll.cmd [Release|Debug]
# MSBuild directly (used by CI)
msbuild Notepad3.sln /m /p:Configuration=Release /p:Platform=x64
# Clean all outputs
Build\Clean.cmdDefault configuration is Release. Build scripts delegate to PowerShell in Build\scripts\.
Run Version.ps1 before building to generate src\VersionEx.h from templates in Versions\. Format: Major.YY.Mdd.Build (build number persisted in Versions\build.txt).
cd test
TestFileVersion.cmd # Verifies built binary version info
TestAhkNotepad3.cmd # AutoHotkey-based GUI tests (requires AutoHotkey)GitHub Actions (.github/workflows/build.yml) builds all four platforms (Win32, x64, x64_AVX2, ARM64) in Release on windows-2022 runners, triggered on push/PR to master.
| File | Purpose |
|---|---|
| Notepad3.c/h | Entry point (wWinMain), window procedure (MainWndProc), global state structs (Globals, Settings, Settings2, Flags, Paths) |
| Edit.c/h | Text manipulation: find/replace (PCRE2 regex), encoding conversion, clipboard, indentation, sorting, bookmarks, folding, auto-complete |
| Styles.c/h | Scintilla styling, lexer selection, theme management, margin configuration |
| Dialogs.c/h | All dialog boxes, DPI-aware UI interactions, window placement |
| Config/Config.cpp/h | INI file management, settings loading/saving, MRU list |
| Encoding.c/h | Encoding detection and conversion (integrates uchardet) |
| SciCall.h | Type-safe inline wrappers for Scintilla direct function calls (avoids SendMessage overhead) |
| DynStrg.c/h | Custom dynamic wide-string type (HSTRINGW) with automatic buffer management |
| PathLib.c/h | Path manipulation via opaque HPATHL handle |
| TypeDefs.h | Core type definitions (DocPos, DocLn, cpi_enc_t), Windows version targeting, compiler macros |
| MuiLanguage.c/h | Multi-language UI support, language DLL loading |
MainWndProc (Notepad3.c)
+-- Scintilla Edit Control (hwndEdit / IDC_EDIT)
+-- Toolbar (via Rebar control)
+-- Status Bar (16 configurable fields)
| Directory | Library | Purpose |
|---|---|---|
scintilla\ |
Scintilla 5.5.8 | Editor component (NP3 patches in np3_patches\, docs in doc\) |
lexilla\ |
Lexilla 5.4.6 | Syntax highlighting (NP3 patches in np3_patches\, docs in doc\) |
scintilla\pcre2\ |
PCRE2 10.47 | Regex engine for find/replace (replaced archived Oniguruma) |
src\uchardet\ |
uchardet | Mozilla encoding detection |
src\tinyexpr\ / src\tinyexprcpp\ |
TinyExpr | Expression evaluator (statusbar) |
src\uthash\ |
uthash | Hash table / dynamic array macros |
src\crypto\ |
Rijndael/SHA-256 | AES-256 encryption |
Boost (via vcpkg.json) |
Boost Regex & IOStreams | Used by grepWinNP3 |
50+ languages, each in a styleLexXXX.c file. All follow the EDITLEXER struct pattern from EditLexer.h:
EDITLEXER lexXXX = {
SCLEX_XXX, // Scintilla lexer ID
"lexerName", // Lexilla lexer name (case-sensitive)
IDS_LEX_XXX_STR, // Resource string ID
L"Config Name", // INI section name
L"ext1; ext2", // Default file extensions
L"", // Extension buffer (runtime)
&KeyWords_XXX, // Keyword lists
{ /* EDITSTYLE array */ }
};To add a new lexer: create styleLexNEW.c, define the EDITLEXER struct, register in Styles.c lexer array, add localization string IDs to resource files.
Resource-based MUI system with 27+ locales. Each locale has a np3_LANG_COUNTRY\ directory with .rc files. Language packs are built as separate DLLs (separate projects in the solution).
- Add
#define IDS_MUI_XXX <id>tolanguage\common_res.h(use next available ID in the appropriate range: 13xxx for errors/warnings, 14xxx for info/prompts) - Add the English string to
language\np3_en_us\strings_en_us.rcin the matchingSTRINGTABLEblock - Add the same English text as placeholder to all other 25 locale
strings_*.rcfiles (translators update later) - Use
InfoBoxLng()/MessageBoxLng()withMB_YESNO,MB_ICONWARNING, etc. to display; check result withIsYesOkay() Settings.MuteMessageBeepcontrols whether to useInfoBoxLng(silent) orMessageBoxLng(with sound) — always provide both paths
- INI file alongside executable (
Notepad3.ini), no registry usage - No auto-creation: runs with defaults if no INI exists; user explicitly creates via "Save Settings Now"
- Admin redirect:
Notepad3.ini=<path>in[Notepad3]section redirects to per-user path (up to 2 levels) - Key paths:
Paths.IniFile(active writable INI),Paths.IniFileDefault(fallback for recovery) - INI init flow:
FindIniFile()->TestIniFile()->CreateIniFile()->LoadSettings() - MiniPath follows the same portable INI and admin-redirect pattern (
minipath\src\Config.cpp). Redirect targets are auto-created viaCreateIniFileEx(). - New parameters: When adding new
Settings2(or other INI) parameters, always document them as commented entries inBuild\Notepad3.ini
Use SHCreateDirectoryExW(NULL, path, NULL) to recursively create directory trees (requires <shlobj.h>, already included in core modules). Check result: SUCCEEDED(hr) || (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)). See CreateIniFile() in src\Config\Config.cpp for the reference pattern.
FileSave()(src\Notepad3.c) — Main save dispatcher. Handles Save, Save As, Save Copy. CallsFileIO()→EditSaveFile()(src\Edit.c)FileLoad()(src\Notepad3.c) — Main load dispatcher. CallsFileIO()→EditLoadFile()(src\Edit.c)EditSaveFile()supports atomic save (temp file +ReplaceFileW) controlled bySettings2.AtomicFileSave- Error handling:
Globals.dwLastErrorholds the Win32 error code after failed I/O.FileSave()checks specific codes (ERROR_ACCESS_DENIED,ERROR_PATH_NOT_FOUND) before falling back to generic error. - File watching:
InstallFileWatching()usesFindFirstChangeNotificationWon the parent directory. Must be stopped before save (InstallFileWatching(false)) and restarted after (InstallFileWatching(true)).
PCRE2 10.47 replaced the archived Oniguruma library. The Scintilla integration lives in scintilla\pcre2\scintilla\PCRE2RegExEngine.cxx, compiled with SCI_OWNREGEX to override Scintilla's built-in regex.
Key components:
PCRE2RegExEngine::FindText— Scintilla regex search (pattern matching viapcre2_match)PCRE2RegExEngine::SubstituteByPosition— Regex replacement with group referencesPCRE2RegExEngine::convertReplExpr— Normalizes replacement strings: converts\1-\9to$1-$9, processes escape sequences (\n,\t,\xHH,\uHHHH)PCRE2RegExEngine::translateRegExpr— Translates Scintilla regex extensions:\</\>word boundaries → lookarounds,\uHHHH→\x{HHHH}RegExFind(exported C function) — Standalone regex find used byEditURLDecodeinEdit.c; wrapsSimplePCRE2Engine
Replacement string backreference syntax (both flavors supported for backward compatibility):
$0-$99and\0-\9— numbered group references${name}/${+name}— named group references- Escape sequences:
\n,\t,\r,\\,\xHH,\uHHHH
URL hotspot regex is defined at src\Edit.c:108 (HYPLNK_REGEX_FULL macro). It matches https?://, ftp://, file:///, file://, mailto:, www., ftp. schemes. The trailing group excludes punctuation (.,:?!) so URLs don't absorb sentence-ending characters.
Windows 10/11 dark mode via IAT (Import Address Table) hooks. Includes stub DLLs for uxtheme and user32.
- LLVM-based
.clang-formatinsrc\— 4-space indentation, Stroustrup brace style, left-aligned pointers, no column limit, no include sorting .editorconfigenforces UTF-8/CRLF for source, 4-space indent for C/C++; Lexilla code uses tabs (preserved from upstream)- String safety via
strsafe.hthroughout; deprecated string functions are disabled
DocPos/DocPosU/DocLnfor Scintilla document positions and line numbers (not rawint)cpi_enc_tfor encoding identifiersHSTRINGWandHPATHL(opaque handle types) instead of rawWCHAR*buffersNOMINMAXis defined globally — usemin()/max()macros or typed equivalents
Always use SciCall.h wrappers (e.g. SciCall_GetTextLength()) instead of raw SendMessage(hwnd, SCI_XXX, ...). Add missing wrappers to SciCall.h if needed.
Wrapper macros follow the naming DeclareSciCall{V|R}{0|01|1|2}:
- V = void return, R = has return value
- 0 = no params, 1 = one param (wParam), 2 = two params, 01 = lParam only (wParam=0)
- The
msgargument is the suffix afterSCI_(e.g.UNDOforSCI_UNDO)
DeclareSciCallV0(Undo, UNDO); // SciCall_Undo()
DeclareSciCallR0(GetTextLength, GETTEXTLENGTH, DocPos); // DocPos SciCall_GetTextLength()
DeclareSciCallV1(SetTechnology, SETTECHNOLOGY, int, technology); // SciCall_SetTechnology(int)
DeclareSciCallR1(SupportsFeature, SUPPORTSFEATURE, bool, int, feature); // bool SciCall_SupportsFeature(int)Application state is centralized in global structs (Globals, Settings, Settings2, Flags, Paths) defined in Notepad3.c. Access these through their defined interfaces rather than adding new globals.
Use _BEGIN_UNDO_ACTION_ / _END_UNDO_ACTION_ macros (defined in Notepad3.h) to group Scintilla operations into single undo steps. These also handle notification limiting during bulk edits.
A Python 3.14 virtual environment is available at .venv\ for scripting tasks (batch file manipulation, locale file updates, code generation, etc.).
# Run a script (from project root, in bash/Cygwin)
.venv/Scripts/python.exe <script.py>
# Install packages
.venv/Scripts/pip.exe install <package>Use this venv instead of system Python (which may not be installed). Useful for bulk operations across the 26 locale strings_*.rc files.
| Pattern | Purpose |
|---|---|
language/common_res.h |
String resource ID definitions |
language/np3_*/strings_*.rc |
Locale string tables (26 files) |
Build/Notepad3.ini |
Reference INI with documented settings |
src/Notepad3.c:FileSave() |
Save flow entry point |
src/Edit.c:EditSaveFile() |
Actual file write logic |
src/Config/Config.cpp |
INI management, settings load/save |
src/Dialogs.c:SaveFileDlg() |
Save As dialog |