1 module dlangide.tools.d.deditorTool; 2 3 import dlangide.tools.editorTool; 4 import dlangide.tools.d.dcdinterface; 5 import dlangide.ui.dsourceedit; 6 import dlangui.widgets.editors; 7 import dlangide.ui.frame; 8 import std.stdio; 9 import std.string; 10 import std.utf; 11 import dlangui.core.logger; 12 13 import std.conv; 14 15 // TODO: async operation in background thread 16 // TODO: effective caretPositionToByteOffset/byteOffsetToCaret impl 17 18 class DEditorTool : EditorTool 19 { 20 this(IDEFrame frame) { 21 super(frame); 22 } 23 24 ~this() { 25 cancelGoToDefinition(); 26 cancelGetDocComments(); 27 cancelGetCompletions(); 28 } 29 30 DCDTask _getDocCommentsTask; 31 override void getDocComments(DSourceEdit editor, TextPosition caretPosition, void delegate(string[]) callback) { 32 cancelGetDocComments(); 33 string[] importPaths = editor.importPaths(); 34 string content = toUTF8(editor.text); 35 auto byteOffset = caretPositionToByteOffset(content, caretPosition); 36 _getDocCommentsTask = _frame.dcdInterface.getDocComments(editor.window, importPaths, editor.filename, content, byteOffset, delegate(DocCommentsResultSet output) { 37 if(output.result == DCDResult.SUCCESS) { 38 auto doc = output.docComments; 39 Log.d("Doc comments: ", doc); 40 if (doc.length) 41 callback(doc); 42 _getDocCommentsTask = null; 43 } 44 }); 45 } 46 47 override void cancelGetDocComments() { 48 if (_getDocCommentsTask) { 49 _getDocCommentsTask.cancel(); 50 _getDocCommentsTask = null; 51 } 52 } 53 54 override void cancelGoToDefinition() { 55 if (_goToDefinitionTask) { 56 _goToDefinitionTask.cancel(); 57 _goToDefinitionTask = null; 58 } 59 } 60 61 override void cancelGetCompletions() { 62 if (_getCompletionsTask) { 63 _getCompletionsTask.cancel(); 64 _getCompletionsTask = null; 65 } 66 } 67 68 DCDTask _goToDefinitionTask; 69 override void goToDefinition(DSourceEdit editor, TextPosition caretPosition) { 70 cancelGoToDefinition(); 71 string[] importPaths = editor.importPaths(); 72 string content = toUTF8(editor.text); 73 auto byteOffset = caretPositionToByteOffset(content, caretPosition); 74 75 76 _goToDefinitionTask = _frame.dcdInterface.goToDefinition(editor.window, importPaths, editor.filename, content, byteOffset, delegate(FindDeclarationResultSet output) { 77 // handle result 78 switch(output.result) { 79 //TODO: Show dialog 80 case DCDResult.FAIL: 81 case DCDResult.NO_RESULT: 82 editor.setFocus(); 83 break; 84 case DCDResult.SUCCESS: 85 auto fileName = output.fileName; 86 if(fileName.indexOf("stdin") == 0) { 87 Log.d("Declaration is in current file. Jumping to it."); 88 } else { 89 //Must open file first to get the content for finding the correct caret position. 90 if (!_frame.openSourceFile(to!string(fileName))) 91 break; 92 if (_frame.currentEditor.parent) 93 _frame.currentEditor.parent.layout(_frame.currentEditor.parent.pos); 94 content = toUTF8(_frame.currentEditor.text); 95 } 96 auto target = to!int(output.offset); 97 auto destPos = byteOffsetToCaret(content, target); 98 _frame.currentEditor.setCaretPos(destPos.line,destPos.pos, true, true); 99 _frame.currentEditor.setFocus(); 100 break; 101 default: 102 break; 103 } 104 _goToDefinitionTask = null; 105 }); 106 107 } 108 109 DCDTask _getCompletionsTask; 110 override void getCompletions(DSourceEdit editor, TextPosition caretPosition, void delegate(dstring[] completions, string[] icons) callback) { 111 string[] importPaths = editor.importPaths(); 112 113 string content = toUTF8(editor.text); 114 auto byteOffset = caretPositionToByteOffset(content, caretPosition); 115 _getCompletionsTask = _frame.dcdInterface.getCompletions(editor.window, importPaths, editor.filename, content, byteOffset, delegate(CompletionResultSet output) { 116 string[] icons; 117 dstring[] labels; 118 foreach(index, label; output.output) { 119 string iconId; 120 char ch = index < output.completionKinds.length ? output.completionKinds[index] : 0; 121 switch(ch) { 122 case 'c': // - class name 123 iconId = "symbol-class"; 124 break; 125 case 'i': // - interface name 126 iconId = "symbol-interface"; 127 break; 128 case 's': // - struct name 129 iconId = "symbol-struct"; 130 break; 131 case 'u': // - union name 132 iconId = "symbol-union"; 133 break; 134 case 'v': // - variable name 135 iconId = "symbol-var"; 136 break; 137 case 'm': // - member variable name 138 iconId = "symbol-membervar"; 139 break; 140 case 'k': // - keyword, built-in version, scope statement 141 iconId = "symbol-keyword"; 142 break; 143 case 'f': // - function or method 144 iconId = "symbol-function"; 145 break; 146 case 'g': // - enum name 147 iconId = "symbol-enum"; 148 break; 149 case 'e': // - enum member 150 iconId = "symbol-enum"; 151 break; 152 case 'P': // - package name 153 iconId = "symbol-package"; 154 break; 155 case 'M': // - module name 156 iconId = "symbol-module"; 157 break; 158 case 'a': // - array 159 iconId = "symbol-array"; 160 break; 161 case 'A': // - associative array 162 iconId = "symbol-array"; 163 break; 164 case 'l': // - alias name 165 iconId = "symbol-alias"; 166 break; 167 case 't': // - template name 168 iconId = "symbol-template"; 169 break; 170 case 'T': // - mixin template name 171 iconId = "symbol-mixintemplate"; 172 break; 173 default: 174 break; 175 } 176 if (!iconId) 177 iconId = "symbol-other"; 178 icons ~= iconId; 179 labels ~= label; 180 } 181 callback(labels, icons); 182 _getCompletionsTask = null; 183 }); 184 } 185 186 private: 187 188 } 189 190 /// convert caret position to byte offset in utf8 content 191 int caretPositionToByteOffset(string content, TextPosition caretPosition) { 192 auto line = 0; 193 auto pos = 0; 194 auto bytes = 0; 195 foreach(c; content) { 196 if(line == caretPosition.line) { 197 if(pos == caretPosition.pos) 198 break; 199 pos++; 200 } else if (line > caretPosition.line) { 201 break; 202 } 203 bytes++; 204 if(c == '\n') { 205 line++; 206 pos = 0; 207 } 208 } 209 return bytes; 210 } 211 212 /// convert byte offset in utf8 content to caret position 213 TextPosition byteOffsetToCaret(string content, int byteOffset) { 214 int bytes = 0; 215 int line = 0; 216 int pos = 0; 217 TextPosition textPos; 218 foreach(c; content) { 219 if(bytes == byteOffset) { 220 //We all good. 221 textPos.line = line; 222 textPos.pos = pos; 223 return textPos; 224 } 225 bytes++; 226 if(c == '\n') 227 { 228 line++; 229 pos = 0; 230 } 231 else { 232 pos++; 233 } 234 } 235 return textPos; 236 }