diff --git a/src/Models/TextMateHelper.cs b/src/Models/TextMateHelper.cs index 38c0aaa72..d2d14eec6 100644 --- a/src/Models/TextMateHelper.cs +++ b/src/Models/TextMateHelper.cs @@ -84,8 +84,6 @@ private record ExtraGrammar(string Scope, List Extensions, string File) public class RegistryOptionsWrapper(ThemeName defaultTheme) : IRegistryOptions { - public string LastScope { get; set; } = string.Empty; - public IRawTheme GetTheme(string scopeName) => _backend.GetTheme(scopeName); public IRawTheme GetDefaultTheme() => _backend.GetDefaultTheme(); public IRawTheme LoadTheme(ThemeName name) => _backend.LoadTheme(name); @@ -98,11 +96,20 @@ public class RegistryOptionsWrapper(ThemeName defaultTheme) : IRegistryOptions public static class TextMateHelper { + private static RegistryOptionsWrapper s_darkRegistry; + private static RegistryOptionsWrapper s_lightRegistry; + + private static RegistryOptionsWrapper GetRegistry(bool isDark) + { + if (isDark) + return s_darkRegistry ??= new RegistryOptionsWrapper(ThemeName.DarkPlus); + return s_lightRegistry ??= new RegistryOptionsWrapper(ThemeName.LightPlus); + } + public static TextMate.Installation CreateForEditor(TextEditor editor) { - return editor.InstallTextMate(Application.Current?.ActualThemeVariant == ThemeVariant.Dark ? - new RegistryOptionsWrapper(ThemeName.DarkPlus) : - new RegistryOptionsWrapper(ThemeName.LightPlus)); + var isDark = Application.Current?.ActualThemeVariant == ThemeVariant.Dark; + return editor.InstallTextMate(GetRegistry(isDark == true)); } public static void SetThemeByApp(TextMate.Installation installation) @@ -114,18 +121,29 @@ public static void SetThemeByApp(TextMate.Installation installation) } } - public static void SetGrammarByFileName(TextMate.Installation installation, string filePath) + public static void SetGrammarByFileName(TextMate.Installation installation, string filePath, ref string lastScope) { if (installation is { RegistryOptions: RegistryOptionsWrapper reg } && !string.IsNullOrEmpty(filePath)) { + // Registry options are shared across editors, so the last grammar scope must stay editor-local. var scope = reg.GetScope(filePath); - if (reg.LastScope != scope) + if (lastScope != scope) { - reg.LastScope = scope; - installation.SetGrammar(reg.GetScope(filePath)); + lastScope = scope; + installation.SetGrammar(scope); GC.Collect(); } } } + + public static void DisposeInstallation(ref TextMate.Installation installation, ref string lastScope, bool collectGarbage = false) + { + installation?.Dispose(); + installation = null; + lastScope = string.Empty; + + if (collectGarbage) + GC.Collect(); + } } } diff --git a/src/Views/AIAssistant.axaml.cs b/src/Views/AIAssistant.axaml.cs index c49e407f9..c622f0ab4 100644 --- a/src/Views/AIAssistant.axaml.cs +++ b/src/Views/AIAssistant.axaml.cs @@ -63,7 +63,7 @@ protected override void OnLoaded(RoutedEventArgs e) if (_textMate == null) { _textMate = Models.TextMateHelper.CreateForEditor(this); - Models.TextMateHelper.SetGrammarByFileName(_textMate, "README.md"); + Models.TextMateHelper.SetGrammarByFileName(_textMate, "README.md", ref _lastGrammarScope); TextArea.TextView.LineTransformers.Add(new LineStyleTransformer()); } } @@ -75,10 +75,7 @@ protected override void OnUnloaded(RoutedEventArgs e) TextArea.TextView.ContextRequested -= OnTextViewContextRequested; if (_textMate != null) - { - _textMate.Dispose(); - _textMate = null; - } + Models.TextMateHelper.DisposeInstallation(ref _textMate, ref _lastGrammarScope); GC.Collect(); } @@ -125,6 +122,7 @@ private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs } private TextMate.Installation _textMate = null; + private string _lastGrammarScope = string.Empty; } public partial class AIAssistant : ChromelessWindow diff --git a/src/Views/Blame.axaml.cs b/src/Views/Blame.axaml.cs index 8bc36160c..4137abf6c 100644 --- a/src/Views/Blame.axaml.cs +++ b/src/Views/Blame.axaml.cs @@ -375,10 +375,7 @@ protected override void OnUnloaded(RoutedEventArgs e) TextArea.TextView.VisualLinesChanged -= OnTextViewVisualLinesChanged; if (_textMate != null) - { - _textMate.Dispose(); - _textMate = null; - } + Models.TextMateHelper.DisposeInstallation(ref _textMate, ref _lastGrammarScope); } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) @@ -388,7 +385,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (change.Property == FileProperty) { if (File is { Length: > 0 }) - Models.TextMateHelper.SetGrammarByFileName(_textMate, File); + Models.TextMateHelper.SetGrammarByFileName(_textMate, File, ref _lastGrammarScope); } if (change.Property == BlameDataProperty) { @@ -454,6 +451,7 @@ private void OnTextViewVisualLinesChanged(object sender, EventArgs e) } private TextMate.Installation _textMate = null; + private string _lastGrammarScope = string.Empty; private string _highlight = string.Empty; } diff --git a/src/Views/CommandLogContentPresenter.cs b/src/Views/CommandLogContentPresenter.cs index 99dc6d13d..64a06eaa3 100644 --- a/src/Views/CommandLogContentPresenter.cs +++ b/src/Views/CommandLogContentPresenter.cs @@ -107,7 +107,7 @@ protected override void OnLoaded(RoutedEventArgs e) if (_textMate == null) { _textMate = Models.TextMateHelper.CreateForEditor(this); - Models.TextMateHelper.SetGrammarByFileName(_textMate, "Log.log"); + Models.TextMateHelper.SetGrammarByFileName(_textMate, "Log.log", ref _lastGrammarScope); TextArea.TextView.LineTransformers.Add(new LineStyleTransformer()); } } @@ -117,10 +117,7 @@ protected override void OnUnloaded(RoutedEventArgs e) base.OnUnloaded(e); if (_textMate != null) - { - _textMate.Dispose(); - _textMate = null; - } + Models.TextMateHelper.DisposeInstallation(ref _textMate, ref _lastGrammarScope); GC.Collect(); } @@ -152,5 +149,6 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang } private TextMate.Installation _textMate = null; + private string _lastGrammarScope = string.Empty; } } diff --git a/src/Views/MergeConflictEditor.axaml.cs b/src/Views/MergeConflictEditor.axaml.cs index 8c7b2621d..1dd565b80 100644 --- a/src/Views/MergeConflictEditor.axaml.cs +++ b/src/Views/MergeConflictEditor.axaml.cs @@ -310,7 +310,7 @@ protected override void OnLoaded(RoutedEventArgs e) _textMate = Models.TextMateHelper.CreateForEditor(this); if (!string.IsNullOrEmpty(FileName)) - Models.TextMateHelper.SetGrammarByFileName(_textMate, FileName); + Models.TextMateHelper.SetGrammarByFileName(_textMate, FileName, ref _lastGrammarScope); TextArea.TextView.ContextRequested += OnTextViewContextRequested; TextArea.TextView.PointerEntered += OnTextViewPointerChanged; @@ -331,10 +331,7 @@ protected override void OnUnloaded(RoutedEventArgs e) TextArea.TextView.VisualLinesChanged -= OnTextViewVisualLinesChanged; if (_textMate != null) - { - _textMate.Dispose(); - _textMate = null; - } + Models.TextMateHelper.DisposeInstallation(ref _textMate, ref _lastGrammarScope); base.OnUnloaded(e); } @@ -346,7 +343,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (change.Property == LinesProperty) UpdateContent(); else if (change.Property == FileNameProperty) - Models.TextMateHelper.SetGrammarByFileName(_textMate, FileName); + Models.TextMateHelper.SetGrammarByFileName(_textMate, FileName, ref _lastGrammarScope); else if (change.Property.Name == nameof(ActualThemeVariant) && change.NewValue != null) Models.TextMateHelper.SetThemeByApp(_textMate); else if (change.Property == SelectedChunkProperty) @@ -539,6 +536,7 @@ private void UpdateSelectedChunkPosition(ViewModels.MergeConflictEditor vm, doub } private TextMate.Installation _textMate; + private string _lastGrammarScope = string.Empty; private ScrollViewer _scrollViewer; } diff --git a/src/Views/RevisionFileContentViewer.axaml.cs b/src/Views/RevisionFileContentViewer.axaml.cs index eddb7b95c..5511eb927 100644 --- a/src/Views/RevisionFileContentViewer.axaml.cs +++ b/src/Views/RevisionFileContentViewer.axaml.cs @@ -67,10 +67,7 @@ protected override void OnUnloaded(RoutedEventArgs e) TextArea.TextView.ContextRequested -= OnTextViewContextRequested; if (_textMate != null) - { - _textMate.Dispose(); - _textMate = null; - } + Models.TextMateHelper.DisposeInstallation(ref _textMate, ref _lastGrammarScope); GC.Collect(); } @@ -82,7 +79,7 @@ protected override void OnDataContextChanged(EventArgs e) if (DataContext is Models.RevisionTextFile source) { Text = source.Content; - Models.TextMateHelper.SetGrammarByFileName(_textMate, source.FileName); + Models.TextMateHelper.SetGrammarByFileName(_textMate, source.FileName, ref _lastGrammarScope); ScrollToHome(); } else @@ -139,19 +136,18 @@ private void UpdateTextMate() _textMate ??= Models.TextMateHelper.CreateForEditor(this); if (DataContext is Models.RevisionTextFile file) - Models.TextMateHelper.SetGrammarByFileName(_textMate, file.FileName); + Models.TextMateHelper.SetGrammarByFileName(_textMate, file.FileName, ref _lastGrammarScope); } else if (_textMate != null) { - _textMate.Dispose(); - _textMate = null; - GC.Collect(); + Models.TextMateHelper.DisposeInstallation(ref _textMate, ref _lastGrammarScope, true); TextArea.TextView.Redraw(); } } private TextMate.Installation _textMate = null; + private string _lastGrammarScope = string.Empty; } public partial class RevisionFileContentViewer : UserControl diff --git a/src/Views/SelfUpdate.axaml.cs b/src/Views/SelfUpdate.axaml.cs index 97f1bc772..0783c6fae 100644 --- a/src/Views/SelfUpdate.axaml.cs +++ b/src/Views/SelfUpdate.axaml.cs @@ -34,7 +34,7 @@ protected override void OnLoaded(RoutedEventArgs e) if (_textMate == null) { _textMate = Models.TextMateHelper.CreateForEditor(this); - Models.TextMateHelper.SetGrammarByFileName(_textMate, "README.md"); + Models.TextMateHelper.SetGrammarByFileName(_textMate, "README.md", ref _lastGrammarScope); } } @@ -43,10 +43,7 @@ protected override void OnUnloaded(RoutedEventArgs e) base.OnUnloaded(e); if (_textMate != null) - { - _textMate.Dispose(); - _textMate = null; - } + Models.TextMateHelper.DisposeInstallation(ref _textMate, ref _lastGrammarScope); GC.Collect(); } @@ -60,6 +57,7 @@ protected override void OnDataContextChanged(EventArgs e) } private TextMate.Installation _textMate = null; + private string _lastGrammarScope = string.Empty; } public partial class SelfUpdate : ChromelessWindow diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index 94940e743..4addf2d0b 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -550,10 +550,7 @@ protected override void OnUnloaded(RoutedEventArgs e) TextArea.TextView.VisualLinesChanged -= OnTextViewVisualLinesChanged; if (_textMate != null) - { - _textMate.Dispose(); - _textMate = null; - } + Models.TextMateHelper.DisposeInstallation(ref _textMate, ref _lastGrammarScope); } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) @@ -576,7 +573,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang } else if (change.Property == FileNameProperty) { - Models.TextMateHelper.SetGrammarByFileName(_textMate, FileName); + Models.TextMateHelper.SetGrammarByFileName(_textMate, FileName, ref _lastGrammarScope); } else if (change.Property.Name == nameof(ActualThemeVariant) && change.NewValue != null) { @@ -773,16 +770,14 @@ private void UpdateTextMate() TextArea.TextView.LineTransformers.Remove(_lineStyleTransformer); _textMate = Models.TextMateHelper.CreateForEditor(this); TextArea.TextView.LineTransformers.Add(_lineStyleTransformer); - Models.TextMateHelper.SetGrammarByFileName(_textMate, FileName); + Models.TextMateHelper.SetGrammarByFileName(_textMate, FileName, ref _lastGrammarScope); } } else { if (_textMate != null) { - _textMate.Dispose(); - _textMate = null; - GC.Collect(); + Models.TextMateHelper.DisposeInstallation(ref _textMate, ref _lastGrammarScope, true); TextArea.TextView.Redraw(); } @@ -885,6 +880,7 @@ private async Task CopyWithoutIndicatorsAsync() private bool _execSizeChanged; private TextMate.Installation _textMate; + private string _lastGrammarScope = string.Empty; private TextLocation _lastSelectStart = TextLocation.Empty; private TextLocation _lastSelectEnd = TextLocation.Empty; private LineStyleTransformer _lineStyleTransformer;