File indexing completed on 2024-04-28 05:49:16

0001 /***************************************************************************
0002                            julia_parser.cpp  -
0003     Kate parser for Julia files to use with Kate's Symbol viewer plugin.
0004                              -------------------
0005     begin                : Feb 25 2023
0006     author               : 2023 Cezar Tigaret
0007     email                : cezar.tigaret@gmail.com
0008  ***************************************************************************/
0009 /***************************************************************************
0010  *                                                                         *
0011  *   SPDX-FileCopyrightText: 2023 Cezar M. Tigaret <cezar.tigaret@gmail.com>
0012  *   SPDX-License-Identifier: LGPL-2.0-or-later
0013  *                                                                         *
0014  ***************************************************************************/
0015 #include "plugin_katesymbolviewer.h"
0016 
0017 #include <KTextEditor/Document>
0018 
0019 enum class Type { Function, Structure, Macro, Method };
0020 
0021 void KatePluginSymbolViewerView::parseJuliaSymbols(void)
0022 {
0023     if (!m_mainWindow->activeView()) {
0024         return;
0025     }
0026 
0027     m_macro->setText(i18n("Show Macros"));
0028     m_struct->setText(i18n("Show Structures"));
0029     m_func->setText(i18n("Show Functions"));
0030 
0031     bool commentLine = false;
0032     bool terseFunctionExpresion = false;
0033 
0034     Type type;
0035     QString name;
0036     QString module_name;
0037     QString params;
0038     QString paramsSubstr;
0039     QString terseFuncAssignment;
0040     QString whereStmt;
0041     QString current_class_name;
0042     QString mutable_kw;
0043     QString lastControl;
0044 
0045     QTreeWidgetItem *node = nullptr;
0046     QTreeWidgetItem *functionNode = nullptr, *mtdNode = nullptr, *clsNode = nullptr;
0047     QTreeWidgetItem *lastMcrNode = nullptr, *lastMtdNode = nullptr, *lastClsNode = nullptr;
0048     QTreeWidgetItem *macroNode = nullptr;
0049     QTreeWidgetItem *lastMacroNode = nullptr;
0050 
0051     KTextEditor::Document *kv = m_mainWindow->activeView()->document();
0052 
0053     if (m_treeOn->isChecked()) {
0054         clsNode = new QTreeWidgetItem(m_symbols, QStringList(i18n("Structures")));
0055         functionNode = new QTreeWidgetItem(m_symbols, QStringList(i18n("Functions")));
0056         macroNode = new QTreeWidgetItem(m_symbols, QStringList(i18n("Macros")));
0057 
0058         functionNode->setIcon(0, m_icon_function);
0059         clsNode->setIcon(0, m_icon_class);
0060         macroNode->setIcon(0, m_icon_context);
0061 
0062         if (m_expandOn->isChecked()) {
0063             m_symbols->expandItem(functionNode);
0064             m_symbols->expandItem(clsNode);
0065             m_symbols->expandItem(macroNode);
0066         }
0067         lastClsNode = clsNode;
0068         lastMcrNode = functionNode;
0069         lastMacroNode = macroNode;
0070         mtdNode = clsNode;
0071         lastMtdNode = clsNode;
0072         m_symbols->setRootIsDecorated(1);
0073     } else {
0074         m_symbols->setRootIsDecorated(0);
0075     }
0076 
0077     static const QString contStr(QChar(0x21b5));
0078     // static const QString contStr(0x21b5);
0079 
0080     static const QRegularExpression comment_regexp(QLatin1String("[#]"), QRegularExpression::UseUnicodePropertiesOption);
0081     static const QRegularExpression ml_docsctring_regexp(QLatin1String("\"\"\""), QRegularExpression::UseUnicodePropertiesOption);
0082     static const QRegularExpression sl_docstring_regexp(QLatin1String("\"\"\"(.*)?\"\"\""), QRegularExpression::UseUnicodePropertiesOption);
0083 
0084     static const QRegularExpression class_regexp(QLatin1String("(@[a-zA-Z0-9_\\s]+)?(?:struct|mutable\\s+struct)\\s+([\\w!a-zA-Z0-9_.]+)"),
0085                                                  QRegularExpression::UseUnicodePropertiesOption);
0086 
0087     static const QRegularExpression function_regexp(
0088         QLatin1String("(@[a-zA-Z0-9_\\s]+)?function\\s+([\\w:!.]+)\\s*(\\(.*[,;:\\{\\}\\s]*\\)?\\s*)?$( where [\\w:<>=.\\{\\}]*\\s?$)?"),
0089         QRegularExpression::UseUnicodePropertiesOption);
0090 
0091     static const QRegularExpression terse_function_regexp(
0092         QLatin1String(
0093             "^(@[a-zA-Z0-9_\\s]+ )?([\\w:.]+)?([\\w:\\{\\}!]+)(\\s?)(\\(.*[\\),;\\s]*\\))(\\s?)(where [\\w:<>.,\\s\\{\\}a-zA-Z0-9]*\\s?)?(\\s?)\\s*=\\s*(.*)$"),
0094         QRegularExpression::UseUnicodePropertiesOption);
0095 
0096     static const QRegularExpression macro_regexp(QLatin1String("^macro ([\\w:\\{\\}!]+)(\\s*)(\\(.*\\))?"), QRegularExpression::UseUnicodePropertiesOption);
0097 
0098     static const QRegularExpression assert_regexp(QLatin1String("@assert"));
0099 
0100     QRegularExpressionMatch match;
0101 
0102     for (int i = 0; i < kv->lines(); i++) {
0103         int line = i;
0104         int indexOfHash = -1;
0105         QString cl = kv->line(i);
0106         if (cl.isEmpty()) {
0107             continue;
0108         }
0109 
0110         // concatenate continued lines and remove continuation marker (from python_parser.cpp)
0111         while (cl[cl.length() - 1] == QLatin1Char('\\')) {
0112             cl = cl.left(cl.length() - 1);
0113             i++;
0114             if (i < kv->lines()) {
0115                 cl += kv->line(i);
0116             } else {
0117                 break;
0118             }
0119             if (cl.isEmpty()) {
0120                 break;
0121             }
0122         }
0123 
0124         QString cl_sp = cl.simplified();
0125 
0126         // strip away comments
0127         indexOfHash = cl_sp.indexOf(QLatin1Char('#'));
0128         if (indexOfHash > 0) {
0129             cl_sp = cl_sp.left(indexOfHash - 1);
0130         } else if (indexOfHash == 0) {
0131             continue;
0132         }
0133         // skip asserts
0134         match = assert_regexp.match(cl_sp);
0135 
0136         if (match.hasMatch()) {
0137             continue;
0138         }
0139 
0140         // skip # comments
0141         match = comment_regexp.match(cl_sp);
0142 
0143         if (match.hasMatch()) {
0144             continue;
0145         }
0146 
0147         // BEGIN skip doc strings """ ... """ or """
0148         // """
0149         //
0150         match = ml_docsctring_regexp.match(cl_sp); // match """ anywhere
0151 
0152         if (match.hasMatch()) {
0153             match = sl_docstring_regexp.match(cl_sp);
0154             if (match.hasMatch()) {
0155                 commentLine = false;
0156                 continue;
0157 
0158             } else {
0159                 commentLine = !commentLine;
0160                 continue;
0161             }
0162         } else {
0163             match = sl_docstring_regexp.match(cl_sp);
0164             if (match.hasMatch()) {
0165                 commentLine = false;
0166                 continue;
0167             }
0168         }
0169 
0170         if (commentLine) {
0171             continue;
0172         }
0173         // END skip doc strings (""" ... """)
0174 
0175         terseFunctionExpresion = false;
0176 
0177         whereStmt.clear();
0178 
0179         match = class_regexp.match(cl_sp);
0180 
0181         if (match.hasMatch()) {
0182             type = Type::Structure;
0183 
0184         } else {
0185             match = macro_regexp.match(cl_sp);
0186             if (match.hasMatch()) {
0187                 type = Type::Macro;
0188             } else {
0189                 match = function_regexp.match(cl_sp);
0190                 if (match.hasMatch()) {
0191                     type = Type::Function;
0192                 } else {
0193                     match = terse_function_regexp.match(cl_sp);
0194                     if (match.hasMatch()) {
0195                         type = Type::Function;
0196                         terseFunctionExpresion = true;
0197                     } else {
0198                         continue;
0199                     }
0200                 }
0201             }
0202         }
0203 
0204         if (match.hasMatch()) {
0205             // either Structure, Macro, or Function egexp have matched
0206             if (type == Type::Structure) {
0207                 name = match.captured(2);
0208                 current_class_name = name;
0209                 params.clear();
0210             } else {
0211                 if (type == Type::Function) {
0212                     if (terseFunctionExpresion) {
0213                         terseFuncAssignment = match.captured(9);
0214                         whereStmt = match.captured(7);
0215                         if (!terseFuncAssignment.isEmpty()) {
0216                             module_name = match.captured(2); // useful when overloading a function from other module (in Julia that's typically 'adding' a
0217                                                              // method to the overloaded function <-> function dispatch mechanism)
0218                             name = match.captured(3);
0219                             if (!module_name.isEmpty()) {
0220                                 name = module_name + name;
0221                             }
0222                             params = match.captured(5);
0223 
0224                             if (!whereStmt.isEmpty()) {
0225                                 params += QLatin1String(" ");
0226                                 params += whereStmt;
0227                             }
0228 
0229                         } else {
0230                             continue;
0231                         }
0232 
0233                     } else {
0234                         name = match.captured(2);
0235                         params = match.captured(3);
0236                         whereStmt = match.captured(4);
0237                     }
0238 
0239                 } else if (type == Type::Macro) {
0240                     name = match.captured(1);
0241                     params = match.captured(3);
0242                 } else {
0243                     continue;
0244                 }
0245 
0246                 if (!params.isEmpty() && !params.endsWith(QLatin1String(")"))) {
0247                     if (!terseFunctionExpresion) {
0248                         if (whereStmt.isEmpty() && !params.contains(QLatin1String("where"))) {
0249                             params += QLatin1Char(' ');
0250                             params += contStr;
0251 
0252                         } else {
0253                             params += QLatin1String(" ");
0254                             params += whereStmt;
0255                             whereStmt.clear();
0256                         }
0257                     }
0258                 }
0259             }
0260 
0261             if (m_typesOn->isChecked()) {
0262                 name += params;
0263             }
0264 
0265             if (m_func->isChecked() && type == Type::Structure) {
0266                 if (m_treeOn->isChecked()) {
0267                     node = new QTreeWidgetItem(clsNode, lastClsNode);
0268                     if (m_expandOn->isChecked()) {
0269                         m_symbols->expandItem(node);
0270                     }
0271                     lastClsNode = node;
0272                     mtdNode = lastClsNode;
0273                     lastMtdNode = lastClsNode;
0274                 } else {
0275                     node = new QTreeWidgetItem(m_symbols);
0276                 }
0277 
0278                 node->setText(0, name);
0279                 node->setIcon(0, m_icon_class);
0280                 node->setText(1, QString::number(line, 10));
0281             }
0282 
0283             if (m_struct->isChecked() && type == Type::Method) {
0284                 if (m_treeOn->isChecked()) {
0285                     node = new QTreeWidgetItem(mtdNode, lastMtdNode);
0286                     lastMtdNode = node;
0287                 } else {
0288                     node = new QTreeWidgetItem(m_symbols);
0289                 }
0290 
0291                 node->setText(0, name);
0292                 node->setIcon(0, m_icon_function);
0293                 node->setText(1, QString::number(line, 10));
0294             }
0295 
0296             if (m_macro->isChecked() && type == Type::Function) {
0297                 if (m_treeOn->isChecked()) {
0298                     node = new QTreeWidgetItem(functionNode, lastMcrNode);
0299                     lastMcrNode = node;
0300                 } else {
0301                     node = new QTreeWidgetItem(m_symbols);
0302                 }
0303 
0304                 node->setText(0, name);
0305                 node->setIcon(0, m_icon_function);
0306                 node->setText(1, QString::number(line, 10));
0307             }
0308 
0309             if (m_macro->isChecked() && type == Type::Macro) {
0310                 if (m_treeOn->isChecked()) {
0311                     node = new QTreeWidgetItem(macroNode, lastMacroNode);
0312                     lastMacroNode = node;
0313                 } else {
0314                     node = new QTreeWidgetItem(m_symbols);
0315                 }
0316 
0317                 node->setText(0, name);
0318                 node->setIcon(0, m_icon_function);
0319                 node->setText(1, QString::number(line, 10));
0320             }
0321 
0322             name.clear();
0323             params.clear();
0324         }
0325     }
0326 }