///|
/// Root-relative source path accepted from UI or protocol input after
/// normalization rejects absolute paths, parent traversal, and NUL bytes.
struct SourcePath {
value : String
} derive(Eq, Debug)
///|
/// Validation failures for user-facing source paths and document URIs.
pub(all) enum SourceError {
MissingPath
InvalidPath
} derive(Eq, Debug)
///|
/// Result wrapper used instead of raising while normalizing source paths.
pub enum SourcePathResult {
ParsedSourcePath(SourcePath)
SourcePathError(SourceError)
} derive(Eq, Debug)
///|
/// Raw file text plus metadata needed to construct a `DocumentSnapshot`.
struct DocumentContent {
text : String
display_name : String
language_id : String
revision : String
} derive(Debug)
///|
/// Builds file content while allowing providers to omit derived metadata.
pub fn DocumentContent::DocumentContent(
text : String,
display_name~ : String = "",
language_id~ : String = "",
revision~ : String = "",
) -> DocumentContent {
{ text, display_name, language_id, revision, }
}
///|
/// Stable protocol/debug label for source validation failures.
pub fn SourceError::to_string(self : SourceError) -> String {
match self {
MissingPath => "MissingPath"
InvalidPath => "InvalidPath"
}
}
///|
/// Normalizes a user-provided workspace path into a safe root-relative path.
pub fn SourcePath::normalize_root_relative_path(
raw_path : String,
) -> SourcePathResult {
if raw_path == "" {
return SourcePathError(MissingPath)
}
if contains_null(raw_path) {
return SourcePathError(InvalidPath)
}
let slash_path = raw_path.replace_all(old="\\", new="/")
if slash_path.has_prefix("/") ||
slash_path.has_prefix("//") ||
is_windows_drive_path(slash_path) {
return SourcePathError(InvalidPath)
}
let segments = []
for segment_view in slash_path.split("/") {
let segment = segment_view
if segment == ".." {
return SourcePathError(InvalidPath)
}
if segment != "" && segment != "." {
segments.push(segment)
}
}
guard segments is [_, ..] else { return SourcePathError(InvalidPath) }
ParsedSourcePath({ value: segments.join("/"), })
}
///|
/// Returns the normalized root-relative path string.
pub fn SourcePath::to_string(self : SourcePath) -> String {
self.value
}
///|
/// Display label for a source path; currently the normalized path itself.
pub fn SourcePath::display_name(self : SourcePath) -> String {
self.value
}
///|
/// Text of one logical line without its line break.
pub fn DocumentSnapshot::line_text(
self : DocumentSnapshot,
line : Int32,
) -> String {
let start = self.line_start_offset(line)
let end = self.line_end_offset(line)
self.text[start:end].to_owned()
}
///|
/// Host-neutral error codes used by file providers and remote workspace
/// responses.
pub(all) enum FileSystemProviderErrorCode {
FileNotFound
FileExists
FileNotADirectory
IsDirectory
NoPermissions
Unavailable
InvalidUri
InvalidData
} derive(Eq, Debug)
///|
/// File-provider failure with optional URI context.
pub struct FileSystemProviderError {
code : FileSystemProviderErrorCode
message : String
uri : @base_common.Uri?
} derive(Debug)
///|
/// Document-level failure with optional URI context.
pub struct DocumentError {
code : FileSystemProviderErrorCode
message : String
uri : @base_common.Uri?
} derive(Debug)
///|
/// Provider read result without exceptions, so browser and native providers can
/// share the same contract.
pub(all) enum FileReadResult {
FileReadOk(DocumentContent)
FileReadError(FileSystemProviderError)
} derive(Debug)
///|
/// File-watch event kind. Providers may coalesce events before delivering
/// them.
pub(all) enum FileChangeType {
Created
Changed
Deleted
} derive(Eq, Debug)
///|
/// One changed document URI reported by a file watch.
pub(all) struct FileChange {
uri : @base_common.Uri
change_type : FileChangeType
} derive(Debug)
///|
/// Watch behavior requested from a provider.
pub struct WatchOptions {
recursive : Bool
excludes : Array[String]
} derive(Debug)
///|
/// Disposable watch handle. Calling `dispose` must stop future callbacks for
/// that watch when the provider can do so.
pub(all) struct FileWatch {
dispose : () -> Unit
}
///|
/// Host filesystem abstraction for readonly document loading and file-change
/// notifications.
pub(open) trait FileSystemProvider {
fn scheme(Self) -> String
async fn read_file(Self, @base_common.Uri) -> FileReadResult
fn watch(Self, @base_common.Uri, WatchOptions, (Array[FileChange]) -> Unit) -> FileWatch
}
///|
/// Default non-recursive watch with no excludes.
pub fn WatchOptions::default() -> WatchOptions {
{ recursive: false, excludes: [], }
}
///|
/// Stops the watch through the provider-supplied disposer.
pub fn FileWatch::dispose(self : FileWatch) -> Unit {
(self.dispose)()
}
///|
/// Converts a document error back to the lower-level error shape when protocol
/// adapters need the existing provider-code JSON format.
pub fn DocumentError::to_file_system_error(
self : DocumentError,
) -> FileSystemProviderError {
{ code: self.code, message: self.message, uri: self.uri, }
}
///|
/// Derives a snapshot from provider content, filling display name, language id,
/// and revision when they are absent.
pub fn DocumentSnapshot::from_content(
uri : @base_common.Uri,
content : DocumentContent,
) -> DocumentSnapshot {
DocumentSnapshot(
uri,
content.display_name,
content.language_id,
content.revision,
content.text,
)
}
///|
/// JSON shape for document read results.
pub impl ToJson for DocumentReadResult with fn to_json(self) {
match self {
DocumentLoaded(document) =>
{ "ok": true, "document": ToJson::to_json(document) }
DocumentNotFound(uri) =>
{ "ok": false, "notFound": true, "uri": Json(uri.to_string()) }
DocumentReadFailed(error) =>
{ "ok": false, "error": ToJson::to_json(error) }
}
}
///|
/// JSON shape for provider errors, including HTTP-like status for the native
/// reference server.
pub impl ToJson for FileSystemProviderError with fn to_json(self) {
let fields : Map[String, Json] = {
"code": Json(self.code.to_string()),
"message": Json(self.message),
"status": Json(self.status()),
}
if self.uri is Some(uri) {
fields["uri"] = Json(uri.to_string())
fields["displayName"] = Json(@base_common.basename_or_authority(uri))
}
Json(fields)
}
///|
/// JSON shape for watch events.
pub impl ToJson for FileChange with fn to_json(self) {
{
"uri": Json(self.uri.to_string()),
"type": Json(self.change_type.to_string()),
}
}
///|
/// Document-provider adapters retained as the documented host-neutral
/// workspace contract.
pub async fn read_document(
provider : &FileSystemProvider,
uri : @base_common.Uri,
) -> DocumentReadResult {
read_document_impl(provider, uri)
}
///|
pub fn watch_file(
provider : &FileSystemProvider,
uri : @base_common.Uri,
listener : (Array[FileChange]) -> Unit,
) -> FileWatch {
watch_file_impl(provider, uri, listener)
}
///|
/// Stable content-derived revision retained for provider implementations.
pub fn revision_from_text(text : String) -> String {
revision_from_text_impl(text)
}
///|
/// Snapshot length in the repo's JavaScript-compatible UTF-16 indexing model.
pub fn DocumentSnapshot::length(self : DocumentSnapshot) -> Int32 {
self.length_impl()
}
///|
/// Number of logical lines, including a final empty line after a newline.
pub fn DocumentSnapshot::line_count(self : DocumentSnapshot) -> Int32 {
self.line_count_impl()
}
///|
pub fn DocumentSnapshot::line_start_offset(
self : DocumentSnapshot,
line : Int32,
) -> Int32 {
self.line_start_offset_impl(line)
}
///|
pub fn DocumentSnapshot::line_end_offset(
self : DocumentSnapshot,
line : Int32,
) -> Int32 {
self.line_end_offset_impl(line)
}
///|
pub fn DocumentSnapshot::slice(
self : DocumentSnapshot,
range : @base_common.OffsetRange,
) -> String {
self.slice_impl(range)
}
///|
pub fn DocumentSnapshot::line_at_offset(
self : DocumentSnapshot,
offset : Int32,
) -> Int32 {
self.line_at_offset_impl(offset)
}
///|
pub fn DocumentSnapshot::position_at_offset(
self : DocumentSnapshot,
offset : Int32,
) -> @base_common.Position {
self.position_at_offset_impl(offset)
}
///|
pub fn DocumentSnapshot::offset_at_position(
self : DocumentSnapshot,
position : @base_common.Position,
) -> Int32 {
self.offset_at_position_impl(position)
}
///|
pub extend DocumentContent with Debug::{to_repr}
///|
pub extend DocumentError with Debug::{to_repr}
///|
pub extend DocumentReadResult with ToJson::{to_json}
///|
pub extend FileChange with Debug::{to_repr}
///|
pub extend FileChange with ToJson::{to_json}
///|
pub extend FileChangeType with Debug::{to_repr}
///|
pub extend FileChangeType with Eq::{equal, not_equal}
///|
pub extend FileReadResult with Debug::{to_repr}
///|
pub extend FileSystemProviderError with Debug::{to_repr}
///|
pub extend FileSystemProviderError with ToJson::{to_json}
///|
pub extend FileSystemProviderErrorCode with Debug::{to_repr}
///|
pub extend FileSystemProviderErrorCode with Eq::{equal, not_equal}
///|
pub extend SourceError with Debug::{to_repr}
///|
pub extend SourceError with Eq::{equal, not_equal}
///|
pub extend SourcePath with Debug::{to_repr}
///|
pub extend SourcePath with Eq::{equal, not_equal}
///|
pub extend SourcePathResult with Debug::{to_repr}
///|
pub extend SourcePathResult with Eq::{equal, not_equal}
///|
pub extend WatchOptions with Debug::{to_repr}