Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qmlcodemarker.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
5
6#include <QtCore/qregularexpression.h>
7
8#include "atom.h"
9#include "node.h"
10#include "qdoclogging.h"
12#include "text.h"
13
14#include <private/qqmljsast_p.h>
15#include <private/qqmljsastfwd_p.h>
16#include <private/qqmljsengine_p.h>
17#include <private/qqmljslexer_p.h>
18#include <private/qqmljsparser_p.h>
19
21
22/*!
23 Returns \c true if the \a code is recognized by the parser.
24 */
25bool QmlCodeMarker::recognizeCode(const QString &code)
26{
27 // Naive pre-check; starts with an import statement or 'CamelCase {'
28 static const QRegularExpression regExp(QStringLiteral("^\\s*(import |([A-Z][a-z0-9]*)+\\s?{)"));
29 if (!regExp.match(code).hasMatch())
30 return false;
31
32 QQmlJS::Engine engine;
33 QQmlJS::Lexer lexer(&engine);
34 QQmlJS::Parser parser(&engine);
35
36 QString newCode = code;
37 extractPragmas(newCode);
38 lexer.setCode(newCode, 1);
39
40 return parser.parse();
41}
42
43/*!
44 Returns \c true if \a ext is any of a list of file extensions
45 for the QML language.
46 */
47bool QmlCodeMarker::recognizeExtension(const QString &ext)
48{
49 return ext == "qml";
50}
51
52/*!
53 Returns \c true if the \a language is recognized. Only "QML" is
54 recognized by this marker.
55 */
56bool QmlCodeMarker::recognizeLanguage(const QString &language)
57{
58 return language == "qml";
59}
60
61/*!
62 Returns the type of atom used to represent QML code in the documentation.
63*/
65{
66 return Atom::Qml;
67}
68
69QString QmlCodeMarker::markedUpCode(const QString &code, const Node *relative,
70 const Location &location)
71{
72 return addMarkUp(code, relative, location);
73}
74
75/*!
76 Constructs and returns the marked up name for the \a node.
77 If the node is any kind of QML function (a method,
78 signal, or handler), "()" is appended to the marked up name.
79 */
81{
82 QString name = linkTag(node, taggedNode(node));
83 if (node->isFunction())
84 name += "()";
85 return name;
86}
87
88QString QmlCodeMarker::addMarkUp(const QString &code, const Node * /* relative */,
89 const Location &location)
90{
91 if (code.trimmed().isEmpty()) {
92 qCDebug(lcQdoc, "QML markup requested for empty code at %ls",
93 qUtf16Printable(location.toString()));
94 return {};
95 }
96
97 QQmlJS::Engine engine;
98 QQmlJS::Lexer lexer(&engine);
99
100 QString newCode = code;
101 QList<QQmlJS::SourceLocation> pragmas = extractPragmas(newCode);
102 lexer.setCode(newCode, 1);
103
104 QQmlJS::Parser parser(&engine);
105 QString output;
106
107 if (parser.parse()) {
108 QQmlJS::AST::UiProgram *ast = parser.ast();
109 // Pass the unmodified code to the visitor so that pragmas and other
110 // unhandled source text can be output.
111 QmlMarkupVisitor visitor(code, pragmas, &engine);
112 QQmlJS::AST::Node::accept(ast, &visitor);
113 if (visitor.hasError()) {
114 location.warning(
115 location.fileName()
116 + QStringLiteral("Unable to analyze QML snippet. The output is incomplete."));
117 }
118 output = visitor.markedUpCode();
119 } else {
120 location.warning(QStringLiteral("Unable to parse QML snippet: \"%1\" at line %2, column %3")
121 .arg(parser.errorMessage())
122 .arg(parser.errorLineNumber())
123 .arg(parser.errorColumnNumber()));
124 output = protect(code);
125 }
126
127 return output;
128}
129
130/*
131 Copied and pasted from
132 src/declarative/qml/qqmlscriptparser.cpp.
133*/
134void replaceWithSpace(QString &str, int idx, int n); // qmlcodeparser.cpp
135
136/*
137 Copied and pasted from
138 src/declarative/qml/qqmlscriptparser.cpp then modified to
139 return a list of removed pragmas.
140
141 Searches for ".pragma <value>" or ".import <stuff>" declarations
142 in \a script. Currently supported pragmas are: library
143*/
144QList<QQmlJS::SourceLocation> QmlCodeMarker::extractPragmas(QString &script)
145{
146 QList<QQmlJS::SourceLocation> removed;
147
148 QQmlJS::Lexer l(nullptr);
149 l.setCode(script, 0);
150
151 int token = l.lex();
152
153 while (true) {
154 if (token != QQmlJSGrammar::T_DOT)
155 break;
156
157 int startOffset = l.tokenOffset();
158 int startLine = l.tokenStartLine();
159 int startColumn = l.tokenStartColumn();
160
161 token = l.lex();
162
163 if (token != QQmlJSGrammar::T_PRAGMA && token != QQmlJSGrammar::T_IMPORT)
164 break;
165 int endOffset = 0;
166 while (startLine == l.tokenStartLine()) {
167 endOffset = l.tokenLength() + l.tokenOffset();
168 token = l.lex();
169 }
170 replaceWithSpace(script, startOffset, endOffset - startOffset);
171 removed.append(QQmlJS::SourceLocation(startOffset, endOffset - startOffset, startLine,
172 startColumn));
173 }
174 return removed;
175}
176
177QT_END_NAMESPACE
The Atom class is the fundamental unit for representing documents internally.
Definition atom.h:19
AtomType
\value AnnotatedList \value AutoLink \value BaseName \value BriefLeft \value BriefRight \value C \val...
Definition atom.h:21
@ Qml
Definition atom.h:79
The Location class provides a way to mark a location in a file.
Definition location.h:20
QString markedUpName(const Node *node) override
Constructs and returns the marked up name for the node.
Atom::AtomType atomType() const override
Returns the type of atom used to represent QML code in the documentation.
Combined button and popup list for selecting options.
void replaceWithSpace(QString &str, int idx, int n)
Copy and paste from src/declarative/qml/qdeclarativescriptparser.cpp.
The Node class is the base class for all the nodes in QDoc's parse tree.
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:100