applyWorkspaceEdit function

Future<List<LspAppliedChange>> applyWorkspaceEdit(
  1. ExecutionEnv env,
  2. LspWorkspaceEdit edit, {
  3. Map<String, int> openFileVersions = const {},
})

Applies edit through env and returns the per-file changes.

Atomicity: edits for EVERY file are validated (overlap + bounds via a dry-run on the current content) and the version guard is checked before the first write; a failure leaves the workspace untouched.

Version guard: when the server's documentChanges advertised a non-null textDocument.version for a URI and openFileVersions tracks that URI at a different version, the edit is stale (the document moved since the server computed it) and application is rejected up front.

Implementation

Future<List<LspAppliedChange>> applyWorkspaceEdit(
  ExecutionEnv env,
  LspWorkspaceEdit edit, {
  Map<String, int> openFileVersions = const {},
}) async {
  _guardVersions(env, edit, openFileVersions);

  // Phase 1: validate and compute new contents without writing anything.
  final planned = <String, String>{};
  final counts = <String, int>{};
  for (final entry in edit.textEdits.entries) {
    final edits = entry.value;
    if (edits.isEmpty) continue;
    final path = uriToFile(entry.key);
    final read = await env.readTextFile(path);
    if (read.isErr) {
      throw StateError('cannot apply LSP edits: ${read.errorOrNull}');
    }
    planned[path] = applyTextEditsToString(read.valueOrNull!, edits);
    counts[path] = edits.length;
  }

  // Phase 2: write.
  final applied = <LspAppliedChange>[];
  for (final entry in planned.entries) {
    final write = await env.writeFile(entry.key, entry.value);
    if (write.isErr) {
      throw StateError('cannot apply LSP edits: ${write.errorOrNull}');
    }
    applied.add(
      LspAppliedChange(path: entry.key, editCount: counts[entry.key]!),
    );
  }
  return applied;
}