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(); }
}
}
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(); }
}
}