Add outline support for the new mixin syntax

Change-Id: I75d020f613daf37812ccf805d75e35091a28c198
Reviewed-on: https://dart-review.googlesource.com/72524
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/computer/computer_outline.dart b/pkg/analysis_server/lib/src/computer/computer_outline.dart
index 1d35cca..490f5b5 100644
--- a/pkg/analysis_server/lib/src/computer/computer_outline.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_outline.dart
@@ -31,41 +31,19 @@
     List<Outline> unitContents = <Outline>[];
     for (CompilationUnitMember unitMember in unit.declarations) {
       if (unitMember is ClassDeclaration) {
-        ClassDeclaration classDeclaration = unitMember;
-        List<Outline> classContents = <Outline>[];
-        for (ClassMember classMember in classDeclaration.members) {
-          if (classMember is ConstructorDeclaration) {
-            ConstructorDeclaration constructorDeclaration = classMember;
-            classContents.add(_newConstructorOutline(constructorDeclaration));
-          }
-          if (classMember is FieldDeclaration) {
-            FieldDeclaration fieldDeclaration = classMember;
-            VariableDeclarationList fields = fieldDeclaration.fields;
-            if (fields != null) {
-              TypeAnnotation fieldType = fields.type;
-              String fieldTypeName = _safeToSource(fieldType);
-              for (VariableDeclaration field in fields.variables) {
-                classContents.add(_newVariableOutline(fieldTypeName,
-                    ElementKind.FIELD, field, fieldDeclaration.isStatic));
-              }
-            }
-          }
-          if (classMember is MethodDeclaration) {
-            MethodDeclaration methodDeclaration = classMember;
-            classContents.add(_newMethodOutline(methodDeclaration));
-          }
-        }
-        unitContents.add(_newClassOutline(classDeclaration, classContents));
-      }
-      if (unitMember is EnumDeclaration) {
+        unitContents.add(_newClassOutline(
+            unitMember, _outlinesForMembers(unitMember.members)));
+      } else if (unitMember is MixinDeclaration) {
+        unitContents.add(_newMixinOutline(
+            unitMember, _outlinesForMembers(unitMember.members)));
+      } else if (unitMember is EnumDeclaration) {
         EnumDeclaration enumDeclaration = unitMember;
         List<Outline> constantOutlines = <Outline>[];
         for (EnumConstantDeclaration constant in enumDeclaration.constants) {
           constantOutlines.add(_newEnumConstant(constant));
         }
         unitContents.add(_newEnumOutline(enumDeclaration, constantOutlines));
-      }
-      if (unitMember is TopLevelVariableDeclaration) {
+      } else if (unitMember is TopLevelVariableDeclaration) {
         TopLevelVariableDeclaration fieldDeclaration = unitMember;
         VariableDeclarationList fields = fieldDeclaration.variables;
         if (fields != null) {
@@ -76,16 +54,13 @@
                 fieldTypeName, ElementKind.TOP_LEVEL_VARIABLE, field, false));
           }
         }
-      }
-      if (unitMember is FunctionDeclaration) {
+      } else if (unitMember is FunctionDeclaration) {
         FunctionDeclaration functionDeclaration = unitMember;
         unitContents.add(_newFunctionOutline(functionDeclaration, true));
-      }
-      if (unitMember is ClassTypeAlias) {
+      } else if (unitMember is ClassTypeAlias) {
         ClassTypeAlias alias = unitMember;
         unitContents.add(_newClassTypeAlias(alias));
-      }
-      if (unitMember is FunctionTypeAlias) {
+      } else if (unitMember is FunctionTypeAlias) {
         FunctionTypeAlias alias = unitMember;
         unitContents.add(_newFunctionTypeAliasOutline(alias));
       }
@@ -280,6 +255,21 @@
     return _nodeOutline(method, element, contents);
   }
 
+  Outline _newMixinOutline(MixinDeclaration node, List<Outline> mixinContents) {
+    node.firstTokenAfterCommentAndMetadata;
+    SimpleIdentifier nameNode = node.name;
+    String name = nameNode.name;
+    Element element = new Element(
+        ElementKind.MIXIN,
+        name,
+        Element.makeFlags(
+            isPrivate: Identifier.isPrivateName(name),
+            isDeprecated: _isDeprecated(node)),
+        location: _getLocationNode(nameNode),
+        typeParameters: _getTypeParametersStr(node.typeParameters));
+    return _nodeOutline(node, element, mixinContents);
+  }
+
   Outline _newUnitOutline(List<Outline> unitContents) {
     Element element = new Element(
         ElementKind.COMPILATION_UNIT, '<unit>', Element.makeFlags(),
@@ -332,6 +322,33 @@
         children: nullIfEmpty(children));
   }
 
