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 }