@@ -189,10 +189,7 @@ bool CompletionInstance::performCachedOperaitonIfPossible(
189
189
return false ;
190
190
191
191
auto &oldInfo = oldState.getCodeCompletionDelayedDeclState ();
192
-
193
- // Currently, only completions within a function body is supported.
194
- if (oldInfo.Kind != CodeCompletionDelayedDeclKind::FunctionBody)
195
- return false ;
192
+ auto *oldSF = oldInfo.ParentContext ->getParentSourceFile ();
196
193
197
194
// Parse the new buffer into temporary SourceFile.
198
195
SourceManager tmpSM;
@@ -204,94 +201,141 @@ bool CompletionInstance::performCachedOperaitonIfPossible(
204
201
TypeCheckerOptions typeckOpts;
205
202
SearchPathOptions searchPathOpts;
206
203
DiagnosticEngine tmpDiags (tmpSM);
207
- std::unique_ptr<ASTContext> Ctx (
204
+ std::unique_ptr<ASTContext> tmpCtx (
208
205
ASTContext::get (langOpts, typeckOpts, searchPathOpts, tmpSM, tmpDiags));
209
- registerIDERequestFunctions (Ctx ->evaluator );
210
- registerTypeCheckerRequestFunctions (Ctx ->evaluator );
211
- registerSILGenRequestFunctions (Ctx ->evaluator );
212
- ModuleDecl *M = ModuleDecl::create (Identifier (), *Ctx );
206
+ registerIDERequestFunctions (tmpCtx ->evaluator );
207
+ registerTypeCheckerRequestFunctions (tmpCtx ->evaluator );
208
+ registerSILGenRequestFunctions (tmpCtx ->evaluator );
209
+ ModuleDecl *tmpM = ModuleDecl::create (Identifier (), *tmpCtx );
213
210
PersistentParserState newState;
214
- SourceFile *newSF =
215
- new (*Ctx ) SourceFile (*M, SourceFileKind::Library , tmpBufferID,
216
- SourceFile::ImplicitModuleImportKind::None);
217
- newSF ->enableInterfaceHash ();
211
+ SourceFile *tmpSF =
212
+ new (*tmpCtx ) SourceFile (*tmpM, oldSF-> Kind , tmpBufferID,
213
+ SourceFile::ImplicitModuleImportKind::None);
214
+ tmpSF ->enableInterfaceHash ();
218
215
// Ensure all non-function-body tokens are hashed into the interface hash
219
- Ctx ->LangOpts .EnableTypeFingerprints = false ;
220
- parseIntoSourceFile (*newSF , tmpBufferID, &newState);
216
+ tmpCtx ->LangOpts .EnableTypeFingerprints = false ;
217
+ parseIntoSourceFile (*tmpSF , tmpBufferID, &newState);
221
218
// Couldn't find any completion token?
222
219
if (!newState.hasCodeCompletionDelayedDeclState ())
223
220
return false ;
224
221
225
222
auto &newInfo = newState.getCodeCompletionDelayedDeclState ();
223
+ unsigned newBufferID;
224
+
225
+ switch (newInfo.Kind ) {
226
+ case CodeCompletionDelayedDeclKind::FunctionBody: {
227
+ // If the interface has changed, AST must be refreshed.
228
+ llvm::SmallString<32 > oldInterfaceHash{};
229
+ llvm::SmallString<32 > newInterfaceHash{};
230
+ oldSF->getInterfaceHash (oldInterfaceHash);
231
+ tmpSF->getInterfaceHash (newInterfaceHash);
232
+ if (oldInterfaceHash != newInterfaceHash)
233
+ return false ;
234
+
235
+ DeclContext *DC =
236
+ getEquivalentDeclContextFromSourceFile (newInfo.ParentContext , oldSF);
237
+ if (!DC)
238
+ return false ;
239
+
240
+ // OK, we can perform fast completion for this. Update the orignal delayed
241
+ // decl state.
242
+
243
+ // Fast completion keeps the buffer in memory for multiple completions.
244
+ // To reduce the consumption, slice the source buffer so it only holds
245
+ // the portion that is needed for the second pass.
246
+ auto startOffset = newInfo.StartOffset ;
247
+ if (newInfo.PrevOffset != ~0u )
248
+ startOffset = newInfo.PrevOffset ;
249
+ auto startLoc = tmpSM.getLocForOffset (tmpBufferID, startOffset);
250
+ startLoc = Lexer::getLocForStartOfLine (tmpSM, startLoc);
251
+ startOffset = tmpSM.getLocOffsetInBuffer (startLoc, tmpBufferID);
252
+
253
+ auto endOffset = newInfo.EndOffset ;
254
+ auto endLoc = tmpSM.getLocForOffset (tmpBufferID, endOffset);
255
+ endLoc = Lexer::getLocForEndOfToken (tmpSM, endLoc);
256
+ endOffset = tmpSM.getLocOffsetInBuffer (endLoc, tmpBufferID);
257
+
258
+ newInfo.StartOffset -= startOffset;
259
+ newInfo.EndOffset -= startOffset;
260
+ if (newInfo.PrevOffset != ~0u )
261
+ newInfo.PrevOffset -= startOffset;
262
+
263
+ auto sourceText =
264
+ completionBuffer->getBuffer ().slice (startOffset, endOffset);
265
+ auto newOffset = Offset - startOffset;
266
+
267
+ newBufferID = SM.addMemBufferCopy (sourceText,
268
+ completionBuffer->getBufferIdentifier ());
269
+ SM.openVirtualFile (SM.getLocForBufferStart (newBufferID),
270
+ tmpSM.getDisplayNameForLoc (startLoc),
271
+ tmpSM.getLineAndColumn (startLoc).first - 1 );
272
+ SM.setCodeCompletionPoint (newBufferID, newOffset);
273
+
274
+ // Construct dummy scopes. We don't need to restore the original scope
275
+ // because they are probably not 'isResolvable()' anyway.
276
+ auto &SI = oldState.getScopeInfo ();
277
+ assert (SI.getCurrentScope () == nullptr );
278
+ Scope Top (SI, ScopeKind::TopLevel);
279
+ Scope Body (SI, ScopeKind::FunctionBody);
280
+
281
+ oldInfo.ParentContext = DC;
282
+ oldInfo.StartOffset = newInfo.StartOffset ;
283
+ oldInfo.EndOffset = newInfo.EndOffset ;
284
+ oldInfo.PrevOffset = newInfo.PrevOffset ;
285
+ oldState.restoreCodeCompletionDelayedDeclState (oldInfo);
286
+
287
+ auto *AFD = cast<AbstractFunctionDecl>(DC);
288
+ if (AFD->isBodySkipped ())
289
+ AFD->setBodyDelayed (AFD->getBodySourceRange ());
290
+
291
+ break ;
292
+ }
293
+ case CodeCompletionDelayedDeclKind::Decl:
294
+ case CodeCompletionDelayedDeclKind::TopLevelCodeDecl: {
295
+ // Support decl/top-level code only if the completion happens in a single
296
+ // file 'main' script (e.g. playground).
297
+ auto *oldM = oldInfo.ParentContext ->getParentModule ();
298
+ if (oldM->getFiles ().size () != 1 || oldSF->Kind != SourceFileKind::Main)
299
+ return false ;
300
+
301
+ // Perform fast completion.
302
+
303
+ // Prepare the new buffer in the source manager.
304
+ auto sourceText = completionBuffer->getBuffer ();
305
+ if (newInfo.Kind == CodeCompletionDelayedDeclKind::TopLevelCodeDecl) {
306
+ // We don't need the source text after the top-level code.
307
+ auto endOffset = newInfo.EndOffset ;
308
+ auto endLoc = tmpSM.getLocForOffset (tmpBufferID, endOffset);
309
+ endLoc = Lexer::getLocForEndOfToken (tmpSM, endLoc);
310
+ endOffset = tmpSM.getLocOffsetInBuffer (endLoc, tmpBufferID);
311
+ sourceText = sourceText.slice (0 , endOffset);
312
+ }
313
+ newBufferID = SM.addMemBufferCopy (sourceText,
314
+ completionBuffer->getBufferIdentifier ());
315
+ SM.setCodeCompletionPoint (newBufferID, Offset);
316
+
317
+ // Create a new module and a source file using the current AST context.
318
+ auto &Ctx = oldM->getASTContext ();
319
+ auto newM = ModuleDecl::create (oldM->getName (), Ctx);
320
+ CompilerInstance::ImplicitImports implicitImport (CI);
321
+ SourceFile *newSF = new (Ctx) SourceFile (*newM, SourceFileKind::Main,
322
+ newBufferID, implicitImport.kind );
323
+ newM->addFile (*newSF);
324
+ CompilerInstance::addAdditionalInitialImportsTo (newSF, implicitImport);
325
+ newSF->enableInterfaceHash ();
326
+
327
+ // Re-parse the whole file. Still re-use imported modules.
328
+ (void )oldState.takeCodeCompletionDelayedDeclState ();
329
+ parseIntoSourceFile (*newSF, newBufferID, &oldState);
330
+ performNameBinding (*newSF);
331
+ bindExtensions (*newSF);
332
+
333
+ assert (oldState.hasCodeCompletionDelayedDeclState () &&
334
+ oldState.getCodeCompletionDelayedDeclState ().Kind == newInfo.Kind );
335
+ break ;
336
+ }
337
+ }
226
338
227
- // The new completion must happens in function body too.
228
- if (newInfo.Kind != CodeCompletionDelayedDeclKind::FunctionBody)
229
- return false ;
230
-
231
- auto *oldSF = oldInfo.ParentContext ->getParentSourceFile ();
232
-
233
- // If the interface has changed, AST must be refreshed.
234
- llvm::SmallString<32 > oldInterfaceHash{};
235
- llvm::SmallString<32 > newInterfaceHash{};
236
- oldSF->getInterfaceHash (oldInterfaceHash);
237
- newSF->getInterfaceHash (newInterfaceHash);
238
- if (oldInterfaceHash != newInterfaceHash)
239
- return false ;
240
-
241
- DeclContext *DC =
242
- getEquivalentDeclContextFromSourceFile (newInfo.ParentContext , oldSF);
243
- if (!DC)
244
- return false ;
245
-
246
- // OK, we can perform fast completion for this. Update the orignal delayed
247
- // decl state.
248
-
249
- // Fast completion keeps the buffer in memory for multiple completions.
250
- // To reduce the consumption, slice the source buffer so it only holds
251
- // the portion that is needed for the second pass.
252
- auto startOffset = newInfo.StartOffset ;
253
- if (newInfo.PrevOffset != ~0u )
254
- startOffset = newInfo.PrevOffset ;
255
- auto startLoc = tmpSM.getLocForOffset (tmpBufferID, startOffset);
256
- startLoc = Lexer::getLocForStartOfLine (tmpSM, startLoc);
257
- startOffset = tmpSM.getLocOffsetInBuffer (startLoc, tmpBufferID);
258
-
259
- auto endOffset = newInfo.EndOffset ;
260
- auto endLoc = tmpSM.getLocForOffset (tmpBufferID, endOffset);
261
- endLoc = Lexer::getLocForEndOfToken (tmpSM, endLoc);
262
- endOffset = tmpSM.getLocOffsetInBuffer (endLoc, tmpBufferID);
263
-
264
- newInfo.StartOffset -= startOffset;
265
- newInfo.EndOffset -= startOffset;
266
- if (newInfo.PrevOffset != ~0u )
267
- newInfo.PrevOffset -= startOffset;
268
-
269
- auto sourceText = completionBuffer->getBuffer ().slice (startOffset, endOffset);
270
- auto newOffset = Offset - startOffset;
271
-
272
- auto newBufferID =
273
- SM.addMemBufferCopy (sourceText, completionBuffer->getBufferIdentifier ());
274
- SM.openVirtualFile (SM.getLocForBufferStart (newBufferID),
275
- tmpSM.getDisplayNameForLoc (startLoc),
276
- tmpSM.getLineAndColumn (startLoc).first - 1 );
277
- SM.setCodeCompletionPoint (newBufferID, newOffset);
278
-
279
- // Construct dummy scopes. We don't need to restore the original scope
280
- // because they are probably not 'isResolvable()' anyway.
281
- auto &SI = oldState.getScopeInfo ();
282
- assert (SI.getCurrentScope () == nullptr );
283
- Scope Top (SI, ScopeKind::TopLevel);
284
- Scope Body (SI, ScopeKind::FunctionBody);
285
-
286
- oldInfo.ParentContext = DC;
287
- oldInfo.StartOffset = newInfo.StartOffset ;
288
- oldInfo.EndOffset = newInfo.EndOffset ;
289
- oldInfo.PrevOffset = newInfo.PrevOffset ;
290
- oldState.restoreCodeCompletionDelayedDeclState (oldInfo);
291
-
292
- auto *AFD = cast<AbstractFunctionDecl>(DC);
293
- if (AFD->isBodySkipped ())
294
- AFD->setBodyDelayed (AFD->getBodySourceRange ());
295
339
if (DiagC)
296
340
CI.addDiagnosticConsumer (DiagC);
297
341
@@ -366,6 +410,9 @@ bool swift::ide::CompletionInstance::performOperation(
366
410
// weaken that hash, disable them here:
367
411
Invocation.getLangOptions ().EnableTypeFingerprints = false ;
368
412
413
+ // We don't need token list.
414
+ Invocation.getLangOptions ().CollectParsedToken = false ;
415
+
369
416
// FIXME: ASTScopeLookup doesn't support code completion yet.
370
417
Invocation.disableASTScopeLookup ();
371
418
0 commit comments