Function list: fix Java parsing and add C# support - #115
Conversation
The function browser missed most Java methods — on a representative file
it found only 2 of 7. The Java regex required ')' immediately followed by
'{', so it failed on `throws` clauses, same-line annotations
(`@Override public ... {`), and Allman-style braces (signature on one line,
'{' on the next, which the Java path never handled). It also listed control
statements like `if`/`while`/`try` as methods.
C# was not a language in the port at all: no LANG_CSHARP, no .cs detection,
no table entry — hence no highlighting and no function list.
Changes:
- function_list_parser.mm: rework the Java path into a shared Java/C# branch
handling annotations, throws clauses, Allman pending-signatures, C#
expression-bodied members (=>), ": base(...)" constructors, "where"
constraints, and class|struct|interface|enum|record containers. Filter
control keywords (separate set so "lock"/"using" stay valid C++ names).
Regexes match modifiers/types token-wise with cheap string prefilters —
the naive form took minutes on large files; this is ~5s on 115k+ lines.
- language_defs.{h,mm}: add LANG_CSHARP=36 (appended at end of table since
sessions persist languageIndex as a raw int), C# keyword/type lists on the
cpp lexer, .cs/.csx detection, and C-style indentation.
- npp_constants.h: move IDM_LANG_BASE 44000 -> 51000. 44000+ is upstream's
plugin-visible view-command range, and the language range check runs first
in MainWndProc, so 44035 (IDM_VIEW_SYNSCROLLV, sent by ComparePlus) was
being misrouted to "set language to x86 Assembly". The move also makes room
for the 37th language.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR improves the macOS function list browser by fixing Java method detection and adding first-class C# support (syntax highlighting via the existing cpp lexer plus function/container extraction). It also moves the language-menu command-ID range to avoid collisions with upstream Notepad++ command IDs that plugins may send by value.
Changes:
- Move
IDM_LANG_BASEto a safe range to prevent plugin-sent upstream view commands (e.g., sync scroll) from being misrouted as “set language”. - Add
LANG_CSHARP, C# language table entry,.cs/.csxdetection, and mark C# as C-style indentation. - Rework the function list parser to share a Java/C# parsing path with annotation/throws handling, Allman-style pending signatures, and C#-specific syntax forms.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| macos/platform/npp_constants.h | Moves language command-ID base away from upstream plugin command ranges; adds explanatory comments. |
| macos/platform/language_defs.mm | Adds C# language definition, extension detection, and C-style indentation classification. |
| macos/platform/language_defs.h | Introduces LANG_CSHARP language index. |
| macos/platform/function_list_parser.mm | Adds Java/C# shared parsing branch and new regexes/filters for more accurate container + function extraction. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Base for language menu items. Must stay clear of upstream menuCmdID.h values | ||
| // (which run up to 50000): plugins send those by value, and the language range | ||
| // check in MainWndProc runs first, so a base of 44000 shadowed upstream view | ||
| // commands such as IDM_VIEW_SYNSCROLLV/H (44035/44036, sent by ComparePlus). |
There was a problem hiding this comment.
Confirmed — the vendored menuCmdID.h tops out at IDM_EDIT_FUNCCALLTIP_NEXT = 50011 (autocomplete block at 50000+). Comment corrected in 744d028; the 51000 base itself is unaffected since nothing upstream sits in 51000–51100 (verified by resolving every #define in the header).
| // bare "name(...) {" is rejected so calls/control statements don't match. | ||
| // Modifiers and type are matched token-wise (no bare \s alternation): | ||
| // whitespace-splitting ambiguity in std::regex costs minutes on large files. | ||
| static const std::regex javaCsClassRe(R"(\b(class|struct|interface|enum|record)\s+([A-Za-z_]\w*))"); |
There was a problem hiding this comment.
Good catch — confirmed with a failing test: record struct Coordinate(...) produced a container literally named "struct". Fixed in 744d028 by matching record class/record struct as a unit in javaCsClassRe (longest alternative first, name capture unchanged). Harness now covers record struct and readonly record class with methods grouped under the right containers.
…curacy - javaCsClassRe now matches "record class X" / "record struct X" (C# 10) as a unit; previously the second keyword was captured as the container name, so "record struct Point(...)" produced a container named "struct". - Correct the IDM_LANG_BASE comment: upstream menuCmdID.h IDs top out at IDM_EDIT_FUNCCALLTIP_NEXT = 50011 (autocomplete block at 50000+), not 50000. The 51000 base itself is unaffected — nothing upstream sits in 51000-51100. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…alEffectView dealloc crash (#116) * Render GFM tables in the markdown viewer plugin The hand-rolled markdown parser had no table support, so pipe-delimited rows fell through to paragraph accumulation and rendered as one wrapped block of text. Add GFM table detection (header + delimiter row), HTML emission with column alignment from `:---:` style delimiters, inline markdown inside cells, padding/truncation of mismatched rows, escaped `\|` handling, and matching light/dark CSS for <table>/<th>/<td>. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address PR #114 review: require header column count to match delimiter Per GFM spec the header row must match the delimiter row in cell count, otherwise the block is not a table. Previously a mismatch was padded / truncated like body rows, which could swallow plain text that happens to contain pipes when followed by a `---` line. Tighten the gate to `headerCells.size() == aligns.size()` and drop the now-redundant header normalization inside emitTable; body rows still pad/truncate as before. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix SC_AUTOMATICFOLD_CLICK value and guard shim command macros Follow-ups from the ComparePlus navigation verification pass: - SC_AUTOMATICFOLD_CLICK was 0x0004 (the value of SC_AUTOMATICFOLD_CHANGE); Scintilla defines it as 0x0002 - Guard GET_WM_COMMAND_* in windowsx.h against redefinition with winuser.h - Silence -Wdeprecated-this-capture (Scintilla) and pragma-message noise (ComparePlus) in the plugin builds Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Bump version to 1.0.8 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Function list: fix Java parsing and add C# support The function browser missed most Java methods — on a representative file it found only 2 of 7. The Java regex required ')' immediately followed by '{', so it failed on `throws` clauses, same-line annotations (`@Override public ... {`), and Allman-style braces (signature on one line, '{' on the next, which the Java path never handled). It also listed control statements like `if`/`while`/`try` as methods. C# was not a language in the port at all: no LANG_CSHARP, no .cs detection, no table entry — hence no highlighting and no function list. Changes: - function_list_parser.mm: rework the Java path into a shared Java/C# branch handling annotations, throws clauses, Allman pending-signatures, C# expression-bodied members (=>), ": base(...)" constructors, "where" constraints, and class|struct|interface|enum|record containers. Filter control keywords (separate set so "lock"/"using" stay valid C++ names). Regexes match modifiers/types token-wise with cheap string prefilters — the naive form took minutes on large files; this is ~5s on 115k+ lines. - language_defs.{h,mm}: add LANG_CSHARP=36 (appended at end of table since sessions persist languageIndex as a raw int), C# keyword/type lists on the cpp lexer, .cs/.csx detection, and C-style indentation. - npp_constants.h: move IDM_LANG_BASE 44000 -> 51000. 44000+ is upstream's plugin-visible view-command range, and the language range check runs first in MainWndProc, so 44035 (IDM_VIEW_SYNSCROLLV, sent by ComparePlus) was being misrouted to "set language to x86 Assembly". The move also makes room for the 37th language. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Address PR #115 review: record struct/class containers, ID comment accuracy - javaCsClassRe now matches "record class X" / "record struct X" (C# 10) as a unit; previously the second keyword was captured as the container name, so "record struct Point(...)" produced a container named "struct". - Correct the IDM_LANG_BASE comment: upstream menuCmdID.h IDs top out at IDM_EDIT_FUNCCALLTIP_NEXT = 50011 (autocomplete block at 50000+), not 50000. The 51000 base itself is unaffected — nothing upstream sits in 51000-51100. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Bump version to 1.0.9; add C# document type to Info.plist Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: remove stale notification observer object filters causing NSVisualEffectView dealloc crash - Changed three NSNotificationCenter registration object: parameters from dangling-capable pointers to nil in ScintillaView.mm (initWithFrame:) - NSWindowWillMoveNotification: self.window -> nil - NSSystemColorsDidChangeNotification: self.window -> nil - NSViewBoundsDidChangeNotification: scrollView.contentView -> nil All three handlers ignore the notification object, so no behavioral change. This eliminates the stale pointer in the notification center's internal data structures that caused a SIGBUS crash when AppKit's internal NSVisualEffectView was deallocated during autorelease pool drain. - Added dealloc to NppAppDelegate to remove observer from NSDistributedNotificationCenter (AppleInterfaceThemeChangedNotification). - Added missing ScintillaBridge_destroyView call for scintillaView2 in applicationWillTerminate: to balance __bridge_retained from createView. Co-Authored-By: Claude <noreply@anthropic.com> * fix: require at least 3 dashes per GFM table delimiter column The GFM spec requires 3+ dashes in delimiter rows (e.g. ---, :---:, ---:). Previously a single dash per column would pass validation, causing false-positive table parsing for inputs that should not be tables. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Brian Tabone <brian.tabone@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
The function browser missed most Java methods, and C# wasn't a supported language at all. This fixes Java parsing and adds full C# support (highlighting + function list).
Java was broken
The Java regex required
)immediately followed by{, so on a representative file it found only 2 of 7 methods. It missed:throwsclauses —void readFile() throws IOException {@Override public String toString() {{on the next (the Java path had no pending-signature handling; C++ did)It also listed control statements (
if/while/try) as methods.C# was absent
No
LANG_CSHARP, no.csdetection, no language-table entry — so C# files got no syntax highlighting and an empty function list.Changes
function_list_parser.mm— rework the Java path into a shared Java/C# branch: annotations,throws, Allman pending-signatures, C# expression-bodied members (=>),: base(...)constructors,whereconstraints, andclass|struct|interface|enum|recordcontainers. Control keywords filtered via a separate set solock/usingstay valid C++ names. Regexes match modifiers/types token-wise behind cheap string prefilters.language_defs.{h,mm}— addLANG_CSHARP = 36(appended at the end of the table, since sessions persistlanguageIndexas a raw int), C# keyword/type lists on thecpplexer (as upstream Notepad++ does),.cs/.csxdetection, C-style indentation.npp_constants.h— moveIDM_LANG_BASE44000 → 51000. This fixes a latent bug: 44000+ is upstream's plugin-visible view-command range, and the language range check runs first inMainWndProc, so 44035 (IDM_VIEW_SYNSCROLLV, sent by ComparePlus) was misrouted to "set language to x86 Assembly". The move also makes room for the 37th language.Verification
Sample.csrenders the full C# tree (classes, ctors incl.: base(...), async methods, expression-bodied members — properties correctly excluded);Java.javarenders all expected rows including every previously-missing pattern.🤖 Generated with Claude Code