XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text;
using XFESystem.Desktop;

namespace XFESystem.Browser;

public sealed partial class BrowserService
{
    private Func<bool>? operationCancellation;
    internal void SetOperationCancellation(Func<bool>? requested) => operationCancellation = requested;
    private bool OperationCancelled => operationCancellation?.Invoke() == true;
    private void CheckOperationCancellation()
    {
        if (OperationCancelled) throw new OperationCanceledException("The editor operation was cancelled at a resource/script boundary.");
    }

    /// <summary>Read real model and rendered view state. This neither calls
    /// render(), fabricates tokens nor discards the remaining browser tasks.</summary>
    internal BrowserDisplayList? ReadyEditorLayout(string source, string path, int width, int height)
    {
        CheckOperationCancellation();
        if (ReadEditorText() != source || runtime is null || document is null) return null;
        string language = path.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) ? "csharp" : "javascript";
        if (runtime.ReadString("xfeMonacoEditor.getModel().getLanguageId()") != language) return null;
        var lines = document.QuerySelectorAll(".view-lines .view-line");
        if (lines.Length == 0) return null;
        var expected = source.Replace("\r\n", "\n").Replace('\r', '\n').Split('\n');
        if (lines.Length > expected.Length) return null;
        bool visibleSource = false, renderedToken = false;
        for (int index = 0; index < lines.Length; index++)
        {
            // Monaco expands tabs/space placeholders in the view. Compare the
            // actual token characters, while the full model readback above
            // separately validates every original character and line ending.
            string actual = ViewCharacters(lines[index].TextContent);
            string original = ViewCharacters(expected[index]);
            if (actual != original) return null;
            if (actual.Length != 0) visibleSource = true;
            if (lines[index].QuerySelectorAll("span[class]").Any(span => span.ClassList.Any(name => name.StartsWith("mtk", StringComparison.Ordinal)))) renderedToken = true;
        }
        if (!string.IsNullOrWhiteSpace(source) && (!visibleSource || !renderedToken)) return null;
        var layout = Layout(new(width, height));
        var viewport = new BrowserRect(0, 0, width, height);
        bool textPaint = layout.Commands.Any(command => command.Kind == BrowserDrawKind.Text && command.Opacity > 0 &&
            !string.IsNullOrWhiteSpace(command.Text) && command.Node?.ParentElement?.Closest(".view-line") is not null &&
            command.Bounds.Intersect(viewport) is { Width: > 0, Height: > 0 });
        if (!string.IsNullOrWhiteSpace(source) && !textPaint) return null;
        return layout;
    }

    private static string ViewCharacters(string text)
    {
        var value = new StringBuilder(text.Length);
        foreach (char character in text)
            if (character is not (' ' or '\t' or '\u00a0' or '\u200b')) value.Append(character);
        return value.ToString();
    }
}