Completion. Rework KeywordSuggestion to better constructors.

As requested in https://dart-review.googlesource.com/c/sdk/+/359240

Change-Id: I5047afaddfd74310303001669177f725aac9c02c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/359261
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2024-03-22 19:04:39 +00:00 committed by Commit Queue
parent 291051e02d
commit 5a65760042
3 changed files with 65 additions and 60 deletions

View file

@ -288,13 +288,6 @@ final class KeywordSuggestion extends CandidateSuggestion {
/// should be positioned.
final int selectionOffset;
/// Initialize a newly created candidate suggestion to suggest the [keyword].
factory KeywordSuggestion.fromKeyword({required Keyword keyword}) {
var lexeme = keyword.lexeme;
return KeywordSuggestion._(
completion: lexeme, selectionOffset: lexeme.length);
}
/// Initialize a newly created candidate suggestion to suggest the [keyword].
///
/// If [annotatedText] is provided. The annotated text is used in cases where
@ -306,41 +299,36 @@ final class KeywordSuggestion extends CandidateSuggestion {
/// be used as the selection offset. If the text doesn't contain a caret, then
/// the insert text will be the annotated text and the selection offset will
/// be at the end of the text.
factory KeywordSuggestion.fromKeywordAndText({
required Keyword? keyword,
factory KeywordSuggestion.fromKeyword({
required Keyword keyword,
required String? annotatedText,
}) {
assert(keyword != null || annotatedText != null);
var completion = '';
int? selectionOffset;
if (keyword != null) {
completion = keyword.lexeme;
}
var completion = keyword.lexeme;
var selectionOffset = completion.length;
if (annotatedText != null) {
var caretIndex = annotatedText.indexOf('^');
if (caretIndex < 0) {
completion += annotatedText;
} else {
selectionOffset = completion.length + caretIndex;
completion += annotatedText.substring(0, caretIndex) +
annotatedText.substring(caretIndex + 1);
}
var (rawText, caretIndex) = annotatedText.withoutCaret;
completion += rawText;
selectionOffset += caretIndex ?? rawText.length;
}
selectionOffset ??= completion.length;
return KeywordSuggestion._(
completion: completion,
selectionOffset: selectionOffset,
);
}
/// Initialize a newly created candidate suggestion to suggest the [keyword].
factory KeywordSuggestion.fromPseudoKeyword({required String keyword}) {
/// If [annotatedText] contains a caret (`^`), then the completion will use
/// the annotated text with the caret removed and the index of the caret will
/// be used as the selection offset. If the text doesn't contain a caret, then
/// the insert text will be the annotated text and the selection offset will
/// be at the end of the text.
factory KeywordSuggestion.fromText(String annotatedText) {
var (rawText, caretIndex) = annotatedText.withoutCaret;
return KeywordSuggestion._(
completion: keyword, selectionOffset: keyword.length);
completion: rawText,
selectionOffset: caretIndex ?? rawText.length,
);
}
/// Initialize a newly created candidate suggestion to suggest a keyword.
@ -632,6 +620,18 @@ final class UriSuggestion extends CandidateSuggestion {
String get completion => uriStr;
}
extension on String {
(String, int?) get withoutCaret {
var caretIndex = indexOf('^');
if (caretIndex < 0) {
return (this, null);
} else {
var rawText = substring(0, caretIndex) + substring(caretIndex + 1);
return (rawText, caretIndex);
}
}
}
extension SuggestionBuilderExtension on SuggestionBuilder {
// TODO(brianwilkerson): Move these to `SuggestionBuilder`, possibly as part
// of splitting it into a legacy builder and an LSP builder.

View file

@ -1064,7 +1064,7 @@ class InScopeCompletionPass extends SimpleAstVisitor<void> {
if (name != null && offset <= name.end) {
keywordHelper.addKeyword(Keyword.ON);
if (featureSet.isEnabled(Feature.inline_class)) {
keywordHelper.addPseudoKeyword('type');
keywordHelper.addText('type');
}
identifierHelper(includePrivateIdentifiers: false).addTopLevelName();
return;
@ -2296,10 +2296,10 @@ class InScopeCompletionPass extends SimpleAstVisitor<void> {
void visitSwitchDefault(SwitchDefault node) {
if (offset <= node.keyword.offset) {
keywordHelper.addKeyword(Keyword.CASE);
keywordHelper.addKeywordFromText(Keyword.DEFAULT, ':');
keywordHelper.addKeywordAndText(Keyword.DEFAULT, ':');
} else if (offset <= node.keyword.end) {
if (node.colon.isSynthetic) {
keywordHelper.addKeywordFromText(Keyword.DEFAULT, ':');
keywordHelper.addKeywordAndText(Keyword.DEFAULT, ':');
} else {
keywordHelper.addKeyword(Keyword.DEFAULT);
}
@ -2377,7 +2377,7 @@ class InScopeCompletionPass extends SimpleAstVisitor<void> {
collector.completionLocation = 'SwitchMember_statement';
if (node.statements.isEmpty || offset <= node.statements.first.offset) {
keywordHelper.addKeyword(Keyword.CASE);
keywordHelper.addKeywordFromText(Keyword.DEFAULT, ':');
keywordHelper.addKeywordAndText(Keyword.DEFAULT, ':');
}
_forStatement(node);
}
@ -2397,10 +2397,10 @@ class InScopeCompletionPass extends SimpleAstVisitor<void> {
collector.completionLocation = 'SwitchMember_statement';
var members = node.members;
keywordHelper.addKeyword(Keyword.CASE);
keywordHelper.addKeywordFromText(Keyword.DEFAULT, ':');
keywordHelper.addKeywordAndText(Keyword.DEFAULT, ':');
if (members.isNotEmpty) {
if (!members.any((element) => element is SwitchDefault)) {
keywordHelper.addKeywordFromText(Keyword.DEFAULT, ':');
keywordHelper.addKeywordAndText(Keyword.DEFAULT, ':');
}
var element = members.elementBefore(offset);
if (element != null) {

View file

@ -205,16 +205,11 @@ class KeywordHelper {
if (before == null && !unit.directives.any((d) => d is LibraryDirective)) {
addKeyword(Keyword.LIBRARY);
}
addKeywordFromText(Keyword.IMPORT, " '^';");
addKeywordFromText(Keyword.EXPORT, " '^';");
addKeywordFromText(Keyword.PART, " '^';");
addKeywordAndText(Keyword.IMPORT, " '^';");
addKeywordAndText(Keyword.EXPORT, " '^';");
addKeywordAndText(Keyword.PART, " '^';");
if (unit.directives.isEmpty) {
collector.addSuggestion(
KeywordSuggestion.fromKeywordAndText(
keyword: null,
annotatedText: "${Keyword.PART.lexeme} ${Keyword.OF.lexeme} '^';",
),
);
addText("${Keyword.PART.lexeme} ${Keyword.OF.lexeme} '^';");
}
}
@ -326,7 +321,7 @@ class KeywordHelper {
if (node.onKeyword.isSynthetic) {
addKeyword(Keyword.ON);
if (node.name == null && featureSet.isEnabled(Feature.inline_class)) {
addPseudoKeyword('type');
addText('type');
}
}
}
@ -430,8 +425,8 @@ class KeywordHelper {
if (_isAbsentOrIn(body?.keyword)) {
addKeyword(Keyword.ASYNC);
if (body is! ExpressionFunctionBody) {
addKeywordFromText(Keyword.ASYNC, '*');
addKeywordFromText(Keyword.SYNC, '*');
addKeywordAndText(Keyword.ASYNC, '*');
addKeywordAndText(Keyword.SYNC, '*');
}
}
}
@ -446,7 +441,7 @@ class KeywordHelper {
if (firstCombinator == null || offset < firstCombinator.offset) {
if (deferredKeyword == null) {
if (asKeyword == null) {
addKeywordFromText(Keyword.DEFERRED, ' as');
addKeywordAndText(Keyword.DEFERRED, ' as');
addKeyword(Keyword.AS);
addKeyword(Keyword.HIDE);
addKeyword(Keyword.SHOW);
@ -473,7 +468,12 @@ class KeywordHelper {
/// Add a keyword suggestion to suggest the [keyword].
void addKeyword(Keyword keyword) {
collector.addSuggestion(KeywordSuggestion.fromKeyword(keyword: keyword));
collector.addSuggestion(
KeywordSuggestion.fromKeyword(
keyword: keyword,
annotatedText: null,
),
);
}
/// Add a keyword suggestion to suggest the [keyword] followed by the
@ -485,9 +485,13 @@ class KeywordHelper {
/// be used as the selection offset. If the text doesn't contain a caret, then
/// the insert text will be the annotated text and the selection offset will
/// be at the end of the text.
void addKeywordFromText(Keyword keyword, String annotatedText) {
collector.addSuggestion(KeywordSuggestion.fromKeywordAndText(
keyword: keyword, annotatedText: annotatedText));
void addKeywordAndText(Keyword keyword, String annotatedText) {
collector.addSuggestion(
KeywordSuggestion.fromKeyword(
keyword: keyword,
annotatedText: annotatedText,
),
);
}
/// Add the keywords that are appropriate when the selection is in a mixin
@ -538,12 +542,6 @@ class KeywordHelper {
addVariablePatternKeywords();
}
/// Add a keyword suggestion to suggest the [keyword].
void addPseudoKeyword(String keyword) {
collector
.addSuggestion(KeywordSuggestion.fromPseudoKeyword(keyword: keyword));
}
/// Add the keywords that are appropriate when the selection is at the
/// beginning of a statement. The [node] provides context to determine which
/// keywords to include.
@ -553,7 +551,7 @@ class KeywordHelper {
} else if (node.inAsyncStarOrSyncStarMethodOrFunction) {
addKeyword(Keyword.AWAIT);
addKeyword(Keyword.YIELD);
addKeywordFromText(Keyword.YIELD, '*');
addKeywordAndText(Keyword.YIELD, '*');
}
if (node.inLoop) {
addKeyword(Keyword.BREAK);
@ -585,11 +583,18 @@ class KeywordHelper {
addKeyword(Keyword.WHILE);
if (node.inAsyncStarOrSyncStarMethodOrFunction) {
addKeyword(Keyword.YIELD);
addKeywordFromText(Keyword.YIELD, '*');
addKeywordAndText(Keyword.YIELD, '*');
}
addKeyword(Keyword.LATE);
}
/// Add a keyword suggestion to suggest the [annotatedText].
void addText(String annotatedText) {
collector.addSuggestion(
KeywordSuggestion.fromText(annotatedText),
);
}
/// Add the keywords that are appropriate when the selection is after the
/// end of a `try` statement. [canHaveFinally] indicates whether it's valid to
/// suggest a `finally` clause.