6#include <QtLanguageServer/private/qlanguageserverspec_p.h>
7#include <QtQmlCompiler/private/qqmljslinter_p.h>
8#include <QtQmlCompiler/private/qqmljslogger_p.h>
9#include <QtQmlDom/private/qqmldom_utils_p.h>
10#include <QtQmlDom/private/qqmldomtop_p.h>
11#include <QtCore/qdebug.h>
12#include <QtCore/qdir.h>
13#include <QtCore/qfileinfo.h>
14#include <QtCore/qlibraryinfo.h>
15#include <QtCore/qtimer.h>
16#include <QtCore/qxpfunctional.h>
32 return DiagnosticSeverity::Hint;
34 return DiagnosticSeverity::Information;
36 return DiagnosticSeverity::Warning;
41 return DiagnosticSeverity::Error;
46 LSPPartialResponse<std::variant<
QList<std::variant<Command, CodeAction>>, std::nullptr_t>,
47 QList<std::variant<Command, CodeAction>>> &&response)
49 QList<std::variant<Command, CodeAction>> responseData;
51 for (
const Diagnostic &diagnostic :
params.context.diagnostics) {
52 if (!diagnostic.data.has_value())
55 const auto &
data = diagnostic.data.value();
57 int version =
data[u
"version"].toInt();
60 QList<WorkspaceEdit::DocumentChange> edits;
62 for (
const QJsonValue &suggestion : suggestions) {
63 QString replacement = suggestion[u
"replacement"].toString();
64 message += suggestion[u
"message"].toString() + u
"\n";
67 textEdit.range = { Position { suggestion[u
"lspBeginLine"].toInt(),
68 suggestion[u
"lspBeginCharacter"].toInt() },
69 Position { suggestion[u
"lspEndLine"].toInt(),
70 suggestion[u
"lspEndCharacter"].toInt() } };
73 TextDocumentEdit textDocEdit;
74 textDocEdit.textDocument = {
params.textDocument, version };
81 edit.documentChanges = edits;
85 action.kind = u
"quickfix"_s.toUtf8();
87 action.title =
message.toUtf8();
89 responseData.append(action);
92 response.sendResponse(responseData);
95void QmlLintSuggestions::registerHandlers(
QLanguageServer *, QLanguageServerProtocol *protocol)
100void QmlLintSuggestions::setupCapabilities(
const QLspSpecification::InitializeParams &,
101 QLspSpecification::InitializeResult &serverInfo)
103 serverInfo.capabilities.codeActionProvider =
true;
107 : m_server(
server), m_codeModel(codeModel)
114 const int startOffset =
location.offset;
118 if (iEnd >
int(fileContents.size()))
119 iEnd = fileContents.size();
121 if (fileContents.at(
i) == u
'\n') {
124 if (
i + 1 < iEnd && fileContents.at(
i) == u
'\r')
135 Diagnostic diagnostic;
136 diagnostic.severity = DiagnosticSeverity::Warning;
137 Range &
range = diagnostic.range;
141 Position &positionEnd =
range.end;
142 positionEnd.line = 1;
144 "qmlls could not find a build directory, without a build directory "
145 "containing a current build there could be spurious warnings, you might "
146 "want to pass the --build-dir <buildDir> option to qmlls, or set the "
147 "environment variable QMLLS_BUILD_DIRS.";
156 Diagnostic diagnostic;
158 Range &
range = diagnostic.range;
163 if (srcLoc.isValid()) {
164 position.line = srcLoc.startLine - 1;
165 position.character = srcLoc.startColumn - 1;
170 if (
message.fixSuggestion && !
message.fixSuggestion->fixDescription().isEmpty()) {
177 diagnostic.message =
message.message.toUtf8();
182 auto suggestion =
message.fixSuggestion;
183 if (!suggestion.has_value())
190 const int line =
cut.isValid() ?
cut.startLine - 1 : 0;
191 const int column =
cut.isValid() ?
cut.startColumn - 1 : 0;
195 object.insert(
"lspBeginCharacter"_L1,
column);
199 if (srcLoc.isValid())
200 advancePositionPastLocation(
cut,
end);
201 object.insert(
"lspEndLine"_L1,
end.line);
202 object.insert(
"lspEndCharacter"_L1,
end.character);
204 object.insert(
"message"_L1, suggestion->fixDescription());
205 object.insert(
"replacement"_L1, suggestion->replacement());
208 fixedSuggestions.
append(
object);
210 data[u
"suggestions"] = fixedSuggestions;
213 data[u
"version"] = version.value();
215 diagnostic.data =
data;
220static bool isSnapshotNew(std::optional<int> snapshotVersion, std::optional<int> processedVersion)
222 if (!snapshotVersion)
224 if (!processedVersion || *snapshotVersion > *processedVersion)
229using namespace std::chrono_literals;
231QmlLintSuggestions::VersionToDiagnose
232QmlLintSuggestions::chooseVersionToDiagnoseHelper(
const QByteArray &
url)
234 const std::chrono::milliseconds maxInvalidTime = 400ms;
237 LastLintUpdate &lastUpdate = m_lastUpdate[
url];
240 if (lastUpdate.version && *lastUpdate.version == snapshot.
docVersion) {
241 qCDebug(lspServerLog) <<
"skipped update of " <<
url <<
"unchanged valid doc";
242 return NoDocumentAvailable{};
251 if (
auto since = lastUpdate.invalidUpdatesSince) {
253 if (std::chrono::steady_clock::now() - *since > maxInvalidTime) {
254 return VersionedDocument{ snapshot.
docVersion, snapshot.
doc };
258 lastUpdate.invalidUpdatesSince = std::chrono::steady_clock::now();
262 return TryAgainLater{ maxInvalidTime };
264 return NoDocumentAvailable{};
267QmlLintSuggestions::VersionToDiagnose
268QmlLintSuggestions::chooseVersionToDiagnose(
const QByteArray &
url)
271 auto versionToDiagnose = chooseVersionToDiagnoseHelper(
url);
272 if (
auto versionedDocument = std::get_if<VersionedDocument>(&versionToDiagnose)) {
275 LastLintUpdate &lastUpdate = m_lastUpdate[
url];
276 lastUpdate.version = versionedDocument->version;
277 lastUpdate.invalidUpdatesSince.reset();
279 return versionToDiagnose;
284 auto versionedDocument = chooseVersionToDiagnose(
url);
287 [](NoDocumentAvailable) {},
288 [
this, &
url](
const TryAgainLater &tryAgainLater) {
290 [
this,
url]() { diagnose(url); });
292 [
this, &
url](
const VersionedDocument &versionedDocument) {
293 diagnoseHelper(
url, versionedDocument);
300void QmlLintSuggestions::diagnoseHelper(
const QByteArray &
url,
301 const VersionedDocument &versionedDocument)
303 auto [version, doc] = versionedDocument;
305 PublishDiagnosticsParams diagnosticParams;
306 diagnosticParams.uri =
url;
307 diagnosticParams.version = version;
309 qCDebug(lintLog) <<
"has doc, do real lint";
312 const QString filename = doc.canonicalFilePath();
318 const QString fileContents = doc.field(Fields::code).value().toString();
326 linter.lintFile(filename, &fileContents, silent,
nullptr, imports, qmltypesFiles,
334 auto messageToDiagnostic = [&advancePositionPastLocation,
340 QList<Diagnostic> diagnostics;
343 Diagnostic diagnostic;
347 Range &
range = diagnostic.range;
353 diagnostic.source =
"domParsing";
355 diagnostics.
append(diagnostic);
361 qsizetype nDiagnostics = diagnostics.size();
362 for (
const auto &messages : { logger->infos(), logger->warnings(), logger->errors() })
365 if (diagnostics.size() != nDiagnostics && imports.size() == 1)
369 diagnosticParams.diagnostics = diagnostics;
371 m_server->
protocol()->notifyPublishDiagnostics(diagnosticParams);
373 << diagnosticParams.diagnostics.size() <<
"issues"
374 << QTypedJson::toJsonValue(diagnosticParams);
void chop(qsizetype n)
Removes n bytes from the end of the byte array.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
\inmodule QtCore\reentrant
void append(const QJsonValue &value)
Inserts value at the end of the array.
\inmodule QtCore\reentrant
iterator insert(const QString &key, const QJsonValue &value)
Inserts a new item with the key key and a value of value.
\inmodule QtCore\reentrant
Implements a server for the language server protocol.
QLanguageServerProtocol * protocol()
constexpr const char * data() const noexcept
constexpr qsizetype size() const noexcept
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Represents an error message connected to the dom.
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QString simplified() const &
QString & append(QChar c)
QByteArray toUtf8() const &
bool singleShot
whether the timer is a single-shot timer
std::optional< int > docVersion
std::optional< int > validDocVersion
QQmlJS::Dom::DomItem validDoc
OpenDocumentSnapshot snapshotByUrl(const QByteArray &url)
QStringList importPaths() const
QStringList buildPathsForFileUrl(const QByteArray &url)
void updatedSnapshot(const QByteArray &url)
void diagnose(const QByteArray &uri)
list append(new Employee("Blackpool", "Stephen"))
QStringList resourceFilesFromBuildFolders(const QStringList &buildFolders)
Combined button and popup list for selecting options.
static Diagnostic messageToDiagnostic_helper(AdvanceFunc advancePositionPastLocation, std::optional< int > version, const Message &message)
static Diagnostic createMissingBuildDirDiagnostic()
static void codeActionHandler(const QByteArray &, const CodeActionParams ¶ms, LSPPartialResponse< std::variant< QList< std::variant< Command, CodeAction > >, std::nullptr_t >, QList< std::variant< Command, CodeAction > > > &&response)
static void advancePositionPastLocation_helper(const QString &fileContents, const QQmlJS::SourceLocation &location, Position &position)
static bool isSnapshotNew(std::optional< int > snapshotVersion, std::optional< int > processedVersion)
static DiagnosticSeverity severityFromMsgType(QtMsgType t)
static jboolean cut(JNIEnv *, jobject)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage return DBusPendingCall DBusPendingCall return DBusPendingCall return dbus_int32_t return DBusServer * server
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint GLsizei const GLchar * message
GLenum GLenum GLsizei void GLsizei void * column
GLsizei GLenum * categories
static QString absolutePath(const QString &path)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
QUrl url("example.com")
[constructor-url-reference]
QGraphicsWidget * textEdit