@@ -9,7 +9,7 @@ import type { LangiumCompletionParser } from '../../parser/langium-parser.js';
99import type { NameProvider } from '../../references/name-provider.js' ;
1010import type { ScopeProvider } from '../../references/scope-provider.js' ;
1111import type { LangiumServices } from '../lsp-services.js' ;
12- import type { AstNode , AstNodeDescription , AstReflection , CstNode , MultiReference , Reference , ReferenceInfo } from '../../syntax-tree.js' ;
12+ import type { AstNode , AstNodeDescription , AstReflection , CstNode , MultiReference , Mutable , Reference , ReferenceInfo } from '../../syntax-tree.js' ;
1313import type { CancellationToken } from '../../utils/cancellation.js' ;
1414import type { MaybePromise } from '../../utils/promise-utils.js' ;
1515import type { LangiumDocument , TextDocument } from '../../workspace/documents.js' ;
@@ -25,7 +25,7 @@ import { CompletionItemKind, CompletionList, Position } from 'vscode-languageser
2525import * as ast from '../../languages/generated/ast.js' ;
2626import { assignMandatoryProperties , getContainerOfType } from '../../utils/ast-utils.js' ;
2727import { findDeclarationNodeAtOffset , findLeafNodeBeforeOffset , getDatatypeNode } from '../../utils/cst-utils.js' ;
28- import { getEntryRule , getExplicitRuleType } from '../../utils/grammar-utils.js' ;
28+ import { getEntryRule } from '../../utils/grammar-utils.js' ;
2929import { stream , type Stream } from '../../utils/stream.js' ;
3030import { findFirstFeatures , findNextFeatures } from './follow-element-computation.js' ;
3131
@@ -208,24 +208,48 @@ export class DefaultCompletionProvider implements CompletionProvider {
208208 // If the parser didn't parse any tokens, return the next features of the entry rule
209209 if ( parserResult . tokenIndex === 0 ) {
210210 const parserRule = getEntryRule ( this . grammar ) ! ;
211- const firstFeatures = findFirstFeatures ( {
212- feature : parserRule . definition ,
213- type : getExplicitRuleType ( parserRule )
214- } ) ;
215- if ( tokens . length > 0 ) {
216- // We have to skip the first token
217- // The interpreter will only look at the next features, which requires every token after the first
218- tokens . shift ( ) ;
219- return findNextFeatures ( firstFeatures . map ( e => [ e ] ) , tokens ) ;
220- } else {
221- return firstFeatures ;
222- }
211+ // Generate a synthetic RuleCall to the entry rule
212+ const syntheticEntryRuleCall = this . buildSyntheticEntryRuleCall ( parserRule ) ;
213+ return findNextFeatures ( [ [ syntheticEntryRuleCall ] ] , tokens ) ;
223214 }
224215 const leftoverTokens = [ ...tokens ] . splice ( parserResult . tokenIndex ) ;
225216 const features = findNextFeatures ( [ parserResult . elementStack . map ( feature => ( { feature } ) ) ] , leftoverTokens ) ;
226217 return features ;
227218 }
228219
220+ protected buildSyntheticEntryRuleCall ( rule : ast . ParserRule ) : NextFeature {
221+ // The "start" node is simply an empty group that is followed by the rule call
222+ const start : ast . Group = {
223+ $type : 'Group' ,
224+ $container : undefined ! ,
225+ elements : [ ]
226+ } ;
227+ const startNext : NextFeature < ast . Group > = {
228+ feature : start
229+ } ;
230+ // This is the element that we want to complete
231+ const ruleCall : ast . RuleCall = {
232+ $type : 'RuleCall' ,
233+ $container : undefined ! ,
234+ rule : {
235+ ref : rule ,
236+ $refText : rule . name
237+ } ,
238+ arguments : [ ]
239+ } ;
240+ const group : ast . Group = {
241+ $type : 'Group' ,
242+ $container : undefined ! ,
243+ elements : [
244+ start ,
245+ ruleCall
246+ ]
247+ } ;
248+ ( start as Mutable < AstNode > ) . $container = group ;
249+ ( ruleCall as Mutable < AstNode > ) . $container = group ;
250+ return startNext ;
251+ }
252+
229253 protected * buildContexts ( document : LangiumDocument , position : Position ) : IterableIterator < CompletionContext > {
230254 const cst = document . parseResult . value . $cstNode ;
231255 if ( ! cst ) {
@@ -303,7 +327,7 @@ export class DefaultCompletionProvider implements CompletionProvider {
303327 ...partialContext ,
304328 tokenOffset : nextTokenStart ,
305329 tokenEndOffset : nextTokenEnd ,
306- features : findFirstFeatures ( parserRule . definition )
330+ features : findFirstFeatures ( parserRule . definition ) . map ( f => f [ f . length - 1 ] ) ,
307331 } ;
308332 } else if ( performNextCompletion ) {
309333 // This context aims to complete the next feature, using the next cst start/end
0 commit comments