1 module dlangide.tools.d.dcdinterface; 2 3 import dlangui.core.logger; 4 import dlangui.core.files; 5 import dlangui.platforms.common.platform; 6 import ddebug.common.queue; 7 8 import core.thread; 9 10 import std.typecons; 11 import std.conv; 12 import std.string; 13 14 enum DCDResult : int { 15 SUCCESS, 16 NO_RESULT, 17 FAIL, 18 } 19 20 alias DocCommentsResultSet = Tuple!(DCDResult, "result", string[], "docComments"); 21 alias FindDeclarationResultSet = Tuple!(DCDResult, "result", string, "fileName", ulong, "offset"); 22 alias CompletionResultSet = Tuple!(DCDResult, "result", dstring[], "output", char[], "completionKinds"); 23 24 import server.autocomplete; 25 import common.messages; 26 27 class DCDTask { 28 protected bool _cancelled; 29 protected CustomEventTarget _guiExecutor; 30 protected string[] _importPaths; 31 protected string _filename; 32 protected string _content; 33 protected int _index; 34 protected AutocompleteRequest request; 35 this(CustomEventTarget guiExecutor, string[] importPaths, in string filename, in string content, int index) { 36 _guiExecutor = guiExecutor; 37 _importPaths = importPaths; 38 _filename = filename; 39 _content = content; 40 _index = index; 41 } 42 @property bool cancelled() { return _cancelled; } 43 void cancel() { 44 synchronized(this) { 45 _cancelled = true; 46 } 47 } 48 void createRequest() { 49 request.sourceCode = cast(ubyte[])_content; 50 request.fileName = _filename; 51 request.cursorPosition = _index; 52 } 53 void performRequest() { 54 // override 55 } 56 void postResults() { 57 // override 58 } 59 void execute() { 60 if (_cancelled) 61 return; 62 createRequest(); 63 performRequest(); 64 synchronized(this) { 65 if (_cancelled) 66 return; 67 _guiExecutor.executeInUiThread(&postResults); 68 } 69 } 70 } 71 72 /// Async interface to DCD 73 class DCDInterface : Thread { 74 75 import dsymbol.modulecache; 76 protected ModuleCache _moduleCache = ModuleCache(new ASTAllocator); 77 protected BlockingQueue!DCDTask _queue; 78 79 this() { 80 super(&threadFunc); 81 _queue = new BlockingQueue!DCDTask(); 82 name = "DCDthread"; 83 start(); 84 } 85 86 ~this() { 87 _queue.close(); 88 join(); 89 destroy(_queue); 90 _queue = null; 91 } 92 93 protected ModuleCache * getModuleCache(in string[] importPaths) { 94 // TODO: clear cache if import paths removed or changed 95 // hold several module cache instances - make cache of caches 96 _moduleCache.addImportPaths(importPaths); 97 return &_moduleCache; 98 } 99 100 void threadFunc() { 101 Log.d("Starting DCD tasks thread"); 102 while (!_queue.closed()) { 103 DCDTask task; 104 if (!_queue.get(task)) 105 break; 106 if (task && !task.cancelled) { 107 Log.d("Execute DCD task"); 108 task.execute(); 109 Log.d("DCD task execution finished"); 110 } 111 } 112 Log.d("Exiting DCD tasks thread"); 113 } 114 115 import dsymbol.modulecache; 116 117 protected string dumpContext(string content, int pos) { 118 if (pos >= 0 && pos <= content.length) { 119 int start = pos; 120 int end = pos; 121 for (int i = 0; start > 0 && content[start - 1] != '\n' && i < 10; i++) 122 start--; 123 for (int i = 0; end < content.length - 1 && content[end] != '\n' && i < 10; i++) 124 end++; 125 return content[start .. pos] ~ "|" ~ content[pos .. end]; 126 } 127 return ""; 128 } 129 130 /// DCD doc comments task 131 class DocCommentsTask : DCDTask { 132 133 protected void delegate(DocCommentsResultSet output) _callback; 134 protected DocCommentsResultSet result; 135 136 this(CustomEventTarget guiExecutor, string[] importPaths, in string filename, in string content, int index, void delegate(DocCommentsResultSet output) callback) { 137 super(guiExecutor, importPaths, filename, content, index); 138 _callback = callback; 139 } 140 141 override void performRequest() { 142 AutocompleteResponse response = getDoc(request, *getModuleCache(_importPaths)); 143 144 result.docComments = response.docComments; 145 result.result = DCDResult.SUCCESS; 146 147 debug(DCD) Log.d("DCD doc comments:\n", result.docComments); 148 149 if (result.docComments is null) { 150 result.result = DCDResult.NO_RESULT; 151 } 152 } 153 override void postResults() { 154 _callback(result); 155 } 156 } 157 158 DCDTask getDocComments(CustomEventTarget guiExecutor, string[] importPaths, string filename, string content, int index, void delegate(DocCommentsResultSet output) callback) { 159 debug(DCD) Log.d("getDocComments: ", dumpContext(content, index)); 160 DocCommentsTask task = new DocCommentsTask(guiExecutor, importPaths, filename, content, index, callback); 161 _queue.put(task); 162 return task; 163 } 164 165 /// DCD go to definition task 166 class GoToDefinitionTask : DCDTask { 167 168 protected void delegate(FindDeclarationResultSet output) _callback; 169 protected FindDeclarationResultSet result; 170 171 this(CustomEventTarget guiExecutor, string[] importPaths, in string filename, in string content, int index, void delegate(FindDeclarationResultSet output) callback) { 172 super(guiExecutor, importPaths, filename, content, index); 173 _callback = callback; 174 } 175 176 override void performRequest() { 177 AutocompleteResponse response = findDeclaration(request, *getModuleCache(_importPaths)); 178 179 result.fileName = response.symbolFilePath; 180 result.offset = response.symbolLocation; 181 result.result = DCDResult.SUCCESS; 182 183 debug(DCD) Log.d("DCD fileName:\n", result.fileName); 184 185 if (result.fileName is null) { 186 result.result = DCDResult.NO_RESULT; 187 } 188 } 189 override void postResults() { 190 _callback(result); 191 } 192 } 193 194 DCDTask goToDefinition(CustomEventTarget guiExecutor, string[] importPaths, in string filename, in string content, int index, void delegate(FindDeclarationResultSet res) callback) { 195 196 debug(DCD) Log.d("DCD GoToDefinition task Context: ", dumpContext(content, index), " importPaths:", importPaths); 197 GoToDefinitionTask task = new GoToDefinitionTask(guiExecutor, importPaths, filename, content, index, callback); 198 _queue.put(task); 199 return task; 200 } 201 202 /// DCD get code completions task 203 class GetCompletionsTask : DCDTask { 204 205 protected void delegate(CompletionResultSet output) _callback; 206 protected CompletionResultSet result; 207 208 this(CustomEventTarget guiExecutor, string[] importPaths, in string filename, in string content, int index, void delegate(CompletionResultSet output) callback) { 209 super(guiExecutor, importPaths, filename, content, index); 210 _callback = callback; 211 } 212 213 override void performRequest() { 214 AutocompleteResponse response = complete(request, *getModuleCache(_importPaths)); 215 if(response.completions is null || response.completions.length == 0){ 216 result.result = DCDResult.NO_RESULT; 217 return; 218 } 219 220 result.result = DCDResult.SUCCESS; 221 result.output.length = response.completions.length; 222 result.completionKinds.length = response.completions.length; 223 int i=0; 224 foreach(s;response.completions) { 225 char type = 0; 226 if (i < response.completionKinds.length) 227 type = response.completionKinds[i]; 228 result.completionKinds[i] = type; 229 result.output[i++] = to!dstring(s); 230 } 231 debug(DCD) Log.d("DCD output:\n", response.completions); 232 } 233 override void postResults() { 234 _callback(result); 235 } 236 } 237 238 DCDTask getCompletions(CustomEventTarget guiExecutor, string[] importPaths, string filename, string content, int index, void delegate(CompletionResultSet output) callback) { 239 240 debug(DCD) Log.d("DCD Context: ", dumpContext(content, index)); 241 GetCompletionsTask task = new GetCompletionsTask(guiExecutor, importPaths, filename, content, index, callback); 242 _queue.put(task); 243 return task; 244 } 245 246 }