Strip file paths from analyzer messages (#664)

diff --git a/lib/src/analysis_server.dart b/lib/src/analysis_server.dart
index 6570eec..c747173 100644
--- a/lib/src/analysis_server.dart
+++ b/lib/src/analysis_server.dart
@@ -19,6 +19,7 @@
 import 'pub.dart';
 import 'scheduler.dart';
 import 'sdk_manager.dart';
+import 'utils.dart' as utils;
 
 final Logger _logger = Logger('analysis_server');
 
@@ -324,20 +325,27 @@
         final issue = proto.AnalysisIssue()
           ..kind = error.severity.toLowerCase()
           ..line = error.location.startLine
-          ..message = error.message
+          ..message = utils.stripFilePaths(error.message)
           ..sourceName = path.basename(error.location.file)
           ..hasFixes = error.hasFix
           ..charStart = error.location.offset
           ..charLength = error.location.length
           ..diagnosticMessages.addAll(error.contextMessages?.map((m) =>
                   proto.DiagnosticMessage(
-                      message: m.message,
+                      message: utils.stripFilePaths(m.message),
                       line: m.location.startLine,
                       charStart: m.location.offset,
                       charLength: m.location.length)) ??
               []);
-        if (error.url != null) issue.url = error.url;
-        if (error.correction != null) issue.correction = error.correction;
+
+        if (error.url != null) {
+          issue.url = error.url;
+        }
+
+        if (error.correction != null) {
+          issue.correction = utils.stripFilePaths(error.correction);
+        }
+
         return issue;
       }).toList();
 
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
new file mode 100644
index 0000000..cfcdd80
--- /dev/null
+++ b/lib/src/utils.dart
@@ -0,0 +1,25 @@
+import 'package:path/path.dart' as path;
+
+String stripFilePaths(String s) {
+  return s.replaceAllMapped(RegExp(r'(?:package:?)?[a-z]*\/\S*'), (match) {
+    final urlString = match.group(0);
+    final pathComponents = path.split(urlString);
+    final isDartPath = pathComponents.contains('lib');
+    final isFlutterPath = isDartPath && pathComponents.contains('flutter');
+    final isPackagePath = urlString.contains('package:');
+    final basename = path.basename(urlString);
+
+    if (isFlutterPath) {
+      return path.join('package:flutter', basename);
+    }
+
+    if (isDartPath) {
+      return path.join('dart:core', basename);
+    }
+
+    if (isPackagePath) {
+      return urlString;
+    }
+    return basename;
+  });
+}
diff --git a/pubspec.lock b/pubspec.lock
index 49961d8..98f4ffc 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -204,6 +204,13 @@
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.5.0"
+  expected_output:
+    dependency: "direct dev"
+    description:
+      name: expected_output
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.2.2"
   file:
     dependency: transitive
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 7c50916..18869a9 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -27,6 +27,7 @@
   build_runner: ^1.10.6
   codemirror: ^0.5.6
   coverage: ^0.14.0
+  expected_output: ^1.2.0
   grinder: ^0.8.0
   mock_request: ^1.0.7
   pedantic: ^1.9.0
diff --git a/test/utils_test.dart b/test/utils_test.dart
new file mode 100644
index 0000000..c537c93
--- /dev/null
+++ b/test/utils_test.dart
@@ -0,0 +1,16 @@
+library utils.tests;
+
+import 'package:dart_services/src/utils.dart';
+import 'package:expected_output/expected_output.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('utils.stripFilePaths', () {
+    for (final dataCase in dataCasesUnder(library: #utils.tests)) {
+      test(dataCase.testDescription, () {
+        final actualOutput = stripFilePaths(dataCase.input);
+        expect(actualOutput, equals(dataCase.expectedOutput));
+      });
+    }
+  });
+}
diff --git a/test/utils_test.unit b/test/utils_test.unit
new file mode 100644
index 0000000..9c6ad3f
--- /dev/null
+++ b/test/utils_test.unit
@@ -0,0 +1,20 @@
+>>> Strips temporary directory paths
+List is defined in /var/folders/4p/y54w9nqj0_n6ryqwn7lxqz6800m6cw/T/DartAnalysisWrapperintLAw/main.dart
+<<<
+List is defined in main.dart
+>>> Strips Dart SDK paths
+List is defined in /Users/ryjohn/.dvm/darts/2.10.5/lib/core/list.dart
+<<<
+List is defined in dart:core/list.dart
+>>> Strips SDK and temporary directory paths
+The argument type 'List<int> (where List is defined in /Users/username/sdk/dart/2.10.5/lib/core/list.dart)' can't be assigned to the parameter type 'List<int> (where List is defined in /var/folders/4p/tmp/T/DartAnalysisWrapperintLAw/main.dart)'.
+<<<
+The argument type 'List<int> (where List is defined in dart:core/list.dart)' can't be assigned to the parameter type 'List<int> (where List is defined in main.dart)'.
+>>> Keeps package: paths intact
+Unused import: 'package:flutter/material.dart'.
+<<<
+Unused import: 'package:flutter/material.dart'.
+>>> Strips Flutter SDK paths
+The argument type 'StatelessWidget (where StatelessWidget is defined in /Users/username/path/to/dart-services/project_templates/flutter_project/main.dart)' can't be assigned to the parameter type 'StatelessWidget (where StatelessWidget is defined in /Users/username/path/to/dart-services/flutter-sdk/packages/flutter/lib/src/widgets/framework.dart)'.
+<<<
+The argument type 'StatelessWidget (where StatelessWidget is defined in main.dart)' can't be assigned to the parameter type 'StatelessWidget (where StatelessWidget is defined in package:flutter/framework.dart)'.