Version 2.12.0-88.0.dev

Merge commit '2076989667f4f93ec40f38dd44c2589a143795fc' into 'dev'
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index 76a9bda..3af28cb 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -3623,7 +3623,10 @@
         <p>
           An overriding implementation of a class member is being suggested.
         </p>
-      </dd><dt class="value">PARAMETER</dt></dl></dd><dt class="typeDefinition"><a name="type_ContextData">ContextData: object</a></dt><dd>
+      </dd><dt class="value">PARAMETER</dt><dt class="value">PACKAGE_NAME</dt><dd>
+        
+        <p>The name of a pub package is being suggested.</p>
+      </dd></dl></dd><dt class="typeDefinition"><a name="type_ContextData">ContextData: object</a></dt><dd>
     <p>
       Information about an analysis context.
     </p>
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
index 5c97433..16daa2d 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
@@ -192,66 +192,30 @@
       CompletionItemResolutionInfo.canParse,
       CompletionItemResolutionInfo.fromJson);
 
-  CompletionItemResolutionInfo(
-      {@required this.file,
-      @required this.offset,
-      @required this.libId,
-      @required this.displayUri,
-      @required this.rOffset,
-      @required this.rLength}) {
+  CompletionItemResolutionInfo({@required this.file, @required this.offset}) {
     if (file == null) {
       throw 'file is required but was not provided';
     }
     if (offset == null) {
       throw 'offset is required but was not provided';
     }
-    if (libId == null) {
-      throw 'libId is required but was not provided';
-    }
-    if (displayUri == null) {
-      throw 'displayUri is required but was not provided';
-    }
-    if (rOffset == null) {
-      throw 'rOffset is required but was not provided';
-    }
-    if (rLength == null) {
-      throw 'rLength is required but was not provided';
-    }
   }
   static CompletionItemResolutionInfo fromJson(Map<String, dynamic> json) {
+    if (DartCompletionItemResolutionInfo.canParse(json, nullLspJsonReporter)) {
+      return DartCompletionItemResolutionInfo.fromJson(json);
+    }
     final file = json['file'];
     final offset = json['offset'];
-    final libId = json['libId'];
-    final displayUri = json['displayUri'];
-    final rOffset = json['rOffset'];
-    final rLength = json['rLength'];
-    return CompletionItemResolutionInfo(
-        file: file,
-        offset: offset,
-        libId: libId,
-        displayUri: displayUri,
-        rOffset: rOffset,
-        rLength: rLength);
+    return CompletionItemResolutionInfo(file: file, offset: offset);
   }
 
-  final String displayUri;
   final String file;
-  final num libId;
   final num offset;
-  final num rLength;
-  final num rOffset;
 
   Map<String, dynamic> toJson() {
     var __result = <String, dynamic>{};
     __result['file'] = file ?? (throw 'file is required but was not set');
     __result['offset'] = offset ?? (throw 'offset is required but was not set');
-    __result['libId'] = libId ?? (throw 'libId is required but was not set');
-    __result['displayUri'] =
-        displayUri ?? (throw 'displayUri is required but was not set');
-    __result['rOffset'] =
-        rOffset ?? (throw 'rOffset is required but was not set');
-    __result['rLength'] =
-        rLength ?? (throw 'rLength is required but was not set');
     return __result;
   }
 
@@ -291,6 +255,105 @@
       } finally {
         reporter.pop();
       }
