using System.Text; namespace XFESystem.Documents; public sealed class DocumentSession { private static readonly UTF8Encoding Utf8 = new(false, true); private readonly SemaphoreSlim _saveLock = new(1, 1); private string _savedText; internal DocumentSession(DocumentLocation location, StoredDocument source, string text) { Location = location; Revision = source.Revision; IsReadOnly = source.IsReadOnly; Text = _savedText = text; } public DocumentLocation Location { get; } public string Revision { get; private set; } public bool IsReadOnly { get; } public string Text { get; set; } public bool IsModified => !string.Equals(Text, _savedText, StringComparison.Ordinal); public string HighlightLanguage => CodeHighlightLanguage.FromPath(Location.Value); internal async ValueTask SaveAsync(IDocumentStorage storage, int maximumBytes, CancellationToken cancellationToken) { await _saveLock.WaitAsync(cancellationToken).ConfigureAwait(false); try { if (IsReadOnly) throw new UnauthorizedAccessException("This document is read-only."); var snapshot = Text ?? throw new InvalidOperationException("Document text cannot be null."); if (Utf8.GetByteCount(snapshot) > maximumBytes) throw new IOException("The document exceeds the size limit."); var content = Utf8.GetBytes(snapshot); Revision = await storage.ReplaceAsync(Location, content, Revision, cancellationToken).ConfigureAwait(false); // Edits made while saving remain dirty: only the actual saved snapshot becomes the baseline. _savedText = snapshot; } finally { _saveLock.Release(); } } }