2018-11-23 09:45:51 +01:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2018 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
|
|
|
**
|
|
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "languageclientoutline.h"
|
|
|
|
|
|
2022-05-19 14:55:15 +02:00
|
|
|
#include "documentsymbolcache.h"
|
2018-11-23 09:45:51 +01:00
|
|
|
#include "languageclientmanager.h"
|
2019-04-05 10:05:25 +02:00
|
|
|
#include "languageclientutils.h"
|
2018-11-23 09:45:51 +01:00
|
|
|
|
|
|
|
|
#include <coreplugin/editormanager/ieditor.h>
|
2022-02-23 17:11:20 +01:00
|
|
|
#include <coreplugin/find/itemviewfind.h>
|
2018-11-23 09:45:51 +01:00
|
|
|
#include <languageserverprotocol/languagefeatures.h>
|
2022-05-02 16:40:03 +02:00
|
|
|
#include <texteditor/outlinefactory.h>
|
2018-11-23 09:45:51 +01:00
|
|
|
#include <texteditor/textdocument.h>
|
|
|
|
|
#include <texteditor/texteditor.h>
|
2022-06-27 09:16:09 +02:00
|
|
|
#include <utils/dropsupport.h>
|
2018-11-23 09:45:51 +01:00
|
|
|
#include <utils/itemviews.h>
|
2022-06-27 09:16:09 +02:00
|
|
|
#include <utils/navigationtreeview.h>
|
2018-11-23 09:45:51 +01:00
|
|
|
#include <utils/treemodel.h>
|
2020-01-27 14:52:46 +01:00
|
|
|
#include <utils/treeviewcombobox.h>
|
2018-11-23 09:45:51 +01:00
|
|
|
#include <utils/utilsicons.h>
|
|
|
|
|
|
2022-02-04 12:05:31 +01:00
|
|
|
#include <QAction>
|
2018-11-23 09:45:51 +01:00
|
|
|
#include <QBoxLayout>
|
2022-02-04 11:27:35 +01:00
|
|
|
#include <QSortFilterProxyModel>
|
2018-11-23 09:45:51 +01:00
|
|
|
|
|
|
|
|
using namespace LanguageServerProtocol;
|
|
|
|
|
|
|
|
|
|
namespace LanguageClient {
|
|
|
|
|
|
2022-06-27 09:34:44 +02:00
|
|
|
const QList<SymbolInformation> sortedSymbols(const QList<SymbolInformation> &symbols)
|
|
|
|
|
{
|
|
|
|
|
auto result = symbols;
|
|
|
|
|
Utils::sort(result, [](const SymbolInformation &a, const SymbolInformation &b){
|
|
|
|
|
return a.location().range().start() < b.location().range().start();
|
|
|
|
|
});
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
const QList<DocumentSymbol> sortedSymbols(const QList<DocumentSymbol> &symbols)
|
|
|
|
|
{
|
|
|
|
|
auto result = symbols;
|
|
|
|
|
Utils::sort(result, [](const DocumentSymbol &a, const DocumentSymbol &b){
|
|
|
|
|
return a.range().start() < b.range().start();
|
|
|
|
|
});
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-23 09:45:51 +01:00
|
|
|
class LanguageClientOutlineItem : public Utils::TypedTreeItem<LanguageClientOutlineItem>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
LanguageClientOutlineItem() = default;
|
|
|
|
|
LanguageClientOutlineItem(const SymbolInformation &info)
|
|
|
|
|
: m_name(info.name())
|
|
|
|
|
, m_range(info.location().range())
|
|
|
|
|
, m_type(info.kind())
|
|
|
|
|
{ }
|
|
|
|
|
|
2021-06-08 14:28:25 +02:00
|
|
|
LanguageClientOutlineItem(const DocumentSymbol &info, const SymbolStringifier &stringifier)
|
2018-11-23 09:45:51 +01:00
|
|
|
: m_name(info.name())
|
|
|
|
|
, m_detail(info.detail().value_or(QString()))
|
|
|
|
|
, m_range(info.range())
|
2021-06-08 14:28:25 +02:00
|
|
|
, m_symbolStringifier(stringifier)
|
2018-11-23 09:45:51 +01:00
|
|
|
, m_type(info.kind())
|
|
|
|
|
{
|
2022-06-27 09:34:44 +02:00
|
|
|
const QList<LanguageServerProtocol::DocumentSymbol> children = sortedSymbols(
|
|
|
|
|
info.children().value_or(QList<DocumentSymbol>()));
|
|
|
|
|
for (const DocumentSymbol &child : children)
|
2021-06-08 14:28:25 +02:00
|
|
|
appendChild(new LanguageClientOutlineItem(child, stringifier));
|
2018-11-23 09:45:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TreeItem interface
|
|
|
|
|
QVariant data(int column, int role) const override
|
|
|
|
|
{
|
|
|
|
|
switch (role) {
|
|
|
|
|
case Qt::DecorationRole:
|
|
|
|
|
return symbolIcon(m_type);
|
|
|
|
|
case Qt::DisplayRole:
|
2021-06-08 14:28:25 +02:00
|
|
|
return m_symbolStringifier
|
|
|
|
|
? m_symbolStringifier(static_cast<SymbolKind>(m_type), m_name, m_detail)
|
|
|
|
|
: m_name;
|
2018-11-23 09:45:51 +01:00
|
|
|
default:
|
|
|
|
|
return Utils::TreeItem::data(column, role);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-27 09:16:09 +02:00
|
|
|
Qt::ItemFlags flags(int column) const override
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(column)
|
|
|
|
|
return Utils::TypedTreeItem<LanguageClientOutlineItem>::flags(column)
|
|
|
|
|
| Qt::ItemIsDragEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 10:13:48 +01:00
|
|
|
Range range() const { return m_range; }
|
2018-11-23 09:45:51 +01:00
|
|
|
Position pos() const { return m_range.start(); }
|
|
|
|
|
bool contains(const Position &pos) const { return m_range.contains(pos); }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QString m_name;
|
|
|
|
|
QString m_detail;
|
|
|
|
|
Range m_range;
|
2021-06-08 14:28:25 +02:00
|
|
|
SymbolStringifier m_symbolStringifier;
|
2018-11-23 09:45:51 +01:00
|
|
|
int m_type = -1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class LanguageClientOutlineModel : public Utils::TreeModel<LanguageClientOutlineItem>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
using Utils::TreeModel<LanguageClientOutlineItem>::TreeModel;
|
2022-06-27 09:16:09 +02:00
|
|
|
void setFilePath(const Utils::FilePath &filePath) { m_filePath = filePath; }
|
|
|
|
|
|
2018-11-23 09:45:51 +01:00
|
|
|
void setInfo(const QList<SymbolInformation> &info)
|
|
|
|
|
{
|
|
|
|
|
clear();
|
2022-06-27 09:34:44 +02:00
|
|
|
for (const SymbolInformation &symbol : sortedSymbols(info))
|
2018-11-23 09:45:51 +01:00
|
|
|
rootItem()->appendChild(new LanguageClientOutlineItem(symbol));
|
|
|
|
|
}
|
|
|
|
|
void setInfo(const QList<DocumentSymbol> &info)
|
|
|
|
|
{
|
|
|
|
|
clear();
|
2022-06-27 09:34:44 +02:00
|
|
|
for (const DocumentSymbol &symbol : sortedSymbols(info))
|
2021-06-08 14:28:25 +02:00
|
|
|
rootItem()->appendChild(new LanguageClientOutlineItem(symbol, m_symbolStringifier));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setSymbolStringifier(const SymbolStringifier &stringifier)
|
|
|
|
|
{
|
|
|
|
|
m_symbolStringifier = stringifier;
|
2018-11-23 09:45:51 +01:00
|
|
|
}
|
2021-06-08 14:28:25 +02:00
|
|
|
|
2022-06-27 09:16:09 +02:00
|
|
|
Qt::DropActions supportedDragActions() const override
|
|
|
|
|
{
|
|
|
|
|
return Qt::MoveAction;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList mimeTypes() const override
|
|
|
|
|
{
|
|
|
|
|
return Utils::DropSupport::mimeTypesForFilePaths();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QMimeData *mimeData(const QModelIndexList &indexes) const override
|
|
|
|
|
{
|
|
|
|
|
auto mimeData = new Utils::DropMimeData;
|
|
|
|
|
for (const QModelIndex &index : indexes) {
|
|
|
|
|
if (LanguageClientOutlineItem *item = itemForIndex(index)) {
|
|
|
|
|
const LanguageServerProtocol::Position pos = item->pos();
|
|
|
|
|
mimeData->addFile(m_filePath, pos.line() + 1, pos.character());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return mimeData;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-08 14:28:25 +02:00
|
|
|
private:
|
|
|
|
|
SymbolStringifier m_symbolStringifier;
|
2022-06-27 09:16:09 +02:00
|
|
|
Utils::FilePath m_filePath;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class DragSortFilterProxyModel : public QSortFilterProxyModel
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
using QSortFilterProxyModel::QSortFilterProxyModel;
|
|
|
|
|
|
|
|
|
|
Qt::DropActions supportedDragActions() const override
|
|
|
|
|
{
|
|
|
|
|
return sourceModel()->supportedDragActions();
|
|
|
|
|
}
|
2018-11-23 09:45:51 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class LanguageClientOutlineWidget : public TextEditor::IOutlineWidget
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-01-31 12:15:43 +01:00
|
|
|
LanguageClientOutlineWidget(Client *client, TextEditor::BaseTextEditor *editor);
|
2018-11-23 09:45:51 +01:00
|
|
|
|
|
|
|
|
// IOutlineWidget interface
|
|
|
|
|
public:
|
|
|
|
|
QList<QAction *> filterMenuActions() const override;
|
|
|
|
|
void setCursorSynchronization(bool syncWithCursor) override;
|
2022-02-04 11:27:35 +01:00
|
|
|
void setSorted(bool) override;
|
|
|
|
|
bool isSorted() const override;
|
|
|
|
|
void restoreSettings(const QVariantMap &map) override;
|
|
|
|
|
QVariantMap settings() const override;
|
2018-11-23 09:45:51 +01:00
|
|
|
|
|
|
|
|
private:
|
2019-04-04 14:36:28 +02:00
|
|
|
void handleResponse(const DocumentUri &uri, const DocumentSymbolsResult &response);
|
2018-11-23 09:45:51 +01:00
|
|
|
void updateTextCursor(const QModelIndex &proxyIndex);
|
|
|
|
|
void updateSelectionInTree(const QTextCursor ¤tCursor);
|
|
|
|
|
void onItemActivated(const QModelIndex &index);
|
|
|
|
|
|
2019-01-31 12:15:43 +01:00
|
|
|
QPointer<Client> m_client;
|
2018-11-23 09:45:51 +01:00
|
|
|
QPointer<TextEditor::BaseTextEditor> m_editor;
|
|
|
|
|
LanguageClientOutlineModel m_model;
|
2022-06-27 09:16:09 +02:00
|
|
|
DragSortFilterProxyModel m_proxyModel;
|
|
|
|
|
Utils::NavigationTreeView m_view;
|
2019-04-04 14:36:28 +02:00
|
|
|
DocumentUri m_uri;
|
2018-11-23 09:45:51 +01:00
|
|
|
bool m_sync = false;
|
2022-02-04 11:27:35 +01:00
|
|
|
bool m_sorted = false;
|
2018-11-23 09:45:51 +01:00
|
|
|
};
|
|
|
|
|
|
2019-01-31 12:15:43 +01:00
|
|
|
LanguageClientOutlineWidget::LanguageClientOutlineWidget(Client *client,
|
2018-11-23 09:45:51 +01:00
|
|
|
TextEditor::BaseTextEditor *editor)
|
|
|
|
|
: m_client(client)
|
|
|
|
|
, m_editor(editor)
|
|
|
|
|
, m_view(this)
|
2019-09-11 14:34:20 +02:00
|
|
|
, m_uri(DocumentUri::fromFilePath(editor->textDocument()->filePath()))
|
2018-11-23 09:45:51 +01:00
|
|
|
{
|
2019-04-04 14:36:28 +02:00
|
|
|
connect(client->documentSymbolCache(),
|
|
|
|
|
&DocumentSymbolCache::gotSymbols,
|
|
|
|
|
this,
|
|
|
|
|
&LanguageClientOutlineWidget::handleResponse);
|
2021-07-09 14:08:24 +02:00
|
|
|
connect(client, &Client::documentUpdated, this, [this](TextEditor::TextDocument *document) {
|
|
|
|
|
if (m_client && m_uri == DocumentUri::fromFilePath(document->filePath()))
|
2021-09-14 17:00:16 +02:00
|
|
|
m_client->documentSymbolCache()->requestSymbols(m_uri, Schedule::Delayed);
|
2019-05-13 15:20:24 +02:00
|
|
|
});
|
|
|
|
|
|
2021-09-14 17:00:16 +02:00
|
|
|
client->documentSymbolCache()->requestSymbols(m_uri, Schedule::Delayed);
|
2018-11-23 09:45:51 +01:00
|
|
|
|
|
|
|
|
auto *layout = new QVBoxLayout;
|
2019-08-29 10:36:01 +02:00
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
2018-11-23 09:45:51 +01:00
|
|
|
layout->setSpacing(0);
|
|
|
|
|
layout->addWidget(Core::ItemViewFind::createSearchableWrapper(&m_view));
|
|
|
|
|
setLayout(layout);
|
2021-06-08 14:28:25 +02:00
|
|
|
m_model.setSymbolStringifier(m_client->symbolStringifier());
|
2022-06-27 09:16:09 +02:00
|
|
|
m_model.setFilePath(editor->textDocument()->filePath());
|
2022-02-04 11:27:35 +01:00
|
|
|
m_proxyModel.setSourceModel(&m_model);
|
|
|
|
|
m_view.setModel(&m_proxyModel);
|
2018-11-23 09:45:51 +01:00
|
|
|
m_view.setHeaderHidden(true);
|
2020-06-18 14:32:14 +02:00
|
|
|
m_view.setExpandsOnDoubleClick(false);
|
2022-02-09 14:48:28 +01:00
|
|
|
m_view.setFrameStyle(QFrame::NoFrame);
|
2022-06-27 09:16:09 +02:00
|
|
|
m_view.setDragEnabled(true);
|
|
|
|
|
m_view.setDragDropMode(QAbstractItemView::DragOnly);
|
2018-11-23 09:45:51 +01:00
|
|
|
connect(&m_view, &QAbstractItemView::activated,
|
|
|
|
|
this, &LanguageClientOutlineWidget::onItemActivated);
|
|
|
|
|
connect(m_editor->editorWidget(), &TextEditor::TextEditorWidget::cursorPositionChanged,
|
|
|
|
|
this, [this](){
|
|
|
|
|
if (m_sync)
|
|
|
|
|
updateSelectionInTree(m_editor->textCursor());
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<QAction *> LanguageClientOutlineWidget::filterMenuActions() const
|
|
|
|
|
{
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientOutlineWidget::setCursorSynchronization(bool syncWithCursor)
|
|
|
|
|
{
|
|
|
|
|
m_sync = syncWithCursor;
|
|
|
|
|
if (m_sync && m_editor)
|
|
|
|
|
updateSelectionInTree(m_editor->textCursor());
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-04 11:27:35 +01:00
|
|
|
void LanguageClientOutlineWidget::setSorted(bool sorted)
|
|
|
|
|
{
|
|
|
|
|
m_sorted = sorted;
|
|
|
|
|
m_proxyModel.sort(sorted ? 0 : -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool LanguageClientOutlineWidget::isSorted() const
|
|
|
|
|
{
|
|
|
|
|
return m_sorted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientOutlineWidget::restoreSettings(const QVariantMap &map)
|
|
|
|
|
{
|
|
|
|
|
setSorted(map.value(QString("LspOutline.Sort"), false).toBool());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVariantMap LanguageClientOutlineWidget::settings() const
|
|
|
|
|
{
|
|
|
|
|
return {{QString("LspOutline.Sort"), m_sorted}};
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-04 14:36:28 +02:00
|
|
|
void LanguageClientOutlineWidget::handleResponse(const DocumentUri &uri,
|
|
|
|
|
const DocumentSymbolsResult &result)
|
2018-11-23 09:45:51 +01:00
|
|
|
{
|
2019-04-04 14:36:28 +02:00
|
|
|
if (uri != m_uri)
|
|
|
|
|
return;
|
|
|
|
|
if (Utils::holds_alternative<QList<SymbolInformation>>(result))
|
|
|
|
|
m_model.setInfo(Utils::get<QList<SymbolInformation>>(result));
|
|
|
|
|
else if (Utils::holds_alternative<QList<DocumentSymbol>>(result))
|
|
|
|
|
m_model.setInfo(Utils::get<QList<DocumentSymbol>>(result));
|
|
|
|
|
else
|
|
|
|
|
m_model.clear();
|
2020-04-26 21:23:03 +02:00
|
|
|
|
|
|
|
|
// The list has changed, update the current items
|
|
|
|
|
updateSelectionInTree(m_editor->textCursor());
|
2018-11-23 09:45:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientOutlineWidget::updateTextCursor(const QModelIndex &proxyIndex)
|
|
|
|
|
{
|
2022-02-04 11:27:35 +01:00
|
|
|
LanguageClientOutlineItem *item = m_model.itemForIndex(m_proxyModel.mapToSource(proxyIndex));
|
2018-11-23 09:45:51 +01:00
|
|
|
const Position &pos = item->pos();
|
|
|
|
|
// line has to be 1 based, column 0 based!
|
|
|
|
|
m_editor->editorWidget()->gotoLine(pos.line() + 1, pos.character(), true, true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-04 07:30:06 +01:00
|
|
|
static LanguageClientOutlineItem *itemForCursor(const LanguageClientOutlineModel &m_model,
|
|
|
|
|
const QTextCursor &cursor)
|
2018-11-23 09:45:51 +01:00
|
|
|
{
|
2022-02-04 07:30:06 +01:00
|
|
|
const Position pos(cursor);
|
|
|
|
|
LanguageClientOutlineItem *result = nullptr;
|
|
|
|
|
m_model.forAllItems([&](LanguageClientOutlineItem *candidate){
|
|
|
|
|
if (!candidate->contains(pos))
|
|
|
|
|
return;
|
|
|
|
|
if (result && candidate->range().contains(result->range()))
|
|
|
|
|
return; // skip item if the range is equal or bigger than the previous found range
|
|
|
|
|
result = candidate;
|
2018-11-23 09:45:51 +01:00
|
|
|
});
|
2022-02-04 07:30:06 +01:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientOutlineWidget::updateSelectionInTree(const QTextCursor ¤tCursor)
|
|
|
|
|
{
|
|
|
|
|
if (LanguageClientOutlineItem *item = itemForCursor(m_model, currentCursor)) {
|
2022-02-04 11:27:35 +01:00
|
|
|
const QModelIndex index = m_proxyModel.mapFromSource(m_model.indexForItem(item));
|
2022-06-07 10:14:09 +02:00
|
|
|
m_view.setCurrentIndex(index);
|
2022-02-04 07:30:06 +01:00
|
|
|
m_view.scrollTo(index);
|
|
|
|
|
} else {
|
|
|
|
|
m_view.clearSelection();
|
|
|
|
|
}
|
2018-11-23 09:45:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientOutlineWidget::onItemActivated(const QModelIndex &index)
|
|
|
|
|
{
|
|
|
|
|
if (!index.isValid() || !m_editor)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
updateTextCursor(index);
|
|
|
|
|
m_editor->widget()->setFocus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool LanguageClientOutlineWidgetFactory::supportsEditor(Core::IEditor *editor) const
|
|
|
|
|
{
|
2021-12-10 10:49:30 +01:00
|
|
|
if (auto doc = qobject_cast<TextEditor::TextDocument *>(editor->document())) {
|
|
|
|
|
if (Client *client = LanguageClientManager::clientForDocument(doc))
|
|
|
|
|
return client->supportsDocumentSymbols(doc);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2018-11-23 09:45:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TextEditor::IOutlineWidget *LanguageClientOutlineWidgetFactory::createWidget(Core::IEditor *editor)
|
|
|
|
|
{
|
|
|
|
|
auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor);
|
|
|
|
|
QTC_ASSERT(textEditor, return nullptr);
|
2021-12-10 10:49:30 +01:00
|
|
|
if (Client *client = LanguageClientManager::clientForDocument(textEditor->textDocument())) {
|
|
|
|
|
if (client->supportsDocumentSymbols(textEditor->textDocument()))
|
|
|
|
|
return new LanguageClientOutlineWidget(client, textEditor);
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
2018-11-23 09:45:51 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-27 14:52:46 +01:00
|
|
|
class OutlineComboBox : public Utils::TreeViewComboBox
|
|
|
|
|
{
|
2022-02-04 12:05:31 +01:00
|
|
|
Q_DECLARE_TR_FUNCTIONS(LanguageClient::OutlineComboBox)
|
2020-01-27 14:52:46 +01:00
|
|
|
public:
|
|
|
|
|
OutlineComboBox(Client *client, TextEditor::BaseTextEditor *editor);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void updateModel(const DocumentUri &resultUri, const DocumentSymbolsResult &result);
|
|
|
|
|
void updateEntry();
|
|
|
|
|
void activateEntry();
|
2021-07-07 07:42:47 +02:00
|
|
|
void documentUpdated(TextEditor::TextDocument *document);
|
2022-02-04 12:05:31 +01:00
|
|
|
void setSorted(bool sorted);
|
2020-01-27 14:52:46 +01:00
|
|
|
|
|
|
|
|
LanguageClientOutlineModel m_model;
|
2022-02-04 12:05:31 +01:00
|
|
|
QSortFilterProxyModel m_proxyModel;
|
2020-01-27 14:52:46 +01:00
|
|
|
QPointer<Client> m_client;
|
|
|
|
|
TextEditor::TextEditorWidget *m_editorWidget;
|
|
|
|
|
const DocumentUri m_uri;
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-06 09:48:06 +01:00
|
|
|
Utils::TreeViewComboBox *LanguageClientOutlineWidgetFactory::createComboBox(
|
|
|
|
|
Client *client, TextEditor::BaseTextEditor *editor)
|
2020-01-27 14:52:46 +01:00
|
|
|
{
|
2022-01-06 09:48:06 +01:00
|
|
|
if (client && client->supportsDocumentSymbols(editor->textDocument()))
|
|
|
|
|
return new OutlineComboBox(client, editor);
|
|
|
|
|
return nullptr;
|
2020-01-27 14:52:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutlineComboBox::OutlineComboBox(Client *client, TextEditor::BaseTextEditor *editor)
|
|
|
|
|
: m_client(client)
|
|
|
|
|
, m_editorWidget(editor->editorWidget())
|
|
|
|
|
, m_uri(DocumentUri::fromFilePath(editor->document()->filePath()))
|
|
|
|
|
{
|
2021-06-08 14:28:25 +02:00
|
|
|
m_model.setSymbolStringifier(client->symbolStringifier());
|
2022-02-04 12:05:31 +01:00
|
|
|
m_proxyModel.setSourceModel(&m_model);
|
|
|
|
|
const bool sorted = LanguageClientSettings::outlineComboBoxIsSorted();
|
|
|
|
|
m_proxyModel.sort(sorted ? 0 : -1);
|
|
|
|
|
setModel(&m_proxyModel);
|
2020-01-27 14:52:46 +01:00
|
|
|
setMinimumContentsLength(13);
|
|
|
|
|
QSizePolicy policy = sizePolicy();
|
|
|
|
|
policy.setHorizontalPolicy(QSizePolicy::Expanding);
|
|
|
|
|
setSizePolicy(policy);
|
|
|
|
|
setMaxVisibleItems(40);
|
|
|
|
|
|
2022-02-04 12:05:31 +01:00
|
|
|
setContextMenuPolicy(Qt::ActionsContextMenu);
|
2022-02-04 12:05:31 +01:00
|
|
|
const QString sortActionText
|
|
|
|
|
= QCoreApplication::translate("TextEditor::Internal::OutlineWidgetStack",
|
|
|
|
|
"Sort Alphabetically");
|
|
|
|
|
auto sortAction = new QAction(sortActionText, this);
|
2022-02-04 12:05:31 +01:00
|
|
|
sortAction->setCheckable(true);
|
|
|
|
|
sortAction->setChecked(sorted);
|
|
|
|
|
addAction(sortAction);
|
|
|
|
|
|
2022-05-02 15:06:08 +02:00
|
|
|
connect(client->documentSymbolCache(),
|
|
|
|
|
&DocumentSymbolCache::gotSymbols,
|
|
|
|
|
this,
|
|
|
|
|
&OutlineComboBox::updateModel);
|
2021-07-07 07:42:47 +02:00
|
|
|
connect(client, &Client::documentUpdated, this, &OutlineComboBox::documentUpdated);
|
2020-01-27 14:52:46 +01:00
|
|
|
connect(m_editorWidget, &TextEditor::TextEditorWidget::cursorPositionChanged,
|
|
|
|
|
this, &OutlineComboBox::updateEntry);
|
|
|
|
|
connect(this, QOverload<int>::of(&QComboBox::activated), this, &OutlineComboBox::activateEntry);
|
2022-02-04 12:05:31 +01:00
|
|
|
connect(sortAction, &QAction::toggled, this, &OutlineComboBox::setSorted);
|
2020-02-28 14:46:11 +01:00
|
|
|
|
2021-07-07 07:42:47 +02:00
|
|
|
documentUpdated(editor->textDocument());
|
2020-01-27 14:52:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OutlineComboBox::updateModel(const DocumentUri &resultUri, const DocumentSymbolsResult &result)
|
|
|
|
|
{
|
|
|
|
|
if (m_uri != resultUri)
|
|
|
|
|
return;
|
|
|
|
|
if (Utils::holds_alternative<QList<SymbolInformation>>(result))
|
|
|
|
|
m_model.setInfo(Utils::get<QList<SymbolInformation>>(result));
|
|
|
|
|
else if (Utils::holds_alternative<QList<DocumentSymbol>>(result))
|
|
|
|
|
m_model.setInfo(Utils::get<QList<DocumentSymbol>>(result));
|
|
|
|
|
else
|
|
|
|
|
m_model.clear();
|
2020-04-26 21:23:03 +02:00
|
|
|
|
2020-06-18 14:32:14 +02:00
|
|
|
view()->expandAll();
|
2020-04-26 21:23:03 +02:00
|
|
|
// The list has changed, update the current item
|
|
|
|
|
updateEntry();
|
2020-01-27 14:52:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OutlineComboBox::updateEntry()
|
|
|
|
|
{
|
2022-02-04 07:30:06 +01:00
|
|
|
if (LanguageClientOutlineItem *item = itemForCursor(m_model, m_editorWidget->textCursor()))
|
2022-02-04 12:05:31 +01:00
|
|
|
setCurrentIndex(m_proxyModel.mapFromSource(m_model.indexForItem(item)));
|
2020-01-27 14:52:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OutlineComboBox::activateEntry()
|
|
|
|
|
{
|
2022-02-04 12:05:31 +01:00
|
|
|
const QModelIndex modelIndex = m_proxyModel.mapToSource(view()->currentIndex());
|
2020-01-27 14:52:46 +01:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-07 07:42:47 +02:00
|
|
|
void OutlineComboBox::documentUpdated(TextEditor::TextDocument *document)
|
2020-01-27 14:52:46 +01:00
|
|
|
{
|
2021-07-07 07:42:47 +02:00
|
|
|
if (document == m_editorWidget->textDocument())
|
2021-09-14 17:00:16 +02:00
|
|
|
m_client->documentSymbolCache()->requestSymbols(m_uri, Schedule::Delayed);
|
2020-01-27 14:52:46 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-04 12:05:31 +01:00
|
|
|
void OutlineComboBox::setSorted(bool sorted)
|
|
|
|
|
{
|
|
|
|
|
LanguageClientSettings::setOutlineComboBoxSorted(sorted);
|
|
|
|
|
m_proxyModel.sort(sorted ? 0 : -1);
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-23 09:45:51 +01:00
|
|
|
} // namespace LanguageClient
|