Merge remote-tracking branch 'origin/4.15'

Conflicts:
	cmake/QtCreatorIDEBranding.cmake
	qbs/modules/qtc/qtc.qbs
	qtcreator_ide_branding.pri

Change-Id: I309fe2a4ea7afac85481fc6466a9a6e58e340019
This commit is contained in:
Eike Ziller
2021-03-18 09:41:37 +01:00
98 changed files with 8105 additions and 3142 deletions

View File

@@ -44,6 +44,7 @@
#include <QTimer>
using namespace Core;
using namespace ProjectExplorer;
using namespace Utils;
namespace ClassView {
@@ -94,7 +95,7 @@ public:
ParserTreeItem::ConstPtr m_root;
QTimer m_timer;
QHash<QString, CPlusPlus::Document::Ptr> m_awaitingDocuments;
QSet<FilePath> m_awaitingDocuments;
//! Internal manager state. \sa Manager::state
bool state = false;
@@ -116,7 +117,15 @@ void ManagerPrivate::cancelScheduledUpdate()
void ManagerPrivate::resetParser()
{
cancelScheduledUpdate();
QMetaObject::invokeMethod(m_parser, &Parser::resetDataToCurrentState, Qt::QueuedConnection);
QHash<FilePath, QPair<QString, FilePaths>> projectData;
for (const Project *project : SessionManager::projects()) {
projectData.insert(project->projectFilePath(),
qMakePair(project->displayName(), project->files(Project::SourceFiles)));
}
QMetaObject::invokeMethod(m_parser, [this, projectData]() {
m_parser->resetData(projectData);
}, Qt::QueuedConnection);
}
/*!
@@ -218,15 +227,26 @@ bool Manager::hasChildren(QStandardItem *item) const
void Manager::initialize()
{
using ProjectExplorer::SessionManager;
d->m_timer.setSingleShot(true);
// connections to enable/disable navi widget factory
SessionManager *sessionManager = SessionManager::instance();
connect(sessionManager, &SessionManager::projectAdded,
this, &Manager::onProjectListChanged);
this, [this](Project *project) {
const FilePath projectPath = project->projectFilePath();
const QString projectName = project->displayName();
const FilePaths projectFiles = project->files(Project::SourceFiles);
QMetaObject::invokeMethod(d->m_parser, [this, projectPath, projectName, projectFiles]() {
d->m_parser->addProject(projectPath, projectName, projectFiles);
}, Qt::QueuedConnection);
});
connect(sessionManager, &SessionManager::projectRemoved,
this, &Manager::onProjectListChanged);
this, [this](Project *project) {
const FilePath projectPath = project->projectFilePath();
QMetaObject::invokeMethod(d->m_parser, [this, projectPath]() {
d->m_parser->removeProject(projectPath);
}, Qt::QueuedConnection);
});
// connect to the progress manager for signals about Parsing tasks
connect(ProgressManager::instance(), &ProgressManager::taskStarted,
@@ -283,12 +303,12 @@ void Manager::initialize()
if (doc.data() == nullptr)
return;
d->m_awaitingDocuments.insert(doc->fileName(), doc);
d->m_awaitingDocuments.insert(FilePath::fromString(doc->fileName()));
d->m_timer.start(400); // Accumulate multiple requests into one, restarts the timer
});
connect(&d->m_timer, &QTimer::timeout, this, [this]() {
const QList<CPlusPlus::Document::Ptr> docsToBeUpdated = d->m_awaitingDocuments.values();
const QSet<FilePath> docsToBeUpdated = d->m_awaitingDocuments;
d->cancelScheduledUpdate();
if (!state() || d->disableCodeParser) // enabling any of them will trigger the total update
return;
@@ -346,20 +366,7 @@ void Manager::onWidgetVisibilityIsChanged(bool visibility)
if (!visibility)
return;
setState(true);
onProjectListChanged();
}
/*!
Reacts to the project list being changed by updating the navigation pane
visibility if necessary.
*/
void Manager::onProjectListChanged()
{
// do nothing if Manager is disabled
if (!state())
return;
// TODO: this one may change into getter (when a new class view widget is being shown)
QMetaObject::invokeMethod(d->m_parser, &Parser::requestCurrentState, Qt::QueuedConnection);
}

View File

@@ -60,7 +60,6 @@ signals:
void treeDataUpdate(QSharedPointer<QStandardItem> result);
private:
void onProjectListChanged();
void initialize();
inline bool state() const;

View File

@@ -32,18 +32,13 @@
// other
#include <cpptools/cppmodelmanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <projectexplorer/project.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QStandardItem>
#include <QElapsedTimer>
#include <QDebug>
#include <QHash>
#include <QSet>
#include <QElapsedTimer>
enum { debug = false };
@@ -67,17 +62,11 @@ namespace Internal {
\brief The Parser class parses C++ information. Multithreading is supported.
*/
/*!
\fn void Parser::treeDataUpdate(QSharedPointer<QStandardItem> result)
Emits a signal about a tree data update.
*/
class ParserPrivate
{
public:
//! Get document from documentList
CPlusPlus::Document::Ptr document(const QString &fileName) const;
CPlusPlus::Document::Ptr document(const Utils::FilePath &fileName) const;
struct DocumentCache {
unsigned treeRevision = 0;
@@ -87,23 +76,20 @@ public:
struct ProjectCache {
unsigned treeRevision = 0;
ParserTreeItem::ConstPtr tree;
QStringList fileList;
QString projectName;
QSet<FilePath> fileNames;
};
// Project file path to its cached data
QHash<QString, DocumentCache> m_documentCache;
QHash<FilePath, DocumentCache> m_documentCache;
// Project file path to its cached data
QHash<QString, ProjectCache> m_projectCache;
// other
//! List for files which has to be parsed
QSet<QString> fileList;
QHash<FilePath, ProjectCache> m_projectCache;
//! Flat mode
bool flatMode = false;
};
CPlusPlus::Document::Ptr ParserPrivate::document(const QString &fileName) const
CPlusPlus::Document::Ptr ParserPrivate::document(const FilePath &fileName) const
{
return m_documentCache.value(fileName).document;
}
@@ -161,16 +147,14 @@ ParserTreeItem::ConstPtr Parser::parse()
QHash<SymbolInformation, ParserTreeItem::ConstPtr> projectTrees;
// TODO: move a call to SessionManager::projects() out of this thread
for (const Project *prj : SessionManager::projects()) {
const QString prjName(prj->displayName());
const QString prjType = prj->projectFilePath().toString();
const SymbolInformation inf(prjName, prjType);
ParserTreeItem::ConstPtr item = addFlatTree(prj);
for (auto it = d->m_projectCache.cbegin(); it != d->m_projectCache.cend(); ++it) {
const ParserPrivate::ProjectCache &projectCache = it.value();
const FilePath projectPath = it.key();
const SymbolInformation projectInfo = { projectCache.projectName, projectPath.toString() };
ParserTreeItem::ConstPtr item = getCachedOrParseProjectTree(projectPath, projectCache.fileNames);
if (item.isNull())
continue;
projectTrees.insert(inf, item);
projectTrees.insert(projectInfo, item);
}
ParserTreeItem::ConstPtr rootItem(new ParserTreeItem(projectTrees));
@@ -189,16 +173,16 @@ ParserTreeItem::ConstPtr Parser::parse()
project.
*/
ParserTreeItem::ConstPtr Parser::getParseProjectTree(const QStringList &fileList,
const QString &projectId)
ParserTreeItem::ConstPtr Parser::getParseProjectTree(const FilePath &projectPath,
const QSet<FilePath> &filesInProject)
{
//! \todo Way to optimize - for documentUpdate - use old cached project and subtract
//! changed files only (old edition), and add curent editions
QList<ParserTreeItem::ConstPtr> docTrees;
unsigned revision = 0;
for (const QString &file : fileList) {
const CPlusPlus::Document::Ptr &doc = d->document(file);
for (const FilePath &fileInProject : filesInProject) {
const CPlusPlus::Document::Ptr &doc = d->document(fileInProject);
if (doc.isNull())
continue;
@@ -210,11 +194,11 @@ ParserTreeItem::ConstPtr Parser::getParseProjectTree(const QStringList &fileList
docTrees.append(docTree);
}
ParserTreeItem::ConstPtr item = ParserTreeItem::mergeTrees(Utils::FilePath::fromString(projectId), docTrees);
ParserTreeItem::ConstPtr item = ParserTreeItem::mergeTrees(projectPath, docTrees);
// update the cache
if (!projectId.isEmpty()) {
ParserPrivate::ProjectCache &projectCache = d->m_projectCache[projectId];
if (!projectPath.isEmpty()) {
ParserPrivate::ProjectCache &projectCache = d->m_projectCache[projectPath];
projectCache.tree = item;
projectCache.treeRevision = revision;
}
@@ -227,15 +211,15 @@ ParserTreeItem::ConstPtr Parser::getParseProjectTree(const QStringList &fileList
Updates the internal cached tree for this project.
*/
ParserTreeItem::ConstPtr Parser::getCachedOrParseProjectTree(const QStringList &fileList,
const QString &projectId)
ParserTreeItem::ConstPtr Parser::getCachedOrParseProjectTree(const FilePath &projectPath,
const QSet<FilePath> &filesInProject)
{
const auto it = d->m_projectCache.constFind(projectId);
const auto it = d->m_projectCache.constFind(projectPath);
if (it != d->m_projectCache.constEnd() && !it.value().tree.isNull()) {
// calculate project's revision
unsigned revision = 0;
for (const QString &file : fileList) {
const CPlusPlus::Document::Ptr &doc = d->document(file);
for (const FilePath &fileInProject : filesInProject) {
const CPlusPlus::Document::Ptr &doc = d->document(fileInProject);
if (doc.isNull())
continue;
revision += doc->revision();
@@ -246,7 +230,7 @@ ParserTreeItem::ConstPtr Parser::getCachedOrParseProjectTree(const QStringList &
return it.value().tree;
}
return getParseProjectTree(fileList, projectId);
return getParseProjectTree(projectPath, filesInProject);
}
/*!
@@ -261,9 +245,7 @@ ParserTreeItem::ConstPtr Parser::getParseDocumentTree(const CPlusPlus::Document:
if (doc.isNull())
return ParserTreeItem::ConstPtr();
const QString &fileName = doc->fileName();
if (!d->fileList.contains(fileName))
return ParserTreeItem::ConstPtr();
const FilePath fileName = FilePath::fromString(doc->fileName());
ParserTreeItem::ConstPtr itemPtr = ParserTreeItem::parseDocument(doc);
@@ -284,7 +266,7 @@ ParserTreeItem::ConstPtr Parser::getCachedOrParseDocumentTree(const CPlusPlus::D
return ParserTreeItem::ConstPtr();
const QString &fileName = doc->fileName();
const auto it = d->m_documentCache.constFind(fileName);
const auto it = d->m_documentCache.constFind(FilePath::fromString(fileName));
if (it != d->m_documentCache.constEnd() && !it.value().tree.isNull()
&& it.value().treeRevision == doc->revision()) {
return it.value().tree;
@@ -297,13 +279,17 @@ ParserTreeItem::ConstPtr Parser::getCachedOrParseDocumentTree(const CPlusPlus::D
the internal storage.
*/
void Parser::updateDocuments(const QList<CPlusPlus::Document::Ptr> &docs)
void Parser::updateDocuments(const QSet<FilePath> &documentPaths)
{
for (const CPlusPlus::Document::Ptr &doc: docs) {
const QString &name = doc->fileName();
updateDocumentsFromSnapshot(documentPaths, CppTools::CppModelManager::instance()->snapshot());
}
// if it is external file (not in any of our projects)
if (!d->fileList.contains(name))
void Parser::updateDocumentsFromSnapshot(const QSet<Utils::FilePath> &documentPaths,
const CPlusPlus::Snapshot &snapshot)
{
for (const FilePath &documentPath : documentPaths) {
CPlusPlus::Document::Ptr doc = snapshot.document(documentPath);
if (doc.isNull())
continue;
getParseDocumentTree(doc);
@@ -311,16 +297,6 @@ void Parser::updateDocuments(const QList<CPlusPlus::Document::Ptr> &docs)
requestCurrentState();
}
/*!
Specifies the files that must be allowed for the parsing as a \a fileList.
Files outside of this list will not be in any tree.
*/
void Parser::setFileList(const QStringList &fileList)
{
d->fileList = Utils::toSet(fileList);
}
/*!
Removes the files defined in the \a fileList from the parsing.
*/
@@ -331,11 +307,11 @@ void Parser::removeFiles(const QStringList &fileList)
return;
for (const QString &name : fileList) {
d->fileList.remove(name);
d->m_documentCache.remove(name);
d->m_projectCache.remove(name);
const FilePath filePath = FilePath::fromString(name);
d->m_documentCache.remove(filePath);
d->m_projectCache.remove(filePath);
for (auto it = d->m_projectCache.begin(); it != d->m_projectCache.end(); ++it)
it.value().fileList.removeOne(name);
it.value().fileNames.remove(filePath);
}
requestCurrentState();
}
@@ -343,75 +319,66 @@ void Parser::removeFiles(const QStringList &fileList)
/*!
Fully resets the internal state of the code parser to \a snapshot.
*/
void Parser::resetData(const CPlusPlus::Snapshot &snapshot)
void Parser::resetData(const QHash<FilePath, QPair<QString, FilePaths>> &projects)
{
d->m_projectCache.clear();
d->m_documentCache.clear();
for (auto it = snapshot.begin(); it != snapshot.end(); ++it)
d->m_documentCache[it.key().toString()].document = it.value();
// recalculate file list
FilePaths fileList;
const CPlusPlus::Snapshot &snapshot = CppTools::CppModelManager::instance()->snapshot();
for (auto it = projects.cbegin(); it != projects.cend(); ++it) {
const auto projectData = it.value();
QSet<FilePath> commonFiles;
for (const auto &fileInProject : projectData.second) {
CPlusPlus::Document::Ptr doc = snapshot.document(fileInProject);
if (doc.isNull())
continue;
commonFiles.insert(fileInProject);
d->m_documentCache[fileInProject].document = doc;
}
d->m_projectCache.insert(it.key(), { 0, nullptr, projectData.first, commonFiles });
}
// TODO: move a call to SessionManager::projects() out of this thread
for (const Project *prj : SessionManager::projects())
fileList += prj->files(Project::SourceFiles);
setFileList(Utils::transform(fileList, &FilePath::toString));
requestCurrentState();
}
void Parser::addProject(const FilePath &projectPath, const QString &projectName,
const FilePaths &filesInProject)
{
const CPlusPlus::Snapshot &snapshot = CppTools::CppModelManager::instance()->snapshot();
QSet<FilePath> commonFiles;
for (const auto &fileInProject : filesInProject) {
CPlusPlus::Document::Ptr doc = snapshot.document(fileInProject);
if (doc.isNull())
continue;
commonFiles.insert(fileInProject);
d->m_documentCache[fileInProject].document = doc;
}
d->m_projectCache.insert(projectPath, { 0, nullptr, projectName, commonFiles });
updateDocumentsFromSnapshot(commonFiles, snapshot);
}
void Parser::removeProject(const FilePath &projectPath)
{
auto it = d->m_projectCache.find(projectPath);
if (it == d->m_projectCache.end())
return;
const QSet<FilePath> &filesInProject = it.value().fileNames;
for (const FilePath &fileInProject : filesInProject)
d->m_documentCache.remove(fileInProject);
d->m_projectCache.erase(it);
requestCurrentState();
}
/*!
Fully resets the internal state of the code parser to the current state.
\sa resetData
*/
void Parser::resetDataToCurrentState()
{
// get latest data
resetData(CppTools::CppModelManager::instance()->snapshot());
}
/*!
Requests to emit a signal with the current tree state.
*/
void Parser::requestCurrentState()
{
// TODO: we need to have a fresh SessionManager data here, which we could pass to parse()
emit treeRegenerated(parse());
}
// TODO: don't use Project class in this thread
QStringList Parser::getAllFiles(const Project *project)
{
if (!project)
return {};
const QString projectPath = project->projectFilePath().toString();
const auto it = d->m_projectCache.constFind(projectPath);
if (it != d->m_projectCache.constEnd())
return it.value().fileList;
const QStringList fileList = Utils::transform(project->files(Project::SourceFiles),
&FilePath::toString);
d->m_projectCache[projectPath].fileList = fileList;
return fileList;
}
// TODO: don't use Project class in this thread
ParserTreeItem::ConstPtr Parser::addFlatTree(const Project *project)
{
if (!project)
return {};
const QStringList fileList = getAllFiles(project);
if (fileList.isEmpty())
return {};
return getCachedOrParseProjectTree(fileList, project->projectFilePath().toString());
}
} // namespace Internal
} // namespace ClassView

View File

@@ -54,28 +54,29 @@ public:
void requestCurrentState();
void removeFiles(const QStringList &fileList);
void resetDataToCurrentState();
void resetData(const QHash<Utils::FilePath, QPair<QString, Utils::FilePaths>> &projects);
void addProject(const Utils::FilePath &projectPath, const QString &projectName,
const Utils::FilePaths &filesInProject);
void removeProject(const Utils::FilePath &projectPath);
void setFlatMode(bool flat);
void updateDocuments(const QList<CPlusPlus::Document::Ptr> &docs);
void updateDocuments(const QSet<Utils::FilePath> &documentPaths);
signals:
void treeRegenerated(const ParserTreeItem::ConstPtr &root);
private:
void setFileList(const QStringList &fileList);
void resetData(const CPlusPlus::Snapshot &snapshot);
void updateDocumentsFromSnapshot(const QSet<Utils::FilePath> &documentPaths,
const CPlusPlus::Snapshot &snapshot);
ParserTreeItem::ConstPtr getParseDocumentTree(const CPlusPlus::Document::Ptr &doc);
ParserTreeItem::ConstPtr getCachedOrParseDocumentTree(const CPlusPlus::Document::Ptr &doc);
ParserTreeItem::ConstPtr getParseProjectTree(const QStringList &fileList, const QString &projectId);
ParserTreeItem::ConstPtr getCachedOrParseProjectTree(const QStringList &fileList,
const QString &projectId);
ParserTreeItem::ConstPtr getParseProjectTree(const Utils::FilePath &projectPath,
const QSet<Utils::FilePath> &filesInProject);
ParserTreeItem::ConstPtr getCachedOrParseProjectTree(const Utils::FilePath &projectPath,
const QSet<Utils::FilePath> &filesInProject);
ParserTreeItem::ConstPtr parse();
QStringList getAllFiles(const ProjectExplorer::Project *project);
ParserTreeItem::ConstPtr addFlatTree(const ProjectExplorer::Project *project);
//! Private class data pointer
ParserPrivate *d;
};

View File

@@ -233,7 +233,8 @@ void addFileSystemNodes(ProjectNode *root, const QList<const FileNode *> &allFil
if (!fileSystemNode->isEmpty()) {
// make file system nodes less probable to be selected when syncing with the current document
fileSystemNode->forEachGenericNode([](Node *n) { n->setPriority(n->priority() + 10); });
fileSystemNode->forEachGenericNode(
[](Node *n) { n->setPriority(n->priority() + Node::DefaultProjectFilePriority + 1); });
root->addNode(std::move(fileSystemNode));
}
}

View File

@@ -1753,6 +1753,11 @@ bool EditorManagerPrivate::closeEditors(const QList<IEditor*> &editors, CloseFla
flags = EditorManager::DoNotSwitchToDesignMode;
activateEditorForDocument(view, document, flags);
}
} else {
// no documents left - set current view since view->removeEditor can
// trigger a focus change, context change, and updateActions, which
// requests the current EditorView
setCurrentView(currentView);
}
}
}
@@ -1766,10 +1771,12 @@ bool EditorManagerPrivate::closeEditors(const QList<IEditor*> &editors, CloseFla
foreach (IEditor *editor, acceptedEditors)
delete editor;
if (focusView)
if (focusView) {
activateView(focusView);
else
} else {
setCurrentView(currentView);
setCurrentEditor(currentView->currentEditor());
}
if (!EditorManager::currentEditor()) {
emit m_instance->currentEditorChanged(nullptr);

View File

@@ -142,6 +142,8 @@ Client::~Client()
}
for (IAssistProcessor *processor : qAsConst(m_runningAssistProcessors))
processor->setAsyncProposalAvailable(nullptr);
qDeleteAll(m_documentHighlightsTimer);
m_documentHighlightsTimer.clear();
updateEditorToolBar(m_openedDocument.keys());
// do not handle messages while shutting down
disconnect(m_clientInterface.data(), &BaseClientInterface::messageReceived,
@@ -437,6 +439,64 @@ void Client::updateFunctionHintProvider(TextEditor::TextDocument *document)
}
}
void Client::requestDocumentHighlights(TextEditor::TextEditorWidget *widget)
{
const auto uri = DocumentUri::fromFilePath(widget->textDocument()->filePath());
if (m_dynamicCapabilities.isRegistered(DocumentHighlightsRequest::methodName).value_or(false)) {
TextDocumentRegistrationOptions option(
m_dynamicCapabilities.option(DocumentHighlightsRequest::methodName));
if (!option.filterApplies(widget->textDocument()->filePath()))
return;
} else {
Utils::optional<Utils::variant<bool, WorkDoneProgressOptions>> provider
= m_serverCapabilities.documentHighlightProvider();
if (!provider.has_value())
return;
if (Utils::holds_alternative<bool>(*provider) && !Utils::get<bool>(*provider))
return;
}
auto runningRequest = m_highlightRequests.find(uri);
if (runningRequest != m_highlightRequests.end())
cancelRequest(runningRequest.value());
DocumentHighlightsRequest request(
TextDocumentPositionParams(TextDocumentIdentifier(uri), Position(widget->textCursor())));
request.setResponseCallback(
[widget = QPointer<TextEditor::TextEditorWidget>(widget), this, uri]
(const DocumentHighlightsRequest::Response &response)
{
m_highlightRequests.remove(uri);
if (!widget)
return;
const Id &id = TextEditor::TextEditorWidget::CodeSemanticsSelection;
QList<QTextEdit::ExtraSelection> selections;
const Utils::optional<DocumentHighlightsResult> &result = response.result();
if (!result.has_value() || holds_alternative<std::nullptr_t>(result.value())) {
widget->setExtraSelections(id, selections);
return;
}
const QTextCharFormat &format =
widget->textDocument()->fontSettings().toTextCharFormat(TextEditor::C_OCCURRENCES);
QTextDocument *document = widget->document();
for (const auto &highlight : get<QList<DocumentHighlight>>(result.value())) {
QTextEdit::ExtraSelection selection{widget->textCursor(), format};
const int &start = highlight.range().start().toPositionInDocument(document);
const int &end = highlight.range().end().toPositionInDocument(document);
if (start < 0 || end < 0)
continue;
selection.cursor.setPosition(start);
selection.cursor.setPosition(end, QTextCursor::KeepAnchor);
selections << selection;
}
widget->setExtraSelections(id, selections);
});
m_highlightRequests[uri] = request.id();
sendContent(request);
}
void Client::activateDocument(TextEditor::TextDocument *document)
{
auto uri = DocumentUri::fromFilePath(document->filePath());
@@ -453,11 +513,11 @@ void Client::activateDocument(TextEditor::TextDocument *document)
for (Core::IEditor *editor : Core::DocumentModel::editorsForDocument(document)) {
updateEditorToolBar(editor);
if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor)) {
textEditor->editorWidget()->addHoverHandler(&m_hoverHandler);
if (symbolSupport().supportsRename(document)) {
textEditor->editorWidget()->addOptionalActions(
TextEditor::TextEditorActionHandler::RenameSymbol);
}
TextEditor::TextEditorWidget *widget = textEditor->editorWidget();
widget->addHoverHandler(&m_hoverHandler);
requestDocumentHighlights(widget);
if (symbolSupport().supportsRename(document))
widget->addOptionalActions(TextEditor::TextEditorActionHandler::RenameSymbol);
}
}
}
@@ -472,8 +532,11 @@ void Client::deactivateDocument(TextEditor::TextDocument *document)
highlighter->clearAllExtraFormats();
}
for (Core::IEditor *editor : Core::DocumentModel::editorsForDocument(document)) {
if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor))
textEditor->editorWidget()->removeHoverHandler(&m_hoverHandler);
if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor)) {
TextEditor::TextEditorWidget *widget = textEditor->editorWidget();
widget->removeHoverHandler(&m_hoverHandler);
widget->setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection, {});
}
}
}
@@ -635,61 +698,38 @@ TextEditor::HighlightingResult createHighlightingResult(const SymbolInformation
void Client::cursorPositionChanged(TextEditor::TextEditorWidget *widget)
{
if (m_documentsToUpdate.contains(widget->textDocument()))
TextEditor::TextDocument *document = widget->textDocument();
if (m_documentsToUpdate.contains(document))
return; // we are currently changing this document so postpone the DocumentHighlightsRequest
const auto uri = DocumentUri::fromFilePath(widget->textDocument()->filePath());
if (m_dynamicCapabilities.isRegistered(DocumentHighlightsRequest::methodName).value_or(false)) {
TextDocumentRegistrationOptions option(
m_dynamicCapabilities.option(DocumentHighlightsRequest::methodName));
if (!option.filterApplies(widget->textDocument()->filePath()))
return;
} else {
Utils::optional<Utils::variant<bool, WorkDoneProgressOptions>> provider
= m_serverCapabilities.documentHighlightProvider();
if (!provider.has_value())
return;
if (Utils::holds_alternative<bool>(*provider) && !Utils::get<bool>(*provider))
return;
QTimer *timer = m_documentHighlightsTimer[widget];
if (!timer) {
const auto uri = DocumentUri::fromFilePath(widget->textDocument()->filePath());
auto runningRequest = m_highlightRequests.find(uri);
if (runningRequest != m_highlightRequests.end())
cancelRequest(runningRequest.value());
timer = new QTimer;
timer->setSingleShot(true);
m_documentHighlightsTimer.insert(widget, timer);
connect(timer, &QTimer::timeout, this, [this, widget]() {
requestDocumentHighlights(widget);
m_documentHighlightsTimer.take(widget)->deleteLater();
});
connect(widget, &QWidget::destroyed, this, [widget, this]() {
delete m_documentHighlightsTimer.take(widget);
});
}
auto runningRequest = m_highlightRequests.find(uri);
if (runningRequest != m_highlightRequests.end())
cancelRequest(runningRequest.value());
DocumentHighlightsRequest request(
TextDocumentPositionParams(TextDocumentIdentifier(uri), Position(widget->textCursor())));
request.setResponseCallback(
[widget = QPointer<TextEditor::TextEditorWidget>(widget), this, uri]
(DocumentHighlightsRequest::Response response)
{
m_highlightRequests.remove(uri);
if (!widget)
return;
QList<QTextEdit::ExtraSelection> selections;
const DocumentHighlightsResult result = response.result().value_or(DocumentHighlightsResult());
if (!holds_alternative<QList<DocumentHighlight>>(result)) {
widget->setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection, selections);
return;
}
const QTextCharFormat &format =
widget->textDocument()->fontSettings().toTextCharFormat(TextEditor::C_OCCURRENCES);
QTextDocument *document = widget->document();
for (const auto &highlight : get<QList<DocumentHighlight>>(result)) {
QTextEdit::ExtraSelection selection{widget->textCursor(), format};
const int &start = highlight.range().start().toPositionInDocument(document);
const int &end = highlight.range().end().toPositionInDocument(document);
if (start < 0 || end < 0)
continue;
selection.cursor.setPosition(start);
selection.cursor.setPosition(end, QTextCursor::KeepAnchor);
selections << selection;
}
widget->setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection, selections);
});
m_highlightRequests[uri] = request.id();
sendContent(request);
const Id selectionsId(TextEditor::TextEditorWidget::CodeSemanticsSelection);
const QList semanticSelections = widget->extraSelections(selectionsId);
if (!semanticSelections.isEmpty()) {
auto selectionContainsPos =
[pos = widget->position()](const QTextEdit::ExtraSelection &selection) {
const QTextCursor cursor = selection.cursor;
return cursor.selectionStart() <= pos && cursor.selectionEnd() >= pos;
};
if (!Utils::anyOf(semanticSelections, selectionContainsPos))
widget->setExtraSelections(selectionsId, {});
}
timer->start(250);
}
SymbolSupport &Client::symbolSupport()
@@ -893,6 +933,8 @@ bool Client::reset()
for (TextEditor::IAssistProcessor *processor : qAsConst(m_runningAssistProcessors))
processor->setAsyncProposalAvailable(nullptr);
m_runningAssistProcessors.clear();
qDeleteAll(m_documentHighlightsTimer);
m_documentHighlightsTimer.clear();
return true;
}

