You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

124 lines
3.9 KiB

using System;
using System.Collections.Generic;
using Avalonia;
using AvaloniaEdit;
using AvaloniaEdit.TextMate;
using TextMateSharp.Grammars;
namespace MyAvaloniaApp.Attached;
/// <summary>
/// 为 <see cref="TextEditor"/> 提供基于 TextMate 的语法高亮附加属性,
/// 通过 MVVM 绑定即可切换语法与主题。
/// </summary>
public static class TextMateHelper
{
public static readonly AttachedProperty<string?> LanguageProperty =
AvaloniaProperty.RegisterAttached<TextEditor, string?>(
"Language",
typeof(TextMateHelper));
private static readonly AttachedProperty<RegistryOptions?> RegistryOptionsProperty =
AvaloniaProperty.RegisterAttached<TextEditor, RegistryOptions?>(
"RegistryOptions",
typeof(TextMateHelper));
private static readonly AttachedProperty<TextMate.Installation?> InstallationProperty =
AvaloniaProperty.RegisterAttached<TextEditor, TextMate.Installation?>(
"Installation",
typeof(TextMateHelper));
private static readonly IReadOnlyDictionary<string, string> LanguageScopes =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["C#"] = "source.cs",
["JavaScript"] = "source.js",
["TypeScript"] = "source.ts",
["HTML"] = "text.html.basic",
["CSS"] = "source.css",
["JSON"] = "source.json",
["XML"] = "text.xml",
["Python"] = "source.python",
["Java"] = "source.java",
["SQL"] = "source.sql"
};
static TextMateHelper() => LanguageProperty.Changed.AddClassHandler<TextEditor>(OnLanguageChanged);
public static void SetLanguage(TextEditor editor, string? value) =>
editor.SetValue(LanguageProperty, value);
public static string? GetLanguage(TextEditor editor) =>
editor.GetValue(LanguageProperty);
private static void OnLanguageChanged(TextEditor editor, AvaloniaPropertyChangedEventArgs change)
{
var language = change.NewValue as string;
var registryOptions = EnsureRegistryOptions(editor);
var installation = EnsureInstallation(editor, registryOptions);
if (installation == null)
{
return;
}
var scope = ResolveScope(language);
if (!string.IsNullOrWhiteSpace(scope))
{
try
{
installation.SetGrammar(scope);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"TextMateHelper: 设置语法失败 - {ex.Message}");
}
}
}
private static RegistryOptions EnsureRegistryOptions(TextEditor editor)
{
var registryOptions = editor.GetValue(RegistryOptionsProperty);
if (registryOptions == null)
{
registryOptions = new RegistryOptions(ThemeName.DarkPlus);
editor.SetValue(RegistryOptionsProperty, registryOptions);
}
return registryOptions;
}
private static TextMate.Installation? EnsureInstallation(TextEditor editor, RegistryOptions registryOptions)
{
var installation = editor.GetValue(InstallationProperty);
if (installation == null)
{
try
{
installation = editor.InstallTextMate(registryOptions);
editor.SetValue(InstallationProperty, installation);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"TextMateHelper: 安装失败 - {ex.Message}");
}
}
return installation;
}
private static string ResolveScope(string? language)
{
if (string.IsNullOrWhiteSpace(language))
{
return "source.cs";
}
return LanguageScopes.TryGetValue(language, out var value)
? value
: "source.cs";
}
}