blob: 960129f284c86dba6bb405cc8614f3e9ee42f65c [file] [log] [blame]
// 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.
library computer.hover;
import 'dart:collection';
import 'package:analysis_services/constants.dart';
import 'package:analysis_services/json.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
/**
* Converts [str] from a Dart Doc string with slashes and stars to a plain text
* representation of the comment.
*/
String _removeDartDocDelimiters(String str) {
if (str == null) {
return null;
}
// remove /** */
if (str.startsWith('/**')) {
str = str.substring(3);
}
if (str.endsWith("*/")) {
str = str.substring(0, str.length - 2);
}
str = str.trim();
// remove leading '* '
List<String> lines = str.split('\n');
StringBuffer sb = new StringBuffer();
bool firstLine = true;
for (String line in lines) {
line = line.trim();
if (line.startsWith("*")) {
line = line.substring(1);
if (line.startsWith(" ")) {
line = line.substring(1);
}
} else if (line.startsWith("///")) {
line = line.substring(3);
if (line.startsWith(" ")) {
line = line.substring(1);
}
}
if (!firstLine) {
sb.write('\n');
}
firstLine = false;
sb.write(line);
}
str = sb.toString();
// done
return str;
}
/**
* A computer for the hover at the specified offset of a Dart [CompilationUnit].
*/
class DartUnitHoverComputer {
final CompilationUnit _unit;
final int _offset;
DartUnitHoverComputer(this._unit, this._offset);
/**
* Returns the computed hover, maybe `null`.
*/
Hover compute() {
AstNode node = new NodeLocator.con1(_offset).searchWithin(_unit);
if (node is Expression) {
Hover hover = new Hover(node.offset, node.length);
// element
Element element = ElementLocator.locateWithOffset(node, _offset);
if (element != null) {
// variable, if synthetic accessor
if (element is PropertyAccessorElement) {
PropertyAccessorElement accessor = element;
if (accessor.isSynthetic) {
element = accessor.variable;
}
}
// description
hover.elementDescription = element.toString();
hover.elementKind = element.kind.displayName;
// library
LibraryElement library = element.library;
if (library != null) {
hover.containingLibraryName = library.name;
hover.containingLibraryPath = library.source.fullName;
}
// documentation
String dartDoc = element.computeDocumentationComment();
dartDoc = _removeDartDocDelimiters(dartDoc);
hover.dartDoc = dartDoc;
}
// parameter
hover.parameter = _safeToString(node.bestParameterElement);
// types
hover.staticType = _safeToString(node.staticType);
hover.propagatedType = _safeToString(node.propagatedType);
// done
return hover;
}
// not an expression
return null;
}
static _safeToString(obj) => obj != null ? obj.toString() : null;
}
class Hover implements HasToJson {
final int offset;
final int length;
String containingLibraryName;
String containingLibraryPath;
String dartDoc;
String elementDescription;
String elementKind;
String parameter;
String propagatedType;
String staticType;
Hover(this.offset, this.length);
factory Hover.fromJson(Map<String, Object> map) {
int offset = map[OFFSET];
int length = map[LENGTH];
Hover hover = new Hover(offset, length);
hover.containingLibraryName = map[CONTAINING_LIBRARY_NAME];
hover.containingLibraryPath = map[CONTAINING_LIBRARY_PATH];
hover.dartDoc = map[DART_DOC];
hover.elementDescription = map[ELEMENT_DESCRIPTION];
hover.elementKind = map[ELEMENT_KIND];
hover.parameter = map[PARAMETER];
hover.propagatedType = map[PROPAGATED_TYPE];
hover.staticType = map[STATIC_TYPE];
return hover;
}
Map<String, Object> toJson() {
Map<String, Object> json = new HashMap<String, Object>();
json[OFFSET] = offset;
json[LENGTH] = length;
if (containingLibraryName != null) {
json[CONTAINING_LIBRARY_NAME] = containingLibraryName;
}
if (containingLibraryName != null) {
json[CONTAINING_LIBRARY_PATH] = containingLibraryPath;
}
if (dartDoc != null) {
json[DART_DOC] = dartDoc;
}
if (elementDescription != null) {
json[ELEMENT_DESCRIPTION] = elementDescription;
}
if (elementKind != null) {
json[ELEMENT_KIND] = elementKind;
}
if (parameter != null) {
json[PARAMETER] = parameter;
}
if (propagatedType != null) {
json[PROPAGATED_TYPE] = propagatedType;
}
if (staticType != null) {
json[STATIC_TYPE] = staticType;
}
return json;
}
}