+  List<Outline> _outlinesForMembers(List<ClassMember> members) {
+    List<Outline> memberOutlines = <Outline>[];
+    for (ClassMember classMember in members) {
+      if (classMember is ConstructorDeclaration) {
+        ConstructorDeclaration constructorDeclaration = classMember;
+        memberOutlines.add(_newConstructorOutline(constructorDeclaration));
+      }
+      if (classMember is FieldDeclaration) {
+        FieldDeclaration fieldDeclaration = classMember;
+        VariableDeclarationList fields = fieldDeclaration.fields;
+        if (fields != null) {
+          TypeAnnotation fieldType = fields.type;
+          String fieldTypeName = _safeToSource(fieldType);
+          for (VariableDeclaration field in fields.variables) {
+            memberOutlines.add(_newVariableOutline(fieldTypeName,
+                ElementKind.FIELD, field, fieldDeclaration.isStatic));
+          }
+        }
+      }
+      if (classMember is MethodDeclaration) {
+        MethodDeclaration methodDeclaration = classMember;
+        memberOutlines.add(_newMethodOutline(methodDeclaration));
+      }
+    }
+    return memberOutlines;
+  }
+
   static String _getTypeParametersStr(TypeParameterList parameters) {
     if (parameters == null) {
       return null;
diff --git a/pkg/analysis_server/test/src/computer/outline_computer_test.dart b/pkg/analysis_server/test/src/computer/outline_computer_test.dart
index 9aa971d..340045a 100644
--- a/pkg/analysis_server/test/src/computer/outline_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/outline_computer_test.dart
@@ -830,6 +830,77 @@
     }
   }
 
+  test_mixin() async {
+    Outline unitOutline = await _computeOutline('''
+mixin M<N> {
+  c(int d) {}
+  String get e => null;
+  set f(int g) {}
+}
+''');
+    List<Outline> topOutlines = unitOutline.children;
+    expect(topOutlines, hasLength(1));
+    // M
+    {
+      Outline outline_M = topOutlines[0];
+      Element element_M = outline_M.element;
+      expect(element_M.kind, ElementKind.MIXIN);
+      expect(element_M.name, "M");
+      expect(element_M.typeParameters, "<N>");
+      {
+        Location location = element_M.location;
+        expect(location.offset, testCode.indexOf("M<N>"));
+        expect(location.length, 1);
+      }
+      expect(element_M.parameters, isNull);
+      expect(element_M.returnType, isNull);
+      // M children
+      List<Outline> outlines_M = outline_M.children;
+      expect(outlines_M, hasLength(3));
+      {
+        Outline outline = outlines_M[0];
+        Element element = outline.element;
+        expect(element.kind, ElementKind.METHOD);
+        expect(element.name, "c");
+        {
+          Location location = element.location;
+          expect(location.offset, testCode.indexOf("c(int d)"));
+          expect(location.length, 1);
+        }
+        expect(element.parameters, "(int d)");
+        expect(element.returnType, "");
+        expect(element.isAbstract, isFalse);
+        expect(element.isStatic, isFalse);
+      }
+      {
+        Outline outline = outlines_M[1];
+        Element element = outline.element;
+        expect(element.kind, ElementKind.GETTER);
+        expect(element.name, "e");
+        {
+          Location location = element.location;
+          expect(location.offset, testCode.indexOf("e => null"));
+          expect(location.length, 1);
+        }
+        expect(element.parameters, isNull);
+        expect(element.returnType, "String");
+      }
+      {
+        Outline outline = outlines_M[2];
+        Element element = outline.element;
+        expect(element.kind, ElementKind.SETTER);
+        expect(element.name, "f");
+        {
+          Location location = element.location;
+          expect(location.offset, testCode.indexOf("f(int g)"));
+          expect(location.length, 1);
+        }
+        expect(element.parameters, "(int g)");
+        expect(element.returnType, "");
+      }
+    }
+  }
+
   test_sourceRanges_fields() async {
     Outline unitOutline = await _computeOutline('''
 class A {