From c032b302ade45a13e72c9d3d75c6c84dce89538f Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 27 Jan 2020 14:52:46 +0100 Subject: LanguageClient: outline combo box for editor toolbar Fixes: QTCREATORBUG-21916 Change-Id: Ia4e1711f0f5e67222e4f5274792820917f4114db Reviewed-by: Christian Stenger --- .../languageclient/languageclientoutline.cpp | 94 +++++++++++++++++++++- src/plugins/languageclient/languageclientoutline.h | 8 ++ src/plugins/languageclient/languageclientutils.cpp | 23 ++++++ 3 files changed, 124 insertions(+), 1 deletion(-) (limited to 'src/plugins') diff --git a/src/plugins/languageclient/languageclientoutline.cpp b/src/plugins/languageclient/languageclientoutline.cpp index a85ff0f6a84..550e391dda4 100644 --- a/src/plugins/languageclient/languageclientoutline.cpp +++ b/src/plugins/languageclient/languageclientoutline.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -218,7 +219,8 @@ void LanguageClientOutlineWidget::onItemActivated(const QModelIndex &index) m_editor->widget()->setFocus(); } -static bool clientSupportsDocumentSymbols(const Client *client, const TextEditor::TextDocument *doc) +bool LanguageClientOutlineWidgetFactory::clientSupportsDocumentSymbols( + const Client *client, const TextEditor::TextDocument *doc) { if (!client) return false; @@ -249,4 +251,94 @@ TextEditor::IOutlineWidget *LanguageClientOutlineWidgetFactory::createWidget(Cor return new LanguageClientOutlineWidget(client, textEditor); } +class OutlineComboBox : public Utils::TreeViewComboBox +{ +public: + OutlineComboBox(Client *client, TextEditor::BaseTextEditor *editor); + +private: + void updateModel(const DocumentUri &resultUri, const DocumentSymbolsResult &result); + void updateEntry(); + void activateEntry(); + void requestSymbols(); + + LanguageClientOutlineModel m_model; + QPointer m_client; + TextEditor::TextEditorWidget *m_editorWidget; + const DocumentUri m_uri; +}; + +Utils::TreeViewComboBox *LanguageClientOutlineWidgetFactory::createComboBox(Client *client, + Core::IEditor *editor) +{ + auto textEditor = qobject_cast(editor); + QTC_ASSERT(textEditor, return nullptr); + TextEditor::TextDocument *document = textEditor->textDocument(); + if (!client || !clientSupportsDocumentSymbols(client, document)) + return nullptr; + + return new OutlineComboBox(client, textEditor); +} + +OutlineComboBox::OutlineComboBox(Client *client, TextEditor::BaseTextEditor *editor) + : m_client(client) + , m_editorWidget(editor->editorWidget()) + , m_uri(DocumentUri::fromFilePath(editor->document()->filePath())) +{ + setModel(&m_model); + setMinimumContentsLength(13); + QSizePolicy policy = sizePolicy(); + policy.setHorizontalPolicy(QSizePolicy::Expanding); + setSizePolicy(policy); + setMaxVisibleItems(40); + + connect(client->documentSymbolCache(), &DocumentSymbolCache::gotSymbols, + this, &OutlineComboBox::updateModel); + connect(editor->textDocument(), &TextEditor::TextDocument::contentsChanged, + this, &OutlineComboBox::requestSymbols); + connect(m_editorWidget, &TextEditor::TextEditorWidget::cursorPositionChanged, + this, &OutlineComboBox::updateEntry); + connect(this, QOverload::of(&QComboBox::activated), this, &OutlineComboBox::activateEntry); +} + +void OutlineComboBox::updateModel(const DocumentUri &resultUri, const DocumentSymbolsResult &result) +{ + if (m_uri != resultUri) + return; + if (Utils::holds_alternative>(result)) + m_model.setInfo(Utils::get>(result)); + else if (Utils::holds_alternative>(result)) + m_model.setInfo(Utils::get>(result)); + else + m_model.clear(); +} + +void OutlineComboBox::updateEntry() +{ + const Position pos(m_editorWidget->textCursor()); + LanguageClientOutlineItem *itemForCursor = m_model.findNonRootItem( + [&](const LanguageClientOutlineItem *item) { return item->contains(pos); }); + if (itemForCursor) + setCurrentIndex(m_model.indexForItem(itemForCursor)); +} + +void OutlineComboBox::activateEntry() +{ + const QModelIndex modelIndex = view()->currentIndex(); + if (modelIndex.isValid()) { + const Position &pos = m_model.itemForIndex(modelIndex)->pos(); + Core::EditorManager::cutForwardNavigationHistory(); + Core::EditorManager::addCurrentPositionToNavigationHistory(); + // line has to be 1 based, column 0 based! + m_editorWidget->gotoLine(pos.line() + 1, pos.character(), true, true); + emit m_editorWidget->activateEditor(); + } +} + +void OutlineComboBox::requestSymbols() +{ + if (m_client) + m_client->documentSymbolCache()->requestSymbols(m_uri); +} + } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientoutline.h b/src/plugins/languageclient/languageclientoutline.h index 100f8a783d8..2384ea7f6ab 100644 --- a/src/plugins/languageclient/languageclientoutline.h +++ b/src/plugins/languageclient/languageclientoutline.h @@ -27,13 +27,21 @@ #include +namespace TextEditor { class TextDocument; } +namespace Utils { class TreeViewComboBox; } + namespace LanguageClient { +class Client; + class LanguageClientOutlineWidgetFactory : public TextEditor::IOutlineWidgetFactory { public: using IOutlineWidgetFactory::IOutlineWidgetFactory; + static Utils::TreeViewComboBox *createComboBox(Client *client, Core::IEditor *editor); + static bool clientSupportsDocumentSymbols(const Client *client, + const TextEditor::TextDocument *doc); // IOutlineWidgetFactory interface public: bool supportsEditor(Core::IEditor *editor) const override; diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp index eeb64b406ea..9cc1ad488fa 100644 --- a/src/plugins/languageclient/languageclientutils.cpp +++ b/src/plugins/languageclient/languageclientutils.cpp @@ -28,6 +28,7 @@ #include "client.h" #include "languageclient_global.h" #include "languageclientmanager.h" +#include "languageclientoutline.h" #include #include @@ -37,6 +38,7 @@ #include #include #include +#include #include #include @@ -246,6 +248,27 @@ void updateEditorToolBar(Core::IEditor *editor) actions.remove(widget); }); } + + static QMap> outlines; + + if (outlines.contains(widget)) { + auto outline = outlines[widget]; + if (outline.first != client + || !LanguageClientOutlineWidgetFactory::clientSupportsDocumentSymbols(client, + document)) { + auto oldAction = outline.second; + widget->toolBar()->removeAction(oldAction); + delete oldAction; + outlines.remove(widget); + } + } + + if (!outlines.contains(widget)) { + if (QWidget *comboBox = LanguageClientOutlineWidgetFactory::createComboBox(client, editor)) { + outlines[widget] = {client, + widget->insertExtraToolBarWidget(TextEditorWidget::Left, comboBox)}; + } + } } const QIcon symbolIcon(int type) -- cgit v1.2.3