applyWorkspaceEdit method
Applies a workspace edit or code action payload coming from the LSP.
The method understands several forms: a map with an edit containing
changes, documentChanges, a raw list of edits, or a command. It will
apply text edits to the currently opened file and update the LSP server
document afterwards.
Implementation
Future<void> applyWorkspaceEdit(dynamic action) async {
if (openedFile == null) return;
final fileUri = Uri.file(openedFile!).toString();
if (action is Map && action.containsKey('command')) {
final String command = action['command'];
final List args = action['arguments'] ?? [];
await lspConfig?.executeCommand(command, args);
return;
} else if (action is Map &&
action.containsKey('edit') &&
(action['edit'] as Map).containsKey('changes')) {
final Map changes = action['edit']['changes'] as Map;
if (changes.containsKey(fileUri)) {
final List edits = List.from(changes[fileUri] as List);
final converted = <Map<String, dynamic>>[];
for (final e in edits) {
try {
final start = e['range']?['start'];
final end = e['range']?['end'];
if (start == null || end == null) continue;
final startOffset =
getLineStartOffset(start['line'] as int) +
(start['character'] as int);
final endOffset =
getLineStartOffset(end['line'] as int) +
(end['character'] as int);
final newText = e['newText'] as String? ?? '';
converted.add({
'start': startOffset,
'end': endOffset,
'newText': newText,
});
} catch (_) {
continue;
}
}
converted.sort(
(a, b) => (b['start'] as int).compareTo(a['start'] as int),
);
for (final ce in converted) {
replaceRange(
ce['start'] as int,
ce['end'] as int,
ce['newText'] as String,
preserveOldCursor: true,
);
}
if (lspConfig != null) {
await lspConfig!.updateDocument(openedFile!, text);
}
}
return;
} else if (action is Map &&
action.containsKey('documentChanges') &&
action['documentChanges'] is List) {
final List docChanges = List.from(action['documentChanges'] as List);
for (final dc in docChanges) {
if (dc is Map) {
final td = dc['textDocument'];
final uri = td != null ? td['uri'] as String? : null;
if (uri == fileUri && dc.containsKey('edits')) {
final List edits = List.from(dc['edits'] as List);
final converted = <Map<String, dynamic>>[];
for (final e in edits) {
try {
final start = e['range']?['start'];
final end = e['range']?['end'];
if (start == null || end == null) continue;
final int startOffset =
getLineStartOffset(start['line'] as int) +
(start['character'] as int);
final int endOffset =
getLineStartOffset(end['line'] as int) +
(end['character'] as int);
final String newText = e['newText'] as String? ?? '';
converted.add({
'start': startOffset,
'end': endOffset,
'newText': newText,
});
} catch (_) {
continue;
}
}
converted.sort(
(a, b) => (b['start'] as int).compareTo(a['start'] as int),
);
for (final ce in converted) {
replaceRange(
ce['start'] as int,
ce['end'] as int,
ce['newText'] as String,
preserveOldCursor: true,
);
}
if (lspConfig != null) {
await lspConfig!.updateDocument(openedFile!, text);
}
}
}
}
return;
} else if (action is List) {
final converted = <Map<String, dynamic>>[];
try {
for (Map<String, dynamic> item in action) {
if (!(item.containsKey('newText') && item.containsKey('range'))) {
return;
}
final start = item['range']?['start'];
final end = item['range']?['end'];
if (start == null || end == null) return;
final startOffset =
getLineStartOffset(start['line'] as int) +
(start['character'] as int);
final endOffset =
getLineStartOffset(end['line'] as int) +
(end['character'] as int);
final newText = item['newText'] as String? ?? '';
converted.add({
'start': startOffset,
'end': endOffset,
'newText': newText,
});
}
} catch (_) {
return;
}
converted.sort(
(a, b) => (b['start'] as int).compareTo(a['start'] as int),
);
for (final ce in converted) {
replaceRange(
ce['start'] as int,
ce['end'] as int,
ce['newText'] as String,
preserveOldCursor: true,
);
}
if (lspConfig != null) {
await lspConfig!.updateDocument(openedFile!, text);
}
}
}