diff --git a/clang-tools-extra/clangd/CodeComplete.h b/clang-tools-extra/clangd/CodeComplete.h --- a/clang-tools-extra/clangd/CodeComplete.h +++ b/clang-tools-extra/clangd/CodeComplete.h @@ -175,9 +175,13 @@ // thse includes may not be accurate for all of them. llvm::SmallVector Includes; + struct FixIt { + std::string Before; + TextEdit Edit; + }; /// Holds information about small corrections that needs to be done. Like /// converting '->' to '.' on member access. - std::vector FixIts; + std::vector FixIts; /// Holds the range of the token we are going to replace with this completion. Range CompletionTokenRange; diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -277,12 +277,25 @@ Completion.Name.back() == '/') Completion.Kind = CompletionItemKind::Folder; for (const auto &FixIt : C.SemaResult->FixIts) { - Completion.FixIts.push_back(toTextEdit( - FixIt, ASTCtx->getSourceManager(), ASTCtx->getLangOpts())); + // FIXME: this should live in SourceCode.h + auto &SM = ASTCtx->getSourceManager(); + CharSourceRange Range = Lexer::makeFileCharRange(FixIt.RemoveRange, SM, + ASTCtx->getLangOpts()); + FileID FID; + unsigned StartOffset; + std::tie(FID, StartOffset) = SM.getDecomposedLoc(Range.getBegin()); + + std::string Code = SM.getBufferData(FID).substr( + StartOffset, SM.getFileOffset(Range.getEnd()) - StartOffset); + + Completion.FixIts.push_back(CodeCompletion::FixIt{ + std::move(Code), toTextEdit(FixIt, ASTCtx->getSourceManager(), + ASTCtx->getLangOpts())}); } - llvm::sort(Completion.FixIts, [](const TextEdit &X, const TextEdit &Y) { - return std::tie(X.range.start.line, X.range.start.character) < - std::tie(Y.range.start.line, Y.range.start.character); + llvm::sort(Completion.FixIts, [](const CodeCompletion::FixIt &X, + const CodeCompletion::FixIt &Y) { + return std::tie(X.Edit.range.start.line, X.Edit.range.start.character) < + std::tie(Y.Edit.range.start.line, Y.Edit.range.start.character); }); Completion.Deprecated |= (C.SemaResult->Availability == CXAvailability_Deprecated); @@ -1817,11 +1830,12 @@ // is mainly to help LSP clients again, so that changes do not effect each // other. for (const auto &FixIt : FixIts) { - if (isRangeConsecutive(FixIt.range, LSP.textEdit->range)) { - LSP.textEdit->newText = FixIt.newText + LSP.textEdit->newText; - LSP.textEdit->range.start = FixIt.range.start; + if (isRangeConsecutive(FixIt.Edit.range, LSP.textEdit->range)) { + LSP.textEdit->newText = FixIt.Edit.newText + LSP.textEdit->newText; + LSP.textEdit->range.start = FixIt.Edit.range.start; + LSP.filterText = FixIt.Before + LSP.filterText; } else { - LSP.additionalTextEdits.push_back(FixIt); + LSP.additionalTextEdits.push_back(FixIt.Edit); } } if (Opts.EnableSnippets) diff --git a/clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts b/clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts --- a/clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts +++ b/clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts @@ -65,6 +65,14 @@ } } +class EnableEditsNearCursorFeature implements vscodelc.StaticFeature { + fillClientCapabilities(capabilities: vscodelc.ClientCapabilities): void { + const extendedCompletionCapabilities: any = capabilities.textDocument.completion; + extendedCompletionCapabilities.editsNearCursor = true; + } + initialize(capabilities: vscodelc.ServerCapabilities, documentSelector: (string | { language: string; scheme?: string; pattern?: string; } | { language?: string; scheme: string; pattern?: string; } | { language?: string; scheme?: string; pattern: string; })[]): void { + } +} /** * this method is called when your extension is activate * your extension is activated the very first time the command is executed @@ -114,6 +122,7 @@ context.subscriptions.push( vscode.Disposable.from(semanticHighlightingFeature)); clangdClient.registerFeature(semanticHighlightingFeature); + clangdClient.registerFeature(new EnableEditsNearCursorFeature); console.log('Clang Language Server is now active!'); context.subscriptions.push(clangdClient.start()); context.subscriptions.push(vscode.commands.registerCommand(