View File

@@ -199,6 +199,7 @@ private:
void updateCompletionProvider(TextEditor::TextDocument *document);
void updateFunctionHintProvider(TextEditor::TextDocument *document);
void requestDocumentHighlights(TextEditor::TextEditorWidget *widget);
void rehighlight();
using ContentHandler = std::function<void(const QByteArray &, QTextCodec *, QString &,
@@ -216,6 +217,7 @@ private:
QMap<TextEditor::TextDocument *,
QList<LanguageServerProtocol::DidChangeTextDocumentParams::TextDocumentContentChangeEvent>>
m_documentsToUpdate;
QMap<TextEditor::TextEditorWidget *, QTimer *> m_documentHighlightsTimer;
QTimer m_documentUpdateTimer;
Utils::Id m_id;
LanguageServerProtocol::ServerCapabilities m_serverCapabilities;

View File

@@ -435,15 +435,10 @@ void LanguageClientManager::editorOpened(Core::IEditor *editor)
if (auto client = clientForDocument(document))
client->symbolSupport().renameSymbol(document, cursor);
});
connect(widget, &TextEditorWidget::cursorPositionChanged, this, [this, widget]() {
// TODO This would better be a compressing timer
QTimer::singleShot(50, this, [widget = QPointer<TextEditorWidget>(widget)]() {
if (!widget)
return;
if (Client *client = clientForDocument(widget->textDocument()))
if (client->reachable())
client->cursorPositionChanged(widget);
});
connect(widget, &TextEditorWidget::cursorPositionChanged, this, [widget]() {
if (Client *client = clientForDocument(widget->textDocument()))
if (client->reachable())
client->cursorPositionChanged(widget);
});
updateEditorToolBar(editor);
if (TextEditor::TextDocument *document = textEditor->textDocument()) {

View File

@@ -29,7 +29,9 @@
#include "annotation.h"
QT_BEGIN_NAMESPACE
class QDir;
QT_END_NAMESPACE
namespace QmlDesigner {

View File

@@ -63,9 +63,9 @@ CurveEditorStyle CurveEditorModel::style() const
{
// Pseudo auto generated. See: CurveEditorStyleDialog
CurveEditorStyle out;
out.backgroundBrush = QBrush(QColor(21, 21, 21));
out.backgroundAlternateBrush = QBrush(QColor(32, 32, 32));
out.fontColor = QColor(255, 255, 255);
out.backgroundBrush = QmlDesigner::Theme::getColor(QmlDesigner::Theme::DSsectionHeadBackground);
out.backgroundAlternateBrush = QmlDesigner::Theme::getColor(QmlDesigner::Theme::DSpanelBackground);
out.fontColor = QmlDesigner::Theme::getColor(QmlDesigner::Theme::DStextColor);
out.gridColor = QColor(114, 116, 118);
out.canvasMargin = 15;
out.zoomInWidth = 99;

View File

@@ -363,7 +363,8 @@ void CurveEditorView::commitKeyframes(TreeItem *item)
void CurveEditorView::commitCurrentFrame(int frame)
{
QmlTimeline timeline = activeTimeline();
timeline.modelNode().setAuxiliaryData("currentFrame@NodeInstance", frame);
if (timeline.isValid())
timeline.modelNode().setAuxiliaryData("currentFrame@NodeInstance", frame);
}
void CurveEditorView::commitStartFrame(int frame)

View File

@@ -31,6 +31,9 @@
#include "treeitem.h"
#include "utils.h"
#include <theme.h>
#include <utils/fileutils.h>
#include <QAction>
#include <QMenu>
#include <QResizeEvent>
@@ -84,6 +87,11 @@ GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent)
applyZoom(m_zoomX, m_zoomY);
update();
const QString css = Theme::replaceCssColors(QString::fromUtf8(
Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css"))));
horizontalScrollBar()->setStyleSheet(css);
verticalScrollBar()->setStyleSheet(css);
}
GraphicsView::~GraphicsView()

View File

@@ -81,7 +81,17 @@ QTabBar::tab:selected {
QLineEdit {
color: creatorTheme.DStextColor;
background-color: creatorTheme.DSdockAreaBackground;
background-color: creatorTheme.DScontrolBackground;
border: 1px solid creatorTheme.DScontrolOutline;
selection-color: creatorTheme.DStextSelectedTextColor;
selection-background-color: creatorTheme.DStextSelectionColor;
}
QLineEdit:hover {
background-color: creatorTheme.DScontrolBackgroundHover;
}
QLineEdit:focus {
background-color: creatorTheme.DScontrolBackgroundInteraction;
border-color: creatorTheme.DScontrolOutlineInteraction;
}

View File

@@ -70,15 +70,17 @@ void TextEditItem::setFormEditorItem(FormEditorItem *formEditorItem)
setGeometry(rect);
NodeMetaInfo metaInfo = m_formEditorItem->qmlItemNode().modelNode().metaInfo();
auto node = m_formEditorItem->qmlItemNode();
auto font = node.instanceValue("font").value<QFont>();
if (metaInfo.isValid() &&
(metaInfo.isSubclassOf("QtQuick.TextEdit")
|| metaInfo.isSubclassOf("QtQuick.Controls.TextArea"))) {
QSize maximumSize = rect.size().toSize();
textEdit()->setFont(font);
activateTextEdit(maximumSize);
} else {
auto lineEdit = TextEditItemWidget::lineEdit();
auto node = m_formEditorItem->qmlItemNode();
lineEdit->setFont(node.instanceValue("font").value<QFont>());
lineEdit->setFont(font);
activateLineEdit();
}

View File

@@ -55,11 +55,10 @@ constexpr int spacingg = 5;
const QColor background = Qt::white;
const QColor labelBackground = qRgb(0x70, 0x70, 0x70);
const QColor canvasBackground = qRgb(0x46, 0x46, 0x46);
const QColor curveLine = qRgb(0xe6, 0xe7, 0xe8);
PresetItemDelegate::PresetItemDelegate() = default;
PresetItemDelegate::PresetItemDelegate(const QColor& background)
: QStyledItemDelegate()
, m_background(background)
{}
void PresetItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &opt,
@@ -80,10 +79,10 @@ void PresetItemDelegate::paint(QPainter *painter,
option.font.setPixelSize(Theme::instance()->smallFontPixelSize());
painter->save();
painter->fillRect(option.rect, canvasBackground);
painter->fillRect(option.rect, m_background);
if (option.text.isEmpty())
painter->fillRect(textRect, canvasBackground);
painter->fillRect(textRect, m_background);
else
painter->fillRect(textRect, Theme::instance()->qmlDesignerButtonColor());
@@ -118,23 +117,25 @@ QSize PresetItemDelegate::sizeHint(const QStyleOptionViewItem &opt, const QModel
return size;
}
QIcon paintPreview()
QIcon paintPreview(const QColor& background)
{
QPixmap pm(iconWidth, iconHeight);
pm.fill(canvasBackground);
pm.fill(background);
return QIcon(pm);
}
QIcon paintPreview(const EasingCurve &curve)
QIcon paintPreview(const EasingCurve &curve, const QColor& background, const QColor& curveColor)
{
const QColor curveLine = Theme::getColor(Theme::DStextColor);
QPixmap pm(iconWidth, iconHeight);
pm.fill(canvasBackground);
pm.fill(background);
QPainter painter(&pm);
painter.setRenderHint(QPainter::Antialiasing, true);
Canvas canvas(iconWidth, iconHeight, 2, 2, 9, 6, 0, 1);
canvas.paintCurve(&painter, curve, curveLine);
canvas.paintCurve(&painter, curve, curveColor);
return QIcon(pm);
}
@@ -159,6 +160,8 @@ PresetList::PresetList(QSettings::Scope scope, QWidget *parent)
, m_scope(scope)
, m_index(-1)
, m_filename(Internal::settingsFullFilePath(scope))
, m_background(Theme::getColor(Theme::DSsectionHeadBackground ))
, m_curveColor(Theme::getColor(Theme::DStextColor))
{
int magic = 4;
int scrollBarWidth = this->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
@@ -168,7 +171,7 @@ PresetList::PresetList(QSettings::Scope scope, QWidget *parent)
setModel(new QStandardItemModel);
setItemDelegate(new PresetItemDelegate);
setItemDelegate(new PresetItemDelegate(m_background));
setSpacing(spacingg);
@@ -260,6 +263,16 @@ bool PresetList::isEditable(const QModelIndex &index) const
return flags.testFlag(Qt::ItemIsEditable);
}
QColor PresetList::backgroundColor() const
{
return m_background;
}
QColor PresetList::curveColor() const
{
return m_curveColor;
}
void PresetList::initialize(int index)
{
m_index = index;
@@ -278,7 +291,7 @@ void PresetList::readPresets()
for (int i = 0; i < curves.size(); ++i) {
QVariant curveData = QVariant::fromValue(curves[i].curve());
auto *item = new QStandardItem(paintPreview(curves[i].curve()), curves[i].name());
auto *item = new QStandardItem(paintPreview(curves[i].curve(), m_background, m_curveColor), curves[i].name());
item->setData(curveData, ItemRole_Data);
item->setEditable(m_scope == QSettings::UserScope);
item->setToolTip(curves[i].name());
@@ -320,7 +333,7 @@ void PresetList::revert(const QModelIndex &index)
for (const auto &curve : curves) {
if (curve.name() == name) {
item->setData(false, ItemRole_Dirty);
item->setData(paintPreview(curve.curve()), Qt::DecorationRole);
item->setData(paintPreview(curve.curve(), m_background, m_curveColor), Qt::DecorationRole);
item->setData(QVariant::fromValue(curve.curve()), ItemRole_Data);
item->setToolTip(name);
return;
@@ -334,7 +347,7 @@ void PresetList::updateCurve(const EasingCurve &curve)
if (!selectionModel()->hasSelection())
return;
QVariant icon = QVariant::fromValue(paintPreview(curve));
QVariant icon = QVariant::fromValue(paintPreview(curve, m_background, m_curveColor));
QVariant curveData = QVariant::fromValue(curve);
for (const auto &index : selectionModel()->selectedIndexes())
@@ -382,7 +395,7 @@ void PresetList::createItem()
void PresetList::createItem(const QString &name, const EasingCurve &curve)
{
auto *item = new QStandardItem(paintPreview(curve), name);
auto *item = new QStandardItem(paintPreview(curve, m_background, m_curveColor), name);
item->setData(QVariant::fromValue(curve), ItemRole_Data);
item->setToolTip(name);
@@ -507,7 +520,8 @@ void PresetEditor::update(const EasingCurve &curve)
m_presets->selectionModel()->clear();
else {
if (m_customs->selectionModel()->hasSelection()) {
QVariant icon = QVariant::fromValue(paintPreview(curve));
QVariant icon = QVariant::fromValue(
paintPreview(curve, m_presets->backgroundColor(), m_presets->curveColor()));
QVariant curveData = QVariant::fromValue(curve);
for (const QModelIndex &index : m_customs->selectionModel()->selectedIndexes())
m_customs->setItemData(index, curveData, icon);

View File

@@ -43,13 +43,16 @@ class PresetItemDelegate : public QStyledItemDelegate
Q_OBJECT
public:
PresetItemDelegate();
PresetItemDelegate(const QColor& background);
void paint(QPainter *painter,
const QStyleOptionViewItem &opt,
const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
QColor m_background;
};
class PresetList : public QListView
@@ -80,6 +83,10 @@ public:
bool isEditable(const QModelIndex &index) const;
QColor backgroundColor() const;
QColor curveColor() const;
void initialize(int index);
void readPresets();
@@ -118,6 +125,10 @@ private:
int m_index;
QString m_filename;
QColor m_background;
QColor m_curveColor;
};
class PresetEditor : public QStackedWidget

View File

@@ -6010,6 +6010,7 @@ void TextEditorWidgetPrivate::toggleBlockVisible(const QTextBlock &block)
void TextEditorWidget::setLanguageSettingsId(Id settingsId)
{
d->m_tabSettingsId = settingsId;
setCodeStyle(TextEditorSettings::codeStyle(settingsId));
}
Id TextEditorWidget::languageSettingsId() const
@@ -6019,20 +6020,24 @@ Id TextEditorWidget::languageSettingsId() const
void TextEditorWidget::setCodeStyle(ICodeStylePreferences *preferences)
{
textDocument()->indenter()->setCodeStylePreferences(preferences);
TextDocument *document = d->m_document.data();
// Not fully initialized yet... wait for TextEditorWidgetPrivate::setupDocumentSignals
if (!document)
return;
document->indenter()->setCodeStylePreferences(preferences);
if (d->m_codeStylePreferences) {
disconnect(d->m_codeStylePreferences, &ICodeStylePreferences::currentTabSettingsChanged,
d->m_document.data(), &TextDocument::setTabSettings);
document, &TextDocument::setTabSettings);
disconnect(d->m_codeStylePreferences, &ICodeStylePreferences::currentValueChanged,
this, &TextEditorWidget::slotCodeStyleSettingsChanged);
}
d->m_codeStylePreferences = preferences;
if (d->m_codeStylePreferences) {
connect(d->m_codeStylePreferences, &ICodeStylePreferences::currentTabSettingsChanged,
d->m_document.data(), &TextDocument::setTabSettings);
document, &TextDocument::setTabSettings);
connect(d->m_codeStylePreferences, &ICodeStylePreferences::currentValueChanged,
this, &TextEditorWidget::slotCodeStyleSettingsChanged);
d->m_document->setTabSettings(d->m_codeStylePreferences->currentTabSettings());
document->setTabSettings(d->m_codeStylePreferences->currentTabSettings());
slotCodeStyleSettingsChanged(d->m_codeStylePreferences->currentValue());
}
}