applyWorkspaceEdit method

Future<void> applyWorkspaceEdit(
  1. dynamic action
)

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