Skip to content

Commit d46f267

Browse files
committed
refactor(editor): single source for the large-document threshold and flatten query equality
1 parent 82ae246 commit d46f267

3 files changed

Lines changed: 29 additions & 21 deletions

File tree

LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/Highlighting/Highlighter.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ import SwiftTreeSitter
1212
import CodeEditLanguages
1313
import OSLog
1414

15+
/// Thresholds for degrading language services on large documents.
16+
public enum EditorHighlighting {
17+
/// Documents longer than this (UTF-16 character count) are not syntax highlighted by default. Above it the
18+
/// per-edit re-parse and re-highlight cost dominates, so the editor stops highlighting to keep typing, scrolling,
19+
/// and deleting responsive, the same way DataGrip and VS Code degrade large files.
20+
public static let maxHighlightableCharacters = 2_000_000
21+
}
22+
1523
/// This class manages fetching syntax highlights from providers, and applying those styles to the editor.
1624
/// Multiple highlight providers can be used to style the editor.
1725
///
@@ -81,10 +89,7 @@ class Highlighter: NSObject {
8189
/// Counts upwards to provide unique IDs for new highlight providers.
8290
private var providerIdCounter: Int
8391

84-
/// Documents longer than this are not highlighted. Above this length the per-edit re-parse and re-highlight cost
85-
/// dominates, so the editor stops syntax highlighting to keep typing, scrolling, and deleting responsive, the same
86-
/// way DataGrip and VS Code degrade large files.
87-
public var maxHighlightableLength: Int = 2_000_000
92+
public var maxHighlightableLength: Int = EditorHighlighting.maxHighlightableCharacters
8893

8994
// MARK: - Init
9095

TablePro/Models/Query/QueryTabState.swift

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -322,22 +322,25 @@ struct TabQueryContent: Equatable {
322322
}
323323

324324
static func == (lhs: TabQueryContent, rhs: TabQueryContent) -> Bool {
325-
// The query can be multiple megabytes and is bridged from the editor's NSTextStorage. Same-box comparison is
326-
// O(1); otherwise use NSString literal equality, which returns in O(1) when the lengths differ (every keystroke
327-
// changes the length) and avoids Swift's canonical Unicode normalization.
328-
guard lhs.queryStorage === rhs.queryStorage || (lhs.query as NSString).isEqual(to: rhs.query),
329-
lhs.queryParameters == rhs.queryParameters,
330-
lhs.isParameterPanelVisible == rhs.isParameterPanelVisible,
331-
lhs.sourceFileURL == rhs.sourceFileURL,
332-
lhs.loadMtime == rhs.loadMtime,
333-
lhs.externalModificationDetected == rhs.externalModificationDetected else {
334-
return false
335-
}
336-
switch (lhs.savedFileContent, rhs.savedFileContent) {
325+
// Cheap scalar fields short-circuit first. The query and saved-file text can be multiple megabytes and are
326+
// bridged from NSTextStorage, so they are compared last and with `sameText` to avoid Swift's canonical Unicode
327+
// comparison (O(n) on the bridged text); the same-box identity check makes an unchanged query O(1).
328+
lhs.isParameterPanelVisible == rhs.isParameterPanelVisible
329+
&& lhs.externalModificationDetected == rhs.externalModificationDetected
330+
&& lhs.sourceFileURL == rhs.sourceFileURL
331+
&& lhs.loadMtime == rhs.loadMtime
332+
&& lhs.queryParameters == rhs.queryParameters
333+
&& (lhs.queryStorage === rhs.queryStorage || sameText(lhs.query, rhs.query))
334+
&& sameText(lhs.savedFileContent, rhs.savedFileContent)
335+
}
336+
337+
/// Literal text equality that skips Swift's canonical Unicode comparison, returning in O(1) when the lengths differ.
338+
private static func sameText(_ lhs: String?, _ rhs: String?) -> Bool {
339+
switch (lhs, rhs) {
337340
case (nil, nil):
338341
return true
339-
case let (lhsSaved?, rhsSaved?):
340-
return (lhsSaved as NSString).isEqual(to: rhsSaved)
342+
case let (lhs?, rhs?):
343+
return (lhs as NSString).isEqual(to: rhs)
341344
default:
342345
return false
343346
}

TablePro/Views/Editor/SQLEditorCoordinator.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ final class SQLEditorCoordinator: TextViewCoordinator, TextViewDelegate {
2121

2222
private static let logger = Logger(subsystem: "com.TablePro", category: "SQLEditorCoordinator")
2323

24-
/// Above this document length inline AI features are suspended, matching the syntax-highlighting cutoff, so a large
25-
/// document does not copy its whole contents to the assistant on every keystroke.
26-
private static let languageServiceLengthLimit = 2_000_000
24+
/// Above this document length inline AI features are suspended, at the same cutoff where syntax highlighting stops,
25+
/// so a large document does not copy its whole contents to the assistant on every keystroke.
26+
private static let languageServiceLengthLimit = EditorHighlighting.maxHighlightableCharacters
2727

2828
@ObservationIgnored weak var controller: TextViewController?
2929
/// Shared schema provider for inline AI suggestions (avoids duplicate schema fetches)

0 commit comments

Comments
 (0)