editTextInExternalEditor function

Future<ExternalEditorResult> editTextInExternalEditor({
  1. String initialText = '',
  2. TerminalDriver? terminalDriver,
  3. ExternalEditorCommand? command,
  4. Map<String, String>? environment,
  5. bool? isWindows,
  6. String? fileName,
  7. String fileExtension = '.txt',
  8. String? workingDirectory,
  9. Encoding encoding = utf8,
  10. bool deleteTempFile = true,
  11. bool failOnNonZeroExit = true,
  12. ExternalEditorProcessRunner? processRunner,
  13. ExternalEditorTempFileFactory? tempFileFactory,
})

Opens initialText in the user's external editor and returns the edited text after the editor exits.

When terminalDriver supports TerminalHandoffDriver, the TUI terminal is restored for the duration of the editor process and resumed afterward. Under runApp the driver is TerminalSession.of(context).driver; without it the editor inherits runApp's capture pipes instead of the terminal and the app looks frozen while the editor eats keystrokes.

Implementation

Future<ExternalEditorResult> editTextInExternalEditor({
  String initialText = '',
  TerminalDriver? terminalDriver,
  ExternalEditorCommand? command,
  Map<String, String>? environment,
  bool? isWindows,
  String? fileName,
  String fileExtension = '.txt',
  String? workingDirectory,
  Encoding encoding = utf8,
  bool deleteTempFile = true,
  bool failOnNonZeroExit = true,
  ExternalEditorProcessRunner? processRunner,
  ExternalEditorTempFileFactory? tempFileFactory,
}) async {
  final extension = _normalizeFileExtension(fileExtension);
  final normalizedFileName = _normalizeFileName(fileName, extension);
  final tempFile = await (tempFileFactory ?? _createSystemTempFile)(
    ExternalEditorTempFileRequest(
      fileName: normalizedFileName,
      fileExtension: extension,
    ),
  );
  final file = tempFile.file;

  try {
    await file.writeAsString(initialText, encoding: encoding);

    final resolved = resolveExternalEditorCommand(
      command: command,
      environment: environment,
      isWindows: isWindows,
    );
    final processCommand = _toProcessCommand(
      resolved.command,
      file.path,
      environment: environment,
      isWindows: isWindows,
      workingDirectory: workingDirectory,
    );
    final runner = processRunner ?? _runExternalEditorProcess;

    Future<int> runEditor() => runner(processCommand);
    final exitCode = terminalDriver == null
        ? await runEditor()
        : await withTerminalHandoff(terminalDriver, runEditor);
    final editedText = await file.readAsString(encoding: encoding);
    final result = ExternalEditorResult(
      initialText: initialText,
      editedText: editedText,
      exitCode: exitCode,
      filePath: file.path,
      editorCommand: resolved.command,
      processCommand: processCommand,
      commandSource: resolved.source,
    );
    if (failOnNonZeroExit && exitCode != 0) {
      throw ExternalEditorException(result);
    }
    return result;
  } finally {
    if (deleteTempFile) {
      final cleanup = tempFile.cleanup;
      if (cleanup != null) {
        await cleanup();
      } else if (await file.exists()) {
        await file.delete();
      }
    }
  }
}