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 content, int index) { 77 ResultSet result; 78 79 debug(DCD) Log.d("DCD Context: ", dumpContext(content, index)); 80 81 string[] arguments = ["-l", "-c"]; 82 arguments ~= [to!string(index)]; 83 84 foreach(p; importPaths) { 85 arguments ~= "-I" ~ p; 86 } 87 if (_port != DCD_DEFAULT_PORT) 88 arguments ~= "-p" ~ to!string(_port); 89 90 bool success = false; 91 dstring[] output = invokeDcd(arguments, content, success); 92 93 if (success) { 94 result.result = DCDResult.SUCCESS; 95 } else { 96 result.result = DCDResult.FAIL; 97 return result; 98 } 99 100 debug(DCD) Log.d("DCD output:\n", output); 101 102 if(output.length > 0) { 103 dstring firstLine = output[0]; 104 if(firstLine.startsWith("Not Found") || firstLine.startsWith("Not found")) { 105 result.result = DCDResult.NO_RESULT; 106 return result; 107 } 108 auto split = firstLine.indexOf("\t"); 109 if(split == -1) { 110 Log.d("DCD output format error."); 111 result.result = DCDResult.FAIL; 112 return result; 113 } 114 115 result.output ~= output[0][0 .. split]; 116 result.output ~= output[0][split+1 .. $]; 117 } else { 118 result.result = DCDResult.NO_RESULT; 119 //result.result = DCDResult.FAIL; 120 } 121 122 return result; 123 } 124 125 ResultSet getCompletions(in string[] importPaths, in string content, int index) { 126 127 debug(DCD) Log.d("DCD Context: ", dumpContext(content, index)); 128 129 ResultSet result; 130 131 string[] arguments = ["-c"]; 132 arguments ~= [to!string(index)]; 133 134 foreach(p; importPaths) { 135 arguments ~= "-I" ~ p; 136 } 137 if (_port != DCD_DEFAULT_PORT) 138 arguments ~= "-p" ~ to!string(_port); 139 140 bool success = false; 141 dstring[] output = invokeDcd(arguments, content, success); 142 143 if (success) { 144 result.result = DCDResult.SUCCESS; 145 } else { 146 result.result = DCDResult.FAIL; 147 return result; 148 } 149 debug(DCD) Log.d("DCD output:\n", output); 150 151 if (output.length == 0) { 152 result.result = DCDResult.NO_RESULT; 153 return result; 154 } 155 156 enum State : int {None = 0, Identifiers, Calltips} 157 State state = State.None; 158 foreach(dstring outputLine ; output) { 159 if(outputLine == "identifiers") { 160 state = State.Identifiers; 161 } 162 else if(outputLine == "calltips") { 163 state = State.Calltips; 164 } 165 else { 166 auto split = outputLine.indexOf("\t"); 167 if(split < 0) { 168 break; 169 } 170 if(state == State.Identifiers) { 171 result.output ~= outputLine[0 .. split]; 172 } 173 } 174 } 175 return result; 176 } 177 }