no-context / moo

Optimised tokenizer/lexer generator! 🐄 Uses /y for performance. Moo.
BSD 3-Clause "New" or "Revised" License
814 stars 65 forks source link

Error message always shows last 5 lines of buffer #183

Open drbild opened 1 year ago

drbild commented 1 year ago

Error messages seem to always show the last 5 lines of the buffer, even when the error occurs earlier in the buffer.

This line seems to always grab the last N lines from buffer, not considering the actual position of the error within the buffer.

drbild commented 1 year ago

CC @airportyh Looks like the lastNLines changes were from your PR that merged in May. Perhaps I'm misunderstanding how that is supposed to work?

tjvr commented 1 year ago

Oh dear! That's definitely not expected.

We might have to revert that PR :disappointed:

oguimbal commented 1 year ago

For those interested, here is a workaround (which could work as a fix, btw):

myLexer.formatError = function (token, message) {
    if (token == null) {
        // An undefined token indicates EOF
        var text = this.buffer.slice(this.index)
        token = {
            value: '',
            text: text,
            offset: this.index,
            lineBreaks: text.indexOf('\n') === -1 ? 0 : 1,
            line: this.line,
            col: this.col,
        }
    }

    const numLinesAround = 2
    const firstDisplayedLine = Math.max(token.line - numLinesAround, 1)
    const lastDisplayedLine = token.line + numLinesAround
    const lastLineDigits = String(lastDisplayedLine).length
    const displayedLines = nLines(
        this.buffer,
        firstDisplayedLine,
        lastDisplayedLine,
    )
        .slice(0, 5)
    const errorLines = []
    errorLines.push(message + " at line " + token.line + " col " + token.col + ":")
    errorLines.push("")
    for (let i = 0; i < displayedLines.length; i++) {
        var line = displayedLines[i]
        var lineNo = firstDisplayedLine + i
        errorLines.push(pad(String(lineNo), lastLineDigits) + "  " + line);
        if (lineNo === token.line) {
            errorLines.push(pad("", lastLineDigits + token.col + 1) + "^")
        }
    }
    return errorLines.join("\n")
}

function nLines(string, from, to) {
    const reg = new RegExp(`^([^\\n]*\\n){${from - 1}}(([^\\n]*\\n){${to - from}})`);
    const res = reg.exec(string);
    return res?.[2]?.split('\n') ?? [];
}

function pad(s, length) {
    if (s.length > length) {
        return s
    }
    return Array(length - s.length + 1).join(" ") + s
}