Skip to content

Commit

Permalink
Add support for CR, CR+LF
Browse files Browse the repository at this point in the history
* Refactor to improve bundle size
* Add support for carriage returns, carriage return + line feeds,
  alongside the existing support for line feeds
* Add out of bounds checking for result, so as to not return offsets,
  points that don’t exist in the document
  • Loading branch information
wooorm committed Nov 1, 2020
1 parent 25dfa46 commit 118d710
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 58 deletions.
68 changes: 23 additions & 45 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,74 +3,52 @@
module.exports = factory

function factory(file) {
var contents = indices(String(file))
var toPoint = offsetToPointFactory(contents)
var value = String(file)
var indices = []
var search = /\r?\n|\r/g

return {
toPoint: toPoint,
toPosition: toPoint,
toOffset: pointToOffsetFactory(contents)
while (search.exec(value)) {
indices.push(search.lastIndex)
}
}

// Factory to get the line and column-based `point` for `offset` in the bound
// indices.
function offsetToPointFactory(indices) {
return offsetToPoint
indices.push(value.length + 1)

return {
toPoint: offsetToPoint,
toPosition: offsetToPoint,
toOffset: pointToOffset
}

// Get the line and column-based `point` for `offset` in the bound indices.
function offsetToPoint(offset) {
var index = -1
var length = indices.length

if (offset < 0) {
return {}
}

while (++index < length) {
if (indices[index] > offset) {
return {
line: index + 1,
column: offset - (indices[index - 1] || 0) + 1,
offset: offset
if (offset > -1 && offset < indices[indices.length - 1]) {
while (++index < indices.length) {
if (indices[index] > offset) {
return {
line: index + 1,
column: offset - (indices[index - 1] || 0) + 1,
offset: offset
}
}
}
}

return {}
}
}

// Factory to get the `offset` for a line and column-based `point` in the
// bound indices.
function pointToOffsetFactory(indices) {
return pointToOffset

// Get the `offset` for a line and column-based `point` in the bound
// indices.
function pointToOffset(point) {
var line = point && point.line
var column = point && point.column
var offset

if (!isNaN(line) && !isNaN(column) && line - 1 in indices) {
return (indices[line - 2] || 0) + column - 1 || 0
offset = (indices[line - 2] || 0) + column - 1 || 0
}

return -1
return offset > -1 && offset < indices[indices.length - 1] ? offset : -1
}
}

// Get indices of line-breaks in `value`.
function indices(value) {
var result = []
var index = value.indexOf('\n')

while (index !== -1) {
result.push(index + 1)
index = value.indexOf('\n', index + 1)
}

result.push(value.length + 1)

return result
}
116 changes: 103 additions & 13 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,66 +33,156 @@ test('location()', function (t) {
'should expose `toPoint` for `file`'
)

t.test('location.toOffset(point)', function (st) {
t.test('location.toOffset(point)', function (t) {
var location = vfileLocation('foo\nbar\nbaz')

st.equals(location.toOffset({}), -1, 'should return `-1` for invalid input')
t.equals(location.toOffset({}), -1, 'should return `-1` for invalid input')

st.equals(
t.equals(
location.toOffset({line: 4, column: 2}),
-1,
'should return `-1` for out of bounds input'
)

st.equals(
t.equals(
location.toOffset({line: 2, column: 2}),
5,
'should return an offset (#1)'
)

st.equals(
t.equals(
location.toOffset({line: 1, column: 1}),
0,
'should return an offset (#2)'
)

st.equals(
t.equals(
location.toOffset({line: 3, column: 4}),
11,
'should return an offset (#3)'
)

st.end()
t.end()
})

t.test('location.toPoint(offset)', function (st) {
t.test('location.toPoint(offset)', function (t) {
var location = vfileLocation('foo\nbar\nbaz')

st.deepEquals(
t.deepEquals(
location.toPoint(-1),
{},
'should return an empty object for invalid input'
)

st.deepEquals(
t.deepEquals(
location.toPoint(12),
{},
'should return an empty object for out of bounds input'
)

st.deepEquals(
t.deepEquals(
location.toPoint(0),
{line: 1, column: 1, offset: 0},
'should return a point (#1)'
)

st.deepEquals(
t.deepEquals(
location.toPoint(11),
{line: 3, column: 4, offset: 11},
'should return a point (#2)'
)

st.end()
t.end()
})

t.test('other tests', function (t) {
var location = vfileLocation('foo')

t.deepEquals(
[location.toPoint(3), location.toPoint(4), location.toPoint(5)],
[{line: 1, column: 4, offset: 3}, {}, {}],
'should return points for offsets around an EOF w/o EOLs'
)

t.deepEquals(
[
location.toOffset({line: 1, column: 4}),
location.toOffset({line: 2, column: 1}),
location.toOffset({line: 2, column: 2})
],
[3, -1, -1],
'should return offsets for points around an EOF w/o EOLs'
)

location = vfileLocation('foo\n')

t.deepEquals(
[location.toPoint(3), location.toPoint(4), location.toPoint(5)],
[{line: 1, column: 4, offset: 3}, {line: 2, column: 1, offset: 4}, {}],
'should return points for offsets around an EOF EOL'
)

t.deepEquals(
[
location.toOffset({line: 1, column: 4}),
location.toOffset({line: 2, column: 1}),
location.toOffset({line: 2, column: 2})
],
[3, 4, -1],
'should return offsets for points around an EOF EOL'
)

location = vfileLocation('foo\rbar')

t.deepEquals(
[location.toPoint(3), location.toPoint(4), location.toPoint(5)],
[
{line: 1, column: 4, offset: 3},
{line: 2, column: 1, offset: 4},
{line: 2, column: 2, offset: 5}
],
'should return points for offsets around carriage returns'
)

t.deepEquals(
[
location.toOffset({line: 1, column: 4}),
location.toOffset({line: 2, column: 1}),
location.toOffset({line: 2, column: 2})
],
[3, 4, 5],
'should return offsets for points around carriage returns'
)

location = vfileLocation('foo\r\nbar')

t.deepEquals(
[
location.toPoint(3),
location.toPoint(4),
location.toPoint(5),
location.toPoint(6)
],
[
{line: 1, column: 4, offset: 3},
{line: 1, column: 5, offset: 4},
{line: 2, column: 1, offset: 5},
{line: 2, column: 2, offset: 6}
],
'should return points for offsets around carriage returns + line feeds'
)

t.deepEquals(
[
location.toOffset({line: 1, column: 4}),
location.toOffset({line: 2, column: 1}),
location.toOffset({line: 2, column: 2})
],
[3, 5, 6],
'should return offsets for points around carriage returns + line feeds'
)

t.end()
})

t.end()
Expand Down

0 comments on commit 118d710

Please sign in to comment.