qmlls: call lsp uris urls
The Language Server Protocol calls several attributes "uri", consistent with their definition in RFC 3986. In Qml URI is normally used as module uri, i.e. a dot separated sequence. As per RFC 3986 URL is a subset of URI, but it turn out that the usages in LSP are actually URLs, so use "url"" for its usages, to keep the uri term as used in qqmlcodemodel unambiguous. Leave the protocol objects usages of uri consistent with the LSP definition, and use a noop lspUriToQmlUrl() to highlight the transition between the two usages of uri. Change-Id: Icf2ab5455e88849991e8a3432c5a2c91503d1285 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
parent
3a4b6b9ba2
commit
e2708875ae
|
@ -123,19 +123,19 @@ QmlLintSuggestions::QmlLintSuggestions(QLanguageServer *server, QmlLsp::QQmlCode
|
|||
&QmlLintSuggestions::diagnose, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
void QmlLintSuggestions::diagnose(const QByteArray &uri)
|
||||
void QmlLintSuggestions::diagnose(const QByteArray &url)
|
||||
{
|
||||
const int maxInvalidMsec = 4000;
|
||||
qCDebug(lintLog) << "diagnose start";
|
||||
QmlLsp::OpenDocumentSnapshot snapshot = m_codeModel->snapshotByUri(uri);
|
||||
QmlLsp::OpenDocumentSnapshot snapshot = m_codeModel->snapshotByUrl(url);
|
||||
QList<Diagnostic> diagnostics;
|
||||
std::optional<int> version;
|
||||
DomItem doc;
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
LastLintUpdate &lastUpdate = m_lastUpdate[uri];
|
||||
LastLintUpdate &lastUpdate = m_lastUpdate[url];
|
||||
if (lastUpdate.version && *lastUpdate.version == version) {
|
||||
qCDebug(lspServerLog) << "skipped update of " << uri << "unchanged valid doc";
|
||||
qCDebug(lspServerLog) << "skipped update of " << url << "unchanged valid doc";
|
||||
return;
|
||||
}
|
||||
if (snapshot.validDocVersion
|
||||
|
@ -153,7 +153,7 @@ void QmlLintSuggestions::diagnose(const QByteArray &uri)
|
|||
} else if (!lastUpdate.invalidUpdatesSince) {
|
||||
lastUpdate.invalidUpdatesSince = QDateTime::currentDateTime();
|
||||
QTimer::singleShot(maxInvalidMsec, Qt::VeryCoarseTimer, this,
|
||||
[this, uri]() { diagnose(uri); });
|
||||
[this, url]() { diagnose(url); });
|
||||
}
|
||||
}
|
||||
if (doc) {
|
||||
|
@ -273,12 +273,12 @@ void QmlLintSuggestions::diagnose(const QByteArray &uri)
|
|||
}
|
||||
}
|
||||
PublishDiagnosticsParams diagnosticParams;
|
||||
diagnosticParams.uri = uri;
|
||||
diagnosticParams.uri = url;
|
||||
diagnosticParams.diagnostics = diagnostics;
|
||||
diagnosticParams.version = version;
|
||||
|
||||
m_server->protocol()->notifyPublishDiagnostics(diagnosticParams);
|
||||
qCDebug(lintLog) << "lint" << QString::fromUtf8(uri) << "found"
|
||||
qCDebug(lintLog) << "lint" << QString::fromUtf8(url) << "found"
|
||||
<< diagnosticParams.diagnostics.size() << "issues"
|
||||
<< QTypedJson::toJsonValue(diagnosticParams);
|
||||
}
|
||||
|
|
|
@ -64,11 +64,11 @@ synchronization here.
|
|||
|
||||
\section2 OpenFiles
|
||||
\list
|
||||
\li snapshotByUri() returns an OpenDocumentSnapshot of an open document. From it you can get the
|
||||
\li snapshotByUrl() returns an OpenDocumentSnapshot of an open document. From it you can get the
|
||||
document, its latest valid version, scope, all connected to a specific version of the document
|
||||
and immutable. The signal updatedSnapshot() is called every time a snapshot changes (also for
|
||||
every partial change: document change, validDocument change, scope change).
|
||||
\li openDocumentByUri() is a lower level and more intrusive access to OpenDocument objects. These
|
||||
\li openDocumentByUrl() is a lower level and more intrusive access to OpenDocument objects. These
|
||||
contains the current snapshot, and shared pointer to a Utils::TextDocument. This is *always* the
|
||||
current version of the document, and has line by line support.
|
||||
Working on it is more delicate and intrusive, because you have to explicitly acquire its mutex()
|
||||
|
@ -124,9 +124,9 @@ QQmlCodeModel::~QQmlCodeModel()
|
|||
}
|
||||
}
|
||||
|
||||
OpenDocumentSnapshot QQmlCodeModel::snapshotByUri(const QByteArray &uri)
|
||||
OpenDocumentSnapshot QQmlCodeModel::snapshotByUrl(const QByteArray &url)
|
||||
{
|
||||
return openDocumentByUri(uri).snapshot;
|
||||
return openDocumentByUrl(url).snapshot;
|
||||
}
|
||||
|
||||
int QQmlCodeModel::indexEvalProgress() const
|
||||
|
@ -272,49 +272,49 @@ void QQmlCodeModel::removeDirectory(const QString &path)
|
|||
currentEnvPtr->removePath(path);
|
||||
}
|
||||
|
||||
QString QQmlCodeModel::uri2Path(const QByteArray &uri, UriLookup options)
|
||||
QString QQmlCodeModel::url2Path(const QByteArray &url, UrlLookup options)
|
||||
{
|
||||
QString res;
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
res = m_uri2path.value(uri);
|
||||
res = m_url2path.value(url);
|
||||
}
|
||||
if (!res.isEmpty() && options == UriLookup::Caching)
|
||||
if (!res.isEmpty() && options == UrlLookup::Caching)
|
||||
return res;
|
||||
QUrl url(QString::fromUtf8(uri));
|
||||
QFileInfo f(url.toLocalFile());
|
||||
QUrl qurl(QString::fromUtf8(url));
|
||||
QFileInfo f(qurl.toLocalFile());
|
||||
QString cPath = f.canonicalFilePath();
|
||||
if (cPath.isEmpty())
|
||||
cPath = f.filePath();
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
if (!res.isEmpty() && res != cPath)
|
||||
m_path2uri.remove(res);
|
||||
m_uri2path.insert(uri, cPath);
|
||||
m_path2uri.insert(cPath, uri);
|
||||
m_path2url.remove(res);
|
||||
m_url2path.insert(url, cPath);
|
||||
m_path2url.insert(cPath, url);
|
||||
}
|
||||
return cPath;
|
||||
}
|
||||
|
||||
void QQmlCodeModel::newOpenFile(const QByteArray &uri, int version, const QString &docText)
|
||||
void QQmlCodeModel::newOpenFile(const QByteArray &url, int version, const QString &docText)
|
||||
{
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
auto &openDoc = m_openDocuments[uri];
|
||||
auto &openDoc = m_openDocuments[url];
|
||||
if (!openDoc.textDocument)
|
||||
openDoc.textDocument = std::make_shared<Utils::TextDocument>();
|
||||
QMutexLocker l2(openDoc.textDocument->mutex());
|
||||
openDoc.textDocument->setVersion(version);
|
||||
openDoc.textDocument->setPlainText(docText);
|
||||
}
|
||||
addOpenToUpdate(uri);
|
||||
addOpenToUpdate(url);
|
||||
openNeedUpdate();
|
||||
}
|
||||
|
||||
OpenDocument QQmlCodeModel::openDocumentByUri(const QByteArray &uri)
|
||||
OpenDocument QQmlCodeModel::openDocumentByUrl(const QByteArray &url)
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
return m_openDocuments.value(uri);
|
||||
return m_openDocuments.value(url);
|
||||
}
|
||||
|
||||
void QQmlCodeModel::indexNeedsUpdate()
|
||||
|
@ -426,12 +426,12 @@ void QQmlCodeModel::openUpdateEnd()
|
|||
qCDebug(codeModelLog) << "openUpdateEnd";
|
||||
}
|
||||
|
||||
void QQmlCodeModel::newDocForOpenFile(const QByteArray &uri, int version, const QString &docText)
|
||||
void QQmlCodeModel::newDocForOpenFile(const QByteArray &url, int version, const QString &docText)
|
||||
{
|
||||
qCDebug(codeModelLog) << "updating doc" << uri << "to version" << version << "("
|
||||
qCDebug(codeModelLog) << "updating doc" << url << "to version" << version << "("
|
||||
<< docText.length() << "chars)";
|
||||
DomItem newCurrent = m_currentEnv.makeCopy(DomItem::CopyOption::EnvConnected).item();
|
||||
QString fPath = uri2Path(uri, UriLookup::ForceLookup);
|
||||
QString fPath = url2Path(url, UrlLookup::ForceLookup);
|
||||
Path p;
|
||||
newCurrent.loadFile(
|
||||
fPath, fPath, docText, QDateTime::currentDateTimeUtc(),
|
||||
|
@ -443,17 +443,17 @@ void QQmlCodeModel::newDocForOpenFile(const QByteArray &uri, int version, const
|
|||
DomItem item = m_currentEnv.path(p);
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
OpenDocument &doc = m_openDocuments[uri];
|
||||
OpenDocument &doc = m_openDocuments[url];
|
||||
if (!doc.textDocument) {
|
||||
qCWarning(lspServerLog)
|
||||
<< "ignoring update to closed document" << QString::fromUtf8(uri);
|
||||
<< "ignoring update to closed document" << QString::fromUtf8(url);
|
||||
return;
|
||||
} else {
|
||||
QMutexLocker l(doc.textDocument->mutex());
|
||||
if (doc.textDocument->version() && *doc.textDocument->version() > version) {
|
||||
qCWarning(lspServerLog)
|
||||
<< "docUpdate: version" << version << "of document"
|
||||
<< QString::fromUtf8(uri) << "is not the latest anymore";
|
||||
<< QString::fromUtf8(url) << "is not the latest anymore";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -461,8 +461,8 @@ void QQmlCodeModel::newDocForOpenFile(const QByteArray &uri, int version, const
|
|||
doc.snapshot.docVersion = version;
|
||||
doc.snapshot.doc = item;
|
||||
} else {
|
||||
qCWarning(lspServerLog) << "skippig update of current doc to obsolete version"
|
||||
<< version << "of document" << QString::fromUtf8(uri);
|
||||
qCWarning(lspServerLog) << "skipping update of current doc to obsolete version"
|
||||
<< version << "of document" << QString::fromUtf8(url);
|
||||
}
|
||||
if (item.field(Fields::isValid).value().toBool(false)) {
|
||||
if (!doc.snapshot.validDocVersion || *doc.snapshot.validDocVersion < version) {
|
||||
|
@ -471,31 +471,31 @@ void QQmlCodeModel::newDocForOpenFile(const QByteArray &uri, int version, const
|
|||
doc.snapshot.validDoc = vDoc;
|
||||
} else {
|
||||
qCWarning(lspServerLog) << "skippig update of valid doc to obsolete version"
|
||||
<< version << "of document" << QString::fromUtf8(uri);
|
||||
<< version << "of document" << QString::fromUtf8(url);
|
||||
}
|
||||
} else {
|
||||
qCWarning(lspServerLog)
|
||||
<< "avoid update of validDoc to " << version << "of document"
|
||||
<< QString::fromUtf8(uri) << "as it is invalid";
|
||||
<< QString::fromUtf8(url) << "as it is invalid";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (codeModelLog().isDebugEnabled()) {
|
||||
qCDebug(codeModelLog) << "finished update doc of " << uri << "to version" << version;
|
||||
snapshotByUri(uri).dump(qDebug() << "postSnapshot",
|
||||
qCDebug(codeModelLog) << "finished update doc of " << url << "to version" << version;
|
||||
snapshotByUrl(url).dump(qDebug() << "postSnapshot",
|
||||
OpenDocumentSnapshot::DumpOption::AllCode);
|
||||
}
|
||||
// we should update the scope in the future thus call addOpen(uri)
|
||||
emit updatedSnapshot(uri);
|
||||
// we should update the scope in the future thus call addOpen(url)
|
||||
emit updatedSnapshot(url);
|
||||
}
|
||||
|
||||
void QQmlCodeModel::closeOpenFile(const QByteArray &uri)
|
||||
void QQmlCodeModel::closeOpenFile(const QByteArray &url)
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
m_openDocuments.remove(uri);
|
||||
m_openDocuments.remove(url);
|
||||
}
|
||||
|
||||
void QQmlCodeModel::openUpdate(const QByteArray &uri)
|
||||
void QQmlCodeModel::openUpdate(const QByteArray &url)
|
||||
{
|
||||
bool updateDoc = false;
|
||||
bool updateScope = false;
|
||||
|
@ -505,7 +505,7 @@ void QQmlCodeModel::openUpdate(const QByteArray &uri)
|
|||
std::shared_ptr<Utils::TextDocument> document;
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
OpenDocument &doc = m_openDocuments[uri];
|
||||
OpenDocument &doc = m_openDocuments[url];
|
||||
document = doc.textDocument;
|
||||
if (!document)
|
||||
return;
|
||||
|
@ -531,23 +531,23 @@ void QQmlCodeModel::openUpdate(const QByteArray &uri)
|
|||
}
|
||||
}
|
||||
if (updateDoc) {
|
||||
newDocForOpenFile(uri, *rNow, docText);
|
||||
newDocForOpenFile(url, *rNow, docText);
|
||||
}
|
||||
if (updateScope) {
|
||||
// to do
|
||||
}
|
||||
}
|
||||
|
||||
void QQmlCodeModel::addOpenToUpdate(const QByteArray &uri)
|
||||
void QQmlCodeModel::addOpenToUpdate(const QByteArray &url)
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
m_openDocumentsToUpdate.insert(uri);
|
||||
m_openDocumentsToUpdate.insert(url);
|
||||
}
|
||||
|
||||
QDebug OpenDocumentSnapshot::dump(QDebug dbg, DumpOptions options)
|
||||
{
|
||||
dbg.noquote().nospace() << "{";
|
||||
dbg << " uri:" << QString::fromUtf8(uri) << "\n";
|
||||
dbg << " url:" << QString::fromUtf8(url) << "\n";
|
||||
dbg << " docVersion:" << (docVersion ? QString::number(*docVersion) : u"*none*"_s) << "\n";
|
||||
if (options & DumpOption::LatestCode) {
|
||||
dbg << " doc: ------------\n"
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
AllCode = LatestCode | ValidCode
|
||||
};
|
||||
Q_DECLARE_FLAGS(DumpOptions, DumpOption)
|
||||
QByteArray uri;
|
||||
QByteArray url;
|
||||
std::optional<int> docVersion;
|
||||
QQmlJS::Dom::DomItem doc;
|
||||
std::optional<int> validDocVersion;
|
||||
|
@ -83,15 +83,15 @@ class QQmlCodeModel : public QObject
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class UriLookup { Caching, ForceLookup };
|
||||
enum class UrlLookup { Caching, ForceLookup };
|
||||
enum class State { Running, Stopping };
|
||||
|
||||
explicit QQmlCodeModel(QObject *parent = nullptr);
|
||||
~QQmlCodeModel();
|
||||
QQmlJS::Dom::DomItem currentEnv();
|
||||
QQmlJS::Dom::DomItem validEnv();
|
||||
OpenDocumentSnapshot snapshotByUri(const QByteArray &uri);
|
||||
OpenDocument openDocumentByUri(const QByteArray &uri);
|
||||
OpenDocumentSnapshot snapshotByUrl(const QByteArray &url);
|
||||
OpenDocument openDocumentByUrl(const QByteArray &url);
|
||||
|
||||
void openNeedUpdate();
|
||||
void indexNeedsUpdate();
|
||||
|
@ -99,12 +99,12 @@ public:
|
|||
void addOpenToUpdate(const QByteArray &);
|
||||
void removeDirectory(const QString &path);
|
||||
// void updateDocument(const OpenDocument &doc);
|
||||
QString uri2Path(const QByteArray &uri, UriLookup options = UriLookup::Caching);
|
||||
void newOpenFile(const QByteArray &uri, int version, const QString &docText);
|
||||
void newDocForOpenFile(const QByteArray &uri, int version, const QString &docText);
|
||||
void closeOpenFile(const QByteArray &uri);
|
||||
QString url2Path(const QByteArray &url, UrlLookup options = UrlLookup::Caching);
|
||||
void newOpenFile(const QByteArray &url, int version, const QString &docText);
|
||||
void newDocForOpenFile(const QByteArray &url, int version, const QString &docText);
|
||||
void closeOpenFile(const QByteArray &url);
|
||||
signals:
|
||||
void updatedSnapshot(const QByteArray &uri);
|
||||
void updatedSnapshot(const QByteArray &url);
|
||||
private:
|
||||
void indexDirectory(const QString &path, int depthLeft);
|
||||
int indexEvalProgress() const; // to be called in the mutex
|
||||
|
@ -130,8 +130,8 @@ private:
|
|||
QQmlJS::Dom::DomItem m_validEnv;
|
||||
QByteArray m_lastOpenDocumentUpdated;
|
||||
QSet<QByteArray> m_openDocumentsToUpdate;
|
||||
QHash<QByteArray, QString> m_uri2path;
|
||||
QHash<QString, QByteArray> m_path2uri;
|
||||
QHash<QByteArray, QString> m_url2path;
|
||||
QHash<QString, QByteArray> m_path2url;
|
||||
QHash<QByteArray, OpenDocument> m_openDocuments;
|
||||
};
|
||||
|
||||
|
|
|
@ -36,6 +36,19 @@
|
|||
QT_BEGIN_NAMESPACE
|
||||
namespace QmlLsp {
|
||||
|
||||
/*
|
||||
* The language server protocol calls "URI" what QML calls "URL".
|
||||
* According to RFC 3986, a URL is a special case of URI that not only
|
||||
* identifies a resource but also shows how to access it.
|
||||
* In QML, however, URIs are distinct from URLs. URIs are the
|
||||
* identifiers of modules, for example "QtQuick.Controls".
|
||||
* In order to not confuse the terms we interpret language server URIs
|
||||
* as URLs in the QML code model.
|
||||
* This method marks a point of translation between the terms, but does
|
||||
* not have to change the actual URI/URL.
|
||||
*/
|
||||
inline QByteArray lspUriToQmlUrl(const QByteArray &uri) { return uri; }
|
||||
|
||||
class QQmlLanguageServer : public QLanguageServerModule
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
@ -42,26 +42,25 @@ TextSynchronization::TextSynchronization(QmlLsp::QQmlCodeModel *codeModel, QObje
|
|||
|
||||
void TextSynchronization::didCloseTextDocument(const DidCloseTextDocumentParams ¶ms)
|
||||
{
|
||||
m_codeModel->closeOpenFile(params.textDocument.uri);
|
||||
m_codeModel->closeOpenFile(QmlLsp::lspUriToQmlUrl(params.textDocument.uri));
|
||||
}
|
||||
|
||||
void TextSynchronization::didOpenTextDocument(const DidOpenTextDocumentParams ¶ms)
|
||||
{
|
||||
const TextDocumentItem &item = params.textDocument;
|
||||
const QString fileName = m_codeModel->uri2Path(item.uri);
|
||||
|
||||
m_codeModel->newOpenFile(item.uri, item.version, item.text);
|
||||
const QString fileName = m_codeModel->url2Path(QmlLsp::lspUriToQmlUrl(item.uri));
|
||||
m_codeModel->newOpenFile(QmlLsp::lspUriToQmlUrl(item.uri), item.version, item.text);
|
||||
}
|
||||
|
||||
void TextSynchronization::didDidChangeTextDocument(const DidChangeTextDocumentParams ¶ms)
|
||||
{
|
||||
QByteArray uri = params.textDocument.uri;
|
||||
const QString fileName = m_codeModel->uri2Path(uri);
|
||||
auto openDoc = m_codeModel->openDocumentByUri(uri);
|
||||
QByteArray url = QmlLsp::lspUriToQmlUrl(params.textDocument.uri);
|
||||
const QString fileName = m_codeModel->url2Path(url);
|
||||
auto openDoc = m_codeModel->openDocumentByUrl(url);
|
||||
std::shared_ptr<Utils::TextDocument> document = openDoc.textDocument;
|
||||
if (!document) {
|
||||
qCWarning(lspServerLog) << "Ingnoring changes to non open or closed document"
|
||||
<< QString::fromUtf8(uri);
|
||||
<< QString::fromUtf8(url);
|
||||
return;
|
||||
}
|
||||
const auto &changes = params.contentChanges;
|
||||
|
@ -86,7 +85,7 @@ void TextSynchronization::didDidChangeTextDocument(const DidChangeTextDocumentPa
|
|||
}
|
||||
document->setVersion(params.textDocument.version);
|
||||
}
|
||||
m_codeModel->addOpenToUpdate(uri);
|
||||
m_codeModel->addOpenToUpdate(url);
|
||||
m_codeModel->openNeedUpdate();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue