@@ -189,10 +189,7 @@ bool CompletionInstance::performCachedOperaitonIfPossible(
189189 return false ;
190190
191191 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 ();
196193
197194 // Parse the new buffer into temporary SourceFile.
198195 SourceManager tmpSM;
@@ -204,94 +201,141 @@ bool CompletionInstance::performCachedOperaitonIfPossible(
204201 TypeCheckerOptions typeckOpts;
205202 SearchPathOptions searchPathOpts;
206203 DiagnosticEngine tmpDiags (tmpSM);
207- std::unique_ptr<ASTContext> Ctx (
204+ std::unique_ptr<ASTContext> tmpCtx (
208205 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 );
213210 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 ();
218215 // 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);
221218 // Couldn't find any completion token?
222219 if (!newState.hasCodeCompletionDelayedDeclState ())
223220 return false ;
224221
225222 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+ }
226338
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 ());
295339 if (DiagC)
296340 CI.addDiagnosticConsumer (DiagC);
297341
@@ -366,6 +410,9 @@ bool swift::ide::CompletionInstance::performOperation(
366410 // weaken that hash, disable them here:
367411 Invocation.getLangOptions ().EnableTypeFingerprints = false ;
368412
413+ // We don't need token list.
414+ Invocation.getLangOptions ().CollectParsedToken = false ;
415+
369416 // FIXME: ASTScopeLookup doesn't support code completion yet.
370417 Invocation.disableASTScopeLookup ();
371418
0 commit comments