1 module nxt.line_column; 2 3 @safe: 4 5 void diagnoseGNUStyle(Args...)(scope const string tag, 6 scope const string path, 7 const LineColumn lc, 8 Args args) 9 { 10 import std.stdio : writeln; 11 debug writeln(path, 12 ":", lc.line + 1, // line offset starts at 1 13 ":", lc.column, // column counter starts at 0 14 ": ", tag, ": ", args); 15 } 16 17 @safe pure nothrow @nogc: 18 19 struct LineColumn 20 { 21 uint line; ///< 32-bit should suffice for now. 22 uint column; ///< 32-bit should suffice for now. 23 } 24 25 /** Convert `offset` at `haystack` to line and column. */ 26 LineColumn offsetLineColumn(scope const char[] haystack, 27 size_t offset) 28 { 29 // find 0-based column offset 30 size_t cursor = offset; // cursor 31 while (cursor != 0) // TODO extend to support UTF-8 32 { 33 if (cursor >= 1) 34 { 35 if (haystack[cursor - 1] == '\n' || // TODO extend to support UTF-8 36 haystack[cursor - 1] == '\r') // TODO extend to support UTF-8 37 { 38 break; 39 } 40 } 41 cursor -= 1; 42 } 43 // cursor is not at beginning of line 44 45 const column = offset-cursor; // column byte offset 46 47 // find 0-based line offset 48 size_t lineCounter = 0; 49 while (cursor != 0) 50 { 51 if (haystack[cursor - 1] == '\n' || 52 haystack[cursor - 1] == '\r') 53 { 54 cursor -= 1; 55 if (cursor != 0 && 56 (haystack[cursor - 1] == '\r')) // DOS-style line ending "\r\n" 57 { 58 cursor -= 1; 59 } 60 else // UNIX-style line ending "\n" 61 { 62 } 63 lineCounter += 1; 64 } 65 else // no line ending at cursor 66 { 67 cursor -= 1; 68 } 69 } 70 71 return typeof(return)(cast(uint)lineCounter, 72 cast(uint)column); 73 } 74 75 @safe pure unittest 76 { 77 auto x = "\nx\n y"; 78 assert(x.length == 5); 79 assert(x.offsetLineColumn(0) == LineColumn(0, 0)); 80 assert(x.offsetLineColumn(1) == LineColumn(1, 0)); 81 assert(x.offsetLineColumn(2) == LineColumn(1, 1)); 82 assert(x.offsetLineColumn(3) == LineColumn(2, 0)); 83 assert(x.offsetLineColumn(4) == LineColumn(2, 1)); 84 } 85 86 version(unittest) 87 { 88 import nxt.dbgio; 89 }