Commit daff59b6 authored by Christoph Cullmann's avatar Christoph Cullmann

use shared pointer for project index, too, no void pointers, use same readtags as ctags plugin

parent ce911e01
......@@ -9,7 +9,7 @@ if (NOT QJSON_FOUND)
return()
endif (NOT QJSON_FOUND)
set(kateprojectplugin_PART_SRCS kateprojectplugin.cpp kateprojectpluginview.cpp kateproject.cpp kateprojectworker.cpp kateprojectitem.cpp kateprojectview.cpp kateprojectcompletion.cpp)
set(kateprojectplugin_PART_SRCS kateprojectplugin.cpp kateprojectpluginview.cpp kateproject.cpp kateprojectworker.cpp kateprojectitem.cpp kateprojectview.cpp kateprojectcompletion.cpp kateprojectindex.cpp)
kde4_add_plugin(kateprojectplugin ${kateprojectplugin_PART_SRCS})
......
This diff is collapsed.
/*
*
* Copyright (c) 1996-2003, Darren Hiebert <dhiebert at users dot sourceforge dot net>
*
* This source code is released for the public domain.
*
* This file defines the public interface for looking up tag entries in tag
* files.
*
* The functions defined in this interface are intended to provide tag file
* support to a software tool. The tag lookups provided are sufficiently fast
* enough to permit opening a sorted tag file, searching for a matching tag,
* then closing the tag file each time a tag is looked up (search times are
* on the order of hundreths of a second, even for huge tag files). This is
* the recommended use of this library for most tool applications. Adhering
* to this approach permits a user to regenerate a tag file at will without
* the tool needing to detect and resynchronize with changes to the tag file.
* Even for an unsorted 24MB tag file, tag searches take about one second.
*/
#ifndef READTAGS_H
#define READTAGS_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* MACROS
*/
/* Options for tagsSetSortType() */
typedef enum {
TAG_UNSORTED, TAG_SORTED, TAG_FOLDSORTED
} sortType ;
/* Options for tagsFind() */
#define TAG_FULLMATCH 0x0
#define TAG_PARTIALMATCH 0x1
#define TAG_OBSERVECASE 0x0
#define TAG_IGNORECASE 0x2
/*
* DATA DECLARATIONS
*/
typedef enum { TagFailure = 0, TagSuccess = 1 } tagResult;
struct sTagFile;
typedef struct sTagFile tagFile;
/* This structure contains information about the tag file. */
typedef struct {
struct {
/* was the tag file successfully opened? */
int opened;
/* errno value when 'opened' is false */
int error_number;
} status;
/* information about the structure of the tag file */
struct {
/* format of tag file (1 = original, 2 = extended) */
short format;
/* how is the tag file sorted? */
sortType sort;
} file;
/* information about the program which created this tag file */
struct {
/* name of author of generating program (may be null) */
const char *author;
/* name of program (may be null) */
const char *name;
/* URL of distribution (may be null) */
const char *url;
/* program version (may be null) */
const char *version;
} program;
} tagFileInfo;
/* This structure contains information about an extension field for a tag.
* These exist at the end of the tag in the form "key:value").
*/
typedef struct {
/* the key of the extension field */
const char *key;
/* the value of the extension field (may be an empty string) */
const char *value;
} tagExtensionField;
/* This structure contains information about a specific tag. */
typedef struct {
/* name of tag */
const char *name;
/* path of source file containing definition of tag */
const char *file;
/* address for locating tag in source file */
struct {
/* pattern for locating source line
* (may be NULL if not present) */
const char *pattern;
/* line number in source file of tag definition
* (may be zero if not known) */
unsigned long lineNumber;
} address;
/* kind of tag (may by name, character, or NULL if not known) */
const char *kind;
/* is tag of file-limited scope? */
short fileScope;
/* miscellaneous extension fields */
struct {
/* number of entries in `list' */
unsigned short count;
/* list of key value pairs */
tagExtensionField *list;
} fields;
} tagEntry;
/*
* FUNCTION PROTOTYPES
*/
/*
* This function must be called before calling other functions in this
* library. It is passed the path to the tag file to read and a (possibly
* null) pointer to a structure which, if not null, will be populated with
* information about the tag file. If successful, the function will return a
* handle which must be supplied to other calls to read information from the
* tag file, and info.status.opened will be set to true. If unsuccessful,
* info.status.opened will be set to false and info.status.error_number will
* be set to the errno value representing the system error preventing the tag
* file from being successfully opened.
*/
extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info);
/*
* This function allows the client to override the normal automatic detection
* of how a tag file is sorted. Permissible values for `type' are
* TAG_UNSORTED, TAG_SORTED, TAG_FOLDSORTED. Tag files in the new extended
* format contain a key indicating whether or not they are sorted. However,
* tag files in the original format do not contain such a key even when
* sorted, preventing this library from taking advantage of fast binary
* lookups. If the client knows that such an unmarked tag file is indeed
* sorted (or not), it can override the automatic detection. Note that
* incorrect lookup results will result if a tag file is marked as sorted when
* it actually is not. The function will return TagSuccess if called on an
* open tag file or TagFailure if not.
*/
extern tagResult tagsSetSortType (tagFile *const file, const sortType type);
/*
* Reads the first tag in the file, if any. It is passed the handle to an
* opened tag file and a (possibly null) pointer to a structure which, if not
* null, will be populated with information about the first tag file entry.
* The function will return TagSuccess another tag entry is found, or
* TagFailure if not (i.e. it reached end of file).
*/
extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry);
/*
* Step to the next tag in the file, if any. It is passed the handle to an
* opened tag file and a (possibly null) pointer to a structure which, if not
* null, will be populated with information about the next tag file entry. The
* function will return TagSuccess another tag entry is found, or TagFailure
* if not (i.e. it reached end of file). It will always read the first tag in
* the file immediately after calling tagsOpen().
*/
extern tagResult tagsNext (tagFile *const file, tagEntry *const entry);
/*
* Retrieve the value associated with the extension field for a specified key.
* It is passed a pointer to a structure already populated with values by a
* previous call to tagsNext(), tagsFind(), or tagsFindNext(), and a string
* containing the key of the desired extension field. If no such field of the
* specified key exists, the function will return null.
*/
extern const char *tagsField (const tagEntry *const entry, const char *const key);
/*
* Find the first tag matching `name'. The structure pointed to by `entry'
* will be populated with information about the tag file entry. If a tag file
* is sorted using the C locale, a binary search algorithm is used to search
* the tag file, resulting in very fast tag lookups, even in huge tag files.
* Various options controlling the matches can be combined by bit-wise or-ing
* certain values together. The available values are:
*
* TAG_PARTIALMATCH
* Tags whose leading characters match `name' will qualify.
*
* TAG_FULLMATCH
* Only tags whose full lengths match `name' will qualify.
*
* TAG_IGNORECASE
* Matching will be performed in a case-insenstive manner. Note that
* this disables binary searches of the tag file.
*
* TAG_OBSERVECASE
* Matching will be performed in a case-senstive manner. Note that
* this enables binary searches of the tag file.
*
* The function will return TagSuccess if a tag matching the name is found, or
* TagFailure if not.
*/
extern tagResult tagsFind (tagFile *const file, tagEntry *const entry, const char *const name, const int options);
/*
* Find the next tag matching the name and options supplied to the most recent
* call to tagsFind() for the same tag file. The structure pointed to by
* `entry' will be populated with information about the tag file entry. The
* function will return TagSuccess if another tag matching the name is found,
* or TagFailure if not.
*/
extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry);
/*
* Call tagsTerminate() at completion of reading the tag file, which will
* close the file and free any internal memory allocated. The function will
* return TagFailure is no file is currently open, TagSuccess otherwise.
*/
extern tagResult tagsClose (tagFile *const file);
#ifdef __cplusplus
}
#endif
#endif
/* vi:set tabstop=8 shiftwidth=4: */
......@@ -29,13 +29,10 @@
#include <qjson/parser.h>
#include <algorithm>
KateProject::KateProject ()
: QObject ()
, m_worker (new KateProjectWorker (this))
, m_file2Item (new QMap<QString, QStandardItem *>())
, m_completionInfo (new QStringList ())
{
/**
* move worker object over and start our worker thread
......@@ -45,19 +42,6 @@ KateProject::KateProject ()
}
KateProject::~KateProject ()
{
/**
* worker must be already gone!
*/
Q_ASSERT (!m_worker);
/**
* delete other data
*/
delete m_completionInfo;
}
void KateProject::triggerDeleteLater ()
{
/**
* only do this once
......@@ -75,11 +59,6 @@ void KateProject::triggerDeleteLater ()
*/
delete m_worker;
m_worker = 0;
/**
* trigger delete later
*/
deleteLater ();
}
bool KateProject::load (const QString &fileName)
......@@ -184,48 +163,28 @@ void KateProject::loadProjectDone (KateProjectSharedQStandardItem topLevel, Kate
emit modelChanged ();
}
void KateProject::loadCompletionDone (void *completionInfo)
void KateProject::loadIndexDone (KateProjectSharedProjectIndex projectIndex)
{
/**
* convert to right types
*/
QStringList *completionInfoList = static_cast<QStringList *> (completionInfo);
/**
* no worker any more, only cleanup the stuff we got from invoke!
* no worker any more, do nothing!
* shared pointers will do deletions
*/
if (!m_worker) {
delete completionInfoList;
if (!m_worker)
return;
}
/**
* setup
* move to our project
*/
delete m_completionInfo;
m_completionInfo = completionInfoList;
m_projectIndex = projectIndex;
}
void KateProject::completionMatches (QStandardItemModel &model, KTextEditor::View *view, const KTextEditor::Range & range)
{
/**
* word to complete
* pass over to index, if around
*/
QString word = view->document()->text(range);
/**
* get all matching things
* use binary search for prefix
*/
QStringList::iterator lowerBound = std::lower_bound (m_completionInfo->begin(), m_completionInfo->end(), word);
while (lowerBound != m_completionInfo->end()) {
if (lowerBound->startsWith (word)) {
model.appendRow (new QStandardItem (*lowerBound));
++lowerBound;
continue;
}
break;
}
if (m_projectIndex)
m_projectIndex->completionMatches (model, view, range);
}
// kate: space-indent on; indent-width 2; replace-tabs on;
......@@ -22,11 +22,10 @@
#define KATE_PROJECT_H
#include <QThread>
#include <QStandardItemModel>
#include <QMap>
#include <QSharedPointer>
#include <ktexteditor/view.h>
#include "kateprojectindex.h"
/**
* Shared pointer data types.
......@@ -38,6 +37,9 @@ Q_DECLARE_METATYPE(KateProjectSharedQStandardItem)
typedef QSharedPointer<QMap<QString, QStandardItem *> > KateProjectSharedQMapStringItem;
Q_DECLARE_METATYPE(KateProjectSharedQMapStringItem)
typedef QSharedPointer<KateProjectIndex> KateProjectSharedProjectIndex;
Q_DECLARE_METATYPE(KateProjectSharedProjectIndex)
/**
* Class representing a project.
* Holds project properties like name, groups, contained files, ...
......@@ -46,25 +48,16 @@ class KateProject : public QObject
{
Q_OBJECT
private:
/**
* deconstruct project
* close() MUST be called before
*/
~KateProject ();
public:
/**
* construct empty project
*/
KateProject ();
/**
* Trigger deleteLater().
* Using delete directly will leak memory, this causes correct de-initialisation even
* with inter-thread events still around (will trigger delete later)
* deconstruct project
*/
void triggerDeleteLater ();
~KateProject ();
/**
* Load a project.
......@@ -153,10 +146,10 @@ class KateProject : public QObject
void loadProjectDone (KateProjectSharedQStandardItem topLevel, KateProjectSharedQMapStringItem file2Item);
/**
* Used for worker to send back the results of completion loading
* @param completionInfo new completion info
* Used for worker to send back the results of index loading
* @param projectIndex new project index
*/
void loadCompletionDone (void *completionInfo);
void loadIndexDone (KateProjectSharedProjectIndex projectIndex);
signals:
/**
......@@ -212,9 +205,9 @@ class KateProject : public QObject
KateProjectSharedQMapStringItem m_file2Item;
/**
* completion info
* project index, if any
*/
QStringList *m_completionInfo;
KateProjectSharedProjectIndex m_projectIndex;
};
#endif
......
/* This file is part of the Kate project.
*
* Copyright (C) 2012 Christoph Cullmann <cullmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kateprojectindex.h"
#include <QProcess>
#include <QDir>
/**
* include ctags reading
*/
#include "ctags/readtags.c"
KateProjectIndex::KateProjectIndex (const QStringList &files)
: m_ctagsIndexFile (QDir::tempPath () + "/kate.project.ctags")
, m_ctagsIndexHandle (0)
{
/**
* load ctags
*/
loadCtags (files);
}
KateProjectIndex::~KateProjectIndex ()
{
/**
* delete ctags handle if any
*/
if (m_ctagsIndexHandle) {
tagsClose (m_ctagsIndexHandle);
m_ctagsIndexHandle = 0;
}
}
void KateProjectIndex::loadCtags (const QStringList &files)
{
/**
* create temporary file
* if not possible, fail
*/
if (!m_ctagsIndexFile.open ())
return;
/**
* close file again, other process will use it
*/
m_ctagsIndexFile.close ();
/**
* try to run ctags for all files in this project
* output to our ctags index file
*/
QProcess ctags;
QStringList args;
args << "-L" << "-" << "-f" << m_ctagsIndexFile.fileName() << "--fields=+z";
ctags.start("ctags", args);
if (!ctags.waitForStarted())
return;
/**
* write files list and close write channel
*/
ctags.write(files.join("\n").toLocal8Bit());
ctags.closeWriteChannel();
/**
* wait for done
*/
if (!ctags.waitForFinished())
return;
/**
* try to open ctags file
*/
tagFileInfo info;
memset (&info, 0, sizeof (tagFileInfo));
m_ctagsIndexHandle = tagsOpen (m_ctagsIndexFile.fileName().toLocal8Bit(), &info);
}
void KateProjectIndex::completionMatches (QStandardItemModel &model, KTextEditor::View *view, const KTextEditor::Range & range)
{
/**
* abort if no ctags index
*/
if (!m_ctagsIndexHandle)
return;
/**
* word to complete
* abort if empty
*/
QByteArray word = view->document()->text(range).toLocal8Bit();
if (word.isEmpty())
return;
/**
* try to search entry
* fail if none found
*/
tagEntry entry;
if (tagsFind (m_ctagsIndexHandle, &entry, word.constData(), TAG_PARTIALMATCH | TAG_OBSERVECASE) != TagSuccess)
return;
/**
* loop over all found tags
* first one is filled by above find, others by find next
*/
do {
/**
* add new completion item
*/
model.appendRow (new QStandardItem (QString::fromLocal8Bit(entry.name)));
printf ("project info created %s\n", entry.name);
} while (tagsFindNext (m_ctagsIndexHandle, &entry) == TagSuccess);
}
// kate: space-indent on; indent-width 2; replace-tabs on;
/* This file is part of the Kate project.
*
* Copyright (C) 2010 Christoph Cullmann <cullmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KATE_PROJECT_INDEX_H
#define KATE_PROJECT_INDEX_H
#include <ktexteditor/document.h>
#include <ktexteditor/view.h>
#include <QStringList>
#include <QTemporaryFile>
#include <QStandardItemModel>
/**
* ctags reading
*/
#include "ctags/readtags.h"
/**
* Class representing the index of a project.
* This includes knowledge from ctags and Co.
* Allows you to search for stuff and to get some useful auto-completion.
* Is created in Worker thread in the background, then passed to project in
* the main thread for usage.
*/
class KateProjectIndex
{
public:
/**
* construct new index for given files
* @param files files to index
*/
KateProjectIndex (const QStringList &files);
/**
* deconstruct project
*/
~KateProjectIndex ();
/**
* Fill in completion matches for given view/range.
* Uses e.g. ctags index.
* @param model model to fill with completion matches
* @param view view we complete for
* @param range range we complete for
*/
void completionMatches (QStandardItemModel &model, KTextEditor::View *view, const KTextEditor::Range & range);
private:
/**
* Load ctags tags.
* @param files files to index
*/
void loadCtags (const QStringList &files);
private:
/**
* ctags index file
*/
QTemporaryFile m_ctagsIndexFile;
/**
* handle to ctags file for querying, if possible
*/
tagFile *m_ctagsIndexHandle;
};
#endif
// kate: space-indent on; indent-width 2; replace-tabs on;
......@@ -41,6 +41,7 @@ KateProjectPlugin::KateProjectPlugin (QObject* parent, const QList<QVariant>&)
*/
qRegisterMetaType<KateProjectSharedQStandardItem>("KateProjectSharedQStandardItem");
qRegisterMetaType<KateProjectSharedQMapStringItem>("KateProjectSharedQMapStringItem");
qRegisterMetaType<KateProjectSharedProjectIndex>("KateProjectSharedProjectIndex");
/**
* connect to important signals, e.g. for auto project loading
......@@ -69,7 +70,7 @@ KateProjectPlugin::~KateProjectPlugin()
/**
* let events still be handled!
*/
project->triggerDeleteLater ();
delete project;
}
/**
......@@ -107,7 +108,7 @@ KateProject *KateProjectPlugin::projectForFileName (const QString &fileName)
*/
KateProject *project = new KateProject ();
if (!project->load (canonicalFilePath)) {
project->triggerDeleteLater ();
delete project;
return 0;
}
......
......@@ -67,9 +67,9 @@ void KateProjectWorker::loadProject (QString fileName, QVariantMap projectMap)
QMetaObject::invokeMethod (m_project, "loadProjectDone", Qt::QueuedConnection, Q_ARG(KateProjectSharedQStandardItem, topLevel), Q_ARG(KateProjectSharedQMapStringItem, file2Item));
/**
* trigger more updates
* load index
*/
loadCtags (files);
loadIndex (files);
}
void KateProjectWorker::loadProject (QStandardItem *parent, const QVariantMap &project, QMap<QString, QStandardItem *> *file2Item)
......@@ -317,86 +317,18 @@ void KateProjectWorker::loadFilesEntry (QStandardItem *parent, const QVariantMap
}
}
void KateProjectWorker::loadCtags (const QStringList &files)
void KateProjectWorker::loadIndex (const QStringList &files)
{
/**
* get some timing stats
* create new index, this will do the loading in the constructor
* wrap it into shared pointer for transfer to main thread
*/
QTime timer;
timer.start ();
KateProjectSharedProjectIndex index (new KateProjectIndex(files));
/**
* try to run ctags for all files in this project
* send new index object back to project
*/
QProcess ctags;
QStringList args;
args << "-L" << "-" << "-f" << "-" << "--fields=+z";
ctags.start("ctags", args);
if (!ctags.waitForStarted())
return;
/**
* write files list and close write channel
*/
ctags.write(files.join("\n").toLocal8Bit());
ctags.closeWriteChannel();
/**
* wait for done
*/
if (!ctags.waitForFinished())
return;
/**
* get results and parse them
*/
QStringList tagLines = QString::fromLocal8Bit (ctags.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts);
QStringList *completionInfo = new QStringList ();