1 module util.terminal; 2 3 import std.stdio; 4 5 import source.location; 6 7 version (Windows) { 8 import core.sys.windows.windows; 9 } 10 11 void outputCaretDiagnostics(FullLocation loc, string fixHint) { 12 uint offset = loc.getStartOffset(); 13 uint start = offset; 14 15 auto source = loc.getSource(); 16 auto content = source.getContent(); 17 18 // This is unexpected end of input. 19 if (start == content.length) { 20 // Find first non white char. 21 import std.ascii; 22 while (start > 0 && isWhite(content[--start])) {} 23 } 24 25 // XXX: We could probably use infos from source manager here. 26 FindStart: while (start > 0) { 27 switch (content[start]) { 28 case '\n': 29 case '\r': 30 start++; 31 break FindStart; 32 33 default: 34 start--; 35 } 36 } 37 38 uint length = loc.length; 39 uint end = offset + loc.length; 40 41 // This is unexpected end of input. 42 if (end > content.length) { 43 end = cast(uint) content.length; 44 } 45 46 FindEnd: while (end < content.length) { 47 switch (content[end]) { 48 case '\n': 49 case '\r': 50 break FindEnd; 51 52 default: 53 end++; 54 } 55 } 56 57 auto line = content[start .. end]; 58 uint index = offset - start; 59 60 // Multi line location 61 if (index < line.length && index + length > line.length) { 62 length = cast(uint) line.length - index; 63 } 64 65 char[] underline; 66 underline.length = index + length; 67 foreach (i; 0 .. index) { 68 underline[i] = (line[i] == '\t') ? '\t' : ' '; 69 } 70 71 underline[index] = '^'; 72 foreach (i; index + 1 .. index + length) { 73 underline[i] = '~'; 74 } 75 76 assert(index == loc.getStartColumn()); 77 78 stderr.write(loc.isMixin() ? "mixin" : source.getFileName().toString(), ":", 79 loc.getStartLineNumber(), ":", index, ":"); 80 81 stderr.writeColouredText(ConsoleColour.Red, " error: "); 82 stderr.writeColouredText(ConsoleColour.White, fixHint, "\n"); 83 84 stderr.writeln(line); 85 stderr.writeColouredText(ConsoleColour.Green, underline, "\n"); 86 87 if (loc.isMixin()) { 88 outputCaretDiagnostics(source.getImportLocation(), "mixed in at"); 89 } 90 } 91 92 /* 93 * ANSI colour codes per ECMA-48 (minus 30). 94 * e.g., Yellow = 3 + 30 = 33. 95 */ 96 enum ConsoleColour { 97 Black = 0, 98 Red = 1, 99 Green = 2, 100 Yellow = 3, 101 Blue = 4, 102 Magenta = 5, 103 Cyan = 6, 104 White = 7, 105 } 106 107 void writeColouredText(T...)(File pipe, ConsoleColour colour, T t) { 108 bool coloursEnabled = true; // XXX: Fix me! 109 110 if (!coloursEnabled) { 111 pipe.write(t); 112 } 113 114 char[5] ansiSequence = [0x1b, '[', '3', '0', 'm']; 115 ansiSequence[3] = cast(char) (colour + '0'); 116 117 // XXX: use \e]11;?\a to get the color to restore 118 pipe.write(ansiSequence, t, "\x1b[0m"); 119 }