+      return true;
+    } else {
+      reporter.reportError('must be of type CompletionItemResolutionInfo');
+      return false;
+    }
+  }
+
+  @override
+  bool operator ==(Object other) {
+    if (other is CompletionItemResolutionInfo &&
+        other.runtimeType == CompletionItemResolutionInfo) {
+      return file == other.file && offset == other.offset && true;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    var hash = 0;
+    hash = JenkinsSmiHash.combine(hash, file.hashCode);
+    hash = JenkinsSmiHash.combine(hash, offset.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+
+  @override
+  String toString() => jsonEncoder.convert(toJson());
+}
+
+class DartCompletionItemResolutionInfo
+    implements CompletionItemResolutionInfo, ToJsonable {
+  static const jsonHandler = LspJsonHandler(
+      DartCompletionItemResolutionInfo.canParse,
+      DartCompletionItemResolutionInfo.fromJson);
+
+  DartCompletionItemResolutionInfo(
+      {@required this.libId,
+      @required this.displayUri,
+      @required this.rOffset,
+      @required this.rLength,
+      @required this.file,
+      @required this.offset}) {
+    if (libId == null) {
+      throw 'libId is required but was not provided';
+    }
+    if (displayUri == null) {
+      throw 'displayUri is required but was not provided';
+    }
+    if (rOffset == null) {
+      throw 'rOffset is required but was not provided';
+    }
+    if (rLength == null) {
+      throw 'rLength is required but was not provided';
+    }
+    if (file == null) {
+      throw 'file is required but was not provided';
+    }
+    if (offset == null) {
+      throw 'offset is required but was not provided';
+    }
+  }
+  static DartCompletionItemResolutionInfo fromJson(Map<String, dynamic> json) {
+    final libId = json['libId'];
+    final displayUri = json['displayUri'];
+    final rOffset = json['rOffset'];
+    final rLength = json['rLength'];
+    final file = json['file'];
+    final offset = json['offset'];
+    return DartCompletionItemResolutionInfo(
+        libId: libId,
+        displayUri: displayUri,
+        rOffset: rOffset,
+        rLength: rLength,
+        file: file,
+        offset: offset);
+  }
+
+  final String displayUri;
+  final String file;
+  final num libId;
+  final num offset;
+  final num rLength;
+  final num rOffset;
+
+  Map<String, dynamic> toJson() {
+    var __result = <String, dynamic>{};
+    __result['libId'] = libId ?? (throw 'libId is required but was not set');
+    __result['displayUri'] =
+        displayUri ?? (throw 'displayUri is required but was not set');
+    __result['rOffset'] =
+        rOffset ?? (throw 'rOffset is required but was not set');
+    __result['rLength'] =
+        rLength ?? (throw 'rLength is required but was not set');
+    __result['file'] = file ?? (throw 'file is required but was not set');
+    __result['offset'] = offset ?? (throw 'offset is required but was not set');
+    return __result;
+  }
+
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
       reporter.push('libId');
       try {
         if (!obj.containsKey('libId')) {
@@ -359,23 +422,57 @@
       } finally {
         reporter.pop();
       }
+      reporter.push('file');
+      try {
+        if (!obj.containsKey('file')) {
+          reporter.reportError('must not be undefined');
+          return false;
+        }
+        if (obj['file'] == null) {
+          reporter.reportError('must not be null');
+          return false;
+        }
+        if (!(obj['file'] is String)) {
+          reporter.reportError('must be of type String');
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('offset');
+      try {
+        if (!obj.containsKey('offset')) {
+          reporter.reportError('must not be undefined');
+          return false;
+        }
+        if (obj['offset'] == null) {
+          reporter.reportError('must not be null');
+          return false;
+        }
+        if (!(obj['offset'] is num)) {
+          reporter.reportError('must be of type num');
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
       return true;
     } else {
-      reporter.reportError('must be of type CompletionItemResolutionInfo');
+      reporter.reportError('must be of type DartCompletionItemResolutionInfo');
       return false;
     }
   }
 
   @override
   bool operator ==(Object other) {
-    if (other is CompletionItemResolutionInfo &&
-        other.runtimeType == CompletionItemResolutionInfo) {
-      return file == other.file &&
-          offset == other.offset &&
-          libId == other.libId &&
+    if (other is DartCompletionItemResolutionInfo &&
+        other.runtimeType == DartCompletionItemResolutionInfo) {
+      return libId == other.libId &&
           displayUri == other.displayUri &&
           rOffset == other.rOffset &&
           rLength == other.rLength &&
+          file == other.file &&
+          offset == other.offset &&
           true;
     }
     return false;
@@ -384,12 +481,12 @@
   @override
   int get hashCode {
     var hash = 0;
-    hash = JenkinsSmiHash.combine(hash, file.hashCode);
-    hash = JenkinsSmiHash.combine(hash, offset.hashCode);
     hash = JenkinsSmiHash.combine(hash, libId.hashCode);
     hash = JenkinsSmiHash.combine(hash, displayUri.hashCode);
     hash = JenkinsSmiHash.combine(hash, rOffset.hashCode);
     hash = JenkinsSmiHash.combine(hash, rLength.hashCode);
+    hash = JenkinsSmiHash.combine(hash, file.hashCode);
+    hash = JenkinsSmiHash.combine(hash, offset.hashCode);
     return JenkinsSmiHash.finish(hash);
   }
 
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
index d7f1610..c7be4e9 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.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/lsp_protocol/protocol_custom_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 import 'package:analysis_server/src/lsp/constants.dart';
@@ -15,10 +16,11 @@
 
 class CompletionResolveHandler
     extends MessageHandler<CompletionItem, CompletionItem> {
+  /// The last completion item we asked to be resolved.
   ///
-  /// The latest completion item we were asked to resolve. We use it to abort
-  /// previous requests.
-  ///
+  /// Used to abort previous requests in async handlers if another resolve request
+  /// arrives while the previous is being processed (for clients that don't send
+  /// cancel events).
   CompletionItem _latestCompletionItem;
 
   CompletionResolveHandler(LspAnalysisServer server) : super(server);
@@ -31,13 +33,23 @@
 
   @override
   Future<ErrorOr<CompletionItem>> handle(
-      CompletionItem item, CancellationToken token) async {
-    // If this isn't an item with resolution data, return the same item back.
-    if (item.data == null) {
+    CompletionItem item,
+    CancellationToken token,
+  ) async {
+    final resolutionInfo = item.data;
+
+    if (resolutionInfo is DartCompletionItemResolutionInfo) {
+      return resolveDartCompletion(item, resolutionInfo, token);
+    } else {
       return success(item);
     }
+  }
 
-    final data = item.data;
+  Future<ErrorOr<CompletionItem>> resolveDartCompletion(
+    CompletionItem item,
+    DartCompletionItemResolutionInfo data,
+    CancellationToken token,
+  ) async {
     final lineInfo = server.getLineInfo(data.file);
     if (lineInfo == null) {
       return error(
@@ -166,7 +178,7 @@
           textEdit: TextEdit(
             // TODO(dantup): If `clientSupportsSnippets == true` then we should map
             // `selection` in to a snippet (see how Dart Code does this).
-            range: toRange(lineInfo, item.data.rOffset, item.data.rLength),
+            range: toRange(lineInfo, data.rOffset, data.rLength),
             newText: newInsertText,
           ),
           additionalTextEdits: thisFilesChanges
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index ce49475..56c7ee0 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -301,7 +301,7 @@
         ? insertText
         : null, // insertText uses label if not set
     // data, used for completionItem/resolve.
-    data: lsp.CompletionItemResolutionInfo(
+    data: lsp.DartCompletionItemResolutionInfo(
         file: file,
         offset: offset,
         libId: includedSuggestionSet.id,
@@ -783,6 +783,7 @@
   int replacementLength, {
   @required bool includeCommitCharacters,
   @required bool completeFunctionCalls,
+  Object resolutionData,
 }) {
   // Build display labels and text to insert. insertText and filterText may
   // differ from label (for ex. if the label includes things like (…)). If
@@ -876,6 +877,7 @@
         : null,
     commitCharacters:
         includeCommitCharacters ? dartCompletionCommitCharacters : null,
+    data: resolutionData,
     detail: getCompletionDetail(suggestion, completionKind,
         supportsDeprecatedFlag || supportsDeprecatedTag),
     documentation:
diff --git a/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart b/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
index 7f9228a..cd4d7bd 100644
--- a/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
+++ b/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
@@ -59,7 +59,8 @@
   const LintRuleProducer();
 
   @override
-  Iterable<CompletionSuggestion> suggestions() sync* {
+  Iterable<CompletionSuggestion> suggestions(
+      YamlCompletionRequest request) sync* {
     for (var rule in Registry.ruleRegistry.rules) {
       yield identifier(rule.name);
     }
diff --git a/pkg/analysis_server/lib/src/services/completion/yaml/producer.dart b/pkg/analysis_server/lib/src/services/completion/yaml/producer.dart
index 834adac..67267f0 100644
--- a/pkg/analysis_server/lib/src/services/completion/yaml/producer.dart
+++ b/pkg/analysis_server/lib/src/services/completion/yaml/producer.dart
@@ -11,7 +11,8 @@
   const BooleanProducer();
 
   @override
-  Iterable<CompletionSuggestion> suggestions() sync* {
+  Iterable<CompletionSuggestion> suggestions(
+      YamlCompletionRequest request) sync* {
     yield identifier('true');
     yield identifier('false');
   }
@@ -24,7 +25,8 @@
   const EmptyProducer();
 
   @override
-  Iterable<CompletionSuggestion> suggestions() sync* {
+  Iterable<CompletionSuggestion> suggestions(
+      YamlCompletionRequest request) sync* {
     // Returns nothing.
   }
 }
@@ -39,7 +41,8 @@
   const EnumProducer(this.values);
 
   @override
-  Iterable<CompletionSuggestion> suggestions() sync* {
+  Iterable<CompletionSuggestion> suggestions(
+      YamlCompletionRequest request) sync* {
     for (var value in values) {
       yield identifier(value);
     }
@@ -56,11 +59,20 @@
   FilePathProducer(this.provider);
 
   @override
-  Iterable<CompletionSuggestion> suggestions() sync* {
+  Iterable<CompletionSuggestion> suggestions(
+      YamlCompletionRequest request) sync* {
     // TODO(brianwilkerson) Implement this.
   }
 }
 
+/// An object that represents the location of the keys/values in a map.
+abstract class KeyValueProducer extends Producer {
+  const KeyValueProducer();
+
+  /// Returns a producer for values of the given [key].
+  Producer producerForKey(String key);
+}
+
 /// An object that represents the location of an element in a list.
 class ListProducer extends Producer {
   /// The producer used to produce suggestions for an element of the list.
@@ -71,8 +83,9 @@
   const ListProducer(this.element);
 
   @override
-  Iterable<CompletionSuggestion> suggestions() sync* {
-    for (var suggestion in element.suggestions()) {
+  Iterable<CompletionSuggestion> suggestions(
+      YamlCompletionRequest request) sync* {
+    for (var suggestion in element.suggestions(request)) {
       // TODO(brianwilkerson) Consider prepending the suggestion with a hyphen
       //  when the current node isn't already preceded by a hyphen. The
       //  cleanest way to do this is probably to access the [element] producer
@@ -84,18 +97,23 @@
 }
 
 /// An object that represents the location of the keys in a map.
-class MapProducer extends Producer {
+class MapProducer extends KeyValueProducer {
   /// A table from the value of a key to the producer used to make suggestions
   /// for the value following the key.
-  final Map<String, Producer> children;
+  final Map<String, Producer> _children;
 
   /// Initialize a location whose valid values are the keys of a map as encoded
   /// by the map of [children].
-  const MapProducer(this.children);
+  const MapProducer(this._children);
+
+  /// Returns a producer for values of the given [key].
+  @override
+  Producer producerForKey(String key) => _children[key];
 
   @override
-  Iterable<CompletionSuggestion> suggestions() sync* {
-    for (var entry in children.entries) {
+  Iterable<CompletionSuggestion> suggestions(
+      YamlCompletionRequest request) sync* {
+    for (var entry in _children.entries) {
       if (entry.value is ListProducer) {
         yield identifier('${entry.key}:');
       } else {
@@ -123,5 +141,11 @@
       false);
 
   /// Return the completion suggestions appropriate to this location.
-  Iterable<CompletionSuggestion> suggestions();
+  Iterable<CompletionSuggestion> suggestions(YamlCompletionRequest request);
+}
+
+class YamlCompletionRequest {
+  final ResourceProvider resourceProvider;
+
+  YamlCompletionRequest(this.resourceProvider);
 }
diff --git a/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart b/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart
index b00768b..828e28e 100644
--- a/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart
+++ b/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart
@@ -59,13 +59,14 @@
   /// the offset of the cursor, return the completions appropriate at that
   /// location.
   YamlCompletionResults getSuggestionsForPath(List<YamlNode> path, int offset) {
+    var request = YamlCompletionRequest(resourceProvider);
     var producer = _producerForPath(path);
     if (producer == null) {
       return const YamlCompletionResults.empty();
     }
     var invalidSuggestions = _siblingsOnPath(path);
     var suggestions = <CompletionSuggestion>[];
-    for (var suggestion in producer.suggestions()) {
+    for (var suggestion in producer.suggestions(request)) {
       if (!invalidSuggestions.contains(suggestion.completion)) {
         suggestions.add(suggestion);
       }
@@ -107,10 +108,10 @@
     var producer = topLevelProducer;
     for (var i = 0; i < path.length - 1; i++) {
       var node = path[i];
-      if (node is YamlMap && producer is MapProducer) {
+      if (node is YamlMap && producer is KeyValueProducer) {
         var key = node.keyAtValue(path[i + 1]);
         if (key is YamlScalar) {
-          producer = (producer as MapProducer).children[key.value];
+          producer = (producer as KeyValueProducer).producerForKey(key.value);
         } else {
           return null;
         }
diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
index 36c6e26f..464139d 100644
--- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
@@ -306,6 +306,7 @@
 ///   OPTIONAL_ARGUMENT
 ///   OVERRIDE
 ///   PARAMETER
+///   PACKAGE_NAME
 /// }
 final Matcher isCompletionSuggestionKind =
     MatchesEnum('CompletionSuggestionKind', [
@@ -317,7 +318,8 @@
   'NAMED_ARGUMENT',
   'OPTIONAL_ARGUMENT',
   'OVERRIDE',
-  'PARAMETER'
+  'PARAMETER',
+  'PACKAGE_NAME'
 ]);
 
 /// ContextData
diff --git a/pkg/analysis_server/tool/lsp_spec/generate_all.dart b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
index 26b8916..2998c8f 100644
--- a/pkg/analysis_server/tool/lsp_spec/generate_all.dart
+++ b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
@@ -182,8 +182,14 @@
 ''';
 
 List<AstNode> getCustomClasses() {
-  Interface interface(String name, List<Member> fields) {
-    return Interface(null, Token.identifier(name), [], [], fields);
+  Interface interface(String name, List<Member> fields, {String baseType}) {
+    return Interface(
+      null,
+      Token.identifier(name),
+      [],
+      [if (baseType != null) Type.identifier(baseType)],
+      fields,
+    );
   }
 
   Field field(String name,
@@ -250,11 +256,17 @@
       [
         field('file', type: 'string'),
         field('offset', type: 'number'),
+      ],
+    ),
+    interface(
+      'DartCompletionItemResolutionInfo',
+      [
         field('libId', type: 'number'),
         field('displayUri', type: 'string'),
         field('rOffset', type: 'number'),
-        field('rLength', type: 'number')
+        field('rLength', type: 'number'),
       ],
+      baseType: 'CompletionItemResolutionInfo',
     ),
   ];
   return customTypes;
diff --git a/pkg/analysis_server/tool/spec/generated/java/types/CompletionSuggestionKind.java b/pkg/analysis_server/tool/spec/generated/java/types/CompletionSuggestionKind.java
index e7595aa..8362cdd 100644
--- a/pkg/analysis_server/tool/spec/generated/java/types/CompletionSuggestionKind.java
+++ b/pkg/analysis_server/tool/spec/generated/java/types/CompletionSuggestionKind.java
@@ -58,4 +58,9 @@
 
   public static final String PARAMETER = "PARAMETER";
 
+  /**
+   * The name of a pub package is being suggested.
+   */
+  public static final String PACKAGE_NAME = "PACKAGE_NAME";
+
 }
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
index 17afcd9..79cd384 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
@@ -1187,6 +1187,7 @@
 ///   OPTIONAL_ARGUMENT
 ///   OVERRIDE
 ///   PARAMETER
+///   PACKAGE_NAME
 /// }
 ///
 /// Clients may not extend, implement or mix-in this class.
@@ -1235,6 +1236,10 @@
   static const CompletionSuggestionKind PARAMETER =
       CompletionSuggestionKind._('PARAMETER');
 
+  /// The name of a pub package is being suggested.
+  static const CompletionSuggestionKind PACKAGE_NAME =
+      CompletionSuggestionKind._('PACKAGE_NAME');
+
   /// A list containing all of the enum values that are defined.
   static const List<CompletionSuggestionKind> VALUES =
       <CompletionSuggestionKind>[
@@ -1246,7 +1251,8 @@
     NAMED_ARGUMENT,
     OPTIONAL_ARGUMENT,
     OVERRIDE,
-    PARAMETER
+    PARAMETER,
+    PACKAGE_NAME
   ];
 
   @override
@@ -1274,6 +1280,8 @@
         return OVERRIDE;
       case 'PARAMETER':
         return PARAMETER;
+      case 'PACKAGE_NAME':
+        return PACKAGE_NAME;
     }
     throw Exception('Illegal enum value: $name');
   }
diff --git a/pkg/analyzer_plugin/doc/api.html b/pkg/analyzer_plugin/doc/api.html
index 777a128..6aaac51 100644
--- a/pkg/analyzer_plugin/doc/api.html
+++ b/pkg/analyzer_plugin/doc/api.html
@@ -1174,7 +1174,10 @@
         <p>
           An overriding implementation of a class member is being suggested.
         </p>
-      </dd><dt class="value">PARAMETER</dt></dl></dd><dt class="typeDefinition"><a name="type_ContextRoot">ContextRoot: object</a></dt><dd>
+      </dd><dt class="value">PARAMETER</dt><dt class="value">PACKAGE_NAME</dt><dd>
+        
+        <p>The name of a pub package is being suggested.</p>
+      </dd></dl></dd><dt class="typeDefinition"><a name="type_ContextRoot">ContextRoot: object</a></dt><dd>
     <p>
       A description of an analysis context.
     </p>
diff --git a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
index 2d0391d..56bcadd 100644
--- a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
+++ b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
@@ -1217,6 +1217,7 @@
 ///   OPTIONAL_ARGUMENT
 ///   OVERRIDE
 ///   PARAMETER
+///   PACKAGE_NAME
 /// }
 ///
 /// Clients may not extend, implement or mix-in this class.
@@ -1266,6 +1267,10 @@
   static const CompletionSuggestionKind PARAMETER =
       CompletionSuggestionKind._('PARAMETER');
 
+  /// The name of a pub package is being suggested.
+  static const CompletionSuggestionKind PACKAGE_NAME =
+      CompletionSuggestionKind._('PACKAGE_NAME');
+
   /// A list containing all of the enum values that are defined.
   static const List<CompletionSuggestionKind> VALUES =
       <CompletionSuggestionKind>[
@@ -1277,7 +1282,8 @@
     NAMED_ARGUMENT,
     OPTIONAL_ARGUMENT,
     OVERRIDE,
-    PARAMETER
+    PARAMETER,
+    PACKAGE_NAME
   ];
 
   @override
@@ -1305,6 +1311,8 @@
         return OVERRIDE;
       case 'PARAMETER':
         return PARAMETER;
+      case 'PACKAGE_NAME':
+        return PACKAGE_NAME;
     }
     throw Exception('Illegal enum value: $name');
   }
diff --git a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
index 092d411..d495c4f 100644
--- a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
+++ b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
@@ -175,6 +175,7 @@
 ///   OPTIONAL_ARGUMENT
 ///   OVERRIDE
 ///   PARAMETER
+///   PACKAGE_NAME
 /// }
 final Matcher isCompletionSuggestionKind =
     MatchesEnum('CompletionSuggestionKind', [
@@ -186,7 +187,8 @@
   'NAMED_ARGUMENT',
   'OPTIONAL_ARGUMENT',
   'OVERRIDE',
-  'PARAMETER'
+  'PARAMETER',
+  'PACKAGE_NAME'
 ]);
 
 /// ContextRoot
diff --git a/pkg/analyzer_plugin/tool/spec/common_types_spec.html b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
index b14bca8..b1c7056 100644
--- a/pkg/analyzer_plugin/tool/spec/common_types_spec.html
+++ b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
@@ -414,6 +414,10 @@
         </p>
       </value>
       <value><code>PARAMETER</code></value>
+      <value>
+        <code>PACKAGE_NAME</code>
+        <p>The name of a pub package is being suggested.</p>
+      </value>
     </enum>
   </type>
   <type name="DiagnosticMessage">
diff --git a/tools/VERSION b/tools/VERSION
index d4fff73..d7f5861 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 87
+PRERELEASE 88
 PRERELEASE_PATCH 0
\ No newline at end of file