Fix string shortening (#3512)

diff --git a/lib/src/log.dart b/lib/src/log.dart
index d8150c0..f845aa6 100644
--- a/lib/src/log.dart
+++ b/lib/src/log.dart
@@ -8,6 +8,7 @@
 import 'dart:io';
 
 import 'package:args/command_runner.dart';
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' as p;
 import 'package:source_span/source_span.dart';
 import 'package:stack_trace/stack_trace.dart';
@@ -336,12 +337,20 @@
   stderr.writeln('---- End log transcript ----');
 }
 
-String _limit(String input, int limit) {
+/// Shortens [input] to at most [limit] characters by omitting the middle part
+/// replacing it with '[...]' if it is too long.
+///
+/// [limit] must be more than 5.
+@visibleForTesting
+String limitLength(String input, int limit) {
   const snip = '[...]';
-  if (input.length < limit - snip.length) return input;
-  return '${input.substring(0, limit ~/ 2 - snip.length)}'
+  assert(limit > snip.length);
+  if (input.length <= limit) return input;
+  final half = (limit - snip.length) ~/ 2;
+  final extra = (limit - snip.length).isOdd ? 1 : 0;
+  return '${input.substring(0, half + extra)}'
       '$snip'
-      '${input.substring(limit)}';
+      '${input.substring(input.length - half)}';
 }
 
 /// Prints relevant system information and the log transcript to [path].
@@ -350,7 +359,7 @@
   buffer.writeln('''
 Information about the latest pub run.
 
-If you believe something is not working right, you can go to 
+If you believe something is not working right, you can go to
 https://github.com/dart-lang/pub/issues/new to post a new issue and attach this file.
 
 Before making this file public, make sure to remove any sensitive information!
@@ -367,14 +376,14 @@
   if (entrypoint != null) {
     buffer.writeln('---- ${p.absolute(entrypoint.pubspecPath)} ----');
     if (fileExists(entrypoint.pubspecPath)) {
-      buffer.writeln(_limit(readTextFile(entrypoint.pubspecPath), 5000));
+      buffer.writeln(limitLength(readTextFile(entrypoint.pubspecPath), 5000));
     } else {
       buffer.writeln('<No pubspec.yaml>');
     }
     buffer.writeln('---- End pubspec.yaml ----');
     buffer.writeln('---- ${p.absolute(entrypoint.lockFilePath)} ----');
     if (fileExists(entrypoint.lockFilePath)) {
-      buffer.writeln(_limit(readTextFile(entrypoint.lockFilePath), 5000));
+      buffer.writeln(limitLength(readTextFile(entrypoint.lockFilePath), 5000));
     } else {
       buffer.writeln('<No pubspec.lock>');
     }
diff --git a/test/log_test.dart b/test/log_test.dart
new file mode 100644
index 0000000..7c0e3b3
--- /dev/null
+++ b/test/log_test.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2022, 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.
+
+import 'package:pub/src/log.dart';
+import 'package:test/test.dart';
+
+void main() {
+  test('limitLength', () {
+    expect(limitLength('', 7), '');
+    expect(limitLength('x', 7), 'x');
+    expect(limitLength('x' * 7, 7), 'x' * 7);
+    expect(limitLength('x' * 8, 7), 'x[...]x');
+    expect(limitLength('x' * 1000, 7), 'x[...]x');
+    expect(limitLength('', 8), '');
+    expect(limitLength('x', 8), 'x');
+    expect(limitLength('x' * 8, 8), 'x' * 8);
+    expect(limitLength('x' * 9, 8), 'xx[...]x');
+    expect(limitLength('x' * 1000, 8), 'xx[...]x');
+  });
+}
diff --git a/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt b/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt
index 2d9306e..14ea76f 100644
--- a/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt
+++ b/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt
@@ -109,7 +109,7 @@
 
 Information about the latest pub run.
 
-If you believe something is not working right, you can go to 
+If you believe something is not working right, you can go to
 https://github.com/dart-lang/pub/issues/new to post a new issue and attach this file.
 
 Before making this file public, make sure to remove any sensitive information!
@@ -269,7 +269,7 @@
 
 Information about the latest pub run.
 
-If you believe something is not working right, you can go to 
+If you believe something is not working right, you can go to
 https://github.com/dart-lang/pub/issues/new to post a new issue and attach this file.
 
 Before making this file public, make sure to remove any sensitive information!