--- /dev/null
+'use strict'
+
+module.exports = factory
+
+function factory(file) {
+ var contents = indices(String(file))
+
+ return {
+ toPosition: offsetToPositionFactory(contents),
+ toOffset: positionToOffsetFactory(contents)
+ }
+}
+
+// Factory to get the line and column-based `position` for `offset` in the bound
+// indices.
+function offsetToPositionFactory(indices) {
+ return offsetToPosition
+
+ // Get the line and column-based `position` for `offset` in the bound indices.
+ function offsetToPosition(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
+ }
+ }
+ }
+
+ return {}
+ }
+}
+
+// Factory to get the `offset` for a line and column-based `position` in the
+// bound indices.
+function positionToOffsetFactory(indices) {
+ return positionToOffset
+
+ // Get the `offset` for a line and column-based `position` in the bound
+ // indices.
+ function positionToOffset(position) {
+ var line = position && position.line
+ var column = position && position.column
+
+ if (!isNaN(line) && !isNaN(column) && line - 1 in indices) {
+ return (indices[line - 2] || 0) + column - 1 || 0
+ }
+
+ return -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
+}