From 3bf61b0d698431c174a21f04dbd9aa8457e58a12 Mon Sep 17 00:00:00 2001 From: Jerzy Kozera Date: Thu, 31 Jan 2013 20:01:03 +0000 Subject: [PATCH] Initial support for Dash docsets --- README.md | 11 ++--- zeal/zealdocsetsregistry.cpp | 83 ++++++++++++++++++++++++++++++------ zeal/zealdocsetsregistry.h | 4 ++ zeal/zealsearchresult.cpp | 4 +- zeal/zealsearchresult.h | 12 +++++- 5 files changed, 93 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 5bc742bc0..fec9f68dc 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ (from WordNet 3.0) -Zeal is a simple documentation browser inspired by [Dash](http://kapeli.com/dash/), but designed from scratch to avoid copyright issues. +Zeal is a simple documentation browser inspired by [Dash](http://kapeli.com/dash/). [Screenshots (imgur)](http://imgur.com/a/VrYQx) @@ -22,13 +22,15 @@ After compiling/unzipping it you need to download docsets and put them in `$HOME Do `tar -jxvf file.tar.bz2` in docsets directory to enable given docset. +You can also use Dash's docsets by putting `.docset` directories in the same directory as above. + ## How to compile Currently Zeal requires Qt 5.0. To compile it, run `qmake` and `make` in the `zeal` directory. ## Windows binary -A 64-bit Windows binary with all dependencies is available to download from Dropbox - [zeal.zip](https://www.dropbox.com/s/2kyb3p4ox271a0d/zeal.zip) (24M). +A 64-bit Windows binary with all dependencies is available to download from Dropbox - [zeal.zip](https://www.dropbox.com/s/g4rtvs17b2zyeea/zeal.zip) (24M). ## TODO @@ -36,9 +38,8 @@ A 64-bit Windows binary with all dependencies is available to download from Drop * Support for global hotkeys under platforms other than Linux/X11 and Windows (OSX) * Search enhancements - some ideas: 1. Allow selecting subset of docsets to search in. - 2. Substring match in case current startswith matching doesn't return anything. - 3. Grouping of similar results (like overloaded functions) - 4. Better docsets formatting (without headers, sidebars etc.) + 2. Grouping of similar results (like overloaded functions) + 3. Better docsets formatting (without headers, sidebars etc.) * More docsets diff --git a/zeal/zealdocsetsregistry.cpp b/zeal/zealdocsetsregistry.cpp index fd5030e55..df457a1e1 100644 --- a/zeal/zealdocsetsregistry.cpp +++ b/zeal/zealdocsetsregistry.cpp @@ -11,11 +11,29 @@ ZealDocsetsRegistry* docsets = ZealDocsetsRegistry::instance(); void ZealDocsetsRegistry::addDocset(const QString& path) { auto dir = QDir(path); - auto db = QSqlDatabase::addDatabase("QSQLITE", dir.dirName()); - db.setDatabaseName(dir.filePath("index.sqlite")); - db.open(); - dbs.insert(dir.dirName(), db); - dirs.insert(dir.dirName(), dir); + auto name = dir.dirName().replace(".docset", ""); + auto db = QSqlDatabase::addDatabase("QSQLITE", name); + if(QFile::exists(dir.filePath("index.sqlite"))) { + db.setDatabaseName(dir.filePath("index.sqlite")); + db.open(); + types.insert(name, ZEAL); + } else { + auto dashFile = QDir(QDir(dir.filePath("Contents")).filePath("Resources")).filePath("docSet.dsidx"); + db.setDatabaseName(dashFile); + db.open(); + auto q = db.exec("select name from sqlite_master where type='table'"); + QStringList tables; + while(q.next()) { + tables.append(q.value(0).toString()); + } + if(tables.contains("searchIndex")) { + types.insert(name, DASH); + } else { + types.insert(name, ZDASH); + } + } + dbs.insert(name, db); + dirs.insert(name, dir); } ZealDocsetsRegistry::ZealDocsetsRegistry() { @@ -36,19 +54,60 @@ void ZealDocsetsRegistry::_runQuery(const QString& query, int queryNum) QList results; for(auto name : names()) { - auto qstr = QString("select name, parent, path from things where name " - "like '%1%' order by lower(name) asc, path asc limit 40").arg(query); - auto q = db(name).exec(qstr); - - while(q.next()) { + QString qstr; + QSqlQuery q; + bool found = false; + bool withSubStrings = false; + while(!found) { + auto curQuery = query; + if(withSubStrings) { + // if nothing found starting with query, search all substrings + curQuery = "%"+query; + } + if(types[name] == ZEAL) { + qstr = QString("select name, parent, path from things where name " + "like '%1%' order by lower(name) asc, path asc limit 40").arg(curQuery); + } else if(types[name] == DASH) { + qstr = QString("select name, null, path from searchIndex where name " + "like '%1%' order by lower(name) asc, path asc limit 40").arg(curQuery); + } else if(types[name] == ZDASH) { + qstr = QString("select ztokenname, null, zpath, zanchor from ztoken " + "join ztokenmetainformation on ztoken.zmetainformation = ztokenmetainformation.z_pk " + "join zfilepath on ztokenmetainformation.zfile = zfilepath.z_pk where ztokenname " + // %.%1% for long Django docset values like django.utils.http + // (Might be not appropriate for other docsets, but I don't have any on hand to test) + "like '%1%' or ztokenname like '%.%1%' order by lower(ztokenname) asc, zpath asc, zanchor asc limit 40").arg(curQuery); + } + q = db(name).exec(qstr); + if(q.next()) { found = true; } + else { + if(withSubStrings) break; + withSubStrings = true; // try again searching for substrings + } + } + if(!found) continue; + do { QString parentName; if(!q.value(1).isNull()) { auto qp = db(name).exec(QString("select name from things where id = %1").arg(q.value(1).toInt())); qp.next(); parentName = qp.value(0).toString(); } - results.append(ZealSearchResult(q.value(0).toString(), parentName, q.value(2).toString(), name)); - } + auto path = q.value(2).toString(); + if(types[name] == DASH || types[name] == ZDASH) { + path = QDir(QDir(QDir("Contents").filePath("Resources")).filePath("Documents")).filePath(path); + } + if(types[name] == ZDASH) { + path += "#" + q.value(3).toString(); + } + auto itemName = q.value(0).toString(); + if(itemName.indexOf('.') != -1 && itemName.indexOf('.') != 0 && q.value(1).isNull()) { + auto splitted = itemName.split("."); + itemName = splitted.at(splitted.size()-1); + parentName = splitted.at(splitted.size()-2); + } + results.append(ZealSearchResult(itemName, parentName, path, name, query)); + } while (q.next()); } qSort(results); if(queryNum != lastQuery) return; // some other queries pending - ignore this one diff --git a/zeal/zealdocsetsregistry.h b/zeal/zealdocsetsregistry.h index c3370abdd..6650adbd0 100644 --- a/zeal/zealdocsetsregistry.h +++ b/zeal/zealdocsetsregistry.h @@ -8,6 +8,8 @@ #include "zealsearchresult.h" +typedef enum {ZEAL, DASH, ZDASH} DocSetType; + class ZealDocsetsRegistry : public QObject { Q_OBJECT @@ -64,8 +66,10 @@ private slots: // if we try to use those two functions by accident static ZealDocsetsRegistry* m_Instance; + // FIXME: DocSet class could be better instead of 3 maps QMap dbs; QMap dirs; + QMap types; QList queryResults; int lastQuery = -1; }; diff --git a/zeal/zealsearchresult.cpp b/zeal/zealsearchresult.cpp index 14f12f658..83375f4cf 100644 --- a/zeal/zealsearchresult.cpp +++ b/zeal/zealsearchresult.cpp @@ -1,6 +1,6 @@ #include "zealsearchresult.h" -ZealSearchResult::ZealSearchResult(const QString& name_, const QString& parentName_, const QString& path_, const QString& docset_) - : name(name_), parentName(parentName_), path(path_), docset(docset_) +ZealSearchResult::ZealSearchResult(const QString& name_, const QString& parentName_, const QString& path_, const QString& docset_, const QString& query_) + : name(name_), parentName(parentName_), path(path_), docset(docset_), query(query_) { } diff --git a/zeal/zealsearchresult.h b/zeal/zealsearchresult.h index 4524fd7bd..d5a03881e 100644 --- a/zeal/zealsearchresult.h +++ b/zeal/zealsearchresult.h @@ -6,21 +6,29 @@ class ZealSearchResult { public: - ZealSearchResult(const QString& name_, const QString& parentName_, const QString& path_, const QString& docset_); + ZealSearchResult(const QString& name_, const QString& parentName_, const QString& path_, const QString& docset_, const QString& query_); const QString& getName() const { return name; }; const QString& getParentName() const { return parentName; }; const QString& getPath() const { return path; }; const QString& getDocsetName() const { return docset; }; bool operator<(const ZealSearchResult& r) const { - return name.toLower() < r.name.toLower() || + if (name.toLower().startsWith(query.toLower()) > r.name.toLower().startsWith(r.query.toLower())) { + // return results that are prefixed with query first + return true; + } else if (name.toLower().startsWith(query.toLower()) < r.name.toLower().startsWith(r.query.toLower())) { + return false; + } else { + return name.toLower() < r.name.toLower() || (name.toLower() == r.name.toLower() && parentName.toLower() < r.parentName.toLower()); + } } private: QString name; QString parentName; QString path; QString docset; + QString query; }; #endif // ZEALSEARCHRESULT_H