Skip to content

Commit

Permalink
feat: Add a split view for code document
Browse files Browse the repository at this point in the history
The right view shows the TreeSitter tree, like in the inspector view.

Related-to KDAB#62
  • Loading branch information
narnaud committed Jul 16, 2024
1 parent cd81b3b commit 7b633c7
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ set(PROJECT_SOURCES
apiexecutorwidget.h
apiexecutorwidget.cpp
apiexecutorwidget.ui
codeview.cpp
codeview.h
documentpalette.h
documentpalette.cpp
fileselector.h
Expand Down
138 changes: 138 additions & 0 deletions src/gui/codeview.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
This file is part of Knut.
SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
SPDX-License-Identifier: GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/

#include "codeview.h"
#include "core/logger.h"
#include "core/textdocument.h"
#include "guisettings.h"
#include "treesitter/languages.h"
#include "treesitter/predicates.h"
#include "treesittertreemodel.h"

#include <QCheckBox>
#include <QPlainTextEdit>
#include <QSplitter>
#include <QTreeView>
#include <QVBoxLayout>

namespace Gui {

CodeView::CodeView(QWidget *parent)
: TextView(parent)
, m_parser(nullptr)
{
auto *action = new QAction(tr("Show TreeSitter Explorer"));
action->setCheckable(true);
action->setChecked(false);
GuiSettings::setIcon(action, ":/gui/file-tree.png");
connect(action, &QAction::triggered, this, &CodeView::toggleTreeView);
addAction(action);
}

void CodeView::setDocument(Core::TextDocument *document)
{
TextView::setDocument(document);

m_treeView = new QTreeView(this);
m_showUnnamed = new QCheckBox(tr("Show unnamed nodes"), this);

auto *splitter = new QSplitter(Qt::Horizontal, this);
splitter->addWidget(document->textEdit());
splitter->setCollapsible(0, false);

QWidget *rightWidget = new QWidget;
QVBoxLayout *vbox = new QVBoxLayout(rightWidget);
vbox->setContentsMargins({});
vbox->addWidget(m_treeView);
vbox->addWidget(m_showUnnamed);
splitter->addWidget(rightWidget);

layout()->addWidget(splitter);

// By default, the treesitter view is not visible
rightWidget->setVisible(false);
}

void CodeView::toggleTreeView()
{
Q_ASSERT(m_treeView && document());
initializeCodeModel();
m_treeView->parentWidget()->setVisible(!m_treeView->isVisible());
}

void CodeView::toggleUnnamedNodes()
{
// technically the text didn't change, but this will force
// a complete re-parse and re-build of the entire tree.
changeText();
}

void CodeView::initializeCodeModel()
{
if (m_treemodel)
return;

m_treemodel = new TreeSitterTreeModel(this);
m_treeView->setModel(m_treemodel);
m_parser = treesitter::Parser::getLanguage(document()->type());
connect(document(), &Core::TextDocument::textChanged, this, &CodeView::changeText);
connect(document(), &Core::TextDocument::positionChanged, this, &CodeView::changeCursor);
connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &CodeView::changeTreeSelection);
connect(m_showUnnamed, &QCheckBox::toggled, this, &CodeView::toggleUnnamedNodes);

changeCursor();
changeText();
}

void CodeView::changeText()
{
QString text;
{
Core::LoggerDisabler disableLogging;
text = document()->text();
}
auto tree = m_parser.parseString(text);
if (tree.has_value()) {
m_treemodel->setTree(std::move(tree.value()), makePredicates(), m_showUnnamed->isChecked());
m_treeView->expandAll();
for (int i = 0; i < 2; i++) {
m_treeView->resizeColumnToContents(i);
}
}
}

void CodeView::changeCursor()
{
int position;
{
Core::LoggerDisabler disableLogging;
position = document()->position();
}
m_treemodel->setCursorPosition(position);
}

void CodeView::changeTreeSelection(const QModelIndex &current)
{
const auto node = m_treemodel->tsNode(current);
if (node.has_value()) {
Core::LoggerDisabler disableLogging;
document()->selectRegion(node->startPosition(), node->endPosition());
}
}

std::unique_ptr<treesitter::Predicates> CodeView::makePredicates()
{
Q_ASSERT(document());
// No need to always log the call to TextDocument::text
Core::LoggerDisabler disabler;
return std::make_unique<treesitter::Predicates>(document()->text());
}

} // namespace Gui
52 changes: 52 additions & 0 deletions src/gui/codeview.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
This file is part of Knut.
SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
SPDX-License-Identifier: GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/

#pragma once

#include "textview.h"
#include "treesitter/parser.h"

class QTreeView;
class QCheckBox;

namespace treesitter {
class Predicates;
}

namespace Gui {

class TreeSitterTreeModel;

class CodeView : public TextView
{
Q_OBJECT

public:
explicit CodeView(QWidget *parent = nullptr);

void setDocument(Core::TextDocument *document) override;

private:
void toggleTreeView();
void toggleUnnamedNodes();

void initializeCodeModel();
void changeText();
void changeCursor();
void changeTreeSelection(const QModelIndex &current);
std::unique_ptr<treesitter::Predicates> makePredicates();

QTreeView *m_treeView = nullptr;
QCheckBox *m_showUnnamed = nullptr;
treesitter::Parser m_parser;
TreeSitterTreeModel *m_treemodel = nullptr;
};

} // namespace Gui
1 change: 1 addition & 0 deletions src/gui/gui.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<file>gui/magnify-minus.png</file>
<file>gui/magnify-plus.png</file>
<file>gui/lightbulb.png</file>
<file>gui/file-tree.png</file>
</qresource>
<qresource prefix="/org.kde.syntax-highlighting/syntax-addons">
<file alias="rc.xml">gui/rc.xml</file>
Expand Down
Binary file added src/gui/gui/file-tree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/gui/knutstyle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ int KnutStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const
return 1;
case PM_TabBarTabVSpace:
return 10;
case PM_SplitterWidth:
return 1;
default:
break;
}
Expand Down
15 changes: 11 additions & 4 deletions src/gui/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

#include "mainwindow.h"
#include "codeview.h"
#include "core/codedocument.h"
#include "core/cppdocument.h"
#include "core/document.h"
Expand Down Expand Up @@ -717,13 +718,19 @@ static QWidget *widgetForDocument(Core::Document *document)
tsView->setTsDocument(qobject_cast<Core::QtTsDocument *>(document));
return tsView;
}
case Core::Document::Type::Json:
case Core::Document::Type::Cpp:
case Core::Document::Type::Json:
case Core::Document::Type::Text:
default: {
auto textView = new TextView();
textView->setDocument(qobject_cast<Core::TextDocument *>(document));
return textView;
if (auto codeDocument = qobject_cast<Core::CodeDocument *>(document)) {
auto codeView = new CodeView();
codeView->setDocument(codeDocument);
return codeView;
} else {
auto textView = new TextView();
textView->setDocument(qobject_cast<Core::TextDocument *>(document));
return textView;
}
}
}
Q_UNREACHABLE();
Expand Down

0 comments on commit 7b633c7

Please sign in to comment.