[analysis_server] Move EOL from ChangeBuilder to FileEditBuilder

Because ChangeBuilder can be used to modify multiple files, the EOL should come from an individual files edit builder and not from the overall ChangeBuilder.

This change renames `eol` on ChangeBuilder to `defaultEol` (since it's still useful to supply a custom default, for example when moving a class from an existing file to a new file, we want to default to the same EOL as the source file), and adds an EOL to file edit builders which is computed from the file content (and falls back to the default from ChangeBuilder if there are none).

My intention here is to not introduce any breaking changes (yet) so the ChangeBuilder constructor interface still has the "eol" named parameter (marked as deprecated) as well as the new `defaultEol` parameter.

I added a new `StringExtension` for `endOfLine` to both analyzer_plugin and analysis_server_plugin as we need this in both places (but I expect one of those projects will ultimately be dropped). This slightly differs from `CorrectionUtils.endOfLine` because it can return `null` which is a signal to fall back to the ChangeBuilder default instead of forcing its own default. (CorrectionUtils.endOfLine now uses that extension, although because of the null/default, it's likely that some - if not all - uses of this should migrate to the new extension and fall back to a default from a ChangeBuilder).

Change-Id: Ie2e6eaba78c96a1cbd79eba881e12bbeef724aa8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/443122
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analysis_server/lib/src/cider/rename.dart b/pkg/analysis_server/lib/src/cider/rename.dart
index 60b9021..d15a18b 100644
--- a/pkg/analysis_server/lib/src/cider/rename.dart
+++ b/pkg/analysis_server/lib/src/cider/rename.dart
@@ -9,7 +9,6 @@
 import 'package:analysis_server/src/services/refactoring/legacy/refactoring.dart';
 import 'package:analysis_server/src/services/search/hierarchy.dart';
 import 'package:analysis_server/src/utilities/change_builder.dart';
-import 'package:analysis_server_plugin/edit/correction_utils.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/source/line_info.dart';
@@ -377,7 +376,6 @@
       return null;
     }
 
-    var utils = CorrectionUtils(resolvedUnit);
     var node = result.node;
     if (node is! NamedCompilationUnitMember) {
       return null;
@@ -391,7 +389,6 @@
         constructorName: newName,
         isConst: node is EnumDeclaration,
       ),
-      eol: utils.endOfLine,
     );
     if (edit == null) {
       return null;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/refactor_command_handler.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/refactor_command_handler.dart
index 6d08468..c35ebfd 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/refactor_command_handler.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/refactor_command_handler.dart
@@ -115,7 +115,7 @@
     var producer = generator(context);
     var builder = ChangeBuilder(
       workspace: context.workspace,
-      eol: context.utils.endOfLine,
+      defaultEol: context.utils.endOfLine,
     );
     var status = await producer.compute(arguments, builder);
 
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_call_super.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_call_super.dart
index 6ca49d0..c3728e2 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_call_super.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_call_super.dart
@@ -94,9 +94,11 @@
     var expression = body.expression;
     var semicolon = body.semicolon;
     var prefix = utils.getLinePrefix(expression.offset);
-    var prefixWithLine = eol + prefix + utils.oneIndent;
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
+      var prefixWithLine = eol + prefix + utils.oneIndent;
+
       builder.addSimpleReplacement(
         range.startStart(body.functionDefinition, expression),
         '{${prefixWithLine}super.$_addition;${prefixWithLine}return ',
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_eol_at_end_of_file.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_eol_at_end_of_file.dart
index 74cbb1b..2320000 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_eol_at_end_of_file.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_eol_at_end_of_file.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
+import 'package:analysis_server_plugin/src/utilities/extensions/string_extension.dart';
 import 'package:analyzer/source/source_range.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
@@ -22,9 +23,13 @@
   @override
   Future<void> compute(ChangeBuilder builder) async {
     var content = unitResult.content;
-    if (!content.endsWith(eol)) {
+    var eol = content.endOfLine;
+    if (eol == null || !content.endsWith(eol)) {
       await builder.addDartFileEdit(file, (builder) {
-        builder.addSimpleInsertion(content.length, eol);
+        // Read the EOL off builder, because it is non-null and always has the
+        // correct default, whereas the original variable could be null if the
+        // file has no EOLs.
+        builder.addSimpleInsertion(content.length, builder.eol);
       });
     } else {
       var index = content.length;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_leading_newline_to_string.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_leading_newline_to_string.dart
index 94efc33..fafeb9e 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_leading_newline_to_string.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_leading_newline_to_string.dart
@@ -29,6 +29,7 @@
     }
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       builder.addSimpleInsertion(stringLiteral.contentsOffset, eol);
     });
   }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_override.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_override.dart
index 9b40df9..5c1f63b 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_override.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_override.dart
@@ -42,6 +42,7 @@
     var exitPosition = Position(file, token.offset - 1);
     var indent = utils.oneIndent;
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       builder.addSimpleReplacement(
         range.startLength(token, 0),
         '@override$eol$indent',
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_redeclare.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_redeclare.dart
index 4399c16..47f0417 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_redeclare.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_redeclare.dart
@@ -29,6 +29,7 @@
     var token = member.firstTokenAfterCommentAndMetadata;
     var indent = utils.oneIndent;
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       builder.addInsertion(token.offset, (builder) {
         builder.write('@');
         builder.writeImportedName([
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_return_null.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_return_null.dart
index 1b66f20..b82af5c 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_return_null.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_return_null.dart
@@ -45,20 +45,22 @@
     } else {
       return;
     }
-    int position;
-    String returnStatement;
-    if (block.statements.isEmpty) {
-      position = block.offset + 1;
-      var prefix = utils.getLinePrefix(block.offset);
-      returnStatement = '$eol$prefix${utils.oneIndent}return null;$eol$prefix';
-    } else {
-      var lastStatement = block.statements.last;
-      position = lastStatement.offset + lastStatement.length;
-      var prefix = utils.getNodePrefix(lastStatement);
-      returnStatement = '$eol${prefix}return null;';
-    }
-
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
+      int position;
+      String returnStatement;
+      if (block.statements.isEmpty) {
+        position = block.offset + 1;
+        var prefix = utils.getLinePrefix(block.offset);
+        returnStatement =
+            '$eol$prefix${utils.oneIndent}return null;$eol$prefix';
+      } else {
+        var lastStatement = block.statements.last;
+        position = lastStatement.offset + lastStatement.length;
+        var prefix = utils.getNodePrefix(lastStatement);
+        returnStatement = '$eol${prefix}return null;';
+      }
+
       builder.addInsertion(position, (builder) {
         builder.write(returnStatement);
       });
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_switch_case_break.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_switch_case_break.dart
index d3d1b1c..c62d7f8 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_switch_case_break.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_switch_case_break.dart
@@ -44,7 +44,7 @@
 
     await builder.addDartFileEdit(file, (builder) {
       builder.addInsertion(lastStatement.end, (builder) {
-        builder.write(eol);
+        builder.writeln();
         builder.write(utils.getNodePrefix(lastStatement));
         builder.write('break;');
       });
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_documentation_into_line.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_documentation_into_line.dart
index 11ef81e..e476938 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_documentation_into_line.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_documentation_into_line.dart
@@ -6,6 +6,7 @@
 import 'package:analysis_server/src/services/correction/assist.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
+import 'package:analysis_server_plugin/src/utilities/extensions/string_extension.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer_plugin/utilities/assist/assist.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
@@ -39,7 +40,12 @@
       return;
     }
     var text = token.lexeme;
-    var lines = text.split(eol);
+    var eol = text.endOfLine;
+    var lines = eol != null ? text.split(eol) : [text];
+    // To simplify the code below which builds prefixes with eols in the loop,
+    // ensure we have a value. In the case of a single line (eol=null) these
+    // eols are only assigned to variables and not used.
+    eol ??= builder.defaultEol;
     var prefix = utils.getNodePrefix(comment);
     var newLines = <String>[];
     var firstLine = true;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_flutter_child.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_flutter_child.dart
index 4c43947..6f1b381 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_flutter_child.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_flutter_child.dart
@@ -33,6 +33,7 @@
     var expression = named.expression;
     if (expression.isWidgetExpression) {
       await builder.addDartFileEdit(file, (builder) {
+        var eol = builder.eol;
         var childLoc = named.offset + 'child'.length;
         builder.addSimpleInsertion(childLoc, 'ren');
         var listLoc = expression.offset;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_into_block_body.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_into_block_body.dart
index 7edfcd8..5f6f520 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_into_block_body.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_into_block_body.dart
@@ -71,6 +71,7 @@
     var sourceRange = range.endEnd(body.beginToken.previous!, body);
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       builder.addReplacement(sourceRange, (builder) {
         builder.write(' ');
         if (body.isAsynchronous) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_into_for_index.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_into_for_index.dart
index ed63422..24e4cc4 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_into_for_index.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_into_for_index.dart
@@ -91,6 +91,7 @@
     var firstBlockLine = utils.getLineContentEnd(body.leftBracket.end);
     // add change
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       // TODO(brianwilkerson): Create linked positions for the loop variable.
       builder.addSimpleReplacement(
         range.startEnd(forStatement, forStatement.rightParenthesis),
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_function_declaration.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_function_declaration.dart
index f34aba0..90f9951 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_function_declaration.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_function_declaration.dart
@@ -57,7 +57,7 @@
           if (before != null) {
             builder.write(before);
           }
-          builder.write(eol);
+          builder.writeln();
           builder.write(utils.getLinePrefix(range.offset));
           if (after != null) {
             builder.write(after);
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_switch_expression.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_switch_expression.dart
index c97a9b1..14e5241 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_switch_expression.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_switch_expression.dart
@@ -561,6 +561,7 @@
     required String text,
     required _Indentation indentation,
   }) {
+    var eol = builder.eol;
     var insertOffset = utils.getLineContentStart(nextLineOffset);
     var nextLinePrefix = utils.getLinePrefix(nextLineOffset);
 
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_switch_statement.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_switch_statement.dart
index 0bcf1cf..05fe6ae 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_switch_statement.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_switch_statement.dart
@@ -273,6 +273,7 @@
     required String beforeCaseExpression,
     required Token semicolon,
   }) async {
+    var eol = builder.eol;
     for (var case_ in switchExpression.cases) {
       var guardedPattern = case_.guardedPattern;
       // `_ =>` -> `default:`
@@ -323,6 +324,7 @@
     var indent = utils.getLinePrefix(declarationStatement.offset);
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       // Replace implicit type with explicit.
       if (declarationList.type == null) {
         var keyword = declarationList.keyword;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart
index 5d0a611..9743dd8 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart
@@ -182,8 +182,6 @@
   Future<void> compute(ChangeBuilder builder) async {
     // prepare environment
     LibraryFragment targetUnit;
-    var prefix = '';
-    var suffix = '';
     var offset = -1;
     String? filePath;
     if (_prefixElement == null) {
@@ -197,7 +195,6 @@
       }
       offset = enclosingMember.end;
       filePath = file;
-      prefix = '$eol$eol';
     } else {
       for (var import in libraryElement2.firstFragment.libraryImports) {
         if (_prefixElement is PrefixElement &&
@@ -209,8 +206,6 @@
             try {
               offset = targetSource.contents.data.length;
               filePath = targetSource.fullName;
-              prefix = eol;
-              suffix = eol;
             } on FileSystemException {
               // If we can't read the file to get the offset, then we can't
               // create a fix.
@@ -226,6 +221,9 @@
 
     var className2 = _className;
     await builder.addDartFileEdit(filePath, (builder) {
+      var eol = builder.eol;
+      var prefix = filePath == file ? '$eol$eol' : eol;
+      var suffix = filePath == file ? '' : eol;
       builder.addInsertion(offset, (builder) {
         builder.write(prefix);
         if (_arguments == null && !_requiresConstConstructor) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_file.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_file.dart
index 223c510..ab51e7d 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_file.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_file.dart
@@ -59,6 +59,7 @@
           var relativeUri = pathContext.split(relativePath).join('/');
 
           await builder.addDartFileEdit(source.fullName, (builder) {
+            var eol = builder.eol;
             builder.addSimpleInsertion(0, "part of '$relativeUri';$eol$eol");
           });
           _fileName = source.shortName;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_function.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_function.dart
index f12aa9f..45e2be4 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_function.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_function.dart
@@ -42,15 +42,15 @@
 
     // prepare environment
     int insertOffset;
-    String sourcePrefix;
     var enclosingMember = node.thisOrAncestorOfType<CompilationUnitMember>();
     if (enclosingMember == null) {
       return;
     }
     insertOffset = enclosingMember.end;
-    sourcePrefix = '$eol$eol';
     // Build method source.
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
+      var sourcePrefix = '$eol$eol';
       builder.addInsertion(insertOffset, (builder) {
         builder.write(sourcePrefix);
         // append return type
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_local_variable.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_local_variable.dart
index e1eb5b0..8d4e306 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_local_variable.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_local_variable.dart
@@ -80,7 +80,7 @@
           type: type,
           typeGroupName: 'TYPE',
         );
-        builder.write(eol);
+        builder.writeln();
         builder.write(prefix);
       });
       builder.addLinkedPosition(range.node(node), 'NAME');
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_method_or_function.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_method_or_function.dart
index 9db1b82..d9c39be 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_method_or_function.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_method_or_function.dart
@@ -131,14 +131,17 @@
     String targetFile,
     int insertOffset,
     bool isStatic,
-    String prefix,
-    String sourcePrefix,
-    String sourceSuffix,
-  ) async {
+    String prefix, {
+    required bool leadingEol,
+    required bool trailingEol,
+  }) async {
     // build method source
     await builder.addDartFileEdit(targetFile, (builder) {
+      var eol = builder.eol;
       builder.addInsertion(insertOffset, (builder) {
-        builder.write(sourcePrefix);
+        if (leadingEol) {
+          builder.writeln();
+        }
         builder.write(prefix);
         // may be static
         if (isStatic) {
@@ -164,7 +167,9 @@
         }
         // close method
         builder.write(' {$eol$prefix}');
-        builder.write(sourceSuffix);
+        if (trailingEol) {
+          builder.writeln();
+        }
       });
       if (targetFile == file) {
         builder.addLinkedPosition(range.node(node), 'NAME');
@@ -183,8 +188,6 @@
     var insertOffset = unit.end;
     // prepare prefix
     var prefix = '';
-    var sourcePrefix = eol;
-    var sourceSuffix = eol;
     await _createExecutable(
       builder,
       functionType,
@@ -193,8 +196,8 @@
       insertOffset,
       false,
       prefix,
-      sourcePrefix,
-      sourceSuffix,
+      leadingEol: true,
+      trailingEol: true,
     );
     _functionName = name;
   }
@@ -236,13 +239,8 @@
     var insertOffset = targetNode.end - 1;
     // prepare prefix
     var prefix = '  ';
-    String sourcePrefix;
-    if (classMembers.isEmpty) {
-      sourcePrefix = '';
-    } else {
-      sourcePrefix = eol;
-    }
-    var sourceSuffix = eol;
+    var leadingEol = classMembers.isNotEmpty;
+    var trailingEol = true;
     await _createExecutable(
       builder,
       functionType,
@@ -251,8 +249,8 @@
       insertOffset,
       isStatic || inStaticContext,
       prefix,
-      sourcePrefix,
-      sourceSuffix,
+      leadingEol: leadingEol,
+      trailingEol: trailingEol,
     );
     _functionName = name;
   }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_missing_overrides.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_missing_overrides.dart
index 09fbbb8..a4a1ee1 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_missing_overrides.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_missing_overrides.dart
@@ -70,8 +70,8 @@
               builder.selectHere();
             }
           } else {
-            builder.write(eol); // After the previous member.
-            builder.write(eol); // Empty line separator.
+            builder.writeln(); // After the previous member.
+            builder.writeln(); // Empty line separator.
             builder.write(prefix);
           }
           numOfMembersWritten++;
@@ -92,7 +92,7 @@
               addSeparatorBetweenDeclarations();
               // Add `@override`.
               builder.write('@override');
-              builder.write(eol);
+              builder.writeln();
               // Add field.
               builder.write(prefix);
               if (targetDeclaration is EnumDeclaration) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart
index 4b871e3..1e3f2c2 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart
@@ -136,8 +136,6 @@
     }
     // prepare environment
     LibraryFragment targetUnit;
-    var prefix = '';
-    var suffix = '';
     var offset = -1;
     String? filePath;
     if (prefixElement == null) {
@@ -151,7 +149,6 @@
       }
       offset = enclosingMember.end;
       filePath = file;
-      prefix = '$eol$eol';
     } else {
       for (var import in libraryElement2.firstFragment.libraryImports) {
         if (prefixElement is PrefixElement &&
@@ -163,8 +160,6 @@
             try {
               offset = targetSource.contents.data.length;
               filePath = targetSource.fullName;
-              prefix = eol;
-              suffix = eol;
             } on FileSystemException {
               // If we can't read the file to get the offset, then we can't
               // create a fix.
@@ -178,6 +173,9 @@
       return;
     }
     await builder.addDartFileEdit(filePath, (builder) {
+      var eol = builder.eol;
+      var prefix = filePath == file ? '$eol$eol' : eol;
+      var suffix = filePath == file ? '' : eol;
       builder.addInsertion(offset, (builder) {
         builder.write(prefix);
         builder.writeMixinDeclaration(_mixinName, nameGroupName: 'NAME');
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_no_such_method.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_no_such_method.dart
index 3e41275..44fc416 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_no_such_method.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_no_such_method.dart
@@ -33,18 +33,18 @@
         builder.selectHere();
         // insert empty line before existing member
         if (targetClass.members.isNotEmpty) {
-          builder.write(eol);
+          builder.writeln();
         }
         // append method
         builder.write(prefix);
         builder.write('@override');
-        builder.write(eol);
+        builder.writeln();
         builder.write(prefix);
         builder.write(
           'dynamic noSuchMethod(Invocation invocation) => '
           'super.noSuchMethod(invocation);',
         );
-        builder.write(eol);
+        builder.writeln();
       });
     });
   }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_children.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_children.dart
index 41193e0..bbe3eee 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_children.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_children.dart
@@ -44,7 +44,6 @@
     await builder.addDartFileEdit(file, (builder) {
       _convertFlutterChildToChildren(
         namedExp,
-        eol,
         utils.getNodeText,
         utils.getLinePrefix,
         utils.getText,
@@ -55,12 +54,12 @@
 
   void _convertFlutterChildToChildren(
     NamedExpression namedExp,
-    String eol,
     String Function(Expression) getNodeText,
     String Function(int) getLinePrefix,
     String Function(int, int) getText,
     FileEditBuilder builder,
   ) {
+    var eol = builder.eol;
     var childArg = namedExp.expression;
     var childLoc = namedExp.offset + 'child'.length;
     builder.addSimpleInsertion(childLoc, 'ren');
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap.dart
index 0abf8c5..904da26 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap.dart
@@ -289,21 +289,21 @@
         var indentNew1 = indentOld + utils.oneIndent;
         var indentNew2 = indentOld + utils.twoIndents;
 
-        builder.write(eol);
+        builder.writeln();
         builder.write(indentNew1);
         builder.write('children: [');
-        builder.write(eol);
+        builder.writeln();
 
         var newSrc = utils.replaceSourceIndent(src, indentOld, indentNew2);
         builder.write(indentNew2);
         builder.write(newSrc);
 
         builder.write(',');
-        builder.write(eol);
+        builder.writeln();
 
         builder.write(indentNew1);
         builder.write('],');
-        builder.write(eol);
+        builder.writeln();
 
         builder.write(indentOld);
         builder.write(')');
@@ -349,6 +349,7 @@
     }
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       builder.addReplacement(range.node(widgetExpr), (builder) {
         if (parentClassElement == null) {
           builder.addSimpleLinkedEdit('WIDGET', 'widget');
@@ -368,12 +369,12 @@
           var indentNew = '$indentOld${utils.oneIndent}';
 
           for (var leadingLine in leadingLines) {
-            builder.write(eol);
+            builder.writeln();
             builder.write(indentNew);
             builder.write(leadingLine);
           }
 
-          builder.write(eol);
+          builder.writeln();
           builder.write(indentNew);
           widgetSrc = utils.replaceSourceIndent(
             widgetSrc,
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_generic.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_generic.dart
index e3e1eb4..97b81a6 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_generic.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_wrap_generic.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/services/correction/assist.dart';
 import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
+import 'package:analysis_server_plugin/src/utilities/extensions/string_extension.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/src/utilities/extensions/flutter.dart';
 import 'package:analyzer_plugin/utilities/assist/assist.dart';
@@ -32,6 +33,7 @@
     )) {
       return;
     }
+    var eol = unitResult.content.endOfLine ?? builder.defaultEol;
     var literalSrc = utils.getNodeText(node);
     var newlineIdx = literalSrc.lastIndexOf(eol);
     if (newlineIdx < 0 || newlineIdx == literalSrc.length - 1) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/join_else_with_if.dart b/pkg/analysis_server/lib/src/services/correction/dart/join_else_with_if.dart
index 1260be7..b750a39 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/join_else_with_if.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/join_else_with_if.dart
@@ -147,8 +147,9 @@
     Block block,
     String? startCommentsSource,
     String prefix,
-    String? endCommentSource,
-  ) {
+    String? endCommentSource, {
+    required String eol,
+  }) {
     var lineRanges = range.node(block);
     var blockSource = utils.getRangeText(lineRanges);
     blockSource = utils.indentSourceLeftRight(blockSource).trimRight();
@@ -219,6 +220,7 @@
     var prefix = utils.getNodePrefix(outerIfStatement);
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       var source = '';
       for (var statement in statements) {
         String newBlockSource;
@@ -240,15 +242,22 @@
         }
 
         var endingComment = statement.endToken.next?.precedingComments;
-        var endCommentSource = _joinCommentsSources([
-          if (endingComment case var comment?) comment,
-        ], prefix);
+        var endCommentSource = _joinCommentsSources(
+          [if (endingComment case var comment?) comment],
+          prefix,
+          eol: eol,
+        );
 
-        var beginCommentsSource = _joinCommentsSources([
-          if (beforeIfKeywordComments case var comment?) comment,
-          if (beforeConditionComments case var comment?) comment,
-          if (statement.beginToken.precedingComments case var comment?) comment,
-        ], prefix);
+        var beginCommentsSource = _joinCommentsSources(
+          [
+            if (beforeIfKeywordComments case var comment?) comment,
+            if (beforeConditionComments case var comment?) comment,
+            if (statement.beginToken.precedingComments case var comment?)
+              comment,
+          ],
+          prefix,
+          eol: eol,
+        );
 
         if (statement case Block block) {
           newBlockSource = _blockSource(
@@ -256,6 +265,7 @@
             beginCommentsSource,
             prefix,
             endCommentSource,
+            eol: eol,
           );
         } else {
           var statementSource = utils.getNodeText(statement);
@@ -299,7 +309,11 @@
     return elses;
   }
 
-  String? _joinCommentsSources(List<CommentToken> comments, String prefix) {
+  String? _joinCommentsSources(
+    List<CommentToken> comments,
+    String prefix, {
+    required String eol,
+  }) {
     if (comments.isEmpty) {
       return null;
     }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_inner.dart b/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_inner.dart
index d3b9ddf..0e8373c 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_inner.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_inner.dart
@@ -93,6 +93,7 @@
     var newSource = utils.indentSourceLeftRight(oldSource);
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       builder.addSimpleReplacement(
         range.node(targetIfStatement),
         'if ($condition) {$eol$newSource$prefix}',
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_outer.dart b/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_outer.dart
index 21a8d83..9976fb0 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_outer.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/join_if_with_outer.dart
@@ -99,6 +99,7 @@
     var newSource = utils.indentSourceLeftRight(oldSource);
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       builder.addSimpleReplacement(
         range.node(outerIfStatement),
         'if ($condition) {$eol$newSource$prefix}',
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/move_annotation_to_library_directive.dart b/pkg/analysis_server/lib/src/services/correction/dart/move_annotation_to_library_directive.dart
index fd937cc..2100fe5 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/move_annotation_to_library_directive.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/move_annotation_to_library_directive.dart
@@ -90,28 +90,30 @@
     if (token == annotation.beginToken) {
       // Do not "move" the annotation. Just slip a library directive below it.
       await builder.addDartFileEdit(file, (builder) {
+        var eol = builder.eol;
         builder.addSimpleInsertion(annotationRange.end, 'library;$eol$eol');
       });
       return;
     }
 
-    int insertionOffset;
-    String prefix;
-    Token? commentOnFirstToken = token.precedingComments;
-    if (commentOnFirstToken != null) {
-      while (commentOnFirstToken!.next != null) {
-        commentOnFirstToken = commentOnFirstToken.next!;
-      }
-      // `token` is now the last of the leading comments (perhaps a Copyright
-      // notice, a Dart language version, etc.)
-      insertionOffset = commentOnFirstToken.end;
-      prefix = '$eol$eol';
-    } else {
-      insertionOffset = 0;
-      prefix = '';
-    }
-
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
+      int insertionOffset;
+      String prefix;
+      Token? commentOnFirstToken = token.precedingComments;
+      if (commentOnFirstToken != null) {
+        while (commentOnFirstToken!.next != null) {
+          commentOnFirstToken = commentOnFirstToken.next!;
+        }
+        // `token` is now the last of the leading comments (perhaps a Copyright
+        // notice, a Dart language version, etc.)
+        insertionOffset = commentOnFirstToken.end;
+        prefix = '$eol$eol';
+      } else {
+        insertionOffset = 0;
+        prefix = '';
+      }
+
       builder.addDeletion(annotationRange);
       var annotationText = utils.getRangeText(annotationRange);
       builder.addSimpleInsertion(
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/move_doc_comment_to_library_directive.dart b/pkg/analysis_server/lib/src/services/correction/dart/move_doc_comment_to_library_directive.dart
index 819f6c8..6cd2b92 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/move_doc_comment_to_library_directive.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/move_doc_comment_to_library_directive.dart
@@ -81,13 +81,14 @@
     if (token.precedingComments == comment.beginToken) {
       // Do not "move" the comment. Just slip a library directive below it.
       await builder.addDartFileEdit(file, (builder) {
+        var eol = builder.eol;
         builder.addSimpleInsertion(commentRange.end, 'library;$eol');
       });
       return;
     }
 
     int insertionOffset;
-    String prefix;
+    bool leadingEols = false;
     Token? commentOnFirstToken = token.precedingComments;
     if (commentOnFirstToken != null) {
       while (commentOnFirstToken!.next != null) {
@@ -96,6 +97,7 @@
         if (commentOnFirstToken == comment.beginToken) {
           // Do not "move" the comment. Just slip a library directive below it.
           await builder.addDartFileEdit(file, (builder) {
+            var eol = builder.eol;
             builder.addSimpleInsertion(commentRange.end, 'library;$eol$eol');
           });
           return;
@@ -104,15 +106,16 @@
       // `token` is now the last of the leading comments (perhaps a Copyright
       // notice, a Dart language version, etc.)
       insertionOffset = commentOnFirstToken.end;
-      prefix = '$eol$eol';
+      leadingEols = true;
     } else {
       insertionOffset = 0;
-      prefix = '';
     }
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       builder.addDeletion(commentRange);
       var commentText = utils.getRangeText(commentRange);
+      var prefix = leadingEols ? '$eol$eol' : '';
       builder.addSimpleInsertion(
         insertionOffset,
         '$prefix${commentText}library;$eol$eol',
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/replace_conditional_with_if_else.dart b/pkg/analysis_server/lib/src/services/correction/dart/replace_conditional_with_if_else.dart
index 8c86978..ab04017 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/replace_conditional_with_if_else.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/replace_conditional_with_if_else.dart
@@ -60,6 +60,7 @@
       var prefix = utils.getNodePrefix(statement);
 
       await builder.addDartFileEdit(file, (builder) {
+        var eol = builder.eol;
         var leftSide = assignment.leftHandSide;
         var conditionSrc = utils.getNodeText(conditional.condition);
         var thenSrc = utils.getNodeText(conditional.thenExpression);
@@ -86,6 +87,7 @@
       var prefix = utils.getNodePrefix(statement);
 
       await builder.addDartFileEdit(file, (builder) {
+        var eol = builder.eol;
         var conditionSrc = utils.getNodeText(conditional.condition);
         var thenSrc = utils.getNodeText(conditional.thenExpression);
         var elseSrc = utils.getNodeText(conditional.elseExpression);
@@ -111,6 +113,7 @@
         var prefix = utils.getNodePrefix(statement);
 
         await builder.addDartFileEdit(file, (builder) {
+          var eol = builder.eol;
           var variable = conditional.parent as VariableDeclaration;
           var variableList = variable.parent as VariableDeclarationList;
           if (variableList.type == null) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/split_and_condition.dart b/pkg/analysis_server/lib/src/services/correction/dart/split_and_condition.dart
index 290c0fb..3455d41 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/split_and_condition.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/split_and_condition.dart
@@ -70,6 +70,7 @@
     }
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       // remove "&& rightCondition"
       builder.addDeletion(
         range.endEnd(binaryExpression.leftOperand, condition),
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/split_variable_declaration.dart b/pkg/analysis_server/lib/src/services/correction/dart/split_variable_declaration.dart
index 2555036..1c4ba44 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/split_variable_declaration.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/split_variable_declaration.dart
@@ -59,6 +59,7 @@
     }
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       if (variableList.type == null) {
         var type = variable.declaredElement!.type;
         if (type is! DynamicType && keyword != null) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/surround_with.dart b/pkg/analysis_server/lib/src/services/correction/dart/surround_with.dart
index c9d4822..97b6dbc 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/surround_with.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/surround_with.dart
@@ -172,6 +172,7 @@
   @override
   Future<void> compute(ChangeBuilder builder) async {
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       builder.addSimpleInsertion(statementsRange.offset, '$indentOld{$eol');
       builder.addSimpleReplacement(
         statementsRange,
@@ -208,14 +209,14 @@
       builder.addReplacement(statementsRange, (builder) {
         builder.write(indentOld);
         builder.write('do {');
-        builder.write(eol);
+        builder.writeln();
         builder.write(indentedCode);
         builder.write(indentOld);
         builder.write('} while (');
         builder.addSimpleLinkedEdit('CONDITION', 'condition');
         builder.write(');');
         builder.selectHere();
-        builder.write(eol);
+        builder.writeln();
       });
     });
   }
@@ -249,12 +250,12 @@
         builder.write('; ');
         builder.addSimpleLinkedEdit('INCREMENT', 'increment');
         builder.write(') {');
-        builder.write(eol);
+        builder.writeln();
         builder.write(indentedCode);
         builder.write(indentOld);
         builder.write('}');
         builder.selectHere();
-        builder.write(eol);
+        builder.writeln();
       });
     });
   }
@@ -284,12 +285,12 @@
         builder.write(' in ');
         builder.addSimpleLinkedEdit('ITERABLE', 'iterable');
         builder.write(') {');
-        builder.write(eol);
+        builder.writeln();
         builder.write(indentedCode);
         builder.write(indentOld);
         builder.write('}');
         builder.selectHere();
-        builder.write(eol);
+        builder.writeln();
       });
     });
   }
@@ -317,12 +318,12 @@
         builder.write('if (');
         builder.addSimpleLinkedEdit('CONDITION', 'condition');
         builder.write(') {');
-        builder.write(eol);
+        builder.writeln();
         builder.write(indentedCode);
         builder.write(indentOld);
         builder.write('}');
         builder.selectHere();
-        builder.write(eol);
+        builder.writeln();
       });
     });
   }
@@ -384,7 +385,7 @@
       builder.addReplacement(statementsRange, (builder) {
         builder.write(indentOld);
         builder.write('try {');
-        builder.write(eol);
+        builder.writeln();
         builder.write(indentedCode);
         builder.write(indentOld);
         builder.write('} on ');
@@ -392,16 +393,16 @@
         builder.write(' catch (');
         builder.addSimpleLinkedEdit('EXCEPTION_VAR', 'e');
         builder.write(') {');
-        builder.write(eol);
+        builder.writeln();
         //
         builder.write(indentNew);
         builder.addSimpleLinkedEdit('CATCH', '// TODO');
         builder.selectHere();
-        builder.write(eol);
+        builder.writeln();
         //
         builder.write(indentOld);
         builder.write('}');
-        builder.write(eol);
+        builder.writeln();
       });
     });
   }
@@ -427,22 +428,22 @@
       builder.addReplacement(statementsRange, (builder) {
         builder.write(indentOld);
         builder.write('try {');
-        builder.write(eol);
+        builder.writeln();
         //
         builder.write(indentedCode);
         //
         builder.write(indentOld);
         builder.write('} finally {');
-        builder.write(eol);
+        builder.writeln();
         //
         builder.write(indentNew);
         builder.addSimpleLinkedEdit('FINALLY', '// TODO');
         builder.selectHere();
-        builder.write(eol);
+        builder.writeln();
         //
         builder.write(indentOld);
         builder.write('}');
-        builder.write(eol);
+        builder.writeln();
       });
     });
   }
@@ -470,12 +471,12 @@
         builder.write('while (');
         builder.addSimpleLinkedEdit('CONDITION', 'condition');
         builder.write(') {');
-        builder.write(eol);
+        builder.writeln();
         builder.write(indentedCode);
         builder.write(indentOld);
         builder.write('}');
         builder.selectHere();
-        builder.write(eol);
+        builder.writeln();
       });
     });
   }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/use_curly_braces.dart b/pkg/analysis_server/lib/src/services/correction/dart/use_curly_braces.dart
index 2716666..3e6a309 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/use_curly_braces.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/use_curly_braces.dart
@@ -174,6 +174,7 @@
   ) {
     _replaceLeftParenthesis(builder, left, right, indent);
 
+    var eol = builder.eol;
     builder.addSimpleInsertion(_endAfterComments(right), '$eol$prefix}');
   }
 
@@ -187,6 +188,7 @@
     if (right is AstNode) {
       right = right.beginToken.precedingComments ?? right;
     }
+    var eol = builder.eol;
     builder.addSimpleReplacement(range.endStart(left, right), ' {$eol$indent');
   }
 
@@ -200,6 +202,7 @@
   ) {
     _replaceLeftParenthesis(builder, left, node, indent);
 
+    var eol = builder.eol;
     var end = _endAfterComments(node);
     builder.addSimpleReplacement(
       SourceRange(end, right.offset - end),
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/pubspec/fix_generator.dart b/pkg/analysis_server/lib/src/services/correction/fix/pubspec/fix_generator.dart
index 066dbfe..db6b67f 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/pubspec/fix_generator.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/pubspec/fix_generator.dart
@@ -166,7 +166,7 @@
     }
     var builder = ChangeBuilder(
       workspace: _NonDartChangeWorkspace(resourceProvider),
-      eol: endOfLine,
+      defaultEol: endOfLine,
     );
 
     var data = diagnostic.data as MissingDependencyData;
@@ -307,7 +307,7 @@
     var packageName = _identifierFrom(context.basename(context.dirname(file)));
     var builder = ChangeBuilder(
       workspace: _NonDartChangeWorkspace(resourceProvider),
-      eol: endOfLine,
+      defaultEol: endOfLine,
     );
     var firstOffset = _initialOffset(node);
     if (firstOffset < 0) {
diff --git a/pkg/analysis_server/lib/src/services/refactoring/legacy/extract_widget.dart b/pkg/analysis_server/lib/src/services/refactoring/legacy/extract_widget.dart
index abd1d6d..28c730a 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/legacy/extract_widget.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/legacy/extract_widget.dart
@@ -142,7 +142,7 @@
   Future<SourceChange> createChange() async {
     var builder = ChangeBuilder(
       session: sessionHelper.session,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     await builder.addDartFileEdit(resolveResult.path, (builder) {
       var expression = _expression;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_constructor.dart b/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_constructor.dart
index 05ab544..53a2523 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_constructor.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_constructor.dart
@@ -227,7 +227,6 @@
         constructorName: newName,
         isConst: node is EnumDeclaration,
       ),
-      eol: utils.endOfLine,
     );
     if (edit == null) {
       return;
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/class_declaration.dart b/pkg/analysis_server/lib/src/services/snippets/dart/class_declaration.dart
index 92332e9..5e3ac22 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/class_declaration.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/class_declaration.dart
@@ -20,7 +20,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
 
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/do_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/do_statement.dart
index ed76c90..8afb562 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/do_statement.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/do_statement.dart
@@ -20,7 +20,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
 
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget.dart b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget.dart
index ab48290..131c55c 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget.dart
@@ -30,7 +30,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
 
     // Checked by isValid().
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget_with_animation.dart b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget_with_animation.dart
index 0f26344..be94aec 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget_with_animation.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget_with_animation.dart
@@ -37,7 +37,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
 
     // Checked by isValid().
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateless_widget.dart b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateless_widget.dart
index cea6b08..367d55a 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateless_widget.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateless_widget.dart
@@ -30,7 +30,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
 
     // Checked by isValid().
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/for_in_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/for_in_statement.dart
index 6a8c923..4203051 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/for_in_statement.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/for_in_statement.dart
@@ -20,7 +20,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
     var varOrFinal = codeStyleOptions.makeLocalsFinal ? 'final' : 'var';
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/for_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/for_statement.dart
index 22220a6..330bb4c 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/for_statement.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/for_statement.dart
@@ -20,7 +20,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
 
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/function_declaration.dart b/pkg/analysis_server/lib/src/services/snippets/dart/function_declaration.dart
index a041923..27e75a2 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/function_declaration.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/function_declaration.dart
@@ -20,7 +20,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
 
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/if_else_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/if_else_statement.dart
index 348a09b..6162dbf 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/if_else_statement.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/if_else_statement.dart
@@ -20,7 +20,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
 
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/if_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/if_statement.dart
index c21b69c..f466dbd 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/if_statement.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/if_statement.dart
@@ -20,7 +20,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
 
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/main_function.dart b/pkg/analysis_server/lib/src/services/snippets/dart/main_function.dart
index 36f9150..faf820c 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/main_function.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/main_function.dart
@@ -30,7 +30,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
 
     var typeProvider = request.unit.typeProvider;
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/switch_expression.dart b/pkg/analysis_server/lib/src/services/snippets/dart/switch_expression.dart
index 99a4540..f421784 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/switch_expression.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/switch_expression.dart
@@ -20,7 +20,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
 
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/switch_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/switch_statement.dart
index d50b6a5..0d3f783 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/switch_statement.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/switch_statement.dart
@@ -20,7 +20,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
 
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/test_definition.dart b/pkg/analysis_server/lib/src/services/snippets/dart/test_definition.dart
index 07ae8c5..9d35d9d 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/test_definition.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/test_definition.dart
@@ -23,7 +23,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
 
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/test_group_definition.dart b/pkg/analysis_server/lib/src/services/snippets/dart/test_group_definition.dart
index 1a1ac7e..ff84d9b 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/test_group_definition.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/test_group_definition.dart
@@ -21,7 +21,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
 
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/try_catch_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/try_catch_statement.dart
index 6ab7366..21cd216 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/try_catch_statement.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/try_catch_statement.dart
@@ -20,7 +20,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
 
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/while_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/while_statement.dart
index 6165e4a..5cb4963 100644
--- a/pkg/analysis_server/lib/src/services/snippets/dart/while_statement.dart
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/while_statement.dart
@@ -20,7 +20,7 @@
   Future<Snippet> compute() async {
     var builder = ChangeBuilder(
       session: request.analysisSession,
-      eol: utils.endOfLine,
+      defaultEol: utils.endOfLine,
     );
     var indent = utils.getLinePrefix(request.offset);
 
diff --git a/pkg/analysis_server/lib/src/utilities/change_builder.dart b/pkg/analysis_server/lib/src/utilities/change_builder.dart
index e8c4665..6093a8a 100644
--- a/pkg/analysis_server/lib/src/utilities/change_builder.dart
+++ b/pkg/analysis_server/lib/src/utilities/change_builder.dart
@@ -16,9 +16,8 @@
   void Function(DartEditBuilder builder) buildEdit, {
   required ResolvedUnitResult resolvedUnit,
   required AnalysisSession session,
-  required String eol,
 }) async {
-  var builder = ChangeBuilder(session: session, eol: eol);
+  var builder = ChangeBuilder(session: session);
   await builder.addDartFileEdit(resolvedUnit.path, (builder) {
     builder.insertConstructor(container, buildEdit);
   });
diff --git a/pkg/analysis_server/test/services/refactoring/agnostic/change_method_signature_test.dart b/pkg/analysis_server/test/services/refactoring/agnostic/change_method_signature_test.dart
index 6142bef..925375f 100644
--- a/pkg/analysis_server/test/services/refactoring/agnostic/change_method_signature_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/agnostic/change_method_signature_test.dart
@@ -3243,7 +3243,7 @@
   ) async {
     var builder = ChangeBuilder(
       session: refactoringContext.session,
-      eol: refactoringContext.utils.endOfLine,
+      defaultEol: refactoringContext.utils.endOfLine,
     );
 
     var status = await computeSourceChange(
diff --git a/pkg/analysis_server_plugin/lib/edit/correction_utils.dart b/pkg/analysis_server_plugin/lib/edit/correction_utils.dart
index b146e6b..fd4e8e9 100644
--- a/pkg/analysis_server_plugin/lib/edit/correction_utils.dart
+++ b/pkg/analysis_server_plugin/lib/edit/correction_utils.dart
@@ -4,6 +4,7 @@
 
 import 'dart:io';
 
+import 'package:analysis_server_plugin/src/utilities/extensions/string_extension.dart';
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/ast/token.dart';
@@ -35,22 +36,7 @@
 
   /// The EOL sequence to use for this [CompilationUnit].
   String get endOfLine {
-    var endOfLine = _endOfLine;
-    if (endOfLine != null) {
-      return endOfLine;
-    }
-
-    var indexOfNewline = _buffer.indexOf('\n');
-    if (indexOfNewline < 0) {
-      // No `\n` (and thus no `\r\n` either) found.
-      return Platform.lineTerminator;
-    }
-
-    if (indexOfNewline > 0 &&
-        _buffer.codeUnitAt(indexOfNewline - 1) == 13 /* \r */) {
-      return _endOfLine = '\r\n';
-    }
-    return _endOfLine = '\n';
+    return _endOfLine ??= _buffer.endOfLine ?? Platform.lineTerminator;
   }
 
   String get oneIndent => _oneIndent;
diff --git a/pkg/analysis_server_plugin/lib/edit/dart/correction_producer.dart b/pkg/analysis_server_plugin/lib/edit/dart/correction_producer.dart
index ae1bdfa..15bb7ac 100644
--- a/pkg/analysis_server_plugin/lib/edit/dart/correction_producer.dart
+++ b/pkg/analysis_server_plugin/lib/edit/dart/correction_producer.dart
@@ -728,13 +728,16 @@
   /// Whether the fixes are being built for the bulk-fix request.
   bool get applyingBulkFixes => _context._applyingBulkFixes;
 
+  /// The default EOL to be used for new files and files that do not have EOLs.
+  ///
+  /// Existing files with EOL markers will always have the same EOL in inserted
+  /// text.
+  String get defaultEol => utils.endOfLine;
+
   /// The diagnostic being fixed, or `null` if this producer is being
   /// used to produce an assist.
   Diagnostic? get diagnostic => _context._diagnostic;
 
-  /// The EOL sequence to use for this [CompilationUnit].
-  String get eol => utils.endOfLine;
-
   String get file => _context.path;
 
   ParsedLibraryResult get libraryResult => _context._libraryResult;
diff --git a/pkg/analysis_server_plugin/lib/src/correction/assist_processor.dart b/pkg/analysis_server_plugin/lib/src/correction/assist_processor.dart
index 61d4191..3703fea 100644
--- a/pkg/analysis_server_plugin/lib/src/correction/assist_processor.dart
+++ b/pkg/analysis_server_plugin/lib/src/correction/assist_processor.dart
@@ -49,7 +49,7 @@
 
     var builder = ChangeBuilder(
       workspace: _assistContext.workspace,
-      eol: producer.eol,
+      defaultEol: producer.defaultEol,
     );
     try {
       if (_performance != null) {
diff --git a/pkg/analysis_server_plugin/lib/src/correction/fix_in_file_processor.dart b/pkg/analysis_server_plugin/lib/src/correction/fix_in_file_processor.dart
index 0fff206..de0065b 100644
--- a/pkg/analysis_server_plugin/lib/src/correction/fix_in_file_processor.dart
+++ b/pkg/analysis_server_plugin/lib/src/correction/fix_in_file_processor.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:analysis_server_plugin/edit/correction_utils.dart';
 import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
 import 'package:analysis_server_plugin/edit/fix/dart_fix_context.dart';
 import 'package:analysis_server_plugin/edit/fix/fix.dart';
@@ -57,8 +58,9 @@
     for (var generator in generators) {
       if (generator(context: StubCorrectionProducerContext.instance)
           .canBeAppliedAcrossSingleFile) {
-        _FixState fixState =
-            _EmptyFixState(ChangeBuilder(workspace: _fixContext.workspace));
+        _FixState fixState = _EmptyFixState(ChangeBuilder(
+            workspace: _fixContext.workspace,
+            defaultEol: CorrectionUtils(_fixContext.unitResult).endOfLine));
 
         // First, try to fix the specific error we started from. We should only
         // include fix-all-in-file when we produce an individual fix at this
diff --git a/pkg/analysis_server_plugin/lib/src/correction/fix_processor.dart b/pkg/analysis_server_plugin/lib/src/correction/fix_processor.dart
index 9fe82b6..6e38e52 100644
--- a/pkg/analysis_server_plugin/lib/src/correction/fix_processor.dart
+++ b/pkg/analysis_server_plugin/lib/src/correction/fix_processor.dart
@@ -71,8 +71,8 @@
       return;
     }
 
-    var builder =
-        ChangeBuilder(workspace: _fixContext.workspace, eol: producer.eol);
+    var builder = ChangeBuilder(
+        workspace: _fixContext.workspace, defaultEol: producer.defaultEol);
     try {
       var fixKind = producer.fixKind;
 
diff --git a/pkg/analysis_server_plugin/lib/src/correction/ignore_diagnostic.dart b/pkg/analysis_server_plugin/lib/src/correction/ignore_diagnostic.dart
index 1600738..8e3eef5 100644
--- a/pkg/analysis_server_plugin/lib/src/correction/ignore_diagnostic.dart
+++ b/pkg/analysis_server_plugin/lib/src/correction/ignore_diagnostic.dart
@@ -134,6 +134,7 @@
     if (_isCodeUnignorable) return;
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       var source = unitResult.content;
 
       // Look for the last blank line in any leading comments (to insert after
@@ -200,6 +201,7 @@
     if (_isCodeUnignorable) return;
 
     await builder.addDartFileEdit(file, (builder) {
+      var eol = builder.eol;
       var offset = diagnostic.problemMessage.offset;
       var lineNumber = unitResult.lineInfo.getLocation(offset).lineNumber - 1;
 
@@ -282,6 +284,7 @@
     bool insertEmptyLineBefore = false,
     bool insertEmptyLineAfter = false,
   }) {
+    var eol = builder.eol;
     var prefix = insertEmptyLineBefore ? eol : '';
     var indent = unitResult.linePrefix(offset);
     var comment = '// $commentPrefix: $_code';
diff --git a/pkg/analysis_server_plugin/lib/src/utilities/extensions/string_extension.dart b/pkg/analysis_server_plugin/lib/src/utilities/extensions/string_extension.dart
new file mode 100644
index 0000000..dc5a952
--- /dev/null
+++ b/pkg/analysis_server_plugin/lib/src/utilities/extensions/string_extension.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+extension StringExtension on String {
+  // This extension is duplicated in analyzer_plugin.
+
+  /// Computes the line ending sequence used in this string.
+  ///
+  /// Returns the first EOL sequence (`\r\n` or `\n`) found in the content.
+  /// If the content contains no EOL sequences, returns null.
+  String? get endOfLine {
+    var indexOfNewline = indexOf('\n');
+    if (indexOfNewline < 0) {
+      // No `\n` (and thus no `\r\n` either) found.
+      return null;
+    }
+
+    if (indexOfNewline > 0 && codeUnitAt(indexOfNewline - 1) == 13 /* \r */) {
+      return '\r\n';
+    }
+    return '\n';
+  }
+}
diff --git a/pkg/analyzer_plugin/api.txt b/pkg/analyzer_plugin/api.txt
index e85dac1..723b419 100644
--- a/pkg/analyzer_plugin/api.txt
+++ b/pkg/analyzer_plugin/api.txt
@@ -1628,7 +1628,8 @@
     addAssist (method: void Function(AssistKind, ChangeBuilder, {List<Object>? args}))
 package:analyzer_plugin/utilities/change_builder/change_builder_core.dart:
   ChangeBuilder (class extends Object):
-    new (constructor: ChangeBuilder Function({String eol, AnalysisSession session, ChangeWorkspace workspace}))
+    new (constructor: ChangeBuilder Function({String? defaultEol, String? eol, AnalysisSession session, ChangeWorkspace workspace}))
+    defaultEol (getter: String)
     selectionRange (getter: SourceRange?)
     sourceChange (getter: SourceChange)
     addDartFileEdit (method: Future<void> Function(String, FutureOr<void> Function(DartFileEditBuilder), {bool createEditsForImports, String Function(Uri) importPrefixGenerator}))
@@ -1647,6 +1648,7 @@
     writeln (method: void Function([String]))
   FileEditBuilder (class extends Object):
     new (constructor: FileEditBuilder Function())
+    eol (getter: String)
     addDeletion (method: void Function(SourceRange))
     addInsertion (method: void Function(int, void Function(EditBuilder)))
     addLinkedPosition (method: void Function(SourceRange, String))
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
index 36b8527..6eda805 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 import 'dart:collection';
+import 'dart:io';
 
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
@@ -14,6 +15,7 @@
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_dart.dart';
 import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_yaml.dart';
+import 'package:analyzer_plugin/src/utilities/extensions/string_extension.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_yaml.dart';
@@ -25,10 +27,6 @@
   /// The workspace in which the change builder should operate.
   final ChangeWorkspace workspace;
 
-  /// The end-of-line marker used in the file being edited, or `null` if the
-  /// default marker should be used.
-  final String? eol;
-
   /// A table mapping group ids to the associated linked edit groups.
   final Map<String, LinkedEditGroup> _linkedEditGroups =
       <String, LinkedEditGroup>{};
@@ -40,6 +38,13 @@
   /// is no selection.
   SourceRange? _selectionRange;
 
+  /// The default EOL to be used for new files and files that do not have EOLs.
+  ///
+  /// Existing files with EOL markers will always have the same EOL in inserted
+  /// text.
+  @override
+  final String defaultEol;
+
   /// A description to be applied to the [SourceEdit]s being built.
   ///
   /// This is usually set temporarily to mark a whole set of fixes with a
@@ -79,9 +84,16 @@
   /// create changes for Dart files, then either a [session] or a [workspace]
   /// must be provided (but not both).
   ChangeBuilderImpl(
-      {AnalysisSession? session, ChangeWorkspace? workspace, this.eol})
+      {AnalysisSession? session,
+      ChangeWorkspace? workspace,
+      @Deprecated('Use defaultEol instead, as this is only a '
+          'default for files without existing EOLs')
+      String? eol,
+      String? defaultEol})
       : assert(session == null || workspace == null),
-        workspace = workspace ?? _SingleSessionWorkspace(session!);
+        assert(eol == null || defaultEol == null),
+        workspace = workspace ?? _SingleSessionWorkspace(session!),
+        defaultEol = defaultEol ?? eol ?? Platform.lineTerminator;
 
   /// Return `true` if this builder has edits to be applied.
   bool get hasEdits {
@@ -193,7 +205,8 @@
     }
     var builder = _genericFileEditBuilders[path];
     if (builder == null) {
-      builder = FileEditBuilderImpl(this, path, 0);
+      var eol = _getLineEnding(path);
+      builder = FileEditBuilderImpl(this, path, 0, eol: eol);
       _genericFileEditBuilders[path] = builder;
       _revertData._addedGenericFileEditBuilders.add(path);
     }
@@ -215,13 +228,18 @@
     }
     var builder = _yamlFileEditBuilders[path];
     if (builder == null) {
+      String content;
+      try {
+        // TODO(dantup): Can this use FileContentCache?
+        content = workspace.resourceProvider.getFile(path).readAsStringSync();
+      } catch (_) {
+        content = '';
+      }
+      var eol = content.endOfLine ?? defaultEol;
+
       builder = YamlFileEditBuilderImpl(
-          this,
-          path,
-          loadYamlDocument(
-              workspace.resourceProvider.getFile(path).readAsStringSync(),
-              recover: true),
-          0);
+          this, path, loadYamlDocument(content, recover: true), 0,
+          eol: eol);
       _yamlFileEditBuilders[path] = builder;
       _revertData._addedYamlFileEditBuilders.add(path);
     }
@@ -262,7 +280,7 @@
       'method now use `commit` and `revert` instead.')
   @override
   ChangeBuilder copy() {
-    var copy = ChangeBuilderImpl(workspace: workspace, eol: eol);
+    var copy = ChangeBuilderImpl(workspace: workspace, defaultEol: defaultEol);
     for (var entry in _linkedEditGroups.entries) {
       copy._linkedEditGroups[entry.key] = _copyLinkedEditGroup(entry.value);
     }
@@ -427,8 +445,22 @@
       }, createEditsForImports: createEditsForImports);
     }
 
+    var eol = result.content.endOfLine ?? defaultEol;
     return DartFileEditBuilderImpl(this, result, timeStamp, libraryEditBuilder,
-        createEditsForImports: createEditsForImports);
+        createEditsForImports: createEditsForImports, eol: eol);
+  }
+
+  /// Reads the EOL used in [filePath], defaulting to [Platform.lineTerminator] if
+  /// there was no line ending or the file cannot be read.
+  String _getLineEnding(String filePath) {
+    String? eol;
+    try {
+      // TODO(dantup): Can this use FileContentCache?
+      var content =
+          workspace.resourceProvider.getFile(filePath).readAsStringSync();
+      eol = content.endOfLine;
+    } catch (_) {}
+    return eol ?? defaultEol;
   }
 
   void _setSelectionRange(SourceRange range) {
@@ -488,10 +520,6 @@
   /// selection is not inside the change being built.
   SourceRange? _selectionRange;
 
-  /// The end-of-line marker used in the file being edited, or `null` if the
-  /// default marker should be used.
-  final String? _eol;
-
   /// The buffer in which the content of the edit is being composed.
   final StringBuffer _buffer = StringBuffer();
 
@@ -503,8 +531,10 @@
 
   /// Initialize a newly created builder to build a source edit.
   EditBuilderImpl(this.fileEditBuilder, this.offset, this.length,
-      {this.description})
-      : _eol = fileEditBuilder.changeBuilder.eol;
+      {this.description});
+
+  /// The end-of-line marker used in the file being edited.
+  String get eol => fileEditBuilder.eol;
 
   /// Create and return an edit representing the replacement of a region of the
   /// file with the accumulated text.
@@ -590,11 +620,7 @@
     if (string != null) {
       _buffer.write(string);
     }
-    if (_eol == null) {
-      _buffer.writeln();
-    } else {
-      _buffer.write(_eol);
-    }
+    _buffer.write(eol);
   }
 }
 
@@ -607,6 +633,10 @@
   /// The source file edit that is being built.
   final SourceFileEdit fileEdit;
 
+  /// The end of line marker being used by this file.
+  @override
+  final String eol;
+
   /// A description to be applied to the changes being built.
   ///
   /// This is usually set temporarily to mark a whole set of fixes with a
@@ -618,7 +648,8 @@
   /// Initialize a newly created builder to build a source file edit within the
   /// change being built by the given [changeBuilder]. The file being edited has
   /// the given absolute [path] and [timeStamp].
-  FileEditBuilderImpl(this.changeBuilder, String path, int timeStamp)
+  FileEditBuilderImpl(this.changeBuilder, String path, int timeStamp,
+      {required this.eol})
       : fileEdit = SourceFileEdit(path, timeStamp);
 
   /// Return `true` if this builder has edits to be applied.
@@ -696,8 +727,9 @@
   @Deprecated('Copying change builders is expensive. Internal users of this '
       'method now use `commit` and `revert` instead.')
   FileEditBuilderImpl copyWith(ChangeBuilderImpl changeBuilder) {
-    var copy =
-        FileEditBuilderImpl(changeBuilder, fileEdit.file, fileEdit.fileStamp);
+    var copy = FileEditBuilderImpl(
+        changeBuilder, fileEdit.file, fileEdit.fileStamp,
+        eol: eol);
     copy.fileEdit.edits.addAll(fileEdit.edits);
     copy.currentChangeDescription = currentChangeDescription;
     return copy;
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index c1f691a..3b4c01f 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -1465,9 +1465,9 @@
   /// The file being edited has the given [resolvedUnit] and [timeStamp].
   DartFileEditBuilderImpl(ChangeBuilderImpl changeBuilder, this.resolvedUnit,
       int timeStamp, this.libraryChangeBuilder,
-      {bool createEditsForImports = true})
+      {required String eol, bool createEditsForImports = true})
       : _createEditsForImports = createEditsForImports,
-        super(changeBuilder, resolvedUnit.path, timeStamp);
+        super(changeBuilder, resolvedUnit.path, timeStamp, eol: eol);
 
   @override
   bool get hasEdits =>
@@ -1567,7 +1567,7 @@
           const {}}) {
     var copy = DartFileEditBuilderImpl(changeBuilder, resolvedUnit,
         fileEdit.fileStamp, editBuilderMap[libraryChangeBuilder],
-        createEditsForImports: _createEditsForImports);
+        eol: eol, createEditsForImports: _createEditsForImports);
     copy.fileEdit.edits.addAll(fileEdit.edits);
     copy.importPrefixGenerator = importPrefixGenerator;
     for (var entry in _librariesToImport.entries) {
@@ -1614,8 +1614,7 @@
       }
     }
 
-    var languageVersion =
-        resolvedUnit.libraryElement.languageVersion.effective;
+    var languageVersion = resolvedUnit.libraryElement.languageVersion.effective;
     var formattedResult =
         DartFormatter(languageVersion: languageVersion).formatSource(
       SourceCode(
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_yaml.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_yaml.dart
index e29b8d7..e8a823d 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_yaml.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_yaml.dart
@@ -43,8 +43,9 @@
   /// change being built by the given [changeBuilder]. The file being edited has
   /// the given [filePath], [document], and [timeStamp].
   YamlFileEditBuilderImpl(ChangeBuilderImpl changeBuilder, String filePath,
-      this.document, int timeStamp)
-      : super(changeBuilder, filePath, timeStamp);
+      this.document, int timeStamp,
+      {required String eol})
+      : super(changeBuilder, filePath, timeStamp, eol: eol);
 
   @override
   void addInsertion(
@@ -67,7 +68,8 @@
       {Map<YamlFileEditBuilderImpl, YamlFileEditBuilderImpl> editBuilderMap =
           const {}}) {
     var copy = YamlFileEditBuilderImpl(
-        changeBuilder, fileEdit.file, document, fileEdit.fileStamp);
+        changeBuilder, fileEdit.file, document, fileEdit.fileStamp,
+        eol: eol);
     copy.fileEdit.edits.addAll(fileEdit.edits);
     return copy;
   }
diff --git a/pkg/analyzer_plugin/lib/src/utilities/extensions/string_extension.dart b/pkg/analyzer_plugin/lib/src/utilities/extensions/string_extension.dart
new file mode 100644
index 0000000..379ec04
--- /dev/null
+++ b/pkg/analyzer_plugin/lib/src/utilities/extensions/string_extension.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+extension StringExtension on String {
+  // This extension is duplicated in analysis_server_plugin.
+
+  /// Computes the line ending sequence used in this string.
+  ///
+  /// Returns the first EOL sequence (`\r\n` or `\n`) found in the content.
+  /// If the content contains no EOL sequences, returns null.
+  String? get endOfLine {
+    var indexOfNewline = indexOf('\n');
+    if (indexOfNewline < 0) {
+      // No `\n` (and thus no `\r\n` either) found.
+      return null;
+    }
+
+    if (indexOfNewline > 0 && codeUnitAt(indexOfNewline - 1) == 13 /* \r */) {
+      return '\r\n';
+    }
+    return '\n';
+  }
+}
diff --git a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
index f142725..b752698 100644
--- a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
@@ -21,15 +21,22 @@
   /// If the builder is used to create changes for Dart files, then either a
   /// [session] or a [workspace] must be provided (but not both).
   ///
-  /// Note that omitting [eol], the EOL sequence to use, can result in
-  /// inconsistent EOL sequences being added to files.
-  // TODO(srawlins): Should `eol` be required? Each `DartFileEditBuilderImpl`
-  //  has a `ResolvedUnitResult` that can be relied on to detect the existing
-  //  EOL sequences, so could `eol` be removed? (dantup: but non-Dart builders?)
+  /// [defaultEol] is the default EOL to be used for new files and files that do
+  /// not have EOLs. Existing files with EOL markers will always have the same
+  /// EOL in inserted text. If not specified, Platform.lineTerminator will be
+  /// used.
   factory ChangeBuilder(
       {AnalysisSession session,
       ChangeWorkspace workspace,
-      String eol}) = ChangeBuilderImpl;
+      @Deprecated('Use defaultEol instead, as this is only a '
+          'default for files without existing EOLs')
+      String? eol,
+      String? defaultEol}) = ChangeBuilderImpl;
+
+  /// The default EOL to be used for new files and files that do not have EOLs.
+  /// Existing files with EOL markers will always have the same EOL in inserted
+  /// text. If not specified, Platform.lineTerminator will be used.
+  String get defaultEol;
 
   /// Return the range of the selection for the change being built, or `null` if
   /// there is no selection.
@@ -125,6 +132,9 @@
 ///
 /// Clients may not extend, implement or mix-in this class.
 abstract class FileEditBuilder {
+  /// The end of line marker used for this file.
+  String get eol;
+
   /// Add a deletion of text specified by the given [range]. The [range] is
   /// relative to the original source. This is fully equivalent to
   ///
diff --git a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
index d8bb470..c938659 100644
--- a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
@@ -55,7 +55,7 @@
     var copy = builder.copy() as ChangeBuilderImpl;
     expect(identical(copy, builder), isFalse);
     expect(copy.workspace, builder.workspace);
-    expect(copy.eol, builder.eol);
+    expect(copy.defaultEol, builder.defaultEol);
   }
 
   @Deprecated('Will be removed when `copy` is removed.')
diff --git a/pkg/analyzer_plugin/test/src/utilities/change_builder/dart/dart_change_builder_mixin.dart b/pkg/analyzer_plugin/test/src/utilities/change_builder/dart/dart_change_builder_mixin.dart
index 1579679..27e003b 100644
--- a/pkg/analyzer_plugin/test/src/utilities/change_builder/dart/dart_change_builder_mixin.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/change_builder/dart/dart_change_builder_mixin.dart
@@ -31,6 +31,8 @@
 
   /// Return a newly created Dart change builder.
   Future<ChangeBuilderImpl> newBuilder() async {
-    return ChangeBuilder(session: await session) as ChangeBuilderImpl;
+    // TODO(dantup): Currently these tests all assume \n and need updating.
+    return ChangeBuilder(session: await session, defaultEol: '\n')
+        as ChangeBuilderImpl;
   }
 }