218 lines
5.5 KiB
D
218 lines
5.5 KiB
D
// Copyright Brian Schott (Sir Alaran) 2012.
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
module cache;
|
|
|
|
import etc.c.sqlite3;
|
|
import std.c.stdlib;
|
|
import std.datetime;
|
|
import std.file;
|
|
import std.uuid;
|
|
import std.array;
|
|
import std.string;
|
|
import std.conv;
|
|
import std.d.lexer;
|
|
|
|
import location;
|
|
import parser;
|
|
import types;
|
|
|
|
private sqlite3* database;
|
|
|
|
version (Posix)
|
|
{
|
|
private immutable char* DB_PATH = "~/.dscanner/cache.db";
|
|
}
|
|
else version (Windows)
|
|
{
|
|
pragma(msg, "Caching not supported on Windows yet");
|
|
immutable string DB_PATH = "";
|
|
}
|
|
|
|
private enum Queries : string
|
|
{
|
|
getUpdateTime = "select mtime from files where filepath = ?",
|
|
insertContainer = "insert into containers values ()",
|
|
deleteContainer = "delete from containers where fileId = ?",
|
|
deleteSymbol = "delete from symbols where containerId = ?",
|
|
deleteFile = "delete from files where path = ?",
|
|
getPublicImports = "select importedId from publicImports where importerId = ?",
|
|
getModuleId = "select id from files where path = ?",
|
|
getContainersByModule = "select id from containers where fileId = ?"
|
|
}
|
|
|
|
private sqlite3* getDatabase()
|
|
{
|
|
if (database !is null)
|
|
return database;
|
|
int status = sqlite3_open(DB_PATH, &database);
|
|
if (status != SQLITE_OK)
|
|
{
|
|
throw new Exception("Could not open %s: %s".format(DB_PATH,
|
|
sqlite3_errmsg(database)));
|
|
}
|
|
return database;
|
|
}
|
|
|
|
void closeDatabase()
|
|
{
|
|
if (database !is null)
|
|
{
|
|
sqlite3_close(database);
|
|
database = null;
|
|
}
|
|
}
|
|
|
|
private long getCachedModTime(sqlite3* db, sqlite3_stmt* statement, string filePath)
|
|
{
|
|
bindText(statement, 1, filePath);
|
|
if (sqlite3_step(statement) != SQLITE_ROW)
|
|
throw new Exception("%s".format(sqlite3_errmsg(db)));
|
|
return sqlite3_column_int64(statement, 1);
|
|
}
|
|
|
|
/**
|
|
* Updates the sqlite database with current autocomplete information for the
|
|
* given modules.
|
|
*/
|
|
void updateCache(string dirs[], string moduleNames[])
|
|
{
|
|
string[] filePaths;
|
|
foreach (moduleName; moduleNames)
|
|
{
|
|
string path = findAbsPath(dirs, moduleName);
|
|
if (path is null)
|
|
continue;
|
|
filePaths ~= path;
|
|
}
|
|
|
|
sqlite3* db = getDatabase();
|
|
sqlite3_stmt* statement;
|
|
scope(exit) { if (statement) sqlite3_finalize(statement); }
|
|
char* pzTail;
|
|
scope(exit) { if (pzTail) free(pzTail); }
|
|
sqlite3_prepare_v2(db, Queries.getUpdateTime.toStringz(),
|
|
cast(int) Queries.getUpdateTime.length + 1, &statement, &pzTail);
|
|
|
|
foreach (string filePath; filePaths)
|
|
{
|
|
immutable long mtime = getCachedModTime(db, statement, filePath);
|
|
SysTime timeLastModified = timeLastModified(filePath);
|
|
// if the times match, we don't need to update the cache.
|
|
if (timeLastModified.stdTime == mtime)
|
|
continue;
|
|
|
|
// // re-parse the module
|
|
// Module m = parseModule(byToken(readText(filePath)).array());
|
|
//
|
|
// updateCache(m);
|
|
|
|
sqlite3_reset(statement);
|
|
}
|
|
}
|
|
|
|
private void updateCache(const Module m)
|
|
in
|
|
{
|
|
assert(m !is null);
|
|
}
|
|
body
|
|
{
|
|
}
|
|
|
|
private string[] getImportedModules(string modulePath, sqlite3_stmt* statement = null)
|
|
{
|
|
auto app = appender!(string[])();
|
|
sqlite3* db = getDatabase();
|
|
bool statementAllocated = false;
|
|
scope(exit) { if (statementAllocated && statement !is null) sqlite3_finalize(statement); }
|
|
if (statement is null)
|
|
{
|
|
statementAllocated = true;
|
|
char* pzTail;
|
|
scope(exit) { if (pzTail) free(pzTail); }
|
|
sqlite3_prepare_v2(db, Queries.getPublicImports.toStringz(),
|
|
cast(int) Queries.getPublicImports.length + 1, &statement, &pzTail);
|
|
}
|
|
|
|
string moduleId = getModuleIdFromPath(modulePath);
|
|
bindText(statement, 1, moduleId);
|
|
while (sqlite3_step(statement) == SQLITE_ROW)
|
|
{
|
|
app.put(to!string(sqlite3_column_text(statement, 1)));
|
|
}
|
|
sqlite3_reset(statement);
|
|
foreach (string imported; app.data)
|
|
{
|
|
string[] r = getImportedModules(imported, statement);
|
|
}
|
|
return app.data;
|
|
}
|
|
|
|
private string getModuleIdFromPath(string filePath)
|
|
{
|
|
sqlite3* db = getDatabase();
|
|
sqlite3_stmt* statement;
|
|
char* pzTail;
|
|
scope(exit) if (pzTail) free(pzTail);
|
|
sqlite3_prepare_v2(db, Queries.getModuleId.toStringz(),
|
|
cast(int) Queries.getModuleId.length + 1, &statement,
|
|
&pzTail);
|
|
bindText(statement, 1, filePath);
|
|
if (sqlite3_step(statement) != SQLITE_ROW)
|
|
return null;
|
|
return to!string(sqlite3_column_text(statement, 1));
|
|
}
|
|
|
|
/**
|
|
* Returns: the container IDs of the containers that have
|
|
* been imported
|
|
*/
|
|
public string[] getContainersImported(string modulePath)
|
|
{
|
|
immutable string moduleId = getModuleIdFromPath(modulePath);
|
|
sqlite3* db = getDatabase();
|
|
sqlite3_stmt* statement;
|
|
char* pzTail;
|
|
scope(exit) if (pzTail) free(pzTail);
|
|
string[] moduleIds = getImportedModules(modulePath);
|
|
string[] containerIds;
|
|
foreach (string id; moduleIds)
|
|
{
|
|
containerIds ~= getContainersByModule(id);
|
|
}
|
|
return containerIds;
|
|
}
|
|
|
|
private string[] getContainersByModule(string moduleId)
|
|
{
|
|
sqlite3* db = getDatabase();
|
|
sqlite3_stmt* statement;
|
|
scope(exit) if (statement !is null) sqlite3_finalize(statement);
|
|
char* pzTail;
|
|
prepareStatement(db, statement, Queries.getContainersByModule);
|
|
bindText(statement, 1, moduleId);
|
|
string[] rVal;
|
|
while (sqlite3_step(statement) == SQLITE_ROW)
|
|
{
|
|
rVal ~= to!string(sqlite3_column_text(statement, 1));
|
|
}
|
|
return rVal;
|
|
}
|
|
|
|
private void prepareStatement(sqlite3* db, sqlite3_stmt* statement, string query)
|
|
{
|
|
char* pzTail;
|
|
scope(exit) if (pzTail) free(pzTail);
|
|
sqlite3_prepare_v2(db, query.toStringz(), cast(int) query.length + 1,
|
|
&statement, &pzTail);
|
|
}
|
|
|
|
private void bindText(sqlite3_stmt* statement, int argPos, string text)
|
|
{
|
|
sqlite3_bind_text(statement, argPos, text.toStringz(),
|
|
cast(int) text.length + 1, SQLITE_TRANSIENT);
|
|
}
|