1 module dlangide.ui.outputpanel; 2 3 import dlangui.all; 4 import dlangide.workspace.workspace; 5 import dlangide.workspace.project; 6 7 import std.utf; 8 import std.regex; 9 import std.algorithm : startsWith; 10 11 /// event listener to navigate by error/warning position 12 interface CompilerLogIssueClickHandler { 13 bool onCompilerLogIssueClick(dstring filename, int line, int column); 14 } 15 16 /// Log widget with parsing of compiler output 17 class CompilerLogWidget : LogWidget { 18 19 Signal!CompilerLogIssueClickHandler compilerLogIssueClickHandler; 20 21 auto ctr = ctRegex!(r"(.+)\((\d+)\): (Error|Warning|Deprecation): (.+)"d); 22 23 /// forward to super c'tor 24 this(string ID) { 25 super(ID); 26 } 27 28 /** 29 Custom text color and style highlight (using text highlight) support. 30 31 Return null if no syntax highlight required for line. 32 */ 33 override protected CustomCharProps[] handleCustomLineHighlight(int line, dstring txt, ref CustomCharProps[] buf) { 34 auto match = matchFirst(txt, ctr); 35 uint defColor = textColor; 36 const uint filenameColor = 0x0000C0; 37 const uint errorColor = 0xFF0000; 38 const uint warningColor = 0x606000; 39 const uint deprecationColor = 0x802040; 40 uint flags = 0; 41 if(!match.empty) { 42 if (buf.length < txt.length) 43 buf.length = txt.length; 44 CustomCharProps[] colors = buf[0..txt.length]; 45 uint cl = filenameColor; 46 flags = TextFlag.Underline; 47 for (int i = 0; i < txt.length; i++) { 48 dstring rest = txt[i..$]; 49 if (rest.startsWith(" Error"d)) { 50 cl = errorColor; 51 flags = 0; 52 } else if (rest.startsWith(" Warning"d)) { 53 cl = warningColor; 54 flags = 0; 55 } else if (rest.startsWith(" Deprecation"d)) { 56 cl = deprecationColor; 57 flags = 0; 58 } 59 colors[i].color = cl; 60 colors[i].textFlags = flags; 61 } 62 return colors; 63 } else if (txt.startsWith("Building ")) { 64 CustomCharProps[] colors = new CustomCharProps[txt.length]; 65 uint cl = defColor; 66 for (int i = 0; i < txt.length; i++) { 67 dstring rest = txt[i..$]; 68 if (i == 9) { 69 cl = filenameColor; 70 flags = TextFlag.Underline; 71 } else if (rest.startsWith(" configuration"d)) { 72 cl = defColor; 73 flags = 0; 74 } 75 colors[i].color = cl; 76 colors[i].textFlags = flags; 77 } 78 return colors; 79 } 80 return null; 81 } 82 83 /// 84 override bool onMouseEvent(MouseEvent event) { 85 86 if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) { 87 super.onMouseEvent(event); 88 89 auto logLine = this.content.line(this._caretPos.line); 90 91 //src\tetris.d(49): Error: found 'return' when expecting ';' following statement 92 93 auto match = matchFirst(logLine, ctr); 94 95 if(!match.empty) { 96 if (compilerLogIssueClickHandler.assigned) { 97 import std.conv:to; 98 compilerLogIssueClickHandler(match[1], to!int(match[2]), 0); 99 } 100 } 101 102 return true; 103 } 104 105 return super.onMouseEvent(event); 106 } 107 } 108 109 /// 110 class OutputPanel : DockWindow { 111 112 Signal!CompilerLogIssueClickHandler compilerLogIssueClickHandler; 113 114 protected CompilerLogWidget _logWidget; 115 116 this(string id) { 117 super(id); 118 _caption.text = "Output"d; 119 dockAlignment = DockAlignment.Bottom; 120 } 121 122 override protected Widget createBodyWidget() { 123 _logWidget = new CompilerLogWidget("logwidget"); 124 _logWidget.readOnly = true; 125 _logWidget.layoutHeight(FILL_PARENT).layoutHeight(FILL_PARENT); 126 _logWidget.compilerLogIssueClickHandler = &onIssueClick; 127 return _logWidget; 128 } 129 130 void appendText(string category, dstring msg) { 131 _logWidget.appendText(msg); 132 } 133 134 void logLine(string category, dstring msg) { 135 appendText(category, msg ~ "\n"); 136 } 137 138 void logLine(dstring msg) { 139 logLine(null, msg); 140 } 141 142 void logLine(string category, string msg) { 143 appendText(category, toUTF32(msg ~ "\n")); 144 } 145 146 void logLine(string msg) { 147 logLine(null, msg); 148 } 149 150 void clear(string category = null) { 151 _logWidget.text = ""d; 152 } 153 154 private bool onIssueClick(dstring fn, int line, int column) 155 { 156 if (compilerLogIssueClickHandler.assigned) { 157 compilerLogIssueClickHandler(fn, line, column); 158 } 159 160 return true; 161 } 162 }