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