File indexing completed on 2024-04-28 04:35:52

0001 /* This file is part of KDevelop
0002  *
0003  * Copyright (C) 2011-2015 Miquel Sabaté Solà <mikisabate@gmail.com>
0004  *
0005  * This program is free software: you can redistribute it and/or modify
0006  * it under the terms of the GNU General Public License as published by
0007  * the Free Software Foundation, either version 3 of the License, or
0008  * (at your option) any later version.
0009  *
0010  * This program is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  * GNU General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU General Public License
0016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017  */
0018 
0019 
0020 // Qt + KDevelop
0021 #include <QtGui/QTextDocument>
0022 #include <language/duchain/types/functiontype.h>
0023 #include <language/duchain/types/structuretype.h>
0024 #include <language/duchain/duchainutils.h>
0025 
0026 // Ruby
0027 #include <duchain/helpers.h>
0028 #include <duchain/declarations/methoddeclaration.h>
0029 #include <duchain/declarations/moduledeclaration.h>
0030 #include <duchain/declarations/variabledeclaration.h>
0031 #include <duchain/navigation/declarationnavigationcontext.h>
0032 
0033 
0034 namespace ruby
0035 {
0036 using namespace KDevelop;
0037 
0038 DeclarationNavigationContext::DeclarationNavigationContext( DeclarationPointer decl,
0039                                                             TopDUContextPointer topContext,
0040                                                             AbstractNavigationContext *prevContext)
0041     : AbstractDeclarationNavigationContext(decl, topContext, prevContext)
0042 {
0043     /* There's nothing to do here! */
0044 }
0045 
0046 QString DeclarationNavigationContext::html(bool shorten)
0047 {
0048     clear();
0049     AbstractDeclarationNavigationContext::html(shorten);
0050     modifyHtml()  += "<html><body><p>";
0051 
0052     if(!declaration().data()) {
0053         modifyHtml() += i18n("<br /> lost declaration <br />");
0054         return currentHtml();
0055     }
0056     if(auto context = previousContext()) {
0057         QString link = createLink(context->name(), context->name(), NavigationAction(context));
0058         modifyHtml() += navigationHighlight(i18n("Back to %1<br />", link));
0059     }
0060 
0061     if (!shorten) {
0062         const MethodDeclaration *mDecl = dynamic_cast<const MethodDeclaration *>(declaration().data());
0063         if (mDecl) {
0064             if (mDecl->qualifiedIdentifier().count() > 1 && mDecl->context() && mDecl->context()->owner()) {
0065                 Declaration *d = declaration()->context()->owner();
0066                 makeLink(declarationName(DeclarationPointer(d)), DeclarationPointer(d), NavigationAction::NavigateDeclaration);
0067                 modifyHtml() += (mDecl->isClassMethod()) ? "::" : "#";
0068             }
0069             htmlFunction();
0070         } else if (declaration()->kind() == Declaration::Instance) {
0071             eventuallyMakeTypeLinks(declaration()->abstractType());
0072             const QString &esc = declarationName(declaration()).toHtmlEscaped();
0073             modifyHtml() += ' ' + nameHighlight(esc) + "<br>";
0074         } else if (declaration()->kind() == Declaration::Type && declaration()->abstractType().dynamicCast<StructureType>()) {
0075             htmlClass();
0076         }
0077     } else if (declaration()->abstractType()) {
0078         eventuallyMakeTypeLinks(declaration()->abstractType());
0079         modifyHtml() += " ";
0080     }
0081 
0082     QString access = stringFromAccess(declaration());
0083     if (!access.isEmpty()) {
0084         access = propertyHighlight(access.toHtmlEscaped());
0085         modifyHtml() += labelHighlight(i18n("Access: %1 ", access));
0086         modifyHtml() += "<br />";
0087     }
0088 
0089     if (!shorten) {
0090         htmlAdditionalNavigation();
0091         modifyHtml() += "<br />";
0092         modifyHtml() += labelHighlight(i18n("Def.: "));
0093         makeLink(QString("%1 :%2").arg(QUrl(declaration()->url().str()).fileName()).arg(declaration()->rangeInCurrentRevision().start().line() + 1), declaration(), NavigationAction::JumpToSource);
0094         modifyHtml() += " ";
0095         modifyHtml() += createLink(i18n("Show uses"), "show_uses", NavigationAction(declaration(), NavigationAction::NavigateUses));
0096         if(!shorten && !declaration()->comment().isEmpty()) {
0097             modifyHtml() += "<br />";
0098             QString comment = QString::fromUtf8(declaration()->comment());
0099             if(!comment.isEmpty()) {
0100                 comment.replace("<br />", "\n");
0101                 comment.replace("<br/>", "\n");
0102                 comment = comment.toHtmlEscaped();
0103                 comment.replace('\n', "<br />");
0104                 modifyHtml() += commentHighlight(comment);
0105                 modifyHtml() += "<br />";
0106             }
0107         }
0108     }
0109 
0110     modifyHtml() += "</p></body></html>";
0111     return currentHtml();
0112 }
0113 
0114 void DeclarationNavigationContext::htmlFunction()
0115 {
0116     const MethodDeclaration *mDecl = dynamic_cast<const MethodDeclaration *>(declaration().data());
0117     Q_ASSERT(mDecl);
0118 
0119     const auto type = declaration()->abstractType().dynamicCast<FunctionType>();
0120     if (!type) {
0121         modifyHtml() += errorHighlight("Invalid type<br/>");
0122         return;
0123     }
0124 
0125     const QString &name = prettyIdentifier(declaration()).toString();
0126     modifyHtml() += nameHighlight(name.toHtmlEscaped());
0127     if (type->arguments().size() > 0) {
0128         bool first = true;
0129         int nDef = 0;
0130         DUContext *ctx = DUChainUtils::argumentContext(declaration().data());
0131 
0132         if (ctx) {
0133             modifyHtml() += "( ";
0134             foreach (Declaration *d, ctx->localDeclarations(topContext().data())) {
0135                 if (!first)
0136                     modifyHtml() += ", ";
0137                 first = false;
0138 
0139                 VariableDeclaration *vd = dynamic_cast<VariableDeclaration *>(d);
0140                 if (vd->hasStar())
0141                     modifyHtml() += " *";
0142                 else if (vd->isBlock())
0143                     modifyHtml() += " &";
0144                 else
0145                     modifyHtml() += " ";
0146                 const QString &aux = vd->identifier().toString();
0147                 modifyHtml() += nameHighlight(aux.toHtmlEscaped());
0148 
0149                 if (vd->isOpt()) {
0150                     const QString &str = mDecl->defaultParameters()[nDef].str();
0151                     modifyHtml() += " " + str.toHtmlEscaped();
0152                     nDef++;
0153                 }
0154             }
0155             modifyHtml() += " )";
0156         }
0157     }
0158 }
0159 
0160 void DeclarationNavigationContext::htmlClass()
0161 {
0162     Q_ASSERT(declaration()->abstractType());
0163     auto klass = declaration()->abstractType().staticCast<StructureType>();
0164     ModuleDeclaration *mDecl = dynamic_cast<ModuleDeclaration *>(klass->declaration(topContext().data()));
0165 
0166     if (mDecl) {
0167         if (mDecl->isModule()) {
0168             modifyHtml() += "module ";
0169             eventuallyMakeTypeLinks(declaration()->abstractType());
0170         } else {
0171             modifyHtml() += "class ";
0172             eventuallyMakeTypeLinks(declaration()->abstractType());
0173 
0174             if (mDecl->baseClass()) {
0175                 AbstractType::Ptr base = mDecl->baseClass().abstractType();
0176                 modifyHtml() += " is a subclass of ";
0177                 eventuallyMakeTypeLinks(base);
0178             }
0179         }
0180         modifyHtml() += " ";
0181         addModuleMixins(mDecl);
0182     }
0183 }
0184 
0185 void DeclarationNavigationContext::makeLink(const QString &name, const DeclarationPointer& declaration,
0186                                             NavigationAction::Type actionType)
0187 {
0188     if (actionType == NavigationAction::JumpToSource
0189             && declaration->url() == builtinsFile()) {
0190         modifyHtml() += i18n("Ruby Kernel");
0191         return;
0192     }
0193     AbstractDeclarationNavigationContext::makeLink(name, declaration, actionType);
0194 }
0195 
0196 void DeclarationNavigationContext::addModuleMixins(ModuleDeclaration *decl)
0197 {
0198     uint nMixins = decl->moduleMixinsSize();
0199     QList<AbstractType::Ptr> includes, extends;
0200     ModuleMixin aux;
0201 
0202     if (nMixins > 0) {
0203         for (uint i = 0; i < nMixins; i++) {
0204             aux = decl->moduleMixins()[i];
0205             if (aux.included)
0206                 includes << aux.module.abstractType();
0207             else
0208                 extends << aux.module.abstractType();
0209         }
0210         if (!includes.isEmpty()) {
0211             modifyHtml() += "<br>Includes: ";
0212             foreach (const AbstractType::Ptr d, includes)
0213                 eventuallyMakeTypeLinks(d);
0214         }
0215         if (!extends.isEmpty()) {
0216             modifyHtml() += "<br>Extends: ";
0217             foreach (const AbstractType::Ptr d, extends)
0218                 eventuallyMakeTypeLinks(d);
0219         }
0220     }
0221 }
0222 
0223 void DeclarationNavigationContext::addMixers(ModuleDeclaration *decl)
0224 {
0225     uint nMixers = decl->mixersSize();
0226     const ModuleMixin *aux;
0227 
0228     if (nMixers > 0) {
0229         aux = decl->mixers();
0230         modifyHtml() += "<br>Mixed in: ";
0231         for (uint i = 0; i < nMixers; i++) {
0232             eventuallyMakeTypeLinks(aux[i].module.abstractType());
0233             if (i != nMixers - 1)
0234                 modifyHtml() += ", ";
0235         }
0236         modifyHtml() += "<br>";
0237     }
0238 }
0239 
0240 }
0241