1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "clangeditordocumentprocessor.h"
27 
28 #include "clangbackendcommunicator.h"
29 #include "clangdiagnostictooltipwidget.h"
30 #include "clangfixitoperation.h"
31 #include "clangfixitoperationsextractor.h"
32 #include "clangmodelmanagersupport.h"
33 #include "clanghighlightingresultreporter.h"
34 #include "clangutils.h"
35 
36 #include <diagnosticcontainer.h>
37 #include <sourcelocationcontainer.h>
38 
39 #include <cpptools/builtincursorinfo.h>
40 #include <cpptools/clangdiagnosticconfigsmodel.h>
41 #include <cpptools/compileroptionsbuilder.h>
42 #include <cpptools/cppcodemodelsettings.h>
43 #include <cpptools/cppmodelmanager.h>
44 #include <cpptools/cpptoolsbridge.h>
45 #include <cpptools/cpptoolsreuse.h>
46 #include <cpptools/cppworkingcopy.h>
47 #include <cpptools/editordocumenthandle.h>
48 
49 #include <texteditor/fontsettings.h>
50 #include <texteditor/texteditor.h>
51 #include <texteditor/texteditorconstants.h>
52 #include <texteditor/texteditorsettings.h>
53 
54 #include <cplusplus/CppDocument.h>
55 
56 #include <utils/algorithm.h>
57 #include <utils/textutils.h>
58 #include <utils/qtcassert.h>
59 #include <utils/runextensions.h>
60 
61 #include <QTextBlock>
62 #include <QVBoxLayout>
63 #include <QWidget>
64 
65 namespace ClangCodeModel {
66 namespace Internal {
67 
ClangEditorDocumentProcessor(BackendCommunicator & communicator,TextEditor::TextDocument * document)68 ClangEditorDocumentProcessor::ClangEditorDocumentProcessor(
69         BackendCommunicator &communicator,
70         TextEditor::TextDocument *document)
71     : BaseEditorDocumentProcessor(document->document(), document->filePath().toString())
72     , m_document(*document)
73     , m_diagnosticManager(document)
74     , m_communicator(communicator)
75     , m_parser(new ClangEditorDocumentParser(document->filePath().toString()))
76     , m_parserRevision(0)
77     , m_semanticHighlighter(document)
78     , m_builtinProcessor(document, /*enableSemanticHighlighter=*/ false)
79 {
80     m_updateBackendDocumentTimer.setSingleShot(true);
81     m_updateBackendDocumentTimer.setInterval(350);
82     connect(&m_updateBackendDocumentTimer, &QTimer::timeout,
83             this, &ClangEditorDocumentProcessor::updateBackendDocumentIfProjectPartExists);
84 
85     connect(m_parser.data(), &ClangEditorDocumentParser::projectPartInfoUpdated,
86             this, &BaseEditorDocumentProcessor::projectPartInfoUpdated);
87 
88     // Forwarding the semantic info from the builtin processor enables us to provide all
89     // editor (widget) related features that are not yet implemented by the clang plugin.
90     connect(&m_builtinProcessor, &CppTools::BuiltinEditorDocumentProcessor::cppDocumentUpdated,
91             this, &ClangEditorDocumentProcessor::cppDocumentUpdated);
92     connect(&m_builtinProcessor, &CppTools::BuiltinEditorDocumentProcessor::semanticInfoUpdated,
93             this, &ClangEditorDocumentProcessor::semanticInfoUpdated);
94 
95     m_parserSynchronizer.setCancelOnWait(true);
96 }
97 
~ClangEditorDocumentProcessor()98 ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor()
99 {
100     m_updateBackendDocumentTimer.stop();
101 
102     if (m_projectPart)
103         closeBackendDocument();
104 }
105 
runImpl(const CppTools::BaseEditorDocumentParser::UpdateParams & updateParams)106 void ClangEditorDocumentProcessor::runImpl(
107         const CppTools::BaseEditorDocumentParser::UpdateParams &updateParams)
108 {
109     m_updateBackendDocumentTimer.start();
110 
111     // Run clang parser
112     disconnect(&m_parserWatcher, &QFutureWatcher<void>::finished,
113                this, &ClangEditorDocumentProcessor::onParserFinished);
114     m_parserWatcher.cancel();
115     m_parserWatcher.setFuture(QFuture<void>());
116 
117     m_parserRevision = revision();
118     connect(&m_parserWatcher, &QFutureWatcher<void>::finished,
119             this, &ClangEditorDocumentProcessor::onParserFinished);
120     const QFuture<void> future = ::Utils::runAsync(&runParser, parser(), updateParams);
121     m_parserWatcher.setFuture(future);
122     m_parserSynchronizer.addFuture(future);
123 
124     // Run builtin processor
125     m_builtinProcessor.runImpl(updateParams);
126 }
127 
recalculateSemanticInfoDetached(bool force)128 void ClangEditorDocumentProcessor::recalculateSemanticInfoDetached(bool force)
129 {
130     m_builtinProcessor.recalculateSemanticInfoDetached(force);
131 }
132 
semanticRehighlight()133 void ClangEditorDocumentProcessor::semanticRehighlight()
134 {
135     const auto matchesEditor = [this](const Core::IEditor *editor) {
136         return editor->document()->filePath() == m_document.filePath();
137     };
138     if (!Utils::contains(Core::EditorManager::visibleEditors(), matchesEditor))
139         return;
140     if (ClangModelManagerSupport::instance()->clientForFile(m_document.filePath()))
141         return;
142 
143     m_semanticHighlighter.updateFormatMapFromFontSettings();
144     if (m_projectPart)
145         requestAnnotationsFromBackend();
146 }
147 
recalculateSemanticInfo()148 CppTools::SemanticInfo ClangEditorDocumentProcessor::recalculateSemanticInfo()
149 {
150     return m_builtinProcessor.recalculateSemanticInfo();
151 }
152 
parser()153 CppTools::BaseEditorDocumentParser::Ptr ClangEditorDocumentProcessor::parser()
154 {
155     return m_parser;
156 }
157 
snapshot()158 CPlusPlus::Snapshot ClangEditorDocumentProcessor::snapshot()
159 {
160    return m_builtinProcessor.snapshot();
161 }
162 
isParserRunning() const163 bool ClangEditorDocumentProcessor::isParserRunning() const
164 {
165     return m_parserWatcher.isRunning();
166 }
167 
hasProjectPart() const168 bool ClangEditorDocumentProcessor::hasProjectPart() const
169 {
170     return !m_projectPart.isNull();
171 }
172 
projectPart() const173 CppTools::ProjectPart::Ptr ClangEditorDocumentProcessor::projectPart() const
174 {
175     return m_projectPart;
176 }
177 
clearProjectPart()178 void ClangEditorDocumentProcessor::clearProjectPart()
179 {
180     m_projectPart.clear();
181 }
182 
diagnosticConfigId() const183 ::Utils::Id ClangEditorDocumentProcessor::diagnosticConfigId() const
184 {
185     return m_diagnosticConfigId;
186 }
187 
updateCodeWarnings(const QVector<ClangBackEnd::DiagnosticContainer> & diagnostics,const ClangBackEnd::DiagnosticContainer & firstHeaderErrorDiagnostic,uint documentRevision)188 void ClangEditorDocumentProcessor::updateCodeWarnings(
189         const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
190         const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic,
191         uint documentRevision)
192 {
193     if (ClangModelManagerSupport::instance()->clientForFile(m_document.filePath()))
194         return;
195 
196     if (documentRevision == revision()) {
197         if (m_invalidationState == InvalidationState::Scheduled)
198             m_invalidationState = InvalidationState::Canceled;
199         m_diagnosticManager.processNewDiagnostics(diagnostics, m_isProjectFile);
200         const auto codeWarnings = m_diagnosticManager.takeExtraSelections();
201         const auto fixitAvailableMarkers = m_diagnosticManager.takeFixItAvailableMarkers();
202         const auto creator = creatorForHeaderErrorDiagnosticWidget(firstHeaderErrorDiagnostic);
203 
204         emit codeWarningsUpdated(revision(),
205                                  codeWarnings,
206                                  creator,
207                                  fixitAvailableMarkers);
208     }
209 }
210 namespace {
211 
212 TextEditor::BlockRange
toTextEditorBlock(QTextDocument * textDocument,const ClangBackEnd::SourceRangeContainer & sourceRangeContainer)213 toTextEditorBlock(QTextDocument *textDocument,
214                   const ClangBackEnd::SourceRangeContainer &sourceRangeContainer)
215 {
216     return {::Utils::Text::positionInText(textDocument,
217                                           sourceRangeContainer.start.line,
218                                           sourceRangeContainer.start.column),
219             ::Utils::Text::positionInText(textDocument,
220                                           sourceRangeContainer.end.line,
221                                           sourceRangeContainer.end.column)};
222 }
223 
224 QList<TextEditor::BlockRange>
toTextEditorBlocks(QTextDocument * textDocument,const QVector<ClangBackEnd::SourceRangeContainer> & ifdefedOutRanges)225 toTextEditorBlocks(QTextDocument *textDocument,
226                    const QVector<ClangBackEnd::SourceRangeContainer> &ifdefedOutRanges)
227 {
228     QList<TextEditor::BlockRange> blockRanges;
229     blockRanges.reserve(ifdefedOutRanges.size());
230 
231     for (const auto &range : ifdefedOutRanges)
232         blockRanges.append(toTextEditorBlock(textDocument, range));
233 
234     return blockRanges;
235 }
236 }
237 
238 const QVector<ClangBackEnd::TokenInfoContainer>
tokenInfos() const239 &ClangEditorDocumentProcessor::tokenInfos() const
240 {
241     return m_tokenInfos;
242 }
243 
clearTaskHubIssues()244 void ClangEditorDocumentProcessor::clearTaskHubIssues()
245 {
246     ClangDiagnosticManager::clearTaskHubIssues();
247 }
248 
generateTaskHubIssues()249 void ClangEditorDocumentProcessor::generateTaskHubIssues()
250 {
251     m_diagnosticManager.generateTaskHubIssues();
252 }
253 
clearTextMarks(const Utils::FilePath & filePath)254 void ClangEditorDocumentProcessor::clearTextMarks(const Utils::FilePath &filePath)
255 {
256     if (ClangEditorDocumentProcessor * const proc = get(filePath.toString())) {
257         proc->m_diagnosticManager.cleanMarks();
258         emit proc->codeWarningsUpdated(proc->revision(), {}, {}, {});
259     }
260 }
261 
updateHighlighting(const QVector<ClangBackEnd::TokenInfoContainer> & tokenInfos,const QVector<ClangBackEnd::SourceRangeContainer> & skippedPreprocessorRanges,uint documentRevision)262 void ClangEditorDocumentProcessor::updateHighlighting(
263         const QVector<ClangBackEnd::TokenInfoContainer> &tokenInfos,
264         const QVector<ClangBackEnd::SourceRangeContainer> &skippedPreprocessorRanges,
265         uint documentRevision)
266 {
267     if (ClangModelManagerSupport::instance()->clientForFile(m_document.filePath()))
268         return;
269     if (documentRevision == revision()) {
270         const auto skippedPreprocessorBlocks = toTextEditorBlocks(textDocument(), skippedPreprocessorRanges);
271         emit ifdefedOutBlocksUpdated(documentRevision, skippedPreprocessorBlocks);
272 
273         m_semanticHighlighter.setHighlightingRunner(
274             [tokenInfos]() {
275                 auto *reporter = new HighlightingResultReporter(tokenInfos);
276                 return reporter->start();
277             });
278         m_semanticHighlighter.run();
279     }
280 }
281 
updateTokenInfos(const QVector<ClangBackEnd::TokenInfoContainer> & tokenInfos,uint documentRevision)282 void ClangEditorDocumentProcessor::updateTokenInfos(
283         const QVector<ClangBackEnd::TokenInfoContainer> &tokenInfos,
284         uint documentRevision)
285 {
286     if (documentRevision != revision())
287         return;
288     m_tokenInfos = tokenInfos;
289     emit tokenInfosUpdated();
290 }
291 
currentLine(const TextEditor::AssistInterface & assistInterface)292 static int currentLine(const TextEditor::AssistInterface &assistInterface)
293 {
294     int line, column;
295     ::Utils::Text::convertPosition(assistInterface.textDocument(), assistInterface.position(),
296                                    &line, &column);
297     return line;
298 }
299 
extraRefactoringOperations(const TextEditor::AssistInterface & assistInterface)300 TextEditor::QuickFixOperations ClangEditorDocumentProcessor::extraRefactoringOperations(
301         const TextEditor::AssistInterface &assistInterface)
302 {
303     ClangFixItOperationsExtractor extractor(m_diagnosticManager.diagnosticsWithFixIts());
304 
305     return extractor.extract(assistInterface.filePath().toString(), currentLine(assistInterface));
306 }
307 
editorDocumentTimerRestarted()308 void ClangEditorDocumentProcessor::editorDocumentTimerRestarted()
309 {
310     m_updateBackendDocumentTimer.stop(); // Wait for the next call to run().
311     m_invalidationState = InvalidationState::Scheduled;
312 }
313 
invalidateDiagnostics()314 void ClangEditorDocumentProcessor::invalidateDiagnostics()
315 {
316     if (m_invalidationState != InvalidationState::Canceled)
317         m_diagnosticManager.invalidateDiagnostics();
318     m_invalidationState = InvalidationState::Off;
319 }
320 
diagnosticTextMarksAt(uint line,uint column) const321 TextEditor::TextMarks ClangEditorDocumentProcessor::diagnosticTextMarksAt(uint line,
322                                                                           uint column) const
323 {
324     return m_diagnosticManager.diagnosticTextMarksAt(line, column);
325 }
326 
setParserConfig(const CppTools::BaseEditorDocumentParser::Configuration & config)327 void ClangEditorDocumentProcessor::setParserConfig(
328         const CppTools::BaseEditorDocumentParser::Configuration &config)
329 {
330     m_parser->setConfiguration(config);
331     m_builtinProcessor.parser()->setConfiguration(config);
332 }
333 
isCursorOnIdentifier(const QTextCursor & textCursor)334 static bool isCursorOnIdentifier(const QTextCursor &textCursor)
335 {
336     QTextDocument *document = textCursor.document();
337     return CppTools::isValidIdentifierChar(document->characterAt(textCursor.position()));
338 }
339 
defaultCursorInfoFuture()340 static QFuture<CppTools::CursorInfo> defaultCursorInfoFuture()
341 {
342     QFutureInterface<CppTools::CursorInfo> futureInterface;
343     futureInterface.reportResult(CppTools::CursorInfo());
344     futureInterface.reportFinished();
345 
346     return futureInterface.future();
347 }
348 
convertPosition(const QTextCursor & textCursor,int * line,int * column)349 static bool convertPosition(const QTextCursor &textCursor, int *line, int *column)
350 {
351     const bool converted = ::Utils::Text::convertPosition(textCursor.document(),
352                                                           textCursor.position(),
353                                                           line,
354                                                           column);
355     QTC_CHECK(converted);
356     return converted;
357 }
358 
359 QFuture<CppTools::CursorInfo>
cursorInfo(const CppTools::CursorInfoParams & params)360 ClangEditorDocumentProcessor::cursorInfo(const CppTools::CursorInfoParams &params)
361 {
362     int line, column;
363     convertPosition(params.textCursor, &line, &column);
364 
365     if (!isCursorOnIdentifier(params.textCursor))
366         return defaultCursorInfoFuture();
367 
368     column = clangColumn(params.textCursor.document()->findBlockByNumber(line - 1), column);
369     const CppTools::SemanticInfo::LocalUseMap localUses
370         = CppTools::BuiltinCursorInfo::findLocalUses(params.semanticInfo.doc, line, column);
371 
372     return m_communicator.requestReferences(simpleFileContainer(),
373                                             static_cast<quint32>(line),
374                                             static_cast<quint32>(column),
375                                             localUses);
376 }
377 
requestLocalReferences(const QTextCursor & cursor)378 QFuture<CppTools::CursorInfo> ClangEditorDocumentProcessor::requestLocalReferences(
379         const QTextCursor &cursor)
380 {
381     int line, column;
382     convertPosition(cursor, &line, &column);
383     ++column; // for 1-based columns
384 
385     // TODO: check that by highlighting items
386     if (!isCursorOnIdentifier(cursor))
387         return defaultCursorInfoFuture();
388 
389     return m_communicator.requestLocalReferences(simpleFileContainer(),
390                                                  static_cast<quint32>(line),
391                                                  static_cast<quint32>(column));
392 }
393 
394 QFuture<CppTools::SymbolInfo>
requestFollowSymbol(int line,int column)395 ClangEditorDocumentProcessor::requestFollowSymbol(int line, int column)
396 {
397     return m_communicator.requestFollowSymbol(simpleFileContainer(),
398                                               static_cast<quint32>(line),
399                                               static_cast<quint32>(column));
400 }
401 
toolTipInfo(const QByteArray & codecName,int line,int column)402 QFuture<CppTools::ToolTipInfo> ClangEditorDocumentProcessor::toolTipInfo(const QByteArray &codecName,
403                                                                          int line,
404                                                                          int column)
405 {
406     return m_communicator.requestToolTip(simpleFileContainer(codecName),
407                                          static_cast<quint32>(line),
408                                          static_cast<quint32>(column));
409 }
410 
clearDiagnosticsWithFixIts()411 void ClangEditorDocumentProcessor::clearDiagnosticsWithFixIts()
412 {
413     m_diagnosticManager.clearDiagnosticsWithFixIts();
414 }
415 
get(const QString & filePath)416 ClangEditorDocumentProcessor *ClangEditorDocumentProcessor::get(const QString &filePath)
417 {
418     auto *processor = CppTools::CppToolsBridge::baseEditorDocumentProcessor(filePath);
419 
420     return qobject_cast<ClangEditorDocumentProcessor*>(processor);
421 }
422 
isProjectPartLoadedOrIsFallback(CppTools::ProjectPart::Ptr projectPart)423 static bool isProjectPartLoadedOrIsFallback(CppTools::ProjectPart::Ptr projectPart)
424 {
425     return projectPart
426         && (projectPart->id().isEmpty() || isProjectPartLoaded(projectPart));
427 }
428 
updateBackendProjectPartAndDocument()429 void ClangEditorDocumentProcessor::updateBackendProjectPartAndDocument()
430 {
431     const CppTools::ProjectPart::Ptr projectPart = m_parser->projectPartInfo().projectPart;
432 
433     if (isProjectPartLoadedOrIsFallback(projectPart)) {
434         updateBackendDocument(*projectPart.data());
435 
436         m_projectPart = projectPart;
437         m_isProjectFile = m_parser->projectPartInfo().hints
438                 & CppTools::ProjectPartInfo::IsFromProjectMatch;
439     }
440 }
441 
onParserFinished()442 void ClangEditorDocumentProcessor::onParserFinished()
443 {
444     if (revision() != m_parserRevision)
445         return;
446 
447     updateBackendProjectPartAndDocument();
448 }
449 
updateBackendDocument(CppTools::ProjectPart & projectPart)450 void ClangEditorDocumentProcessor::updateBackendDocument(CppTools::ProjectPart &projectPart)
451 {
452     // On registration we send the document content immediately as an unsaved
453     // file, because
454     //   (1) a refactoring action might have opened and already modified
455     //       this document.
456     //   (2) it prevents an extra preamble generation on first user
457     //       modification of the document in case the line endings on disk
458     //       differ from the ones returned by textDocument()->toPlainText(),
459     //       like on Windows.
460 
461     if (m_projectPart) {
462         if (projectPart.id() == m_projectPart->id())
463             return;
464     }
465 
466     const auto clangOptions = createClangOptions(projectPart, filePath());
467     m_diagnosticConfigId = clangOptions.first;
468 
469     m_communicator.documentsOpened(
470         {fileContainerWithOptionsAndDocumentContent(clangOptions.second, projectPart.headerPaths)});
471     setLastSentDocumentRevision(filePath(), revision());
472 }
473 
closeBackendDocument()474 void ClangEditorDocumentProcessor::closeBackendDocument()
475 {
476     QTC_ASSERT(m_projectPart, return);
477     m_communicator.documentsClosed({ClangBackEnd::FileContainer(filePath(), m_projectPart->id())});
478 }
479 
updateBackendDocumentIfProjectPartExists()480 void ClangEditorDocumentProcessor::updateBackendDocumentIfProjectPartExists()
481 {
482     if (m_projectPart) {
483         const ClangBackEnd::FileContainer fileContainer = fileContainerWithDocumentContent();
484         m_communicator.documentsChangedWithRevisionCheck(fileContainer);
485     }
486 }
487 
requestAnnotationsFromBackend()488 void ClangEditorDocumentProcessor::requestAnnotationsFromBackend()
489 {
490     const auto fileContainer = fileContainerWithDocumentContent();
491     m_communicator.requestAnnotations(fileContainer);
492 }
493 
494 CppTools::BaseEditorDocumentProcessor::HeaderErrorDiagnosticWidgetCreator
creatorForHeaderErrorDiagnosticWidget(const ClangBackEnd::DiagnosticContainer & firstHeaderErrorDiagnostic)495 ClangEditorDocumentProcessor::creatorForHeaderErrorDiagnosticWidget(
496         const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic)
497 {
498     if (firstHeaderErrorDiagnostic.text.isEmpty())
499         return CppTools::BaseEditorDocumentProcessor::HeaderErrorDiagnosticWidgetCreator();
500 
501     return [firstHeaderErrorDiagnostic]() {
502         auto vbox = new QVBoxLayout;
503         vbox->setContentsMargins(10, 0, 0, 2);
504         vbox->setSpacing(2);
505 
506         vbox->addWidget(ClangDiagnosticWidget::createWidget({firstHeaderErrorDiagnostic},
507                                                             ClangDiagnosticWidget::InfoBar, {}));
508 
509         auto widget = new QWidget;
510         widget->setLayout(vbox);
511 
512         return widget;
513     };
514 }
515 
simpleFileContainer(const QByteArray & codecName) const516 ClangBackEnd::FileContainer ClangEditorDocumentProcessor::simpleFileContainer(
517     const QByteArray &codecName) const
518 {
519     return ClangBackEnd::FileContainer(filePath(),
520                                        Utf8String(),
521                                        false,
522                                        revision(),
523                                        Utf8String::fromByteArray(codecName));
524 }
525 
fileContainerWithOptionsAndDocumentContent(const QStringList & compilationArguments,const ProjectExplorer::HeaderPaths headerPaths) const526 ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithOptionsAndDocumentContent(
527     const QStringList &compilationArguments, const ProjectExplorer::HeaderPaths headerPaths) const
528 {
529     auto theHeaderPaths
530         = ::Utils::transform<QVector>(headerPaths, [](const ProjectExplorer::HeaderPath path) {
531               return Utf8String(QDir::toNativeSeparators(path.path));
532     });
533     theHeaderPaths << QDir::toNativeSeparators(
534         ClangModelManagerSupport::instance()->dummyUiHeaderOnDiskDirPath());
535 
536     return ClangBackEnd::FileContainer(filePath(),
537                                        Utf8StringVector(compilationArguments),
538                                        theHeaderPaths,
539                                        textDocument()->toPlainText(),
540                                        true,
541                                        revision());
542 }
543 
544 ClangBackEnd::FileContainer
fileContainerWithDocumentContent() const545 ClangEditorDocumentProcessor::fileContainerWithDocumentContent() const
546 {
547     return ClangBackEnd::FileContainer(filePath(),
548                                        textDocument()->toPlainText(),
549                                        true,
550                                        revision());
551 }
552 
553 } // namespace Internal
554 } // namespace ClangCodeModel
555