Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ trim_trailing_whitespace = true
[*.ps1]
indent_size = 4

[*.py]
charset = utf-8
indent_size = 4
end_of_line = lf

[tests/**]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
trim_trailing_whitespace = true

[*.{vcxproj,filters,props}]
charset = utf-8

Expand Down
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
*.props text eol=crlf
*.md text eol=lf
*.ps1 text eol=lf
*.py text eol=lf
*.in text eol=lf
*.sh text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
Expand Down
20 changes: 12 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,16 @@ jobs:
New-Item -ItemType Directory -Path bin -Force | Out-Null
./build/build.ps1 -Architecture '${{ matrix.architecture }}' -Configuration '${{ matrix.configuration }}' *> bin/build.log
if ($LASTEXITCODE -ne 0) { Get-Content bin/build.log -Tail 80; exit 1 }
- name: Test editor themes
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: '3.13'
architecture: ${{ matrix.architecture }}
- name: Install locked test dependencies
run: python -m pip install --require-hashes -r tests/requirements.txt
- name: Test native editor
run: |
try {
./build/test-themes.ps1 -Executable './bin/VS2022/${{ matrix.configuration }}_${{ matrix.architecture }}/Notepad2.exe' *> bin/tests.log
} catch {
Get-Content bin/tests.log -Tail 80
throw
}
python -m pytest --executable './bin/VS2022/${{ matrix.configuration }}_${{ matrix.architecture }}/Notepad2.exe' --architecture '${{ matrix.architecture }}' --configuration '${{ matrix.configuration }}' --junitxml bin/test-results.xml *> bin/tests.log
if ($LASTEXITCODE -ne 0) { Get-Content bin/tests.log -Tail 100; exit 1 }
Get-Content bin/tests.log
- name: Package portable release
if: matrix.configuration == 'Release'
Expand All @@ -64,7 +66,9 @@ jobs:
if: always()
with:
name: logs-${{ matrix.configuration }}-${{ matrix.architecture }}
path: bin/*.log
path: |
bin/*.log
bin/test-results.xml
retention-days: 14
release:
name: Publish release
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@
*.p12
/.env
/.env.*
/.venv/
/.pytest_cache/
**/__pycache__/
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
- Theme scope is the editor only: text, syntax colors, margins, caret, selections,
folding and highlights. Application chrome (menus, dialogs, toolbar, status bar,
title bar and scrollbars) is out of scope, not a planned follow-up.
- Keep the single-document interface. Multi-tab editing and Notepad++ parity or
compatibility are not product goals. Standalone patches from Notepad3/Notepad4
may be evaluated surgically; their frameworks and feature stacks remain excluded.
- Use pytest and pywinauto's Win32 backend for native UI regression tests, with
only a small adapter for Scintilla messages. Keep test dependencies separate
from the application. PowerShell remains appropriate for build/release scripts.
- Finish each logical change with a granular commit after relevant validation.
Do not ask again whether to commit completed, authorized work. Stage explicit
paths/hunks and inspect the staged diff. Keep dependency updates, build changes,
Expand Down
36 changes: 26 additions & 10 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,30 @@ demonstrates a concrete benefit.

## Fork review backlog

GitHub issues hold the review notes, source commit links, acceptance criteria,
and eventual decisions. These are candidates for selective changes, not approved
imports; none was built or runtime-tested during the initial review.

| Issue | Candidate | Review focus |
Three independent agent reviews and a maintainer cross-check selected four
surgical corrections, now implemented locally in separate commits. The unsaved
buffer defect was reproduced against the pre-fix binary. Native regression tests
cover prompting/cancellation, production MRU functions, source charset and startup
show-state selection; actual standard-user UAC interaction remains a manual check.
The fork review introduced no dependency upgrade or subsystem. A separately
requested improvement adds selection-aware line copy/cut using existing Scintilla
actions and clipboard markers. UI regression tests use pytest and pywinauto.

| Priority | Accepted correction | Source |
| --- | --- | --- |
| [#1](https://github.com/itlezy/notepad2-mod/issues/1) | antiufo | Recovery/autosave; preserve upstream fixes and recovery buffer-fix chain. |
| [#2](https://github.com/itlezy/notepad2-mod/issues/2) | johnwait | Selective editor fixes; exclude wholesale regex/Scintilla migration. |
| [#3](https://github.com/itlezy/notepad2-mod/issues/3) | mercury233 | Small find/replace, reload, and context-menu improvements. |
| [#4](https://github.com/itlezy/notepad2-mod/issues/4) | tnotstar | Initial CMake build; Debug support was explicitly unfinished. |
| [#5](https://github.com/itlezy/notepad2-mod/issues/5) | waskosky | Aggressive reload/append merging; require data-preservation review. |
| First | [#9: save prompting for nonempty untitled documents](https://github.com/itlezy/notepad2-mod/issues/9) | johnwait; remove C-string content sniffing that misclassifies leading NUL and whitespace. |
| Next | [#10: skip empty MRU slots](https://github.com/itlezy/notepad2-mod/issues/10) | antiufo; one null guard before path comparison, without recovery features. |
| Next | [#11: explicit UTF-8 source charset](https://github.com/itlezy/notepad2-mod/issues/11) | mercury233; all four app configurations, unchanged execution charset/resources/vendor code. |
| Lower | [#12: elevated-window show-state flags](https://github.com/itlezy/notepad2-mod/issues/12) | johnwait; default to visible only when no explicit show-state flag was supplied. |

Closed review decisions remain in [#1](https://github.com/itlezy/notepad2-mod/issues/1),
[#2](https://github.com/itlezy/notepad2-mod/issues/2), and
[#3](https://github.com/itlezy/notepad2-mod/issues/3). Recovery/autosave, regex-engine
replacement, Scintilla migration, broad refactors, notification redesign, and
optional context-menu/focus/shortcut changes are excluded. Several apparent fixes
repair code introduced by those forks and do not apply here.

[CMake (#4)](https://github.com/itlezy/notepad2-mod/issues/4) and
[aggressive reload/append merging (#5)](https://github.com/itlezy/notepad2-mod/issues/5)
are rejected and closed as not planned. Keep the existing build and file-watching
models; revisit only if a concrete product need changes that decision.
29 changes: 23 additions & 6 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ minimal, focused patches with a clear benefit. This fork will not move to
Notepad3/Notepad4 or adopt their feature stacks; matching newer editors or
dependency versions is not a goal.

The interface remains single-document. Multi-tab editing and Notepad++ parity
are out of scope; isolated fixes from other editors may be evaluated surgically.

The upstream repository, [XhmikosR/notepad2-mod](https://github.com/XhmikosR/notepad2-mod),
is archived and was last updated in July 2018. Ongoing maintenance for this fork
happens here; upstream history, licensing, and contributor credits are preserved.
Expand Down Expand Up @@ -80,6 +83,12 @@ Compatibility with other Windows versions has not been verified.

## Changed keyboard shortcuts compared to Notepad2:

With no selection, **Ctrl+C / Ctrl+Insert** copy the current line and
**Ctrl+X / Shift+Delete** cut it. These line operations include a line-ending and
use Scintilla's existing line-clipboard marker: **Ctrl+V / Shift+Insert** insert
the line above the caret's current line. With a selection, normal copy/cut/paste
behavior is retained. Cutting is undoable and respects read-only documents.

* <kbd>Ctrl+Alt+F2</kbd> Expand selection to next match.
* <kbd>Ctrl+Alt+Shift+F2</kbd> Expand selection to previous match.
* <kbd>Ctrl+Shift+Enter</kbd> New line with toggled auto indent option.
Expand Down Expand Up @@ -151,16 +160,24 @@ The packaging command is `./build/package.ps1 -Architecture x64` (or `x86`). It
uses Release output and writes into ignored `bin/packages`; supply a fresh
`-OutputDirectory` when repackaging an already-created version locally.

To run the native theme integration checks against a built executable:
Tests use Python 3.13, pytest, and pywinauto's Win32 backend. Test dependencies
are locked with hashes and are not application dependencies. PowerShell is used
for building, not UI automation. To test a built executable:

```powershell
.\build\test-themes.ps1
.\build\test-themes.ps1 -Executable .\bin\VS2022\Release_x86\Notepad2.exe
py -3.13 -m venv .venv
.\.venv\Scripts\python.exe -m pip install --require-hashes -r tests/requirements.txt
.\.venv\Scripts\python.exe -m pytest
.\.venv\Scripts\python.exe -m pytest --executable .\bin\VS2022\Release_x86\Notepad2.exe --architecture x86
```

The checks launch isolated instances with temporary settings, exercise all 38
schemes and customization Preview/Cancel/Reset, and verify palette/font separation,
switching, restart persistence, invalid defaults, and disabled settings saving.
Use `-BaselineExecutable <path>` for a seven-run small-file startup/private-memory/
numeric edit/idle comparison. These checks do not replace visual contrast review
or establish large-file performance.
They also exercise save prompts with embedded NUL/whitespace, exact-byte Cancel
preservation, production MRU functions, source charset, and startup show-state
contracts. The latter do not replace an interactive standard-user UAC test.
Use `--configuration Debug` for the corresponding native helper runtime. A
pre-fix binary can be checked with `-m pytest tests/test_save_prompts.py
--legacy-save-behavior --executable <path>` to reproduce the nine loss paths.
These tests do not replace visual review or establish large-file performance.
6 changes: 5 additions & 1 deletion build/publish-release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,17 @@ $notes = Join-Path $PackageDirectory 'release-notes.txt'
@"
Native Original/Dark editor palettes on Scintilla 3.7.6, with a VS 2022 build.

Focused corrections protect nonempty untitled documents during save prompting, skip empty recent-file slots, make application source encoding deterministic, and honor elevated-launch show-state flags.

With no selection, Ctrl+C / Ctrl+Insert copy the current line and Ctrl+X / Shift+Delete cut it. Ctrl+V / Shift+Insert paste line-marked clipboard content above the current line. Normal selected-text behavior is retained.

Choose View > Editor Theme > Dark. Existing Original colors and font preferences are retained. Application chrome is unchanged.

Download the portable ZIP for your architecture and extract it into a writable folder. Preserve your existing Notepad2.ini when upgrading. These binaries are unsigned. SHA256SUMS.txt provides archive integrity checks.

Source commit: $commit
Executable version: $version
Validation: Release/Debug x86/x64 builds and native theme integration tests, including all 38 schemes and customization/persistence checks.
Validation: Release/Debug x86/x64 builds with pytest and pywinauto, including all 38 schemes, customization/persistence, save-prompt data preservation, line shortcuts, and native helper contracts. Actual standard-user UAC interaction remains a manual check.
"@ | Set-Content $notes -Encoding utf8
$existing = & gh release view $Tag --json isDraft 2>$null
if ($LASTEXITCODE -eq 0) {
Expand Down
65 changes: 65 additions & 0 deletions build/test-fork-helpers.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Exercises production MRU/path functions, plus compiler and startup contracts.
#include <windows.h>
#include <shlwapi.h>
#include <stdio.h>
#include "Scintilla.h"
#include "Helpers.h"
#include "Version.h"
#include "fork-contracts.h"

HINSTANCE g_hInstance;
UINT16 g_uWinVer;
HMODULE hModUxTheme;
WCHAR szIniFile[MAX_PATH];

#define CHECK(condition) do { if (!(condition)) { printf("Failed at line %d\n",__LINE__); return 1; } } while (0)

int main(void)
{
WCHAR path[MAX_PATH],second[MAX_PATH],relative[] = L"test-file.txt";
LPMRULIST list = MRU_Create(L"Test",MRU_NOCASE,3);
STARTUPINFO si = {0};
int i;
g_hInstance = GetModuleHandle(NULL);
GetModuleFileName(NULL,path,MAX_PATH);
PathRemoveFileSpec(path);
lstrcpy(second,path);
PathAppend(path,L"test-file.txt");
PathAppend(second,L"second.txt");
CHECK(MRU_AddFile(list,path,FALSE,FALSE)); // Initially every slot is NULL.
CHECK(lstrcmpi(list->pszItems[0],path) == 0);
CHECK(MRU_AddFile(list,second,FALSE,FALSE)); // Partial list.
CHECK(MRU_AddFile(list,path,FALSE,FALSE)); // Existing absolute duplicate.
CHECK(lstrcmpi(list->pszItems[0],path) == 0 && lstrcmpi(list->pszItems[1],second) == 0);
MRU_Empty(list);
list->pszItems[1] = StrDup(relative); // Null gap before a relative duplicate.
CHECK(MRU_AddFile(list,path,FALSE,FALSE));
CHECK(lstrcmpi(list->pszItems[0],path) == 0);
MRU_Empty(list);
SetEnvironmentVariable(L"NP2_MRU_TEST",second);
list->pszItems[1] = StrDup(L"%NP2_MRU_TEST%");
CHECK(MRU_AddFile(list,second,FALSE,FALSE));
CHECK(lstrcmpi(list->pszItems[0],second) == 0);
MRU_Empty(list);
for (i = 0; i < 5; ++i) {
WCHAR name[MAX_PATH];
wsprintf(name,L"C:\\test-%d.txt",i);
CHECK(MRU_AddFile(list,name,FALSE,FALSE));
}
CHECK(lstrcmp(list->pszItems[0],L"C:\\test-4.txt") == 0);
CHECK(lstrcmp(list->pszItems[2],L"C:\\test-2.txt") == 0);
MRU_Destroy(list);
CHECK(VERSION_LEGALCOPYRIGHT_LONG[0] == 0x00A9);
CHECK(wcschr(titlePunctuation,0x0384) && wcschr(titlePunctuation,0x2019));
CHECK(StartupShowState(si) == SW_SHOWNORMAL);
si.wShowWindow = SW_SHOWMAXIMIZED;
CHECK(StartupShowState(si) == SW_SHOWNORMAL); // Value irrelevant without flag.
si.dwFlags = STARTF_USESHOWWINDOW;
CHECK(StartupShowState(si) == SW_SHOWMAXIMIZED);
si.wShowWindow = SW_HIDE;
CHECK(StartupShowState(si) == SW_HIDE);
si.wShowWindow = SW_SHOWMINIMIZED;
CHECK(StartupShowState(si) == SW_SHOWMINIMIZED);
puts("MRU, UTF-8 source and startup show-state contracts passed.");
return 0;
}
Loading