File indexing completed on 2024-04-28 04:38:54
0001 /* 0002 SPDX-FileCopyrightText: 1999-2001 Bernd Gehrmann <bernd@kdevelop.org> 0003 SPDX-FileCopyrightText: 2007 Dukju Ahn <dukjuahn@gmail.com> 0004 SPDX-FileCopyrightText: 2010 Benjamin Port <port.benjamin@gmail.com> 0005 SPDX-FileCopyrightText: 2010 Julien Desgats <julien.desgats@gmail.com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "grepviewplugin.h" 0011 #include "grepdialog.h" 0012 #include "grepoutputmodel.h" 0013 #include "grepoutputdelegate.h" 0014 #include "grepjob.h" 0015 #include "grepoutputview.h" 0016 #include "debug.h" 0017 0018 #include <QAction> 0019 #include <QDBusConnection> 0020 #include <QKeySequence> 0021 #include <QMimeDatabase> 0022 0023 #include <KActionCollection> 0024 #include <KLocalizedString> 0025 #include <KParts/MainWindow> 0026 #include <KTextEditor/Document> 0027 #include <KTextEditor/View> 0028 0029 #include <interfaces/icore.h> 0030 #include <interfaces/iuicontroller.h> 0031 #include <interfaces/idocument.h> 0032 #include <interfaces/idocumentcontroller.h> 0033 #include <interfaces/iproject.h> 0034 #include <interfaces/contextmenuextension.h> 0035 #include <project/projectmodel.h> 0036 #include <util/path.h> 0037 #include <language/interfaces/editorcontext.h> 0038 0039 #include <utility> 0040 0041 static QString patternFromSelection(const KDevelop::IDocument* doc) 0042 { 0043 if (!doc) 0044 return QString(); 0045 0046 QString pattern; 0047 KTextEditor::Range range = doc->textSelection(); 0048 if( range.isValid() ) 0049 { 0050 pattern = doc->textDocument()->text( range ); 0051 } 0052 if( pattern.isEmpty() ) 0053 { 0054 pattern = doc->textWord(); 0055 } 0056 0057 // Before anything, this removes line feeds from the 0058 // beginning and the end. 0059 int len = pattern.length(); 0060 if (len > 0 && pattern[0] == QLatin1Char('\n')) { 0061 pattern.remove(0, 1); 0062 len--; 0063 } 0064 if (len > 0 && pattern[len-1] == QLatin1Char('\n')) 0065 pattern.truncate(len-1); 0066 return pattern; 0067 } 0068 0069 GrepViewPlugin::GrepViewPlugin( QObject *parent, const QVariantList & ) 0070 : KDevelop::IPlugin( QStringLiteral("kdevgrepview"), parent ), m_currentJob(nullptr) 0071 { 0072 setXMLFile(QStringLiteral("kdevgrepview.rc")); 0073 0074 QDBusConnection::sessionBus().registerObject( QStringLiteral("/org/kdevelop/GrepViewPlugin"), 0075 this, QDBusConnection::ExportScriptableSlots ); 0076 0077 QAction*action = actionCollection()->addAction(QStringLiteral("edit_grep")); 0078 action->setText(i18nc("@action", "Find/Replace in Fi&les...")); 0079 actionCollection()->setDefaultShortcut( action, QKeySequence(QStringLiteral("Ctrl+Alt+F")) ); 0080 connect(action, &QAction::triggered, this, &GrepViewPlugin::showDialogFromMenu); 0081 action->setToolTip( i18nc("@info:tooltip", "Search for expressions over several files") ); 0082 action->setWhatsThis( i18nc("@info:whatsthis", 0083 "Opens the 'Find/Replace in Files' dialog. There you " 0084 "can enter a regular expression which is then " 0085 "searched for within all files in the directories " 0086 "you specify. Matches will be displayed, you " 0087 "can switch to a match directly. You can also do replacement.") ); 0088 action->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); 0089 0090 // instantiate delegate, it's supposed to be deleted via QObject inheritance 0091 new GrepOutputDelegate(this); 0092 m_factory = new GrepOutputViewFactory(this); 0093 core()->uiController()->addToolView(i18nc("@title:window", "Find/Replace in Files"), m_factory); 0094 } 0095 0096 GrepOutputViewFactory* GrepViewPlugin::toolViewFactory() const 0097 { 0098 return m_factory; 0099 } 0100 0101 GrepViewPlugin::~GrepViewPlugin() 0102 { 0103 } 0104 0105 void GrepViewPlugin::unload() 0106 { 0107 for (const QPointer<GrepDialog>& p : qAsConst(m_currentDialogs)) { 0108 if (p) { 0109 p->reject(); 0110 p->deleteLater(); 0111 } 0112 } 0113 0114 core()->uiController()->removeToolView(m_factory); 0115 } 0116 0117 void GrepViewPlugin::startSearch(const QString& pattern, const QString& directory, bool show) 0118 { 0119 m_directory = directory; 0120 showDialog(false, pattern, show); 0121 } 0122 0123 KDevelop::ContextMenuExtension GrepViewPlugin::contextMenuExtension(KDevelop::Context* context, QWidget* parent) 0124 { 0125 KDevelop::ContextMenuExtension extension = KDevelop::IPlugin::contextMenuExtension(context, parent); 0126 if( context->type() == KDevelop::Context::ProjectItemContext ) { 0127 auto* ctx = static_cast<KDevelop::ProjectItemContext*>(context); 0128 QList<KDevelop::ProjectBaseItem*> items = ctx->items(); 0129 // verify if there is only one folder selected 0130 if ((items.count() == 1) && (items.first()->folder())) { 0131 auto* action = new QAction(i18nc("@action:inmenu", "Find/Replace in This Folder..."), parent); 0132 action->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); 0133 m_contextMenuDirectory = items.at(0)->folder()->path().toLocalFile(); 0134 connect( action, &QAction::triggered, this, &GrepViewPlugin::showDialogFromProject); 0135 extension.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, action ); 0136 } 0137 } 0138 0139 if ( context->type() == KDevelop::Context::EditorContext ) { 0140 auto* econtext = static_cast<KDevelop::EditorContext*>(context); 0141 if ( econtext->view()->selection() ) { 0142 auto* action = new QAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18nc("@action:inmenu", "&Find/Replace in Files..."), parent); 0143 connect(action, &QAction::triggered, this, &GrepViewPlugin::showDialogFromMenu); 0144 extension.addAction(KDevelop::ContextMenuExtension::ExtensionGroup, action); 0145 } 0146 } 0147 0148 if(context->type() == KDevelop::Context::FileContext) { 0149 auto* fcontext = static_cast<KDevelop::FileContext*>(context); 0150 // TODO: just stat() or QFileInfo().isDir() for local files? should be faster than mime type checking 0151 QMimeType mimetype = QMimeDatabase().mimeTypeForUrl(fcontext->urls().at(0)); 0152 static const QMimeType directoryMime = QMimeDatabase().mimeTypeForName(QStringLiteral("inode/directory")); 0153 if (mimetype == directoryMime) { 0154 auto* action = new QAction(i18nc("@action:inmenu", "Find/Replace in This Folder..."), parent); 0155 action->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); 0156 m_contextMenuDirectory = fcontext->urls().at(0).toLocalFile(); 0157 connect( action, &QAction::triggered, this, &GrepViewPlugin::showDialogFromProject); 0158 extension.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, action ); 0159 } 0160 } 0161 return extension; 0162 } 0163 0164 void GrepViewPlugin::showDialog(bool setLastUsed, const QString& pattern, bool show) 0165 { 0166 // check if dialog pointers are still valid, remove them otherwise 0167 m_currentDialogs.removeAll(QPointer<GrepDialog>()); 0168 0169 auto* const dlg = new GrepDialog(this, nullptr, core()->uiController()->activeMainWindow(), show); 0170 m_currentDialogs << dlg; 0171 0172 if (!show) { 0173 // The UI is uninitialized, so the settings must be read from config. 0174 dlg->setLastUsedSettings(); 0175 } 0176 0177 if(!pattern.isEmpty()) 0178 { 0179 dlg->setPattern(pattern); 0180 } 0181 else if(!setLastUsed) 0182 { 0183 QString pattern = patternFromSelection(core()->documentController()->activeDocument()); 0184 if (!pattern.isEmpty()) { 0185 dlg->setPattern(std::move(pattern)); 0186 } 0187 } 0188 0189 //if directory is empty then use a default value from the config file. 0190 if (!m_directory.isEmpty()) { 0191 dlg->setSearchLocations(m_directory); 0192 } 0193 0194 if(show) 0195 dlg->show(); 0196 else{ 0197 dlg->startSearch(); 0198 dlg->deleteLater(); 0199 } 0200 } 0201 0202 void GrepViewPlugin::showDialogFromMenu() 0203 { 0204 showDialog(); 0205 } 0206 0207 void GrepViewPlugin::showDialogFromProject() 0208 { 0209 rememberSearchDirectory(m_contextMenuDirectory); 0210 showDialog(); 0211 } 0212 0213 void GrepViewPlugin::rememberSearchDirectory(QString const & directory) 0214 { 0215 m_directory = directory; 0216 } 0217 0218 GrepJob* GrepViewPlugin::newGrepJob() 0219 { 0220 if(m_currentJob != nullptr) 0221 { 0222 m_currentJob->kill(); 0223 } 0224 m_currentJob = new GrepJob(); 0225 connect(m_currentJob, &GrepJob::finished, this, &GrepViewPlugin::jobFinished); 0226 return m_currentJob; 0227 } 0228 0229 GrepJob* GrepViewPlugin::grepJob() 0230 { 0231 return m_currentJob; 0232 } 0233 0234 void GrepViewPlugin::jobFinished(KJob* job) 0235 { 0236 if(job == m_currentJob) 0237 { 0238 m_currentJob = nullptr; 0239 emit grepJobFinished(job->error() == KJob::NoError); 0240 } 0241 } 0242 0243 #include "moc_grepviewplugin.cpp"