Skip to content

feat: current statement highlighting and large document safety caps#442

Merged
datlechin merged 10 commits intomainfrom
feat/editor-statement-highlighting
Mar 24, 2026
Merged

feat: current statement highlighting and large document safety caps#442
datlechin merged 10 commits intomainfrom
feat/editor-statement-highlighting

Conversation

@datlechin
Copy link
Collaborator

@datlechin datlechin commented Mar 24, 2026

Summary

  • Highlight the background of the SQL statement under the cursor in multi-statement editors
  • Add safety caps to prevent highlighting from degrading on very large SQL dumps
  • 29 new tests covering statement detection, theme colors, and scanner edge cases

Current statement highlighting

  • Uses CodeEditTextView's EmphasisManager with .outline(color:, fill: true) style — same system as find panel and bracket matching
  • Debounced at 150ms with generation counter to prevent stale updates during rapid typing/cursor movement
  • Skips highlighting when: single statement (no ;), multi-cursor, empty doc, or document >5MB
  • New currentStatementHighlight theme color (light: #F0F4FA, dark: #1A2332)

Large document safety caps

  • Highlighter.maxHighlightableLength (5MB) — documents exceeding this are shown as plain text
  • HighlightProviderState caps to 2 chunks per cycle (8192 chars) for documents >50KB

Tests

  • CurrentStatementHighlighterTests (9 tests): multi-statement, strings, comments, edge cases
  • ThemeDefinitionTests (7 tests): defaults, Codable round-trip, backward compatibility
  • SQLStatementScannerLocatedTests (13 tests): offsets, comments, backticks, large input
  • Fix pre-existing TabDiskActorTests compile error (save() now throws)

Test plan

  • Open query tab, type SELECT 1; SELECT 2; SELECT 3; — background highlight follows cursor
  • Single statement (no ;) — no highlight
  • Open large SQL dump (>50KB) — highlighting stays responsive
  • Open huge file (>5MB) — opens as plain text, no hang
  • Switch light/dark mode — highlight color updates
  • Run tests: all 29 new tests pass

Summary by CodeRabbit

  • New Features

    • Added current statement highlight color option to editor themes.
  • Improvements

    • Improved syntax highlighting performance and stability for large documents by automatically limiting processing for files over 50KB and skipping files exceeding 5MB.

- Highlight the background of the SQL statement under cursor using
  EmphasisManager (outline with fill). Debounced at 150ms with generation
  counter to prevent stale updates.
- Skip highlighting for single statements (no semicolons), multi-cursor,
  or documents >5MB.
- Add maxHighlightableLength (5MB) guard to Highlighter — documents
  exceeding this are shown as plain text.
- Cap highlight chunks to 2 per cycle (8192 chars) for documents >50KB
  to keep the editor responsive on large SQL dumps.
- Add currentStatementHighlight theme color (light: #F0F4FA, dark: #1A2332).
… scanner

- CurrentStatementHighlighterTests: 9 tests for statement detection logic
  (multi-statement, single statement, strings, comments, edge cases)
- ThemeDefinitionTests: 7 tests for currentStatementHighlight color
  (defaults, round-trip, backward compatibility)
- SQLStatementScannerLocatedTests: 13 tests for locatedStatementAtCursor
  (offsets, comments, backticks, large input, edge cases)
- Fix pre-existing TabDiskActorTests compile error (save() now throws)
@coderabbitai
Copy link

coderabbitai bot commented Mar 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes introduce document size safety caps for syntax highlighting (skip >5MB documents, throttle >50KB), extend the theme system with a new currentStatementHighlight color field, update async test methods to propagate errors, and add comprehensive test suites for SQL statement scanning and theme validation.

Changes

Cohort / File(s) Summary
Highlighting Safety Caps
CHANGELOG.md, LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/Highlighting/HighlightProviderState.swift, LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/Highlighting/Highlighter.swift
Added document size thresholds: maxHighlightableLength (5MB) in Highlighter to skip highlighting entirely, and largeDocThreshold (50KB) in HighlightProviderState to cap computed highlight ranges to 2. Guards added at two control-flow points to prevent highlighting recalculation on oversized documents.
Theme Color Extension
TablePro/Theme/ResolvedThemeColors.swift, TablePro/Theme/ThemeDefinition.swift
Added currentStatementHighlight color field to EditorThemeColors with default value #F0F4FA, and corresponding AppKit/SwiftUI resolved properties in ResolvedEditorColors with fallback support in JSON decoding.
Test Updates
TableProTests/Core/Storage/TabDiskActorTests.swift
Changed 17 async test methods from async to async throws and updated save method calls from await to try await to propagate potential errors; load/loadLastQuery remain plain await.
New Test Suites
TableProTests/Core/Utilities/SQLStatementScannerLocatedTests.swift, TableProTests/Theme/ThemeDefinitionTests.swift
Added SQLStatementScannerLocatedTests with 182 lines covering locatedStatementAtCursor offset/range mapping across multi-statements, comments, quoted identifiers, and edge cases. Added ThemeDefinitionTests with 137 lines validating currentStatementHighlight defaults, JSON round-tripping, backward compatibility with omitted field, and custom value preservation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Safety caps and colors bright,
Large documents held just right,
Five megabytes? We'll politely decline,
With tests that verify our design.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and concisely summarizes the two main changes: adding current statement highlighting and implementing safety caps for large documents.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/editor-statement-highlighting

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- Clear emphasis immediately on text change (before debounced update)
  to avoid drawing a stale range that extends beyond the document
- Validate NSMaxRange(stmtRange) <= docLength before adding emphasis
EmphasisManager creates a CATextLayer on top with black foreground color,
which overwrites syntax highlighting and hides the cursor. Switch to
NSTextStorage.addAttribute(.backgroundColor) which draws behind text,
preserving syntax colors and cursor visibility.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
TableProTests/Core/Storage/TabDiskActorTests.swift (1)

61-64: Minor: Test methods marked throws but contain no throwing calls.

These test methods are declared async throws but only call non-throwing APIs (load(), clear(), loadLastQuery()). This is harmless in Swift Testing but slightly imprecise. Consider keeping them as just async for clarity, or leave as-is for consistency across the test suite.

Also applies to: 103-105, 174-177

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TableProTests/Core/Storage/TabDiskActorTests.swift` around lines 61 - 64,
Several test methods (e.g., loadReturnsNilForUnknown) are declared "async
throws" but only call non-throwing APIs like actor.load(), actor.clear(), and
actor.loadLastQuery(); change their signatures to just "async" (remove "throws")
to reflect they don't throw and update any other test methods in the same test
class that only call those non-throwing methods the same way.
TableProTests/Core/Utilities/SQLStatementScannerLocatedTests.swift (1)

142-157: Consider adding a test to verify the 10k character highlight range cap.

The largeInput test verifies that scanning doesn't crash on large inputs, which is good. However, the coding guidelines indicate that regex/highlight ranges should be capped at 10k characters for SQL dumps. Consider adding a test that verifies the statement scanner or highlighter properly limits the returned range for very long single statements.

Based on learnings: "Cap regex/highlight ranges at 10k characters for SQL dumps which can have single lines with millions of characters."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TableProTests/Core/Utilities/SQLStatementScannerLocatedTests.swift` around
lines 142 - 157, Add a test that asserts the scanner enforces the 10k character
cap: in or alongside the existing largeInput test, create a very long
single-statement SQL string (>10_000 chars), call
SQLStatementScanner.locatedStatementAtCursor(in:cursorPosition:), then verify
the returned located object enforces the cap by asserting located.sql.count (or
the highlighted range length represented by located) is <= 10_000 and that
located.offset stays within the expected window (>= 0 and < nsSQL.length). Use
the same symbols from the diff (largeInput,
SQLStatementScanner.locatedStatementAtCursor, located.sql, located.offset) so
the test checks the 10k highlight/range limit for huge single-line statements.
TablePro/Views/Editor/CurrentStatementHighlighter.swift (1)

88-113: Consider capping the highlight range to protect against extremely long single statements.

The 5MB document cap (line 16) prevents highlighting huge documents, but a single SQL statement could still be very long (e.g., a multi-megabyte INSERT with many values). Per coding guidelines, highlight ranges should be capped at 10k characters for SQL dumps which can have single lines with millions of characters.

Consider adding a check to skip highlighting or truncate the range when stmtNS.length exceeds a threshold:

🛡️ Suggested guard
         let stmtNS = located.sql as NSString
+        // Skip highlighting extremely long statements to avoid performance issues
+        guard stmtNS.length <= 10_000 else {
+            clearHighlight()
+            return
+        }
         let stmtRange = NSRange(location: located.offset, length: stmtNS.length)

Based on learnings: "Cap regex/highlight ranges at 10k characters for SQL dumps which can have single lines with millions of characters."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/Views/Editor/CurrentStatementHighlighter.swift` around lines 88 -
113, The highlight can blow up for extremely long single statements; inside the
block after computing stmtNS and stmtRange (from
SQLStatementScanner.locatedStatementAtCursor) add a guard to cap the range
length (e.g., maxLen = 10_000): if stmtNS.length > maxLen either skip
highlighting (call clearHighlight/return) or set stmtRange.length = maxLen and
adjust emphasis range accordingly; ensure you compare/update
lastHighlightedRange using the capped/truncated range and then create Emphasis
with that capped range before calling
textView.emphasisManager?.removeEmphases(for: Self.groupId) and addEmphases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@TablePro/Views/Editor/CurrentStatementHighlighter.swift`:
- Around line 88-113: The highlight can blow up for extremely long single
statements; inside the block after computing stmtNS and stmtRange (from
SQLStatementScanner.locatedStatementAtCursor) add a guard to cap the range
length (e.g., maxLen = 10_000): if stmtNS.length > maxLen either skip
highlighting (call clearHighlight/return) or set stmtRange.length = maxLen and
adjust emphasis range accordingly; ensure you compare/update
lastHighlightedRange using the capped/truncated range and then create Emphasis
with that capped range before calling
textView.emphasisManager?.removeEmphases(for: Self.groupId) and addEmphases.

In `@TableProTests/Core/Storage/TabDiskActorTests.swift`:
- Around line 61-64: Several test methods (e.g., loadReturnsNilForUnknown) are
declared "async throws" but only call non-throwing APIs like actor.load(),
actor.clear(), and actor.loadLastQuery(); change their signatures to just
"async" (remove "throws") to reflect they don't throw and update any other test
methods in the same test class that only call those non-throwing methods the
same way.

In `@TableProTests/Core/Utilities/SQLStatementScannerLocatedTests.swift`:
- Around line 142-157: Add a test that asserts the scanner enforces the 10k
character cap: in or alongside the existing largeInput test, create a very long
single-statement SQL string (>10_000 chars), call
SQLStatementScanner.locatedStatementAtCursor(in:cursorPosition:), then verify
the returned located object enforces the cap by asserting located.sql.count (or
the highlighted range length represented by located) is <= 10_000 and that
located.offset stays within the expected window (>= 0 and < nsSQL.length). Use
the same symbols from the diff (largeInput,
SQLStatementScanner.locatedStatementAtCursor, located.sql, located.offset) so
the test checks the 10k highlight/range limit for huge single-line statements.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 926c4196-2811-43ea-8721-abd4d36b2594

📥 Commits

Reviewing files that changed from the base of the PR and between cb9a272 and 7c8cb37.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/Highlighting/HighlightProviding/HighlightProviderState.swift
  • LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/Highlighting/Highlighter.swift
  • TablePro/Theme/ResolvedThemeColors.swift
  • TablePro/Theme/ThemeDefinition.swift
  • TablePro/Views/Editor/CurrentStatementHighlighter.swift
  • TablePro/Views/Editor/SQLEditorCoordinator.swift
  • TableProTests/Core/Storage/TabDiskActorTests.swift
  • TableProTests/Core/Utilities/SQLStatementScannerLocatedTests.swift
  • TableProTests/Theme/ThemeDefinitionTests.swift
  • TableProTests/Views/Editor/CurrentStatementHighlighterTests.swift

…storage attribute

NSTextStorage .backgroundColor conflicts with the syntax highlighting
pipeline (Highlighter.setAttributes overwrites it) and stacks visually
with the editor's selectedLineBackgroundColor. Using a CALayer with
zPosition: -1 draws behind text, preserving syntax colors, cursor, and
current line highlight independently.
The editor's selectedLineBackgroundColor stacks with our statement
background, creating a double-highlight on the cursor line. Save and
clear the line highlight color when statement highlighting is active,
restore it when inactive (single statement or cleared).
…caps)

The statement highlighting fought CodeEditSourceEditor's rendering pipeline
at every turn — EmphasisManager overwrites text colors, NSTextStorage
attributes conflict with the highlighter, CALayer stacks with current-line
highlight. The feature needs native CESS support (TextSelectionManager
background ranges) to work correctly.

Kept: large document safety caps (5MB skip, 50KB throttle), theme color
definition, scanner located tests, theme definition tests.
@datlechin datlechin merged commit f63e9cc into main Mar 24, 2026
3 checks passed
@datlechin datlechin deleted the feat/editor-statement-highlighting branch March 24, 2026 13:47
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.

1 participant