diff --git a/dlangide.visualdproj b/dlangide.visualdproj
index 85d2fb9..a37a585 100644
--- a/dlangide.visualdproj
+++ b/dlangide.visualdproj
@@ -214,6 +214,7 @@
+
diff --git a/src/dlangide/tools/d/dparser.d b/src/dlangide/tools/d/dparser.d
new file mode 100644
index 0000000..9451410
--- /dev/null
+++ b/src/dlangide/tools/d/dparser.d
@@ -0,0 +1,124 @@
+module dlangide.tools.d.dparser;
+
+import std.d.lexer;
+import std.d.parser;
+import std.d.ast;
+import std.algorithm;
+import std.string;
+import std.path;
+import std.file;
+import std.conv;
+
+class DParsedModule {
+ protected string _moduleName;
+ protected string _moduleFile;
+ protected StringCache* _cache;
+ protected Module _ast;
+ const(Token)[] _tokens;
+ LexerConfig _lexerConfig;
+
+ @property string filename() {
+ return _moduleFile;
+ }
+
+ this(StringCache* cache, string filename) {
+ _cache = cache;
+ _moduleFile = filename;
+ }
+
+ void parse(ubyte[] sourceCode) {
+ _tokens = getTokensForParser(sourceCode, _lexerConfig, _cache);
+ _ast = parseModule(_tokens, _moduleFile);
+ }
+}
+
+/// D source code parsing service
+class DParsingService {
+
+ protected static __gshared DParsingService _instance;
+ /// singleton
+ static @property DParsingService instance() {
+ if (!_instance) {
+ _instance = new DParsingService();
+ }
+ return _instance;
+ }
+ /// destroy singleton
+ static void shutdown() {
+ destroy(_instance);
+ _instance = null;
+ }
+
+ protected StringCache _cache;
+ protected string[] _importPaths;
+ protected DParsedModule[] _modules;
+ protected DParsedModule[string] _moduleByName;
+ protected DParsedModule[string] _moduleByFile;
+ protected bool[string] _notFoundModules;
+
+ this() {
+ _cache = StringCache(16);
+ }
+
+ DParsedModule scan(ubyte[] sourceCode, string filename) {
+ destroy(_notFoundModules);
+ DParsedModule res = new DParsedModule(&_cache, filename);
+ res.parse(sourceCode);
+ return res;
+ }
+
+ /// converts some.module.name to some/module/name.d
+ string moduleNameToPackagePath(string moduleName) {
+ string[] pathSegments = moduleName.split(".");
+ string normalized = buildNormalizedPath(pathSegments);
+ return normalized ~ ".d";
+ }
+
+ string findModuleFile(string moduleName) {
+ string packagePath = moduleNameToPackagePath(moduleName);
+ foreach(ip; _importPaths) {
+ string path = buildNormalizedPath(ip, packagePath);
+ if (path.exists && path.isFile)
+ return path;
+ }
+ return null;
+ }
+
+ DParsedModule getOrParseModule(string moduleName) {
+ if (auto m = moduleName in _moduleByName) {
+ return *m;
+ }
+ if (moduleName in _notFoundModules)
+ return null; // already listed as not found
+ string filename = findModuleFile(moduleName);
+ if (!filename) {
+ _notFoundModules[moduleName] = true;
+ return null;
+ }
+ try {
+ DParsedModule res = new DParsedModule(&_cache, filename);
+ ubyte[] sourceCode = cast(ubyte[])read(filename);
+ res.parse(sourceCode);
+ _moduleByName[moduleName] = res;
+ _moduleByFile[filename] = res;
+ return res;
+ } catch (Exception e) {
+ _notFoundModules[moduleName] = true;
+ return null;
+ }
+ }
+
+ void addImportPaths(string[] paths) {
+ foreach(p; paths) {
+ string ap = absolutePath(buildNormalizedPath(p));
+ bool found = false;
+ foreach(ip; _importPaths)
+ if (ip.equal(ap)) {
+ found = true;
+ break;
+ }
+ if (!found)
+ _importPaths ~= ap;
+ }
+ }
+}