1 module dlangide.tools.d.dcdinterface; 2 3 import dlangui.core.logger; 4 import dlangui.core.files; 5 6 import dlangide.builders.extprocess; 7 8 import std.typecons; 9 import std.conv; 10 import std.string; 11 12 const DCD_SERVER_PORT_FOR_DLANGIDE = 9167; 13 const DCD_DEFAULT_PORT = 9166; 14 15 enum DCDResult : int { 16 DCD_NOT_RUNNING = 0, 17 SUCCESS, 18 NO_RESULT, 19 FAIL, 20 } 21 alias ResultSet = Tuple!(DCDResult, "result", dstring[], "output"); 22 23 24 //Interface to DCD 25 //TODO: Check if server is running, start server if needed etc. 26 class DCDInterface { 27 28 private int _port; 29 //ExternalProcess dcdProcess; 30 //ProtectedTextStorage stdoutTarget; 31 this(int port = DCD_SERVER_PORT_FOR_DLANGIDE) { 32 _port = port; 33 //dcdProcess = new ExternalProcess(); 34 //stdoutTarget = new ProtectedTextStorage(); 35 } 36 37 protected string dumpContext(string content, int pos) { 38 if (pos >= 0 && pos <= content.length) { 39 int start = pos; 40 int end = pos; 41 for (int i = 0; start > 0 && content[start - 1] != '\n' && i < 10; i++) 42 start--; 43 for (int i = 0; end < content.length - 1 && content[end] != '\n' && i < 10; i++) 44 end++; 45 return content[start .. pos] ~ "|" ~ content[pos .. end]; 46 } 47 return ""; 48 } 49 50 protected dstring[] invokeDcd(string[] arguments, string content, out bool success) { 51 success = false; 52 ExternalProcess dcdProcess = new ExternalProcess(); 53 54 ProtectedTextStorage stdoutTarget = new ProtectedTextStorage(); 55 56 version(Windows) { 57 string dcd_client_name = "dcd-client.exe"; 58 string dcd_client_dir = null; 59 } else { 60 string dcd_client_name = "dcd-client"; 61 string dcd_client_dir = "/usr/bin"; 62 } 63 dcdProcess.run(dcd_client_name, arguments, dcd_client_dir, stdoutTarget); 64 65 dcdProcess.write(content); 66 dcdProcess.wait(); 67 68 dstring[] output = stdoutTarget.readText.splitLines(); 69 70 if(dcdProcess.poll() == ExternalProcessState.Stopped) { 71 success = true; 72 } 73 return output; 74 } 75 76 ResultSet goToDefinition(in string[] importPaths, in string filename, in string content, int index) { 77 ResultSet result; 78 79 version(USE_LIBDPARSE) { 80 import dlangide.tools.d.dparser; 81 DParsingService.instance.addImportPaths(importPaths); 82 DParsedModule m = DParsingService.instance.findDeclaration(cast(ubyte[])content, filename, index); 83 } 84 85 debug(DCD) Log.d("DCD Context: ", dumpContext(content, index)); 86 87 string[] arguments = ["-l", "-c"]; 88 arguments ~= [to!string(index)]; 89 90 foreach(p; importPaths) { 91 arguments ~= "-I" ~ p; 92 } 93 if (_port != DCD_DEFAULT_PORT) 94 arguments ~= "-p" ~ to!string(_port); 95 96 bool success = false; 97 dstring[] output = invokeDcd(arguments, content, success); 98 99 if (success) { 100 result.result = DCDResult.SUCCESS; 101 } else { 102 result.result = DCDResult.FAIL; 103 return result; 104 } 105 106 debug(DCD) Log.d("DCD output:\n", output); 107 108 if(output.length > 0) { 109 dstring firstLine = output[0]; 110 if(firstLine.startsWith("Not Found") || firstLine.startsWith("Not found")) { 111 result.result = DCDResult.NO_RESULT; 112 return result; 113 } 114 auto split = firstLine.indexOf("\t"); 115 if(split == -1) { 116 Log.d("DCD output format error."); 117 result.result = DCDResult.FAIL; 118 return result; 119 } 120 121 result.output ~= output[0][0 .. split]; 122 result.output ~= output[0][split+1 .. $]; 123 } else { 124 result.result = DCDResult.NO_RESULT; 125 //result.result = DCDResult.FAIL; 126 } 127 128 return result; 129 } 130 131 ResultSet getCompletions(in string[] importPaths, in string filename, in string content, int index) { 132 133 debug(DCD) Log.d("DCD Context: ", dumpContext(content, index)); 134 135 ResultSet result; 136 137 string[] arguments = ["-c"]; 138 arguments ~= [to!string(index)]; 139 140 foreach(p; importPaths) { 141 arguments ~= "-I" ~ p; 142 } 143 if (_port != DCD_DEFAULT_PORT) 144 arguments ~= "-p" ~ to!string(_port); 145 146 bool success = false; 147 dstring[] output = invokeDcd(arguments, content, success); 148 149 if (success) { 150 result.result = DCDResult.SUCCESS; 151 } else { 152 result.result = DCDResult.FAIL; 153 return result; 154 } 155 debug(DCD) Log.d("DCD output:\n", output); 156 157 if (output.length == 0) { 158 result.result = DCDResult.NO_RESULT; 159 return result; 160 } 161 162 enum State : int {None = 0, Identifiers, Calltips} 163 State state = State.None; 164 foreach(dstring outputLine ; output) { 165 if(outputLine == "identifiers") { 166 state = State.Identifiers; 167 } 168 else if(outputLine == "calltips") { 169 state = State.Calltips; 170 } 171 else { 172 auto split = outputLine.indexOf("\t"); 173 if(split < 0) { 174 break; 175 } 176 if(state == State.Identifiers) { 177 result.output ~= outputLine[0 .. split]; 178 } 179 } 180 } 181 return result; 182 } 183 }