astring icon indicating copy to clipboard operation
astring copied to clipboard

Line counter should track all line ends, not just at end of segment

Open martypdx opened this issue 1 year ago • 5 comments

Motivation

Want to use lineEnd anywhere code chunk with source maps:

// normally calculated from state.indent, lineEnd, etc.
state.write(`\n   `);

Expected behavior

state.line increments for each occurance of lineEnd in code

Actual behavior

state.line only increments once when newLine is at end of code chunk

martypdx avatar Feb 19 '24 19:02 martypdx

Here's a possible algorithm:

function testTrack(code, lineEnd) {
    const state = { line: 1, column: 0 };
    // these lines replace those in writeAndMap
    if(code.length > 0) {
        const segments = code.split(lineEnd);
        state.line += (segments.length - 1) * lineEnd.length;
        state.column += segments.at(-1).length;
    }
    return state;
}

test('track new line', ({ expect }) => {
    expect(testTrack(`    `, `\n`)).toEqual({ column: 4, line: 1, });
    expect(testTrack(`\n    `, `\n`)).toEqual({ column: 4, line: 2, });
    expect(testTrack(`    \n`, `\n`)).toEqual({ column: 0, line: 2, });
    expect(testTrack(`    \n    `, `\n`)).toEqual({ column: 4, line: 2, });
    expect(testTrack(`\n\n`, `\n`)).toEqual({ column: 0, line: 3, });
    expect(testTrack(`\n\n    `, `\n`)).toEqual({ column: 4, line: 3, });
    expect(testTrack(`    \n\n    `, `\n`)).toEqual({ column: 4, line: 3, });
    expect(testTrack(`    \n\n`, `\n`)).toEqual({ column: 0, line: 3, });
    expect(testTrack(`\n    \n`, `\n`)).toEqual({ column: 0, line: 3, });
});

test('track \r\n', ({ expect }) => {
    expect(testTrack(`    `, `\r\n`)).toEqual({ column: 4, line: 1, });
    expect(testTrack(`\r\n    `, `\r\n`)).toEqual({ column: 4, line: 3, });
    expect(testTrack(`    \r\n`, `\r\n`)).toEqual({ column: 0, line: 3, });
    expect(testTrack(`    \r\n    `, `\r\n`)).toEqual({ column: 4, line: 3, });
    expect(testTrack(`\r\n\r\n`, `\r\n`)).toEqual({ column: 0, line: 5, });
    expect(testTrack(`\r\n\r\n    `, `\r\n`)).toEqual({ column: 4, line: 5, });
    expect(testTrack(`    \r\n\r\n    `, `\r\n`)).toEqual({ column: 4, line: 5, });
    expect(testTrack(`    \r\n\r\n`, `\r\n`)).toEqual({ column: 0, line: 5, });
    expect(testTrack(`\r\n    \r\n`, `\r\n`)).toEqual({ column: 0, line: 5, });
});

martypdx avatar Feb 19 '24 19:02 martypdx

Ugh, but it doesn't count in a string literal, right? Like let s = '\n';

martypdx avatar Feb 19 '24 20:02 martypdx

How about doing this instead?

state.write('\n');
state.write('   ');

davidbonnet avatar Jun 13 '24 16:06 davidbonnet

Yep, that's I ended up doing:

Process (write to state) line endings independently before the new line indentation.

To me, this limitation is fine, so feel free to close. Probably good to add a note in the docs.

martypdx avatar Jun 13 '24 17:06 martypdx

Makes sense, it should indeed be clarified in the documentation. Note that alternate approaches would considerably slow down the code generation.

davidbonnet avatar Jul 21 '24 10:07 davidbonnet