Version 2.12.0-279.0.dev

Merge commit '8363ed81b0bf1195f9d975760214eb6a79e1dcec' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/contribution_sorter.dart b/pkg/analysis_server/lib/src/services/completion/dart/contribution_sorter.dart
deleted file mode 100644
index e2889ab..0000000
--- a/pkg/analysis_server/lib/src/services/completion/dart/contribution_sorter.dart
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2014, 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:analysis_server/src/provisional/completion/dart/completion_dart.dart';
-import 'package:analyzer_plugin/protocol/protocol_common.dart';
-
-/// The abstract class [DartContributionSorter] defines the behavior of objects
-/// that are used to adjust the relevance of an existing list of suggestions.
-/// This is a long-lived object that should not maintain state between
-/// calls to it's [sort] method.
-abstract class DartContributionSorter {
-  /// After [CompletionSuggestion]s have been computed,
-  /// this method is called to adjust the relevance of those suggestions.
-  /// Return a [Future] that completes when the suggestions have been updated.
-  Future sort(DartCompletionRequest request,
-      Iterable<CompletionSuggestion> suggestions);
-}
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 3d49664..ec7ad30 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -4,9 +4,7 @@
 
 import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/plugin/plugin_manager.dart';
-import 'package:analysis_server/src/provisional/completion/completion_core.dart';
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
-import 'package:analysis_server/src/services/completion/dart/contribution_sorter.dart';
 import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
@@ -776,16 +774,3 @@
     });
   }
 }
-
-class MockRelevancySorter implements DartContributionSorter {
-  bool enabled = true;
-
-  @override
-  Future sort(
-      CompletionRequest request, Iterable<CompletionSuggestion> suggestions) {
-    if (!enabled) {
-      throw 'unexpected sort';
-    }
-    return Future.value();
-  }
-}
diff --git a/pkg/analysis_server/tool/code_completion/relevance_table_generator.dart b/pkg/analysis_server/tool/code_completion/relevance_table_generator.dart
index 601c1de..df9e5d3 100644
--- a/pkg/analysis_server/tool/code_completion/relevance_table_generator.dart
+++ b/pkg/analysis_server/tool/code_completion/relevance_table_generator.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 'dart:convert';
 import 'dart:io' as io;
 
 import 'package:_fe_analyzer_shared/src/base/syntactic_entity.dart';
@@ -126,6 +127,39 @@
   /// Initialize a newly created set of relevance data to be empty.
   RelevanceData();
 
+  /// Initialize a newly created set of relevance data based on the content of
+  /// the JSON encoded string.
+  RelevanceData.fromJson(String encoded) {
+    var map = json.decode(encoded) as Map<String, Map<String, String>>;
+    for (var contextEntry in map.entries) {
+      var contextMap = byKind.putIfAbsent(contextEntry.key, () => {});
+      for (var kindEntry in contextEntry.value.entries) {
+        _Kind kind;
+        var key = kindEntry.key;
+        if (key.startsWith('e')) {
+          kind = _ElementKind(ElementKind(key.substring(1)));
+        } else if (key.startsWith('k')) {
+          kind = _Keyword(Keyword.keywords[key.substring(1)]);
+        } else {
+          throw StateError('Invalid initial character in unique key "$key"');
+        }
+        contextMap[kind] = int.parse(kindEntry.value);
+      }
+    }
+  }
+
+  /// Add the data from the given relevance [data] to this set of relevance
+  /// data.
+  void addData(RelevanceData data) {
+    for (var contextEntry in data.byKind.entries) {
+      var contextMap = byKind.putIfAbsent(contextEntry.key, () => {});
+      for (var kindEntry in contextEntry.value.entries) {
+        var kind = kindEntry.key;
+        contextMap[kind] = (contextMap[kind] ?? 0) + kindEntry.value;
+      }
+    }
+  }
+
   /// Add the data from the given relevance [data] to this set of data.
   void addDataFrom(RelevanceData data) {
     _addToMap(byKind, data.byKind);
@@ -146,6 +180,19 @@
     contextMap[key] = (contextMap[key] ?? 0) + 1;
   }
 
+  /// Convert this data to a JSON encoded format.
+  String toJson() {
+    var map = <String, Map<String, String>>{};
+    for (var contextEntry in byKind.entries) {
+      var kindMap = <String, String>{};
+      for (var kindEntry in contextEntry.value.entries) {
+        kindMap[kindEntry.key.uniqueKey] = kindEntry.value.toString();
+      }
+      map[contextEntry.key] = kindMap;
+    }
+    return json.encode(map);
+  }
+
   /// Add the data in the [source] map to the [target] map.
   void _addToMap<K>(Map<K, Map<K, int>> target, Map<K, Map<K, int>> source) {
     for (var outerEntry in source.entries) {
@@ -1590,6 +1637,9 @@
       instances.putIfAbsent(elementKind, () => _ElementKind._(elementKind));
 
   _ElementKind._(this.elementKind);
+
+  @override
+  String get uniqueKey => 'e${elementKind.name}';
 }
 
 /// A wrapper for a keyword to allow keywords and element kinds to be used as
@@ -1603,8 +1653,15 @@
       instances.putIfAbsent(keyword, () => _Keyword._(keyword));
 
   _Keyword._(this.keyword);
+
+  @override
+  String get uniqueKey => 'k${keyword.lexeme}';
 }
 
 /// A superclass for [_ElementKind] and [_Keyword] to allow keywords and element
 /// kinds to be used as keys in a single table.
-class _Kind {}
+abstract class _Kind {
+  /// Return the unique key used when representing an instance of a subclass in
+  /// a JSON format.
+  String get uniqueKey;
+}
diff --git a/tools/VERSION b/tools/VERSION
index 6eb7104..0b7de8c 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 278
+PRERELEASE 279
 PRERELEASE_PATCH 0
\ No newline at end of file