Commit 5fd34266 authored by Christoph Cullmann's avatar Christoph Cullmann

Merge branch 'project-ctags' into 'master'

Project ctags

See merge request !36
parents 1584e21b bf04e170
......@@ -20,6 +20,7 @@
#include "kateproject.h"
#include "kateprojectworker.h"
#include "kateprojectplugin.h"
#include <klocalizedstring.h>
......@@ -33,14 +34,17 @@
#include <QPlainTextDocumentLayout>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QJsonObject>
#include <QJsonArray>
#include <utility>
KateProject::KateProject(ThreadWeaver::Queue *weaver)
KateProject::KateProject(ThreadWeaver::Queue *weaver, KateProjectPlugin *plugin)
: QObject()
, m_fileLastModified()
, m_notesDocument(nullptr)
, m_untrackedDocumentsRoot(nullptr)
, m_weaver(weaver)
, m_plugin(plugin)
{
}
......@@ -103,6 +107,32 @@ QVariantMap KateProject::readProjectFile() const
return QVariantMap();
}
/**
* convenience; align with auto-generated projects
* generate 'name' and 'files' if not specified explicitly,
* so those parts need not be given if only wishes to specify additional
* project configuration (e.g. build, ctags)
*/
if (project.isObject()) {
auto dir = QFileInfo(m_fileName).dir();
auto object = project.object();
auto name = object[QStringLiteral("name")];
if (name.isUndefined() || name.isNull()) {
name = dir.dirName();
}
auto files = object[QStringLiteral("files")];
if (files.isUndefined() || files.isNull()) {
// support all we can, could try to detect,
// but it will be sorted out upon loading anyway
QJsonArray afiles;
for (const auto &t : {QStringLiteral("git"), QStringLiteral("hg"), QStringLiteral("svn"), QStringLiteral("darcs")}) {
afiles.push_back(QJsonObject {{t, true}});
}
files = afiles;
}
project.setObject(object);
}
return project.toVariant().toMap();
}
......@@ -147,7 +177,15 @@ bool KateProject::load(const QVariantMap &globalProject, bool force)
emit projectMapChanged();
// trigger loading of project in background thread
auto w = new KateProjectWorker(m_baseDir, m_projectMap);
QString indexDir;
if (m_plugin->getIndexEnabled()) {
indexDir = m_plugin->getIndexDirectory().toLocalFile();
// if empty, use regular tempdir
if (indexDir.isEmpty()) {
indexDir = QDir::tempPath();
}
}
auto w = new KateProjectWorker(m_baseDir, indexDir, m_projectMap, force);
connect(w, &KateProjectWorker::loadDone, this, &KateProject::loadProjectDone);
connect(w, &KateProjectWorker::loadIndexDone, this, &KateProject::loadIndexDone);
m_weaver->stream() << w;
......
......@@ -79,8 +79,16 @@ struct
/// If set, it may contain extra options for ctags command used to populate the auto completion popup in Kate.
struct ctags
{
/// If "enable" is set to "1", a ctags index file is generated.
/// If not present, generation of index depends on project plugin setting.
bool enable;
/// "options" can be set to a list of ctags options. You may need to escape character "\".
vector< string > options;
/// "index_file" can be set to path of ctags file to generate.
/// A relative path is wrt to the project base directory.
string index_file;
}
};
......
......@@ -47,6 +47,8 @@ namespace ThreadWeaver
class Queue;
}
class KateProjectPlugin;
/**
* Class representing a project.
* Holds project properties like name, groups, contained files, ...
......@@ -59,7 +61,7 @@ public:
/**
* construct empty project
*/
KateProject(ThreadWeaver::Queue *weaver);
KateProject(ThreadWeaver::Queue *weaver, KateProjectPlugin *plugin);
/**
* deconstruct project
......@@ -307,6 +309,11 @@ private:
* project configuration (read from file or injected)
*/
QVariantMap m_globalProject;
/**
* Project plugin (configuration)
*/
KateProjectPlugin *m_plugin;
};
#endif
......@@ -24,6 +24,7 @@
#include <QCheckBox>
#include <QGroupBox>
#include <KLocalizedString>
#include <KUrlRequester>
KateProjectConfigPage::KateProjectConfigPage(QWidget *parent, KateProjectPlugin *plugin)
: KTextEditor::ConfigPage(parent)
......@@ -50,6 +51,22 @@ KateProjectConfigPage::KateProjectConfigPage(QWidget *parent, KateProjectPlugin
group->setLayout(vbox);
layout->addWidget(group);
vbox = new QVBoxLayout();
group = new QGroupBox(i18nc("Groupbox title", "Project Index"), this);
group->setWhatsThis(i18n("Project ctags index settings"));
m_cbIndexEnabled = new QCheckBox(i18n("Enable indexing"), this);
vbox->addWidget(m_cbIndexEnabled);
auto label = new QLabel(this);
label->setText(QStringLiteral("Directory for index files"));
vbox->addWidget(label);
m_indexPath = new KUrlRequester(this);
m_indexPath->setToolTip(QStringLiteral("The system tempory directory is used if not specified, which may overflow for very large repositories"));
vbox->addWidget(m_indexPath);
vbox->addStretch(1);
group->setLayout(vbox);
layout->addWidget(group);
layout->insertStretch(-1, 10);
reset();
......@@ -57,6 +74,9 @@ KateProjectConfigPage::KateProjectConfigPage(QWidget *parent, KateProjectPlugin
connect(m_cbAutoGit, &QCheckBox::stateChanged, this, &KateProjectConfigPage::slotMyChanged);
connect(m_cbAutoSubversion, &QCheckBox::stateChanged, this, &KateProjectConfigPage::slotMyChanged);
connect(m_cbAutoMercurial, &QCheckBox::stateChanged, this, &KateProjectConfigPage::slotMyChanged);
connect(m_cbIndexEnabled, &QCheckBox::stateChanged, this, &KateProjectConfigPage::slotMyChanged);
connect(m_indexPath, &KUrlRequester::textChanged, this, &KateProjectConfigPage::slotMyChanged);
connect(m_indexPath, &KUrlRequester::urlSelected, this, &KateProjectConfigPage::slotMyChanged);
}
QString KateProjectConfigPage::name() const
......@@ -83,6 +103,7 @@ void KateProjectConfigPage::apply()
m_changed = false;
m_plugin->setAutoRepository(m_cbAutoGit->checkState() == Qt::Checked, m_cbAutoSubversion->checkState() == Qt::Checked, m_cbAutoMercurial->checkState() == Qt::Checked);
m_plugin->setIndex(m_cbIndexEnabled->checkState() == Qt::Checked, m_indexPath->url());
}
void KateProjectConfigPage::reset()
......@@ -90,6 +111,8 @@ void KateProjectConfigPage::reset()
m_cbAutoGit->setCheckState(m_plugin->autoGit() ? Qt::Checked : Qt::Unchecked);
m_cbAutoSubversion->setCheckState(m_plugin->autoSubversion() ? Qt::Checked : Qt::Unchecked);
m_cbAutoMercurial->setCheckState(m_plugin->autoMercurial() ? Qt::Checked : Qt::Unchecked);
m_cbIndexEnabled->setCheckState(m_plugin->getIndexEnabled() ? Qt::Checked : Qt::Unchecked);
m_indexPath->setUrl(m_plugin->getIndexDirectory());
m_changed = false;
}
......
......@@ -24,6 +24,7 @@
class KateProjectPlugin;
class QWidget;
class QCheckBox;
class KUrlRequester;
class KateProjectConfigPage : public KTextEditor::ConfigPage
{
......@@ -50,6 +51,8 @@ private:
QCheckBox *m_cbAutoGit;
QCheckBox *m_cbAutoSubversion;
QCheckBox *m_cbAutoMercurial;
QCheckBox *m_cbIndexEnabled;
KUrlRequester *m_indexPath;
KateProjectPlugin *m_plugin;
bool m_changed = false;
};
......
......@@ -28,14 +28,27 @@
*/
#include "ctags/readtags.c"
KateProjectIndex::KateProjectIndex(const QStringList &files, const QVariantMap &ctagsMap)
: m_ctagsIndexFile(QDir::tempPath() + QStringLiteral("/kate.project.ctags"))
, m_ctagsIndexHandle(nullptr)
KateProjectIndex::KateProjectIndex(const QString &baseDir, const QString &indexDir, const QStringList &files, const QVariantMap &ctagsMap, bool force)
: m_ctagsIndexHandle(nullptr)
{
// allow project to override and specify a (re-usable) indexfile
// otherwise fall-back to a temporary file if nothing specified
auto ctagsFile = ctagsMap.value(QStringLiteral("index_file"));
if (ctagsFile.userType() == QMetaType::QString) {
auto path = ctagsFile.toString();
if (!QDir::isAbsolutePath(path)) {
path = QDir(baseDir).absoluteFilePath(path);
}
m_ctagsIndexFile.reset(new QFile(path));
} else {
// indexDir is typically QDir::tempPath() or otherwise specified in configuration
m_ctagsIndexFile.reset(new QTemporaryFile(indexDir + QStringLiteral("/kate.project.ctags")));
}
/**
* load ctags
*/
loadCtags(files, ctagsMap);
loadCtags(files, ctagsMap, force);
}
KateProjectIndex::~KateProjectIndex()
......@@ -49,20 +62,29 @@ KateProjectIndex::~KateProjectIndex()
}
}
void KateProjectIndex::loadCtags(const QStringList &files, const QVariantMap &ctagsMap)
void KateProjectIndex::loadCtags(const QStringList &files, const QVariantMap &ctagsMap, bool force)
{
/**
* only overwrite existing index upon reload
* (a temporary index file will never exist)
*/
if (m_ctagsIndexFile->exists() && !force) {
openCtags();
return;
}
/**
* create temporary file
* if not possible, fail
*/
if (!m_ctagsIndexFile.open()) {
if (!m_ctagsIndexFile->open(QIODevice::ReadWrite)) {
return;
}
/**
* close file again, other process will use it
*/
m_ctagsIndexFile.close();
m_ctagsIndexFile->close();
/**
* try to run ctags for all files in this project
......@@ -70,7 +92,7 @@ void KateProjectIndex::loadCtags(const QStringList &files, const QVariantMap &ct
*/
QProcess ctags;
QStringList args;
args << QStringLiteral("-L") << QStringLiteral("-") << QStringLiteral("-f") << m_ctagsIndexFile.fileName() << QStringLiteral("--fields=+K+n");
args << QStringLiteral("-L") << QStringLiteral("-") << QStringLiteral("-f") << m_ctagsIndexFile->fileName() << QStringLiteral("--fields=+K+n");
const QString keyOptions = QStringLiteral("options");
for (const QVariant &optVariant : ctagsMap[keyOptions].toList()) {
args << optVariant.toString();
......@@ -93,22 +115,27 @@ void KateProjectIndex::loadCtags(const QStringList &files, const QVariantMap &ct
return;
}
openCtags();
}
void KateProjectIndex::openCtags()
{
/**
* file not openable, bad
*/
if (!m_ctagsIndexFile.open()) {
if (!m_ctagsIndexFile->open(QIODevice::ReadOnly)) {
return;
}
/**
* get size
*/
qint64 size = m_ctagsIndexFile.size();
qint64 size = m_ctagsIndexFile->size();
/**
* close again
*/
m_ctagsIndexFile.close();
m_ctagsIndexFile->close();
/**
* empty file, bad
......@@ -117,12 +144,20 @@ void KateProjectIndex::loadCtags(const QStringList &files, const QVariantMap &ct
return;
}
/**
* close current
*/
if (m_ctagsIndexHandle) {
tagsClose(m_ctagsIndexHandle);
m_ctagsIndexHandle = nullptr;
}
/**
* try to open ctags file
*/
tagFileInfo info;
memset(&info, 0, sizeof(tagFileInfo));
m_ctagsIndexHandle = tagsOpen(m_ctagsIndexFile.fileName().toLocal8Bit().constData(), &info);
m_ctagsIndexHandle = tagsOpen(m_ctagsIndexFile->fileName().toLocal8Bit().constData(), &info);
}
void KateProjectIndex::findMatches(QStandardItemModel &model, const QString &searchWord, MatchType type)
......
......@@ -48,7 +48,7 @@ public:
* @param files files to index
* @param ctagsMap ctags section for extra options
*/
KateProjectIndex(const QStringList &files, const QVariantMap &ctagsMap);
KateProjectIndex(const QString &baseDir, const QString &indexDir, const QStringList &files, const QVariantMap &ctagsMap, bool force);
/**
* deconstruct project
......@@ -96,13 +96,18 @@ private:
* @param files files to index
* @param ctagsMap ctags section for extra options
*/
void loadCtags(const QStringList &files, const QVariantMap &ctagsMap);
void loadCtags(const QStringList &files, const QVariantMap &ctagsMap, bool force);
/**
* Open ctags tags.
*/
void openCtags();
private:
/**
* ctags index file
*/
QTemporaryFile m_ctagsIndexFile;
QScopedPointer<QFile> m_ctagsIndexFile;
/**
* handle to ctags file for querying, if possible
......
......@@ -76,6 +76,9 @@ KateProjectPlugin::KateProjectPlugin(QObject *parent, const QList<QVariant> &)
connect(KTextEditor::Editor::instance()->application(), &KTextEditor::Application::documentCreated, this, &KateProjectPlugin::slotDocumentCreated);
connect(&m_fileWatcher, &QFileSystemWatcher::directoryChanged, this, &KateProjectPlugin::slotDirectoryChanged);
// read configuration prior to cwd project setup below
readConfig();
#ifdef HAVE_CTERMID
/**
* open project for our current working directory, if this kate has a terminal
......@@ -91,8 +94,6 @@ KateProjectPlugin::KateProjectPlugin(QObject *parent, const QList<QVariant> &)
}
#endif
readConfig();
for (auto document : KTextEditor::Editor::instance()->application()->documents()) {
slotDocumentCreated(document);
}
......@@ -134,7 +135,7 @@ KTextEditor::ConfigPage *KateProjectPlugin::configPage(int number, QWidget *pare
KateProject *KateProjectPlugin::createProjectForFileName(const QString &fileName)
{
KateProject *project = new KateProject(m_weaver);
KateProject *project = new KateProject(m_weaver, this);
if (!project->loadFromFile(fileName)) {
delete project;
return nullptr;
......@@ -293,7 +294,7 @@ KateProject *KateProjectPlugin::createProjectForRepository(const QString &type,
cnf[QStringLiteral("name")] = dir.dirName();
cnf[QStringLiteral("files")] = (QVariantList() << files);
KateProject *project = new KateProject(m_weaver);
KateProject *project = new KateProject(m_weaver, this);
project->loadFromData(cnf, dir.canonicalPath());
m_projects.append(project);
......@@ -325,6 +326,23 @@ bool KateProjectPlugin::autoMercurial() const
return m_autoMercurial;
}
void KateProjectPlugin::setIndex(bool enabled, const QUrl &directory)
{
m_indexEnabled = enabled;
m_indexDirectory = directory;
writeConfig();
}
bool KateProjectPlugin::getIndexEnabled() const
{
return m_indexEnabled;
}
QUrl KateProjectPlugin::getIndexDirectory() const
{
return m_indexDirectory;
}
void KateProjectPlugin::readConfig()
{
KConfigGroup config(KSharedConfig::openConfig(), "project");
......@@ -343,6 +361,9 @@ void KateProjectPlugin::readConfig()
if (autorepository.contains(MercurialConfig)) {
m_autoMercurial = true;
}
m_indexEnabled = config.readEntry("index", false);
m_indexDirectory = config.readEntry("indexDirectory", QUrl());
}
void KateProjectPlugin::writeConfig()
......@@ -363,6 +384,9 @@ void KateProjectPlugin::writeConfig()
}
config.writeEntry("autorepository", repos);
config.writeEntry("index", m_indexEnabled);
config.writeEntry("indexDirectory", m_indexDirectory);
}
#if KTEXTEDITOR_VERSION >= QT_VERSION_CHECK(5, 63, 0)
......
......@@ -111,6 +111,10 @@ public:
bool autoSubversion() const;
bool autoMercurial() const;
void setIndex(bool enabled, const QUrl &directory);
bool getIndexEnabled() const;
QUrl getIndexDirectory() const;
Q_SIGNALS:
/**
* Signal that a new project got created.
......@@ -179,6 +183,8 @@ private:
bool m_autoGit : 1;
bool m_autoSubversion : 1;
bool m_autoMercurial : 1;
bool m_indexEnabled : 1;
QUrl m_indexDirectory;
ThreadWeaver::Queue *m_weaver;
};
......
......@@ -31,11 +31,13 @@
#include <QTime>
#include <QSettings>
KateProjectWorker::KateProjectWorker(const QString &baseDir, const QVariantMap &projectMap)
KateProjectWorker::KateProjectWorker(const QString &baseDir, const QString &indexDir, const QVariantMap &projectMap, bool force)
: QObject()
, ThreadWeaver::Job()
, m_baseDir(baseDir)
, m_indexDir(indexDir)
, m_projectMap(projectMap)
, m_force(force)
{
Q_ASSERT(!m_baseDir.isEmpty());
}
......@@ -58,7 +60,7 @@ void KateProjectWorker::run(ThreadWeaver::JobPointer, ThreadWeaver::Thread *)
emit loadDone(topLevel, file2Item);
// trigger index loading, will internally handle enable/disabled
loadIndex(files);
loadIndex(files, m_force);
}
void KateProjectWorker::loadProject(QStandardItem *parent, const QVariantMap &project, QMap<QString, KateProjectItem *> *file2Item)
......@@ -462,13 +464,21 @@ QStringList KateProjectWorker::filesFromDirectory(const QDir &_dir, bool recursi
return files;
}
void KateProjectWorker::loadIndex(const QStringList &files)
void KateProjectWorker::loadIndex(const QStringList &files, bool force)
{
const QString keyCtags = QStringLiteral("ctags");
const QVariantMap ctagsMap = m_projectMap[keyCtags].toMap();
/**
* load index, if enabled
* before this was default on, which is dangerous for large repositories, e.g. out-of-memory or out-of-disk
* if specified in project map; use that setting, otherwise fall back to global setting
*/
if (!m_projectMap[QStringLiteral("index")].toBool()) {
bool indexEnabled = !m_indexDir.isEmpty();
auto indexValue = ctagsMap[QStringLiteral("enable")];
if (!indexValue.isNull()) {
indexEnabled = indexValue.toBool();
}
if (!indexEnabled) {
emit loadIndexDone(KateProjectSharedProjectIndex());
return;
}
......@@ -477,7 +487,7 @@ void KateProjectWorker::loadIndex(const QStringList &files)
* create new index, this will do the loading in the constructor
* wrap it into shared pointer for transfer to main thread
*/
const QString keyCtags = QStringLiteral("ctags");
KateProjectSharedProjectIndex index(new KateProjectIndex(files, m_projectMap[keyCtags].toMap()));
KateProjectSharedProjectIndex index(new KateProjectIndex(m_baseDir, m_indexDir, files, ctagsMap, force));
emit loadIndexDone(index);
}
......@@ -45,7 +45,7 @@ public:
*/
typedef QMap<QString, KateProjectItem *> MapString2Item;
explicit KateProjectWorker(const QString &baseDir, const QVariantMap &projectMap);
explicit KateProjectWorker(const QString &baseDir, const QString &indexDir, const QVariantMap &projectMap, bool force);
void run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) override;
......@@ -75,7 +75,7 @@ private:
* Load index for whole project.
* @param files list of all project files to index
*/
void loadIndex(const QStringList &files);
void loadIndex(const QStringList &files, bool force);
QStringList findFiles(const QDir &dir, const QVariantMap &filesEntry);
......@@ -97,7 +97,14 @@ private:
* project base directory name
*/
const QString m_baseDir;
/**
* index directory
*/
const QString m_indexDir;
const QVariantMap m_projectMap;
const bool m_force;
};
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment