File indexing completed on 2024-04-28 05:48:55

0001 /*
0002     SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 #include "gotosymboldialog.h"
0007 #include "lspclientserver.h"
0008 
0009 #include <KLocalizedString>
0010 #include <KSyntaxHighlighting/Theme>
0011 #include <KTextEditor/Editor>
0012 #include <KTextEditor/MainWindow>
0013 #include <KTextEditor/View>
0014 
0015 #include <QFileInfo>
0016 #include <QPainter>
0017 #include <QStandardItemModel>
0018 #include <QStyledItemDelegate>
0019 
0020 #include <drawing_utils.h>
0021 #include <ktexteditor_utils.h>
0022 
0023 static constexpr int SymbolInfoRole = Qt::UserRole + 1;
0024 
0025 struct GotoSymbolItem {
0026     QUrl fileUrl;
0027     KTextEditor::Cursor pos;
0028     LSPSymbolKind kind;
0029 };
0030 Q_DECLARE_METATYPE(GotoSymbolItem)
0031 Q_DECLARE_TYPEINFO(GotoSymbolItem, Q_MOVABLE_TYPE);
0032 
0033 class GotoSymbolHUDStyleDelegate : public QStyledItemDelegate
0034 {
0035 public:
0036     GotoSymbolHUDStyleDelegate(QObject *parent)
0037         : QStyledItemDelegate(parent)
0038     {
0039     }
0040 
0041     void setColors()
0042     {
0043         using KSyntaxHighlighting::Theme;
0044         auto theme = KTextEditor::Editor::instance()->theme();
0045         normalColor = theme.textColor(Theme::Normal);
0046         typeColor = theme.textColor(Theme::DataType);
0047         keywordColor = theme.textColor(Theme::Keyword);
0048         funcColor = theme.textColor(Theme::Function);
0049     }
0050 
0051     void setFont(const QFont &font)
0052     {
0053         monoFont = font;
0054     }
0055 
0056     void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
0057     {
0058         QStyleOptionViewItem options = option;
0059         initStyleOption(&options, index);
0060         options.icon = Utils::colorIcon(options.icon, normalColor);
0061 
0062         auto style = options.widget->style();
0063 
0064         painter->save();
0065 
0066         QString text = options.text;
0067         options.text = QString();
0068         style->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget);
0069 
0070         const auto textRect = options.widget->style()->subElementRect(QStyle::SE_ItemViewItemText, &options, options.widget);
0071 
0072         auto symbol = index.data(SymbolInfoRole).value<GotoSymbolItem>();
0073         auto kind = symbol.kind;
0074 
0075         QList<QTextLayout::FormatRange> fmts;
0076         int colons = text.indexOf(QStringLiteral("::"));
0077         int i = 0;
0078         // container name
0079         if (colons != -1) {
0080             QTextCharFormat fmt;
0081             fmt.setForeground(keywordColor);
0082             fmt.setFont(monoFont);
0083             fmts.append({0, colons, fmt});
0084             i = colons + 2;
0085         }
0086         // symbol name
0087         {
0088             QTextCharFormat f;
0089             f.setForeground(colorForSymbolKind(kind));
0090             f.setFont(monoFont);
0091             fmts.append({i, int(text.length() - i), f});
0092         }
0093 
0094         // add file name to the text we are going to display
0095         auto file = QFileInfo(symbol.fileUrl.toLocalFile()).fileName();
0096         int textLength = text.length();
0097         text += QStringLiteral(" ") + file;
0098 
0099         // file name
0100         {
0101             QTextCharFormat f;
0102             f.setForeground(Qt::gray);
0103             fmts.append({textLength, int(text.length() - textLength), f});
0104         }
0105 
0106         options.rect = textRect;
0107         Utils::paintItemViewText(painter, text, options, fmts);
0108 
0109         painter->restore();
0110     }
0111 
0112 private:
0113     QColor colorForSymbolKind(LSPSymbolKind kind) const
0114     {
0115         switch (kind) {
0116         case LSPSymbolKind::File:
0117         case LSPSymbolKind::Module:
0118         case LSPSymbolKind::Namespace:
0119         case LSPSymbolKind::Package:
0120             return keywordColor;
0121         case LSPSymbolKind::Struct:
0122         case LSPSymbolKind::Class:
0123         case LSPSymbolKind::Interface:
0124             return typeColor;
0125         case LSPSymbolKind::Enum:
0126             return typeColor;
0127         case LSPSymbolKind::Method:
0128         case LSPSymbolKind::Function:
0129         case LSPSymbolKind::Constructor:
0130             return funcColor;
0131         // all others considered/assumed Variable
0132         case LSPSymbolKind::Variable:
0133         case LSPSymbolKind::Constant:
0134         case LSPSymbolKind::String:
0135         case LSPSymbolKind::Number:
0136         case LSPSymbolKind::Property:
0137         case LSPSymbolKind::Field:
0138         default:
0139             return normalColor;
0140         }
0141     }
0142 
0143     QColor funcColor;
0144     QColor keywordColor;
0145     QColor typeColor;
0146     QColor normalColor;
0147 
0148     QFont monoFont;
0149 };
0150 
0151 GotoSymbolHUDDialog::GotoSymbolHUDDialog(KTextEditor::MainWindow *mainWindow, std::shared_ptr<LSPClientServer> server)
0152     : HUDDialog(nullptr, mainWindow->window())
0153     , model(new QStandardItemModel(this))
0154     , mainWindow(mainWindow)
0155     , server(std::move(server))
0156 {
0157     m_lineEdit.setPlaceholderText(i18n("Filter..."));
0158 
0159     m_treeView.setModel(model);
0160     auto delegate = new GotoSymbolHUDStyleDelegate(this);
0161     m_treeView.setItemDelegate(delegate);
0162     setPaletteToEditorColors();
0163 
0164     connect(&m_lineEdit, &QLineEdit::textChanged, this, &GotoSymbolHUDDialog::slotTextChanged);
0165     connect(KTextEditor::Editor::instance(), &KTextEditor::Editor::configChanged, this, &GotoSymbolHUDDialog::setPaletteToEditorColors);
0166 }
0167 
0168 void GotoSymbolHUDDialog::setPaletteToEditorColors()
0169 {
0170     auto pal = m_treeView.palette();
0171     auto e = KTextEditor::Editor::instance();
0172     auto bg = QColor::fromRgba(e->theme().editorColor(KSyntaxHighlighting::Theme::BackgroundColor));
0173     auto fg = QColor::fromRgba(e->theme().textColor(KSyntaxHighlighting::Theme::Normal));
0174     auto hl = QColor::fromRgba(e->theme().editorColor(KSyntaxHighlighting::Theme::TextSelection));
0175     pal.setColor(QPalette::Base, bg);
0176     pal.setColor(QPalette::Text, fg);
0177     pal.setColor(QPalette::Highlight, hl);
0178     m_treeView.setPalette(pal);
0179 
0180     auto *delegate = static_cast<GotoSymbolHUDStyleDelegate *>(m_treeView.itemDelegate());
0181     delegate->setColors();
0182     delegate->setFont(Utils::editorFont());
0183 }
0184 
0185 void GotoSymbolHUDDialog::slotReturnPressed(const QModelIndex &index)
0186 {
0187     auto symbol = index.data(SymbolInfoRole).value<GotoSymbolItem>();
0188     if (!symbol.fileUrl.isValid() || symbol.fileUrl.isEmpty()) {
0189         return;
0190     }
0191 
0192     auto v = mainWindow->openUrl(symbol.fileUrl);
0193     if (v) {
0194         v->setCursorPosition(symbol.pos);
0195     }
0196     close();
0197 }
0198 
0199 void GotoSymbolHUDDialog::openDialog()
0200 {
0201     exec();
0202 }
0203 
0204 QIcon GotoSymbolHUDDialog::iconForSymbolKind(LSPSymbolKind kind) const
0205 {
0206     switch (kind) {
0207     case LSPSymbolKind::File:
0208     case LSPSymbolKind::Module:
0209     case LSPSymbolKind::Namespace:
0210     case LSPSymbolKind::Package:
0211         return m_icon_pkg;
0212     case LSPSymbolKind::Struct:
0213     case LSPSymbolKind::Class:
0214     case LSPSymbolKind::Interface:
0215         return m_icon_class;
0216     case LSPSymbolKind::Enum:
0217         return m_icon_typedef;
0218     case LSPSymbolKind::Method:
0219     case LSPSymbolKind::Function:
0220     case LSPSymbolKind::Constructor:
0221         return m_icon_function;
0222     // all others considered/assumed Variable
0223     case LSPSymbolKind::Variable:
0224     case LSPSymbolKind::Constant:
0225     case LSPSymbolKind::String:
0226     case LSPSymbolKind::Number:
0227     case LSPSymbolKind::Property:
0228     case LSPSymbolKind::Field:
0229     default:
0230         return m_icon_var;
0231     }
0232 }
0233 
0234 void GotoSymbolHUDDialog::slotTextChanged(const QString &text)
0235 {
0236     /**
0237      * empty text can lead to return *all* symbols of the workspace, which may choke the dialog
0238      * so we ignore it
0239      *
0240      * Also, at least 2 characters must be there to start getting symbols from the server
0241      */
0242     if (!server || text.isEmpty() || text.length() < 2) {
0243         return;
0244     }
0245 
0246     auto hh = [this](const std::vector<LSPSymbolInformation> &symbols) {
0247         model->clear();
0248         for (const auto &sym : symbols) {
0249             auto item = new QStandardItem(iconForSymbolKind(sym.kind), sym.name);
0250             item->setData(QVariant::fromValue(GotoSymbolItem{sym.url, sym.range.start(), sym.kind}), SymbolInfoRole);
0251             model->appendRow(item);
0252         }
0253         m_treeView.setCurrentIndex(model->index(0, 0));
0254     };
0255     server->workspaceSymbol(text, this, hh);
0256 }