File indexing completed on 2024-04-21 04:35:54
0001 /* This file is part of KDevelop 0002 * 0003 * Copyright (C) 2012-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 #include <duchain/loader.h> 0020 0021 #include <QtCore/QDirIterator> 0022 #include <QtCore/QProcess> 0023 0024 #include <duchain/editorintegrator.h> 0025 #include <parser/node.h> 0026 0027 namespace ruby { 0028 0029 QList<KDevelop::Path> Loader::s_rubyPath; 0030 QList<KDevelop::Path> Loader::s_gemPath; 0031 0032 KDevelop::Path Loader::getRequiredFile(Node *node, 0033 const EditorIntegrator *editor, 0034 bool local) 0035 { 0036 QList<KDevelop::Path> searchPaths; 0037 0038 // Get the name of the file and update the cache of search paths. 0039 auto name = editor->tokenToString(node); 0040 if (name.startsWith(QStringLiteral("'")) || 0041 name.startsWith(QStringLiteral("\""))) { 0042 0043 // remove surrounding ' 0044 name.replace(name[0], ""); 0045 } 0046 if (!name.endsWith(QStringLiteral(".rb"))) { 0047 name += ".rb"; 0048 } 0049 searchPaths << KDevelop::Path(editor->url().toUrl()).parent(); 0050 if (!local) { 0051 fillUrlCache(); 0052 searchPaths << s_rubyPath; 0053 } 0054 0055 // Check first in the standard search path. 0056 int i = 0; 0057 for (const KDevelop::Path &path : searchPaths) { 0058 KDevelop::Path url(path, name); 0059 QFile script(url.path()); 0060 QFileInfo info(url.path()); 0061 if (script.exists() && !info.isDir()) { 0062 // Sort the cache to break this loop sooner next time. 0063 if (i > 1) { 0064 s_rubyPath.prepend(s_rubyPath.at(i - 1)); 0065 s_rubyPath.removeAt(i); 0066 } 0067 return url; 0068 } 0069 i++; 0070 } 0071 if (local) { 0072 return KDevelop::Path(); 0073 } 0074 0075 // This is not a local search and we haven't found it yet, go for the gems. 0076 name.chop(3); // .rb 0077 return getGem(name); 0078 } 0079 0080 QVector<KDevelop::IncludeItem> 0081 Loader::getFilesInSearchPath(const QString &url, 0082 const QString &hint, 0083 const KDevelop::Path &relative) 0084 { 0085 int number = 0; 0086 QList<KDevelop::Path> paths; 0087 QVector<KDevelop::IncludeItem> res; 0088 0089 if (relative.isValid()) { 0090 paths << relative; 0091 } else { 0092 fillUrlCache(); 0093 paths = s_rubyPath; 0094 0095 /* Gem paths need some extra work :P */ 0096 foreach (const KDevelop::Path &path, s_gemPath) { 0097 QDir dir(path.path() + "/"); 0098 QStringList list = dir.entryList(QStringList() << hint + "*"); 0099 foreach (const QString &inside, list) { 0100 paths << KDevelop::Path(path, inside + "lib"); 0101 } 0102 } 0103 } 0104 0105 for (const KDevelop::Path &path : paths) { 0106 KDevelop::Path aux(path, url); 0107 QDirIterator it(aux.path()); 0108 0109 while (it.hasNext()) { 0110 it.next(); 0111 KDevelop::IncludeItem item; 0112 item.name = it.fileInfo().fileName(); 0113 if (item.name.startsWith(QStringLiteral(".")) || 0114 item.name.endsWith(QStringLiteral("~")) || 0115 item.name.endsWith(QStringLiteral(".so"))) { 0116 0117 continue; 0118 } 0119 item.pathNumber = number; 0120 item.isDirectory = it.fileInfo().isDir(); 0121 item.basePath = aux.toUrl(); 0122 res << item; 0123 } 0124 number++; 0125 } 0126 return res; 0127 } 0128 0129 KDevelop::Path Loader::getGem(const QString &name) 0130 { 0131 if (name.isEmpty()) { 0132 return KDevelop::Path(); 0133 } 0134 0135 QStringList filter{ QString(name[0]) + "*" }; 0136 const QString &real = name + QStringLiteral(".rb"); 0137 0138 for (const KDevelop::Path &path : s_gemPath) { 0139 QDir dir(path.path()); 0140 QStringList list = dir.entryList(filter, QDir::Dirs); 0141 int i = 0; 0142 for (const QString &inside : list) { 0143 KDevelop::Path url(path, inside + "/lib/" + real); 0144 QFile script(url.path()); 0145 QFileInfo info(url.path()); 0146 if (script.exists() && !info.isDir()) { 0147 // Sort the cache to break this loop sooner next time. 0148 if (i > 1) { 0149 s_gemPath.prepend(s_gemPath.at(i - 1)); 0150 s_gemPath.removeAt(i); 0151 } 0152 return url; 0153 } 0154 i++; 0155 } 0156 } 0157 return KDevelop::Path(); 0158 } 0159 0160 0161 void Loader::fillUrlCache() 0162 { 0163 QProcess ruby; 0164 QList<QByteArray> rpaths, epaths; 0165 0166 if (urlsCached()) { 0167 return; 0168 } 0169 s_rubyPath = {}; 0170 s_gemPath = {}; 0171 0172 QStringList code{ "ruby", "-e", "puts $:; STDERR.puts Gem.path" }; 0173 ruby.start(QStringLiteral("/usr/bin/env"), code); 0174 ruby.waitForFinished(); 0175 rpaths = ruby.readAllStandardOutput().split('\n'); 0176 epaths = ruby.readAllStandardError().split('\n'); 0177 0178 /* For both rpaths and epaths, the last item is empty */ 0179 for (int it = 0; it < rpaths.size() - 1; it++) { 0180 s_rubyPath << KDevelop::Path(rpaths.at(it)); 0181 } 0182 for (int it = 0; it < epaths.size() - 1; it++) { 0183 KDevelop::Path aux(epaths.at(it)); 0184 aux.addPath(QStringLiteral("gems")); 0185 s_rubyPath << aux; 0186 } 0187 } 0188 0189 }