Skip to content

Render GFM tables in markdown viewer plugin - #114

Merged
hybridmachine merged 4 commits into
macos-portfrom
feature-markdown-viewer-plugin
May 28, 2026
Merged

Render GFM tables in markdown viewer plugin#114
hybridmachine merged 4 commits into
macos-portfrom
feature-markdown-viewer-plugin

Conversation

@hybridmachine

Copy link
Copy Markdown
Owner

Summary

  • The hand-rolled markdown parser in MarkdownViewerPlusPlus.mm had no table support, so any GFM table rendered as one wrapped paragraph of text.
  • Adds GFM table detection (header row + delimiter), HTML emission with :---: / ---: / :--- column alignment, inline markdown inside cells, pad-short / truncate-long handling for mismatched rows, and \| unescaping.
  • Adds <table> / <th> / <td> / thead / zebra-striped tbody styles to baseCss(), plus matching overrides in the existing dark-mode media query.

Single file changed: plugins/markdownViewerPlusPlus/src/macOS/MarkdownViewerPlusPlus.mm (+160 / -5).

Known v1 limitation noted in source: a literal | inside a backtick code span inside a cell is treated as a cell separator, since cells split before inline markdown runs. Not present in observed real-world docs; can be addressed later if needed.

Test plan

  • cmake --build macos/build --config Debug --target MarkdownViewerPlusPlus succeeds.
  • Build PaperWasp Debug, open docs/superpowers/plans/2026-05-25-a-maze-ing-vertical-slice.md (or any GFM file with tables); confirm the "Phases" and "Spec coverage by task" tables render as real bordered HTML tables.
  • Inline `code` inside cells remains formatted as <code>.
  • Toggle macOS appearance light/dark; table borders + header background switch correctly.
  • Regression: open a markdown file with headings, lists, code fences, and blockquotes; confirm rendering is unchanged.
  • Edge: table at EOF (no trailing blank line) still renders.
  • Edge: short row (fewer pipes than header) gets padded with empty cells, not dropped.
  • Edge: standalone line with pipes but no following delimiter still renders as a paragraph, not a broken table.

🤖 Generated with Claude Code

Brian Tabone and others added 3 commits May 25, 2026 16:34
… sort out but we'll do those on different commits
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
Copilot AI review requested due to automatic review settings May 28, 2026 04:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds table rendering support to the macOS MarkdownViewer++ plugin and brings supporting plugin/docking scaffolding into the PaperWasp macOS build so the Markdown preview can be packaged and shown as a docked native panel.

Changes:

  • Adds GFM-style table parsing/emission and table styling to the macOS Markdown viewer.
  • Adds macOS plugin docking support and routes NPP docking messages.
  • Imports upstream MarkdownViewer++ reference files and packaging/license metadata.

Reviewed changes

Copilot reviewed 62 out of 92 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
plugins/markdownViewerPlusPlus/src/macOS/MarkdownViewerPlusPlus.mm Adds table parsing, HTML emission, and table CSS for the native macOS viewer.
macos/platform/plugin_docking.h Declares plugin docking panel helpers.
macos/platform/plugin_docking.mm Implements native macOS docking container behavior for plugin panels.
macos/platform/panel_layout.mm Integrates plugin docking visibility into right-side panel layout.
macos/platform/nppm_handler.mm Handles NPP docking messages for plugins.
macos/CMakeLists.txt Builds and packages MarkdownViewerPlusPlus and plugin docking support.
PowerEditor/src/MISC/sha2/sha-256.h Adds standard integer/size includes needed by the header API.
plugins/markdownViewerPlusPlus/IMPORT.txt Documents upstream import source and macOS port location.
plugins/markdownViewerPlusPlus/README.md Adds upstream MarkdownViewer++ README reference.
plugins/markdownViewerPlusPlus/LICENSE.md Adds upstream MIT license.
plugins/markdownViewerPlusPlus/.gitignore Adds upstream Visual Studio ignore rules.
plugins/markdownViewerPlusPlus/.gitattributes Adds upstream Git attributes.
plugins/markdownViewerPlusPlus/_config.yml Adds upstream GitHub Pages config.
plugins/markdownViewerPlusPlus/appveyor.yml Adds upstream CI config.
plugins/markdownViewerPlusPlus/license/* Adds third-party license notices.
plugins/markdownViewerPlusPlus/MarkdownViewerPlusPlus/** Adds upstream C#/WinForms reference implementation and resources.
Files not reviewed (7)
  • plugins/markdownViewerPlusPlus/MarkdownViewerPlusPlus/Forms/AboutDialog.Designer.cs: Language not supported
  • plugins/markdownViewerPlusPlus/MarkdownViewerPlusPlus/Forms/AbstractRenderer.Designer.cs: Language not supported
  • plugins/markdownViewerPlusPlus/MarkdownViewerPlusPlus/Forms/MarkdownViewerOptions.Designer.cs: Language not supported
  • plugins/markdownViewerPlusPlus/MarkdownViewerPlusPlus/Forms/OptionsPanelGeneral.Designer.cs: Language not supported
  • plugins/markdownViewerPlusPlus/MarkdownViewerPlusPlus/Forms/OptionsPanelHTML.Designer.cs: Language not supported
  • plugins/markdownViewerPlusPlus/MarkdownViewerPlusPlus/Forms/OptionsPanelPDF.Designer.cs: Language not supported
  • plugins/markdownViewerPlusPlus/MarkdownViewerPlusPlus/Properties/Resources.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +370 to +377
if (parseTableDelimiter(lines[i + 1], aligns))
{
auto headerCells = splitTableCells(stripped);
if (!headerCells.empty())
{
flushParagraph();
closeList();
i = emitTable(html, lines, i, aligns);
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>
@hybridmachine
hybridmachine merged commit bb070b5 into macos-port May 28, 2026
23 of 30 checks passed
hybridmachine added a commit that referenced this pull request Jun 13, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants