Use existing parser code for auto-complete

This works very well when the user is typing at the end of a statement, but won't provide accurate results when editing in the middle.
This commit is contained in:
Simon Binder 2019-09-27 22:06:14 +02:00
parent b684a7be69
commit ab787b82be
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
5 changed files with 62 additions and 3 deletions

View File

@ -1,7 +1,8 @@
import 'package:analyzer/dart/element/type.dart';
bool isFromMoor(DartType type) {
return type.element.library.location.components.first.contains('moor');
return type.element?.library?.location?.components?.first?.contains('moor') ??
false;
}
bool isColumn(DartType type) {

View File

@ -11,4 +11,34 @@ abstract class HintDescription {
factory HintDescription.token(TokenType type) = TokensDescription.single;
Iterable<Suggestion> suggest(CalculationRequest request);
HintDescription mergeWith(HintDescription other) {
return CombinedDescription()
..descriptions.add(this)
..descriptions.add(other);
}
}
/// A [HintDescription] that bundles multiple independent [HintDescription]s.
class CombinedDescription extends HintDescription {
/// The descriptions to consult when using [suggest]. This list may be
/// modified at runtime.
final List<HintDescription> descriptions = [];
@override
Iterable<Suggestion> suggest(CalculationRequest request) {
return descriptions.expand((s) => s.suggest(request));
}
@override
HintDescription mergeWith(HintDescription other) {
if (other is CombinedDescription) {
// optimization: flatten into single list
descriptions.addAll(other.descriptions);
} else {
descriptions.add(other);
}
return this;
}
}

View File

@ -29,7 +29,15 @@ class AutoCompleteEngine {
} else {
// ensure that the hints list stays sorted by offset
final position = _lastHintBefore(hint.offset);
_hints.insert(position + 1, hint);
final hintBefore = _hints[position];
// if we already have a hint at this position, merge them together
if (hintBefore.before == hint.before) {
hintBefore.description =
hintBefore.description.mergeWith(hint.description);
} else {
_hints.insert(position + 1, hint);
}
}
}
@ -100,7 +108,7 @@ class Hint {
int get offset => before?.span?.end?.offset ?? 0;
final HintDescription description;
HintDescription description;
Hint(this.before, this.description);
}

View File

@ -53,17 +53,33 @@ abstract class ParserBase {
ParserBase(this.tokens, this.enableMoorExtensions, this.autoComplete);
bool get _reportAutoComplete => autoComplete != null;
void _suggestHint(HintDescription description) {
final tokenBefore = _current == 0 ? null : _previous;
autoComplete?.addHint(Hint(tokenBefore, description));
}
void _suggestHintForTokens(Iterable<TokenType> types) {
final relevant =
types.where(isKeyword).map((type) => HintDescription.token(type));
final description = CombinedDescription()..descriptions.addAll(relevant);
_suggestHint(description);
}
void _suggestHintForToken(TokenType type) {
if (isKeyword(type)) {
_suggestHint(HintDescription.token(type));
}
}
bool get _isAtEnd => _peek.type == TokenType.eof;
Token get _peek => tokens[_current];
Token get _peekNext => tokens[_current + 1];
Token get _previous => tokens[_current - 1];
bool _match(Iterable<TokenType> types) {
if (_reportAutoComplete) _suggestHintForTokens(types);
for (var type in types) {
if (_check(type)) {
_advance();
@ -74,6 +90,7 @@ abstract class ParserBase {
}
bool _matchOne(TokenType type) {
if (_reportAutoComplete) _suggestHintForToken(type);
if (_check(type)) {
_advance();
return true;

View File

@ -257,6 +257,9 @@ const Map<String, TokenType> moorKeywords = {
'IMPORT': TokenType.import,
};
/// Returns true if the [type] belongs to a keyword
bool isKeyword(TokenType type) => reverseKeywords.containsKey(type);
class Token {
final TokenType type;