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
2 changes: 1 addition & 1 deletion lib/core/editor/querya_code_editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class _QueryaCodeEditorState extends State<QueryaCodeEditor> {
if (controller is! QueryaHighlightController) return;

final editor = queryaTheme.editor;
final tokenHash = Object.hashAll(queryaTheme.tokenColors);
final tokenHash = queryaTheme.tokenColorsHash;
if (_lastEditorTheme == editor && _lastTokenColorsHash == tokenHash) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/core/editor/syntax_highlight_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ abstract final class SyntaxHighlightService {
final cacheKey = Object.hash(
language,
queryaTheme.editor,
Object.hashAll(queryaTheme.tokenColors),
queryaTheme.tokenColorsHash,
);
final cached = _pairCache[cacheKey];
if (cached != null) return cached;
Expand Down
11 changes: 11 additions & 0 deletions lib/core/theme/querya_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ class QueryaTheme {
/// VS Code `tokenColors` for syntax highlighting (imported themes).
final List<TokenColorRule> tokenColors;

/// Hashes of token colour lists, keyed by the list itself: themes built by
/// [copyWith] and [lerp] (one per animation frame) share the list, so each
/// list is hashed once however many themes point at it.
static final Expando<int> _tokenColorsHashes =
Expando<int>('QueryaTheme.tokenColorsHash');

/// Hash of [tokenColors], computed once per list (#1353): the editor asks
/// for it on every build, and a full VS Code theme has hundreds of rules.
int get tokenColorsHash =>
_tokenColorsHashes[tokenColors] ??= Object.hashAll(tokenColors);

static const QueryaTheme darkDefault = QueryaTheme(
workbench: QueryaWorkbenchTheme.darkDefault,
editor: QueryaEditorTheme.darkDefault,
Expand Down
51 changes: 51 additions & 0 deletions test/core/theme/querya_theme_token_colors_hash_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:querya_desktop/core/theme/parser/vscode_theme_manifest.dart';
import 'package:querya_desktop/core/theme/querya_theme.dart';

void main() {
const rules = [
TokenColorRule(scopes: ['keyword'], foreground: '#ff0000'),
TokenColorRule(scopes: ['string', 'string.quoted'], foreground: '#00ff00'),
];

group('QueryaTheme.tokenColorsHash (#1353)', () {
test('equals the hash of the token colours', () {
final theme = QueryaTheme.darkDefault.copyWith(tokenColors: rules);

expect(theme.tokenColorsHash, Object.hashAll(rules));
});

test('is stable across reads', () {
final theme = QueryaTheme.darkDefault.copyWith(tokenColors: rules);

expect(theme.tokenColorsHash, theme.tokenColorsHash);
});

test('themes sharing a list share the hash', () {
final a = QueryaTheme.darkDefault.copyWith(tokenColors: rules);
final b = a.copyWith(brightness: a.brightness);
final mid = QueryaTheme.lerp(a, b, 0.25);

expect(b.tokenColorsHash, a.tokenColorsHash);
expect(mid.tokenColorsHash, a.tokenColorsHash);
});

test('different rules give a different hash', () {
final a = QueryaTheme.darkDefault.copyWith(tokenColors: rules);
final b = QueryaTheme.darkDefault.copyWith(
tokenColors: const [
TokenColorRule(scopes: ['comment'], foreground: '#888888'),
],
);

expect(a.tokenColorsHash, isNot(b.tokenColorsHash));
});

test('a theme without token colours has the empty-list hash', () {
expect(
QueryaTheme.darkDefault.tokenColorsHash,
Object.hashAll(const <TokenColorRule>[]),
);
});
});
}
Loading