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
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";
|
|
}
|
|
}
|
|
|
|
|