magic-string
Advanced tools
Comparing version 0.10.2 to 0.11.0-alpha
# changelog | ||
## 0.11.0-alpha | ||
* Add `move()` method (WIP – no sourcemap support yet) | ||
* Refactor internals to support `move()` | ||
## 0.10.2 | ||
@@ -4,0 +9,0 @@ |
@@ -5,17 +5,57 @@ 'use strict'; | ||
function Patch(start, end, content, original, storeName) { | ||
function Chunk(start, end, content) { | ||
this.start = start; | ||
this.end = end; | ||
this.original = content; | ||
this.content = content; | ||
this.original = original; | ||
this.storeName = storeName; | ||
this.storeName = false; | ||
this.edited = false; | ||
} | ||
Patch.prototype = { | ||
Chunk.prototype = { | ||
clone: function clone() { | ||
return new Patch(this.start, this.end, this.content, this.original, this.storeName); | ||
var chunk = new Chunk(this.start, this.end, this.original); | ||
chunk.content = this.content; | ||
chunk.storeName = this.storeName; | ||
chunk.edited = this.edited; | ||
return chunk; | ||
}, | ||
edit: function edit(content, storeName) { | ||
this.content = content; | ||
this.storeName = storeName; | ||
this.edited = true; | ||
return this; | ||
}, | ||
split: function split(index) { | ||
if (index === this.start) return this; | ||
var sliceIndex = index - this.start; | ||
var originalBefore = this.original.slice(0, sliceIndex); | ||
var originalAfter = this.original.slice(sliceIndex); | ||
this.original = originalBefore; | ||
var newChunk = new Chunk(index, this.end, originalAfter); | ||
this.end = index; | ||
if (this.edited) { | ||
if (this.content.length) throw new Error('Cannot split a chunk that has already been edited ("' + this.original + '")'); | ||
// zero-length edited chunks are a special case (overlapping replacements) | ||
newChunk.edit('', false); | ||
this.content = ''; | ||
} else { | ||
this.content = originalBefore; | ||
} | ||
return newChunk; | ||
} | ||
}; | ||
var _btoa = undefined; | ||
var _btoa = void 0; | ||
@@ -26,3 +66,3 @@ if (typeof window !== 'undefined' && typeof window.btoa === 'function') { | ||
/* global Buffer */ | ||
_btoa = function (str) { | ||
_btoa = function _btoa(str) { | ||
return new Buffer(str).toString('base64'); | ||
@@ -87,3 +127,3 @@ }; | ||
function encodeMappings(original, intro, patches, hires, sourcemapLocations, sourceIndex, offsets, names) { | ||
function encodeMappings(original, intro, chunks, hires, sourcemapLocations, sourceIndex, offsets, names) { | ||
var rawLines = []; | ||
@@ -131,41 +171,42 @@ | ||
for (var i = 0; i < patches.length; i += 1) { | ||
var patch = patches[i]; | ||
var addSegmentForPatch = patch.storeName || patch.start > originalCharIndex; | ||
for (var i = 0; i < chunks.length; i += 1) { | ||
var chunk = chunks[i]; | ||
addSegmentsUntil(patch.start); | ||
if (chunk.edited) { | ||
if (i > 0 || chunk.content.length) { | ||
rawSegments.push({ | ||
generatedCodeLine: generatedCodeLine, | ||
generatedCodeColumn: generatedCodeColumn, | ||
sourceCodeLine: sourceCodeLine, | ||
sourceCodeColumn: sourceCodeColumn, | ||
sourceCodeName: chunk.storeName ? names.indexOf(chunk.original) : -1, | ||
sourceIndex: sourceIndex | ||
}); | ||
} | ||
if (addSegmentForPatch) { | ||
rawSegments.push({ | ||
generatedCodeLine: generatedCodeLine, | ||
generatedCodeColumn: generatedCodeColumn, | ||
sourceCodeLine: sourceCodeLine, | ||
sourceCodeColumn: sourceCodeColumn, | ||
sourceCodeName: patch.storeName ? names.indexOf(patch.original) : -1, | ||
sourceIndex: sourceIndex | ||
}); | ||
} | ||
var lines = chunk.content.split('\n'); | ||
var lastLine = lines.pop(); | ||
var lines = patch.content.split('\n'); | ||
var lastLine = lines.pop(); | ||
if (lines.length) { | ||
generatedCodeLine += lines.length; | ||
rawLines[generatedCodeLine] = rawSegments = []; | ||
generatedCodeColumn = lastLine.length; | ||
} else { | ||
generatedCodeColumn += lastLine.length; | ||
} | ||
if (lines.length) { | ||
generatedCodeLine += lines.length; | ||
rawLines[generatedCodeLine] = rawSegments = []; | ||
generatedCodeColumn = lastLine.length; | ||
} else { | ||
generatedCodeColumn += lastLine.length; | ||
} | ||
lines = chunk.original.split('\n'); | ||
lastLine = lines.pop(); | ||
lines = patch.original.split('\n'); | ||
lastLine = lines.pop(); | ||
if (lines.length) { | ||
sourceCodeLine += lines.length; | ||
sourceCodeColumn = lastLine.length; | ||
if (lines.length) { | ||
sourceCodeLine += lines.length; | ||
sourceCodeColumn = lastLine.length; | ||
} else { | ||
sourceCodeColumn += lastLine.length; | ||
} | ||
} else { | ||
sourceCodeColumn += lastLine.length; | ||
addSegmentsUntil(chunk.end); | ||
} | ||
originalCharIndex = patch.end; | ||
originalCharIndex = chunk.end; | ||
} | ||
@@ -235,2 +276,4 @@ | ||
var chunk = new Chunk(0, string.length, string); | ||
Object.defineProperties(this, { | ||
@@ -240,3 +283,4 @@ original: { writable: true, value: string }, | ||
intro: { writable: true, value: '' }, | ||
patches: { writable: true, value: [] }, | ||
chunks: { writable: true, value: [chunk] }, | ||
moves: { writable: true, value: [] }, | ||
filename: { writable: true, value: options.filename }, | ||
@@ -263,4 +307,4 @@ indentExclusionRanges: { writable: true, value: options.indentExclusionRanges }, | ||
cloned.patches = this.patches.map(function (patch) { | ||
return patch.clone(); | ||
cloned.chunks = this.chunks.map(function (chunk) { | ||
return chunk.clone(); | ||
}); | ||
@@ -297,7 +341,5 @@ | ||
getMappings: function getMappings(hires, sourceIndex, offsets, names) { | ||
return encodeMappings(this.original, this.intro, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names); | ||
return encodeMappings(this.original, this.intro, this.chunks, hires, this.sourcemapLocations, sourceIndex, offsets, names); | ||
}, | ||
indent: function indent(indentStr, options) { | ||
var _this = this; | ||
var pattern = /^[^\r\n]/gm; | ||
@@ -337,43 +379,52 @@ | ||
var chunkIndex = void 0; | ||
var charIndex = 0; | ||
var patchIndex = 0; | ||
var indentUntil = function indentUntil(end) { | ||
while (charIndex < end) { | ||
for (chunkIndex = 0; chunkIndex < this.chunks.length; chunkIndex += 1) { | ||
// can't cache this.chunks.length, it may change | ||
var chunk = this.chunks[chunkIndex]; | ||
var end = chunk.end; | ||
if (chunk.edited) { | ||
if (!isExcluded[charIndex]) { | ||
var char = _this.original[charIndex]; | ||
chunk.content = chunk.content.replace(pattern, replacer); | ||
if (char === '\n') { | ||
shouldIndentNextCharacter = true; | ||
} else if (char !== '\r' && shouldIndentNextCharacter) { | ||
_this.patches.splice(patchIndex, 0, new Patch(charIndex, charIndex, indentStr, '', false)); | ||
shouldIndentNextCharacter = false; | ||
patchIndex += 1; | ||
if (chunk.content.length) { | ||
shouldIndentNextCharacter = chunk.content[chunk.content.length - 1] === '\n'; | ||
} | ||
} | ||
} else { | ||
charIndex = chunk.start; | ||
charIndex += 1; | ||
} | ||
}; | ||
while (charIndex < end) { | ||
if (!isExcluded[charIndex]) { | ||
var char = this.original[charIndex]; | ||
for (; patchIndex < this.patches.length; patchIndex += 1) { | ||
// can't cache this.patches.length, it may change | ||
var patch = this.patches[patchIndex]; | ||
if (char === '\n') { | ||
shouldIndentNextCharacter = true; | ||
} else if (char !== '\r' && shouldIndentNextCharacter) { | ||
shouldIndentNextCharacter = false; | ||
indentUntil(patch.start); | ||
var indentation = new Chunk(charIndex, charIndex, '').edit(indentStr, false); | ||
var remainder = chunk.split(charIndex); | ||
if (!isExcluded[charIndex]) { | ||
patch.content = patch.content.replace(pattern, replacer); | ||
if (charIndex === chunk.start) { | ||
this.chunks.splice(chunkIndex, 0, indentation); | ||
chunkIndex += 1; | ||
} else { | ||
this.chunks.splice(chunkIndex + 1, 0, indentation, remainder); | ||
chunkIndex += 2; | ||
} | ||
if (patch.content.length) { | ||
shouldIndentNextCharacter = patch.content[patch.content.length - 1] === '\n'; | ||
chunk = remainder; | ||
} | ||
} | ||
charIndex += 1; | ||
} | ||
} | ||
charIndex = patch.end; | ||
charIndex = chunk.end; | ||
} | ||
indentUntil(this.original.length); | ||
this.outro = this.outro.replace(pattern, replacer); | ||
@@ -384,10 +435,18 @@ | ||
insert: function insert(index, content) { | ||
if (typeof content !== 'string') { | ||
throw new TypeError('inserted content must be a string'); | ||
} | ||
if (typeof content !== 'string') throw new TypeError('inserted content must be a string'); | ||
this.patch(index, index, content); | ||
this._split(index); | ||
var next = this.chunks.findIndex(function (chunk) { | ||
return chunk.end > index; | ||
}); | ||
if (! ~next) next = this.chunks.length; | ||
var newChunk = new Chunk(index, index, '').edit(content, false); | ||
this.chunks.splice(next, 0, newChunk); | ||
return this; | ||
}, | ||
// get current location of character in original string | ||
@@ -400,2 +459,13 @@ locate: function locate(character) { | ||
}, | ||
move: function move(start, end, index) { | ||
if (index >= start && index <= end) throw new Error('Cannot move a selection inside itself'); | ||
this._split(start); | ||
this._split(end); | ||
this._split(index); | ||
this.moves.push({ start: start, end: end, index: index }); | ||
return this; | ||
}, | ||
overwrite: function overwrite(start, end, content, storeName) { | ||
@@ -406,45 +476,23 @@ if (typeof content !== 'string') { | ||
this.patch(start, end, content, storeName); | ||
return this; | ||
}, | ||
patch: function patch(start, end, content, storeName) { | ||
var original = this.original.slice(start, end); | ||
if (storeName) this.storedNames[original] = true; | ||
this._split(start); | ||
this._split(end); | ||
var i = this.patches.length; | ||
while (i--) { | ||
var previous = this.patches[i]; | ||
if (storeName) { | ||
var original = this.original.slice(start, end); | ||
this.storedNames[original] = true; | ||
} | ||
// TODO can we tidy this up? | ||
var firstIndex = this.chunks.findIndex(function (chunk) { | ||
return chunk.start === start && chunk.original.length; | ||
}); | ||
var lastIndex = this.chunks.findIndex(function (chunk) { | ||
return chunk.start === end; | ||
}); | ||
if (! ~firstIndex) firstIndex = this.chunks.length; | ||
if (! ~lastIndex) lastIndex = this.chunks.length; | ||
// if this completely covers previous patch, remove it | ||
if (start !== end && start <= previous.start && end >= previous.end) { | ||
// unless it's an insert at the start | ||
if (previous.start === previous.end && previous.start === start) break; | ||
// or it's an insert at the end | ||
if (previous.start === previous.end && previous.end === end) continue; | ||
this.patches.splice(i, 1); | ||
} | ||
var newChunk = new Chunk(start, end, this.original.slice(start, end)).edit(content, storeName); | ||
// if it overlaps, throw error | ||
else if (start < previous.end && end > previous.start) { | ||
// special case – it's okay to remove overlapping ranges | ||
if (!previous.content.length && !content.length) { | ||
previous.start = Math.min(start, previous.start); | ||
previous.end = Math.max(end, previous.end); | ||
return; | ||
} | ||
throw new Error('Cannot overwrite the same content twice: \'' + original + '\''); | ||
} | ||
// if this precedes previous patch, stop search | ||
else if (start >= previous.end) { | ||
break; | ||
} | ||
} | ||
var patch = new Patch(start, end, content, original, storeName); | ||
this.patches.splice(i + 1, 0, patch); | ||
return patch; | ||
this.chunks.splice(firstIndex, lastIndex - firstIndex, newChunk); | ||
return this; | ||
}, | ||
@@ -458,7 +506,39 @@ prepend: function prepend(content) { | ||
remove: function remove(start, end) { | ||
if (start < 0 || end > this.original.length) { | ||
throw new Error('Character is out of bounds'); | ||
while (start < 0) { | ||
start += this.original.length; | ||
}while (end < 0) { | ||
end += this.original.length; | ||
}if (start === end) return this; | ||
if (start < 0 || end > this.original.length) throw new Error('Character is out of bounds'); | ||
if (start > end) throw new Error('end must be greater than start'); | ||
var firstIndex = this.chunks.findIndex(function (chunk) { | ||
return chunk.start <= start && chunk.end > start; | ||
}); | ||
var chunk = this.chunks[firstIndex]; | ||
// if the chunk contains `start`, split | ||
if (chunk.start < start) { | ||
if (chunk.edited && chunk.content.length) throw new Error('nope'); | ||
this._split(start); | ||
firstIndex += 1; | ||
} | ||
this.patch(start, end, ''); | ||
var lastIndex = this.chunks.findIndex(function (chunk) { | ||
return chunk.start < end && chunk.end >= end; | ||
}); | ||
chunk = this.chunks[lastIndex]; | ||
// if the chunk contains `end`, split | ||
if (chunk.start < end) { | ||
if (chunk.edited && chunk.content.length) throw new Error('nope'); | ||
this._split(end); | ||
} | ||
lastIndex += 1; | ||
var newChunk = new Chunk(start, end, this.original.slice(start, end)).edit('', false); | ||
this.chunks.splice(firstIndex, lastIndex - firstIndex, newChunk); | ||
return this; | ||
@@ -481,36 +561,24 @@ }, | ||
end += this.original.length; | ||
}var firstPatchIndex = 0; | ||
var lastPatchIndex = this.patches.length; | ||
}var result = ''; | ||
while (lastPatchIndex--) { | ||
var patch = this.patches[lastPatchIndex]; | ||
if (end >= patch.start && end < patch.end) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors'); | ||
// TODO handle moves | ||
// TODO this is weird, rewrite it | ||
if (patch.start > end) continue; | ||
break; | ||
} | ||
for (var i = 0; i < this.chunks.length; i += 1) { | ||
var chunk = this.chunks[i]; | ||
for (firstPatchIndex = 0; firstPatchIndex <= lastPatchIndex; firstPatchIndex += 1) { | ||
var patch = this.patches[firstPatchIndex]; | ||
if (start > patch.start && start <= patch.end) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors'); | ||
if (chunk.end <= start) continue; | ||
if (chunk.start >= end) break; | ||
if (start <= patch.start) { | ||
break; | ||
} | ||
} | ||
if (chunk.start < start || chunk.end > end) { | ||
if (chunk.edited) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors'); | ||
var result = ''; | ||
var lastIndex = start; | ||
var sliceStart = Math.max(start - chunk.start, 0); | ||
var sliceEnd = Math.min(chunk.content.length - (chunk.end - end), chunk.content.length); | ||
for (var i = firstPatchIndex; i <= lastPatchIndex; i += 1) { | ||
var patch = this.patches[i]; | ||
result += this.original.slice(lastIndex, patch.start); | ||
result += patch.content; | ||
lastIndex = patch.end; | ||
result += chunk.content.slice(sliceStart, sliceEnd); | ||
} else { | ||
result += chunk.content; | ||
} | ||
} | ||
result += this.original.slice(lastIndex, end); | ||
return result; | ||
@@ -525,4 +593,47 @@ }, | ||
}, | ||
_sortChunks: function _sortChunks() { | ||
var chunks = this.chunks.slice(); | ||
// TODO there must be a better way than this... | ||
this.moves.forEach(function (move, i) { | ||
var firstIndex = chunks.findIndex(function (chunk) { | ||
return chunk.start === move.start; | ||
}); | ||
var lastIndex = chunks.findIndex(function (chunk, i) { | ||
return i >= firstIndex && chunk.end === move.end; | ||
}) + 1; | ||
if (!lastIndex) lastIndex = chunks.length; | ||
var insertionIndex = chunks.findIndex(function (chunk) { | ||
return chunk.start === move.index; | ||
}); | ||
if (! ~insertionIndex) insertionIndex = chunks.length; | ||
var num = lastIndex - firstIndex; | ||
if (firstIndex < insertionIndex) insertionIndex -= num; | ||
var toMove = chunks.splice(firstIndex, num); | ||
chunks.splice.apply(chunks, [insertionIndex, 0].concat(toMove)); | ||
}); | ||
return chunks; | ||
}, | ||
_split: function _split(index) { | ||
// TODO bisect | ||
for (var i = 0; i < this.chunks.length; i += 1) { | ||
var chunk = this.chunks[i]; | ||
if (chunk.start === index || chunk.end === index) return; | ||
if (chunk.start < index && chunk.end > index) { | ||
var newChunk = chunk.split(index); | ||
this.chunks.splice(i + 1, 0, newChunk); | ||
return; | ||
} | ||
} | ||
}, | ||
toString: function toString() { | ||
return this.intro + this.slice(0, this.original.length) + this.outro; | ||
return this.intro + this._sortChunks().map(function (chunk) { | ||
return chunk.content; | ||
}).join('') + this.outro; | ||
}, | ||
@@ -542,17 +653,17 @@ trimLines: function trimLines() { | ||
var charIndex = this.original.length; | ||
var i = this.patches.length; | ||
var i = this.chunks.length; | ||
while (i--) { | ||
var patch = this.patches[i]; | ||
var chunk = this.chunks[i]; | ||
if (charIndex > patch.end) { | ||
var _slice = this.original.slice(patch.end, charIndex); | ||
if (charIndex > chunk.end) { | ||
var _slice = this.original.slice(chunk.end, charIndex); | ||
var _match = rx.exec(_slice); | ||
if (_match) { | ||
this.patch(charIndex - _match[0].length, charIndex, ''); | ||
this.chunk(charIndex - _match[0].length, charIndex, ''); | ||
} | ||
if (!_match || _match[0].length < _slice.length) { | ||
// there is non-whitespace after the patch | ||
// there is non-whitespace after the chunk | ||
return this; | ||
@@ -562,6 +673,6 @@ } | ||
patch.content = patch.content.replace(rx, ''); | ||
if (patch.content) return this; | ||
chunk.content = chunk.content.replace(rx, ''); | ||
if (chunk.content) return this; | ||
charIndex = patch.start; | ||
charIndex = chunk.start; | ||
} | ||
@@ -572,3 +683,3 @@ | ||
var match = rx.exec(slice); | ||
if (match) this.patch(charIndex - match[0].length, charIndex, ''); | ||
if (match) this.chunk(charIndex - match[0].length, charIndex, ''); | ||
@@ -585,13 +696,13 @@ return this; | ||
for (var i = 0; i < this.patches.length; i += 1) { | ||
var patch = this.patches[i]; | ||
for (var i = 0; i < this.chunks.length; i += 1) { | ||
var chunk = this.chunks[i]; | ||
if (charIndex < patch.start) { | ||
var _slice2 = this.original.slice(charIndex, patch.start); | ||
if (charIndex < chunk.start) { | ||
var _slice2 = this.original.slice(charIndex, chunk.start); | ||
var _match2 = rx.exec(_slice2); | ||
if (_match2) this.patch(charIndex, charIndex + _match2[0].length, ''); | ||
if (_match2) this.chunk(charIndex, charIndex + _match2[0].length, ''); | ||
if (!_match2 || _match2[0].length < _slice2.length) { | ||
// there is non-whitespace before the patch | ||
// there is non-whitespace before the chunk | ||
return this; | ||
@@ -601,6 +712,6 @@ } | ||
patch.content = patch.content.replace(rx, ''); | ||
if (patch.content) return this; | ||
chunk.content = chunk.content.replace(rx, ''); | ||
if (chunk.content) return this; | ||
charIndex = patch.end; | ||
charIndex = chunk.end; | ||
} | ||
@@ -611,3 +722,3 @@ | ||
var match = rx.exec(slice); | ||
if (match) this.patch(charIndex, charIndex + match[0].length, ''); | ||
if (match) this.chunk(charIndex, charIndex + match[0].length, ''); | ||
@@ -708,3 +819,3 @@ return this; | ||
var prefix = i > 0 ? getSemis(source.separator) || ',' : ''; | ||
var mappings = undefined; | ||
var mappings = void 0; | ||
@@ -809,3 +920,3 @@ // we don't bother encoding sources without a filename | ||
if (!this.intro) { | ||
var source = undefined; | ||
var source = void 0; | ||
var i = 0; | ||
@@ -830,3 +941,3 @@ | ||
var source = undefined; | ||
var source = void 0; | ||
var i = this.sources.length - 1; | ||
@@ -833,0 +944,0 @@ |
import { encode } from 'vlq'; | ||
function Patch(start, end, content, original, storeName) { | ||
function Chunk(start, end, content) { | ||
this.start = start; | ||
this.end = end; | ||
this.original = content; | ||
this.content = content; | ||
this.original = original; | ||
this.storeName = storeName; | ||
this.storeName = false; | ||
this.edited = false; | ||
} | ||
Patch.prototype = { | ||
Chunk.prototype = { | ||
clone: function clone() { | ||
return new Patch(this.start, this.end, this.content, this.original, this.storeName); | ||
var chunk = new Chunk(this.start, this.end, this.original); | ||
chunk.content = this.content; | ||
chunk.storeName = this.storeName; | ||
chunk.edited = this.edited; | ||
return chunk; | ||
}, | ||
edit: function edit(content, storeName) { | ||
this.content = content; | ||
this.storeName = storeName; | ||
this.edited = true; | ||
return this; | ||
}, | ||
split: function split(index) { | ||
if (index === this.start) return this; | ||
var sliceIndex = index - this.start; | ||
var originalBefore = this.original.slice(0, sliceIndex); | ||
var originalAfter = this.original.slice(sliceIndex); | ||
this.original = originalBefore; | ||
var newChunk = new Chunk(index, this.end, originalAfter); | ||
this.end = index; | ||
if (this.edited) { | ||
if (this.content.length) throw new Error('Cannot split a chunk that has already been edited ("' + this.original + '")'); | ||
// zero-length edited chunks are a special case (overlapping replacements) | ||
newChunk.edit('', false); | ||
this.content = ''; | ||
} else { | ||
this.content = originalBefore; | ||
} | ||
return newChunk; | ||
} | ||
}; | ||
var _btoa = undefined; | ||
var _btoa = void 0; | ||
@@ -23,3 +63,3 @@ if (typeof window !== 'undefined' && typeof window.btoa === 'function') { | ||
/* global Buffer */ | ||
_btoa = function (str) { | ||
_btoa = function _btoa(str) { | ||
return new Buffer(str).toString('base64'); | ||
@@ -84,3 +124,3 @@ }; | ||
function encodeMappings(original, intro, patches, hires, sourcemapLocations, sourceIndex, offsets, names) { | ||
function encodeMappings(original, intro, chunks, hires, sourcemapLocations, sourceIndex, offsets, names) { | ||
var rawLines = []; | ||
@@ -128,41 +168,42 @@ | ||
for (var i = 0; i < patches.length; i += 1) { | ||
var patch = patches[i]; | ||
var addSegmentForPatch = patch.storeName || patch.start > originalCharIndex; | ||
for (var i = 0; i < chunks.length; i += 1) { | ||
var chunk = chunks[i]; | ||
addSegmentsUntil(patch.start); | ||
if (chunk.edited) { | ||
if (i > 0 || chunk.content.length) { | ||
rawSegments.push({ | ||
generatedCodeLine: generatedCodeLine, | ||
generatedCodeColumn: generatedCodeColumn, | ||
sourceCodeLine: sourceCodeLine, | ||
sourceCodeColumn: sourceCodeColumn, | ||
sourceCodeName: chunk.storeName ? names.indexOf(chunk.original) : -1, | ||
sourceIndex: sourceIndex | ||
}); | ||
} | ||
if (addSegmentForPatch) { | ||
rawSegments.push({ | ||
generatedCodeLine: generatedCodeLine, | ||
generatedCodeColumn: generatedCodeColumn, | ||
sourceCodeLine: sourceCodeLine, | ||
sourceCodeColumn: sourceCodeColumn, | ||
sourceCodeName: patch.storeName ? names.indexOf(patch.original) : -1, | ||
sourceIndex: sourceIndex | ||
}); | ||
} | ||
var lines = chunk.content.split('\n'); | ||
var lastLine = lines.pop(); | ||
var lines = patch.content.split('\n'); | ||
var lastLine = lines.pop(); | ||
if (lines.length) { | ||
generatedCodeLine += lines.length; | ||
rawLines[generatedCodeLine] = rawSegments = []; | ||
generatedCodeColumn = lastLine.length; | ||
} else { | ||
generatedCodeColumn += lastLine.length; | ||
} | ||
if (lines.length) { | ||
generatedCodeLine += lines.length; | ||
rawLines[generatedCodeLine] = rawSegments = []; | ||
generatedCodeColumn = lastLine.length; | ||
} else { | ||
generatedCodeColumn += lastLine.length; | ||
} | ||
lines = chunk.original.split('\n'); | ||
lastLine = lines.pop(); | ||
lines = patch.original.split('\n'); | ||
lastLine = lines.pop(); | ||
if (lines.length) { | ||
sourceCodeLine += lines.length; | ||
sourceCodeColumn = lastLine.length; | ||
if (lines.length) { | ||
sourceCodeLine += lines.length; | ||
sourceCodeColumn = lastLine.length; | ||
} else { | ||
sourceCodeColumn += lastLine.length; | ||
} | ||
} else { | ||
sourceCodeColumn += lastLine.length; | ||
addSegmentsUntil(chunk.end); | ||
} | ||
originalCharIndex = patch.end; | ||
originalCharIndex = chunk.end; | ||
} | ||
@@ -232,2 +273,4 @@ | ||
var chunk = new Chunk(0, string.length, string); | ||
Object.defineProperties(this, { | ||
@@ -237,3 +280,4 @@ original: { writable: true, value: string }, | ||
intro: { writable: true, value: '' }, | ||
patches: { writable: true, value: [] }, | ||
chunks: { writable: true, value: [chunk] }, | ||
moves: { writable: true, value: [] }, | ||
filename: { writable: true, value: options.filename }, | ||
@@ -260,4 +304,4 @@ indentExclusionRanges: { writable: true, value: options.indentExclusionRanges }, | ||
cloned.patches = this.patches.map(function (patch) { | ||
return patch.clone(); | ||
cloned.chunks = this.chunks.map(function (chunk) { | ||
return chunk.clone(); | ||
}); | ||
@@ -294,7 +338,5 @@ | ||
getMappings: function getMappings(hires, sourceIndex, offsets, names) { | ||
return encodeMappings(this.original, this.intro, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names); | ||
return encodeMappings(this.original, this.intro, this.chunks, hires, this.sourcemapLocations, sourceIndex, offsets, names); | ||
}, | ||
indent: function indent(indentStr, options) { | ||
var _this = this; | ||
var pattern = /^[^\r\n]/gm; | ||
@@ -334,43 +376,52 @@ | ||
var chunkIndex = void 0; | ||
var charIndex = 0; | ||
var patchIndex = 0; | ||
var indentUntil = function indentUntil(end) { | ||
while (charIndex < end) { | ||
for (chunkIndex = 0; chunkIndex < this.chunks.length; chunkIndex += 1) { | ||
// can't cache this.chunks.length, it may change | ||
var chunk = this.chunks[chunkIndex]; | ||
var end = chunk.end; | ||
if (chunk.edited) { | ||
if (!isExcluded[charIndex]) { | ||
var char = _this.original[charIndex]; | ||
chunk.content = chunk.content.replace(pattern, replacer); | ||
if (char === '\n') { | ||
shouldIndentNextCharacter = true; | ||
} else if (char !== '\r' && shouldIndentNextCharacter) { | ||
_this.patches.splice(patchIndex, 0, new Patch(charIndex, charIndex, indentStr, '', false)); | ||
shouldIndentNextCharacter = false; | ||
patchIndex += 1; | ||
if (chunk.content.length) { | ||
shouldIndentNextCharacter = chunk.content[chunk.content.length - 1] === '\n'; | ||
} | ||
} | ||
} else { | ||
charIndex = chunk.start; | ||
charIndex += 1; | ||
} | ||
}; | ||
while (charIndex < end) { | ||
if (!isExcluded[charIndex]) { | ||
var char = this.original[charIndex]; | ||
for (; patchIndex < this.patches.length; patchIndex += 1) { | ||
// can't cache this.patches.length, it may change | ||
var patch = this.patches[patchIndex]; | ||
if (char === '\n') { | ||
shouldIndentNextCharacter = true; | ||
} else if (char !== '\r' && shouldIndentNextCharacter) { | ||
shouldIndentNextCharacter = false; | ||
indentUntil(patch.start); | ||
var indentation = new Chunk(charIndex, charIndex, '').edit(indentStr, false); | ||
var remainder = chunk.split(charIndex); | ||
if (!isExcluded[charIndex]) { | ||
patch.content = patch.content.replace(pattern, replacer); | ||
if (charIndex === chunk.start) { | ||
this.chunks.splice(chunkIndex, 0, indentation); | ||
chunkIndex += 1; | ||
} else { | ||
this.chunks.splice(chunkIndex + 1, 0, indentation, remainder); | ||
chunkIndex += 2; | ||
} | ||
if (patch.content.length) { | ||
shouldIndentNextCharacter = patch.content[patch.content.length - 1] === '\n'; | ||
chunk = remainder; | ||
} | ||
} | ||
charIndex += 1; | ||
} | ||
} | ||
charIndex = patch.end; | ||
charIndex = chunk.end; | ||
} | ||
indentUntil(this.original.length); | ||
this.outro = this.outro.replace(pattern, replacer); | ||
@@ -381,10 +432,18 @@ | ||
insert: function insert(index, content) { | ||
if (typeof content !== 'string') { | ||
throw new TypeError('inserted content must be a string'); | ||
} | ||
if (typeof content !== 'string') throw new TypeError('inserted content must be a string'); | ||
this.patch(index, index, content); | ||
this._split(index); | ||
var next = this.chunks.findIndex(function (chunk) { | ||
return chunk.end > index; | ||
}); | ||
if (! ~next) next = this.chunks.length; | ||
var newChunk = new Chunk(index, index, '').edit(content, false); | ||
this.chunks.splice(next, 0, newChunk); | ||
return this; | ||
}, | ||
// get current location of character in original string | ||
@@ -397,2 +456,13 @@ locate: function locate(character) { | ||
}, | ||
move: function move(start, end, index) { | ||
if (index >= start && index <= end) throw new Error('Cannot move a selection inside itself'); | ||
this._split(start); | ||
this._split(end); | ||
this._split(index); | ||
this.moves.push({ start: start, end: end, index: index }); | ||
return this; | ||
}, | ||
overwrite: function overwrite(start, end, content, storeName) { | ||
@@ -403,45 +473,23 @@ if (typeof content !== 'string') { | ||
this.patch(start, end, content, storeName); | ||
return this; | ||
}, | ||
patch: function patch(start, end, content, storeName) { | ||
var original = this.original.slice(start, end); | ||
if (storeName) this.storedNames[original] = true; | ||
this._split(start); | ||
this._split(end); | ||
var i = this.patches.length; | ||
while (i--) { | ||
var previous = this.patches[i]; | ||
if (storeName) { | ||
var original = this.original.slice(start, end); | ||
this.storedNames[original] = true; | ||
} | ||
// TODO can we tidy this up? | ||
var firstIndex = this.chunks.findIndex(function (chunk) { | ||
return chunk.start === start && chunk.original.length; | ||
}); | ||
var lastIndex = this.chunks.findIndex(function (chunk) { | ||
return chunk.start === end; | ||
}); | ||
if (! ~firstIndex) firstIndex = this.chunks.length; | ||
if (! ~lastIndex) lastIndex = this.chunks.length; | ||
// if this completely covers previous patch, remove it | ||
if (start !== end && start <= previous.start && end >= previous.end) { | ||
// unless it's an insert at the start | ||
if (previous.start === previous.end && previous.start === start) break; | ||
// or it's an insert at the end | ||
if (previous.start === previous.end && previous.end === end) continue; | ||
this.patches.splice(i, 1); | ||
} | ||
var newChunk = new Chunk(start, end, this.original.slice(start, end)).edit(content, storeName); | ||
// if it overlaps, throw error | ||
else if (start < previous.end && end > previous.start) { | ||
// special case – it's okay to remove overlapping ranges | ||
if (!previous.content.length && !content.length) { | ||
previous.start = Math.min(start, previous.start); | ||
previous.end = Math.max(end, previous.end); | ||
return; | ||
} | ||
throw new Error('Cannot overwrite the same content twice: \'' + original + '\''); | ||
} | ||
// if this precedes previous patch, stop search | ||
else if (start >= previous.end) { | ||
break; | ||
} | ||
} | ||
var patch = new Patch(start, end, content, original, storeName); | ||
this.patches.splice(i + 1, 0, patch); | ||
return patch; | ||
this.chunks.splice(firstIndex, lastIndex - firstIndex, newChunk); | ||
return this; | ||
}, | ||
@@ -455,7 +503,39 @@ prepend: function prepend(content) { | ||
remove: function remove(start, end) { | ||
if (start < 0 || end > this.original.length) { | ||
throw new Error('Character is out of bounds'); | ||
while (start < 0) { | ||
start += this.original.length; | ||
}while (end < 0) { | ||
end += this.original.length; | ||
}if (start === end) return this; | ||
if (start < 0 || end > this.original.length) throw new Error('Character is out of bounds'); | ||
if (start > end) throw new Error('end must be greater than start'); | ||
var firstIndex = this.chunks.findIndex(function (chunk) { | ||
return chunk.start <= start && chunk.end > start; | ||
}); | ||
var chunk = this.chunks[firstIndex]; | ||
// if the chunk contains `start`, split | ||
if (chunk.start < start) { | ||
if (chunk.edited && chunk.content.length) throw new Error('nope'); | ||
this._split(start); | ||
firstIndex += 1; | ||
} | ||
this.patch(start, end, ''); | ||
var lastIndex = this.chunks.findIndex(function (chunk) { | ||
return chunk.start < end && chunk.end >= end; | ||
}); | ||
chunk = this.chunks[lastIndex]; | ||
// if the chunk contains `end`, split | ||
if (chunk.start < end) { | ||
if (chunk.edited && chunk.content.length) throw new Error('nope'); | ||
this._split(end); | ||
} | ||
lastIndex += 1; | ||
var newChunk = new Chunk(start, end, this.original.slice(start, end)).edit('', false); | ||
this.chunks.splice(firstIndex, lastIndex - firstIndex, newChunk); | ||
return this; | ||
@@ -478,36 +558,24 @@ }, | ||
end += this.original.length; | ||
}var firstPatchIndex = 0; | ||
var lastPatchIndex = this.patches.length; | ||
}var result = ''; | ||
while (lastPatchIndex--) { | ||
var patch = this.patches[lastPatchIndex]; | ||
if (end >= patch.start && end < patch.end) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors'); | ||
// TODO handle moves | ||
// TODO this is weird, rewrite it | ||
if (patch.start > end) continue; | ||
break; | ||
} | ||
for (var i = 0; i < this.chunks.length; i += 1) { | ||
var chunk = this.chunks[i]; | ||
for (firstPatchIndex = 0; firstPatchIndex <= lastPatchIndex; firstPatchIndex += 1) { | ||
var patch = this.patches[firstPatchIndex]; | ||
if (start > patch.start && start <= patch.end) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors'); | ||
if (chunk.end <= start) continue; | ||
if (chunk.start >= end) break; | ||
if (start <= patch.start) { | ||
break; | ||
} | ||
} | ||
if (chunk.start < start || chunk.end > end) { | ||
if (chunk.edited) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors'); | ||
var result = ''; | ||
var lastIndex = start; | ||
var sliceStart = Math.max(start - chunk.start, 0); | ||
var sliceEnd = Math.min(chunk.content.length - (chunk.end - end), chunk.content.length); | ||
for (var i = firstPatchIndex; i <= lastPatchIndex; i += 1) { | ||
var patch = this.patches[i]; | ||
result += this.original.slice(lastIndex, patch.start); | ||
result += patch.content; | ||
lastIndex = patch.end; | ||
result += chunk.content.slice(sliceStart, sliceEnd); | ||
} else { | ||
result += chunk.content; | ||
} | ||
} | ||
result += this.original.slice(lastIndex, end); | ||
return result; | ||
@@ -522,4 +590,47 @@ }, | ||
}, | ||
_sortChunks: function _sortChunks() { | ||
var chunks = this.chunks.slice(); | ||
// TODO there must be a better way than this... | ||
this.moves.forEach(function (move, i) { | ||
var firstIndex = chunks.findIndex(function (chunk) { | ||
return chunk.start === move.start; | ||
}); | ||
var lastIndex = chunks.findIndex(function (chunk, i) { | ||
return i >= firstIndex && chunk.end === move.end; | ||
}) + 1; | ||
if (!lastIndex) lastIndex = chunks.length; | ||
var insertionIndex = chunks.findIndex(function (chunk) { | ||
return chunk.start === move.index; | ||
}); | ||
if (! ~insertionIndex) insertionIndex = chunks.length; | ||
var num = lastIndex - firstIndex; | ||
if (firstIndex < insertionIndex) insertionIndex -= num; | ||
var toMove = chunks.splice(firstIndex, num); | ||
chunks.splice.apply(chunks, [insertionIndex, 0].concat(toMove)); | ||
}); | ||
return chunks; | ||
}, | ||
_split: function _split(index) { | ||
// TODO bisect | ||
for (var i = 0; i < this.chunks.length; i += 1) { | ||
var chunk = this.chunks[i]; | ||
if (chunk.start === index || chunk.end === index) return; | ||
if (chunk.start < index && chunk.end > index) { | ||
var newChunk = chunk.split(index); | ||
this.chunks.splice(i + 1, 0, newChunk); | ||
return; | ||
} | ||
} | ||
}, | ||
toString: function toString() { | ||
return this.intro + this.slice(0, this.original.length) + this.outro; | ||
return this.intro + this._sortChunks().map(function (chunk) { | ||
return chunk.content; | ||
}).join('') + this.outro; | ||
}, | ||
@@ -539,17 +650,17 @@ trimLines: function trimLines() { | ||
var charIndex = this.original.length; | ||
var i = this.patches.length; | ||
var i = this.chunks.length; | ||
while (i--) { | ||
var patch = this.patches[i]; | ||
var chunk = this.chunks[i]; | ||
if (charIndex > patch.end) { | ||
var _slice = this.original.slice(patch.end, charIndex); | ||
if (charIndex > chunk.end) { | ||
var _slice = this.original.slice(chunk.end, charIndex); | ||
var _match = rx.exec(_slice); | ||
if (_match) { | ||
this.patch(charIndex - _match[0].length, charIndex, ''); | ||
this.chunk(charIndex - _match[0].length, charIndex, ''); | ||
} | ||
if (!_match || _match[0].length < _slice.length) { | ||
// there is non-whitespace after the patch | ||
// there is non-whitespace after the chunk | ||
return this; | ||
@@ -559,6 +670,6 @@ } | ||
patch.content = patch.content.replace(rx, ''); | ||
if (patch.content) return this; | ||
chunk.content = chunk.content.replace(rx, ''); | ||
if (chunk.content) return this; | ||
charIndex = patch.start; | ||
charIndex = chunk.start; | ||
} | ||
@@ -569,3 +680,3 @@ | ||
var match = rx.exec(slice); | ||
if (match) this.patch(charIndex - match[0].length, charIndex, ''); | ||
if (match) this.chunk(charIndex - match[0].length, charIndex, ''); | ||
@@ -582,13 +693,13 @@ return this; | ||
for (var i = 0; i < this.patches.length; i += 1) { | ||
var patch = this.patches[i]; | ||
for (var i = 0; i < this.chunks.length; i += 1) { | ||
var chunk = this.chunks[i]; | ||
if (charIndex < patch.start) { | ||
var _slice2 = this.original.slice(charIndex, patch.start); | ||
if (charIndex < chunk.start) { | ||
var _slice2 = this.original.slice(charIndex, chunk.start); | ||
var _match2 = rx.exec(_slice2); | ||
if (_match2) this.patch(charIndex, charIndex + _match2[0].length, ''); | ||
if (_match2) this.chunk(charIndex, charIndex + _match2[0].length, ''); | ||
if (!_match2 || _match2[0].length < _slice2.length) { | ||
// there is non-whitespace before the patch | ||
// there is non-whitespace before the chunk | ||
return this; | ||
@@ -598,6 +709,6 @@ } | ||
patch.content = patch.content.replace(rx, ''); | ||
if (patch.content) return this; | ||
chunk.content = chunk.content.replace(rx, ''); | ||
if (chunk.content) return this; | ||
charIndex = patch.end; | ||
charIndex = chunk.end; | ||
} | ||
@@ -608,3 +719,3 @@ | ||
var match = rx.exec(slice); | ||
if (match) this.patch(charIndex, charIndex + match[0].length, ''); | ||
if (match) this.chunk(charIndex, charIndex + match[0].length, ''); | ||
@@ -705,3 +816,3 @@ return this; | ||
var prefix = i > 0 ? getSemis(source.separator) || ',' : ''; | ||
var mappings = undefined; | ||
var mappings = void 0; | ||
@@ -806,3 +917,3 @@ // we don't bother encoding sources without a filename | ||
if (!this.intro) { | ||
var source = undefined; | ||
var source = void 0; | ||
var i = 0; | ||
@@ -827,3 +938,3 @@ | ||
var source = undefined; | ||
var source = void 0; | ||
var i = this.sources.length - 1; | ||
@@ -830,0 +941,0 @@ |
@@ -7,17 +7,57 @@ (function (global, factory) { | ||
function Patch(start, end, content, original, storeName) { | ||
function Chunk(start, end, content) { | ||
this.start = start; | ||
this.end = end; | ||
this.original = content; | ||
this.content = content; | ||
this.original = original; | ||
this.storeName = storeName; | ||
this.storeName = false; | ||
this.edited = false; | ||
} | ||
Patch.prototype = { | ||
Chunk.prototype = { | ||
clone: function clone() { | ||
return new Patch(this.start, this.end, this.content, this.original, this.storeName); | ||
var chunk = new Chunk(this.start, this.end, this.original); | ||
chunk.content = this.content; | ||
chunk.storeName = this.storeName; | ||
chunk.edited = this.edited; | ||
return chunk; | ||
}, | ||
edit: function edit(content, storeName) { | ||
this.content = content; | ||
this.storeName = storeName; | ||
this.edited = true; | ||
return this; | ||
}, | ||
split: function split(index) { | ||
if (index === this.start) return this; | ||
var sliceIndex = index - this.start; | ||
var originalBefore = this.original.slice(0, sliceIndex); | ||
var originalAfter = this.original.slice(sliceIndex); | ||
this.original = originalBefore; | ||
var newChunk = new Chunk(index, this.end, originalAfter); | ||
this.end = index; | ||
if (this.edited) { | ||
if (this.content.length) throw new Error('Cannot split a chunk that has already been edited ("' + this.original + '")'); | ||
// zero-length edited chunks are a special case (overlapping replacements) | ||
newChunk.edit('', false); | ||
this.content = ''; | ||
} else { | ||
this.content = originalBefore; | ||
} | ||
return newChunk; | ||
} | ||
}; | ||
var _btoa = undefined; | ||
var _btoa = void 0; | ||
@@ -28,3 +68,3 @@ if (typeof window !== 'undefined' && typeof window.btoa === 'function') { | ||
/* global Buffer */ | ||
_btoa = function (str) { | ||
_btoa = function _btoa(str) { | ||
return new Buffer(str).toString('base64'); | ||
@@ -135,3 +175,3 @@ }; | ||
function encodeMappings(original, intro, patches, hires, sourcemapLocations, sourceIndex, offsets, names) { | ||
function encodeMappings(original, intro, chunks, hires, sourcemapLocations, sourceIndex, offsets, names) { | ||
var rawLines = []; | ||
@@ -179,41 +219,42 @@ | ||
for (var i = 0; i < patches.length; i += 1) { | ||
var patch = patches[i]; | ||
var addSegmentForPatch = patch.storeName || patch.start > originalCharIndex; | ||
for (var i = 0; i < chunks.length; i += 1) { | ||
var chunk = chunks[i]; | ||
addSegmentsUntil(patch.start); | ||
if (chunk.edited) { | ||
if (i > 0 || chunk.content.length) { | ||
rawSegments.push({ | ||
generatedCodeLine: generatedCodeLine, | ||
generatedCodeColumn: generatedCodeColumn, | ||
sourceCodeLine: sourceCodeLine, | ||
sourceCodeColumn: sourceCodeColumn, | ||
sourceCodeName: chunk.storeName ? names.indexOf(chunk.original) : -1, | ||
sourceIndex: sourceIndex | ||
}); | ||
} | ||
if (addSegmentForPatch) { | ||
rawSegments.push({ | ||
generatedCodeLine: generatedCodeLine, | ||
generatedCodeColumn: generatedCodeColumn, | ||
sourceCodeLine: sourceCodeLine, | ||
sourceCodeColumn: sourceCodeColumn, | ||
sourceCodeName: patch.storeName ? names.indexOf(patch.original) : -1, | ||
sourceIndex: sourceIndex | ||
}); | ||
} | ||
var lines = chunk.content.split('\n'); | ||
var lastLine = lines.pop(); | ||
var lines = patch.content.split('\n'); | ||
var lastLine = lines.pop(); | ||
if (lines.length) { | ||
generatedCodeLine += lines.length; | ||
rawLines[generatedCodeLine] = rawSegments = []; | ||
generatedCodeColumn = lastLine.length; | ||
} else { | ||
generatedCodeColumn += lastLine.length; | ||
} | ||
if (lines.length) { | ||
generatedCodeLine += lines.length; | ||
rawLines[generatedCodeLine] = rawSegments = []; | ||
generatedCodeColumn = lastLine.length; | ||
} else { | ||
generatedCodeColumn += lastLine.length; | ||
} | ||
lines = chunk.original.split('\n'); | ||
lastLine = lines.pop(); | ||
lines = patch.original.split('\n'); | ||
lastLine = lines.pop(); | ||
if (lines.length) { | ||
sourceCodeLine += lines.length; | ||
sourceCodeColumn = lastLine.length; | ||
if (lines.length) { | ||
sourceCodeLine += lines.length; | ||
sourceCodeColumn = lastLine.length; | ||
} else { | ||
sourceCodeColumn += lastLine.length; | ||
} | ||
} else { | ||
sourceCodeColumn += lastLine.length; | ||
addSegmentsUntil(chunk.end); | ||
} | ||
originalCharIndex = patch.end; | ||
originalCharIndex = chunk.end; | ||
} | ||
@@ -283,2 +324,4 @@ | ||
var chunk = new Chunk(0, string.length, string); | ||
Object.defineProperties(this, { | ||
@@ -288,3 +331,4 @@ original: { writable: true, value: string }, | ||
intro: { writable: true, value: '' }, | ||
patches: { writable: true, value: [] }, | ||
chunks: { writable: true, value: [chunk] }, | ||
moves: { writable: true, value: [] }, | ||
filename: { writable: true, value: options.filename }, | ||
@@ -311,4 +355,4 @@ indentExclusionRanges: { writable: true, value: options.indentExclusionRanges }, | ||
cloned.patches = this.patches.map(function (patch) { | ||
return patch.clone(); | ||
cloned.chunks = this.chunks.map(function (chunk) { | ||
return chunk.clone(); | ||
}); | ||
@@ -345,7 +389,5 @@ | ||
getMappings: function getMappings(hires, sourceIndex, offsets, names) { | ||
return encodeMappings(this.original, this.intro, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names); | ||
return encodeMappings(this.original, this.intro, this.chunks, hires, this.sourcemapLocations, sourceIndex, offsets, names); | ||
}, | ||
indent: function indent(indentStr, options) { | ||
var _this = this; | ||
var pattern = /^[^\r\n]/gm; | ||
@@ -385,43 +427,52 @@ | ||
var chunkIndex = void 0; | ||
var charIndex = 0; | ||
var patchIndex = 0; | ||
var indentUntil = function indentUntil(end) { | ||
while (charIndex < end) { | ||
for (chunkIndex = 0; chunkIndex < this.chunks.length; chunkIndex += 1) { | ||
// can't cache this.chunks.length, it may change | ||
var chunk = this.chunks[chunkIndex]; | ||
var end = chunk.end; | ||
if (chunk.edited) { | ||
if (!isExcluded[charIndex]) { | ||
var char = _this.original[charIndex]; | ||
chunk.content = chunk.content.replace(pattern, replacer); | ||
if (char === '\n') { | ||
shouldIndentNextCharacter = true; | ||
} else if (char !== '\r' && shouldIndentNextCharacter) { | ||
_this.patches.splice(patchIndex, 0, new Patch(charIndex, charIndex, indentStr, '', false)); | ||
shouldIndentNextCharacter = false; | ||
patchIndex += 1; | ||
if (chunk.content.length) { | ||
shouldIndentNextCharacter = chunk.content[chunk.content.length - 1] === '\n'; | ||
} | ||
} | ||
} else { | ||
charIndex = chunk.start; | ||
charIndex += 1; | ||
} | ||
}; | ||
while (charIndex < end) { | ||
if (!isExcluded[charIndex]) { | ||
var char = this.original[charIndex]; | ||
for (; patchIndex < this.patches.length; patchIndex += 1) { | ||
// can't cache this.patches.length, it may change | ||
var patch = this.patches[patchIndex]; | ||
if (char === '\n') { | ||
shouldIndentNextCharacter = true; | ||
} else if (char !== '\r' && shouldIndentNextCharacter) { | ||
shouldIndentNextCharacter = false; | ||
indentUntil(patch.start); | ||
var indentation = new Chunk(charIndex, charIndex, '').edit(indentStr, false); | ||
var remainder = chunk.split(charIndex); | ||
if (!isExcluded[charIndex]) { | ||
patch.content = patch.content.replace(pattern, replacer); | ||
if (charIndex === chunk.start) { | ||
this.chunks.splice(chunkIndex, 0, indentation); | ||
chunkIndex += 1; | ||
} else { | ||
this.chunks.splice(chunkIndex + 1, 0, indentation, remainder); | ||
chunkIndex += 2; | ||
} | ||
if (patch.content.length) { | ||
shouldIndentNextCharacter = patch.content[patch.content.length - 1] === '\n'; | ||
chunk = remainder; | ||
} | ||
} | ||
charIndex += 1; | ||
} | ||
} | ||
charIndex = patch.end; | ||
charIndex = chunk.end; | ||
} | ||
indentUntil(this.original.length); | ||
this.outro = this.outro.replace(pattern, replacer); | ||
@@ -432,10 +483,18 @@ | ||
insert: function insert(index, content) { | ||
if (typeof content !== 'string') { | ||
throw new TypeError('inserted content must be a string'); | ||
} | ||
if (typeof content !== 'string') throw new TypeError('inserted content must be a string'); | ||
this.patch(index, index, content); | ||
this._split(index); | ||
var next = this.chunks.findIndex(function (chunk) { | ||
return chunk.end > index; | ||
}); | ||
if (! ~next) next = this.chunks.length; | ||
var newChunk = new Chunk(index, index, '').edit(content, false); | ||
this.chunks.splice(next, 0, newChunk); | ||
return this; | ||
}, | ||
// get current location of character in original string | ||
@@ -448,2 +507,13 @@ locate: function locate(character) { | ||
}, | ||
move: function move(start, end, index) { | ||
if (index >= start && index <= end) throw new Error('Cannot move a selection inside itself'); | ||
this._split(start); | ||
this._split(end); | ||
this._split(index); | ||
this.moves.push({ start: start, end: end, index: index }); | ||
return this; | ||
}, | ||
overwrite: function overwrite(start, end, content, storeName) { | ||
@@ -454,45 +524,23 @@ if (typeof content !== 'string') { | ||
this.patch(start, end, content, storeName); | ||
return this; | ||
}, | ||
patch: function patch(start, end, content, storeName) { | ||
var original = this.original.slice(start, end); | ||
if (storeName) this.storedNames[original] = true; | ||
this._split(start); | ||
this._split(end); | ||
var i = this.patches.length; | ||
while (i--) { | ||
var previous = this.patches[i]; | ||
if (storeName) { | ||
var original = this.original.slice(start, end); | ||
this.storedNames[original] = true; | ||
} | ||
// TODO can we tidy this up? | ||
var firstIndex = this.chunks.findIndex(function (chunk) { | ||
return chunk.start === start && chunk.original.length; | ||
}); | ||
var lastIndex = this.chunks.findIndex(function (chunk) { | ||
return chunk.start === end; | ||
}); | ||
if (! ~firstIndex) firstIndex = this.chunks.length; | ||
if (! ~lastIndex) lastIndex = this.chunks.length; | ||
// if this completely covers previous patch, remove it | ||
if (start !== end && start <= previous.start && end >= previous.end) { | ||
// unless it's an insert at the start | ||
if (previous.start === previous.end && previous.start === start) break; | ||
// or it's an insert at the end | ||
if (previous.start === previous.end && previous.end === end) continue; | ||
this.patches.splice(i, 1); | ||
} | ||
var newChunk = new Chunk(start, end, this.original.slice(start, end)).edit(content, storeName); | ||
// if it overlaps, throw error | ||
else if (start < previous.end && end > previous.start) { | ||
// special case – it's okay to remove overlapping ranges | ||
if (!previous.content.length && !content.length) { | ||
previous.start = Math.min(start, previous.start); | ||
previous.end = Math.max(end, previous.end); | ||
return; | ||
} | ||
throw new Error('Cannot overwrite the same content twice: \'' + original + '\''); | ||
} | ||
// if this precedes previous patch, stop search | ||
else if (start >= previous.end) { | ||
break; | ||
} | ||
} | ||
var patch = new Patch(start, end, content, original, storeName); | ||
this.patches.splice(i + 1, 0, patch); | ||
return patch; | ||
this.chunks.splice(firstIndex, lastIndex - firstIndex, newChunk); | ||
return this; | ||
}, | ||
@@ -506,7 +554,39 @@ prepend: function prepend(content) { | ||
remove: function remove(start, end) { | ||
if (start < 0 || end > this.original.length) { | ||
throw new Error('Character is out of bounds'); | ||
while (start < 0) { | ||
start += this.original.length; | ||
}while (end < 0) { | ||
end += this.original.length; | ||
}if (start === end) return this; | ||
if (start < 0 || end > this.original.length) throw new Error('Character is out of bounds'); | ||
if (start > end) throw new Error('end must be greater than start'); | ||
var firstIndex = this.chunks.findIndex(function (chunk) { | ||
return chunk.start <= start && chunk.end > start; | ||
}); | ||
var chunk = this.chunks[firstIndex]; | ||
// if the chunk contains `start`, split | ||
if (chunk.start < start) { | ||
if (chunk.edited && chunk.content.length) throw new Error('nope'); | ||
this._split(start); | ||
firstIndex += 1; | ||
} | ||
this.patch(start, end, ''); | ||
var lastIndex = this.chunks.findIndex(function (chunk) { | ||
return chunk.start < end && chunk.end >= end; | ||
}); | ||
chunk = this.chunks[lastIndex]; | ||
// if the chunk contains `end`, split | ||
if (chunk.start < end) { | ||
if (chunk.edited && chunk.content.length) throw new Error('nope'); | ||
this._split(end); | ||
} | ||
lastIndex += 1; | ||
var newChunk = new Chunk(start, end, this.original.slice(start, end)).edit('', false); | ||
this.chunks.splice(firstIndex, lastIndex - firstIndex, newChunk); | ||
return this; | ||
@@ -529,36 +609,24 @@ }, | ||
end += this.original.length; | ||
}var firstPatchIndex = 0; | ||
var lastPatchIndex = this.patches.length; | ||
}var result = ''; | ||
while (lastPatchIndex--) { | ||
var patch = this.patches[lastPatchIndex]; | ||
if (end >= patch.start && end < patch.end) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors'); | ||
// TODO handle moves | ||
// TODO this is weird, rewrite it | ||
if (patch.start > end) continue; | ||
break; | ||
} | ||
for (var i = 0; i < this.chunks.length; i += 1) { | ||
var chunk = this.chunks[i]; | ||
for (firstPatchIndex = 0; firstPatchIndex <= lastPatchIndex; firstPatchIndex += 1) { | ||
var patch = this.patches[firstPatchIndex]; | ||
if (start > patch.start && start <= patch.end) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors'); | ||
if (chunk.end <= start) continue; | ||
if (chunk.start >= end) break; | ||
if (start <= patch.start) { | ||
break; | ||
} | ||
} | ||
if (chunk.start < start || chunk.end > end) { | ||
if (chunk.edited) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors'); | ||
var result = ''; | ||
var lastIndex = start; | ||
var sliceStart = Math.max(start - chunk.start, 0); | ||
var sliceEnd = Math.min(chunk.content.length - (chunk.end - end), chunk.content.length); | ||
for (var i = firstPatchIndex; i <= lastPatchIndex; i += 1) { | ||
var patch = this.patches[i]; | ||
result += this.original.slice(lastIndex, patch.start); | ||
result += patch.content; | ||
lastIndex = patch.end; | ||
result += chunk.content.slice(sliceStart, sliceEnd); | ||
} else { | ||
result += chunk.content; | ||
} | ||
} | ||
result += this.original.slice(lastIndex, end); | ||
return result; | ||
@@ -573,4 +641,47 @@ }, | ||
}, | ||
_sortChunks: function _sortChunks() { | ||
var chunks = this.chunks.slice(); | ||
// TODO there must be a better way than this... | ||
this.moves.forEach(function (move, i) { | ||
var firstIndex = chunks.findIndex(function (chunk) { | ||
return chunk.start === move.start; | ||
}); | ||
var lastIndex = chunks.findIndex(function (chunk, i) { | ||
return i >= firstIndex && chunk.end === move.end; | ||
}) + 1; | ||
if (!lastIndex) lastIndex = chunks.length; | ||
var insertionIndex = chunks.findIndex(function (chunk) { | ||
return chunk.start === move.index; | ||
}); | ||
if (! ~insertionIndex) insertionIndex = chunks.length; | ||
var num = lastIndex - firstIndex; | ||
if (firstIndex < insertionIndex) insertionIndex -= num; | ||
var toMove = chunks.splice(firstIndex, num); | ||
chunks.splice.apply(chunks, [insertionIndex, 0].concat(toMove)); | ||
}); | ||
return chunks; | ||
}, | ||
_split: function _split(index) { | ||
// TODO bisect | ||
for (var i = 0; i < this.chunks.length; i += 1) { | ||
var chunk = this.chunks[i]; | ||
if (chunk.start === index || chunk.end === index) return; | ||
if (chunk.start < index && chunk.end > index) { | ||
var newChunk = chunk.split(index); | ||
this.chunks.splice(i + 1, 0, newChunk); | ||
return; | ||
} | ||
} | ||
}, | ||
toString: function toString() { | ||
return this.intro + this.slice(0, this.original.length) + this.outro; | ||
return this.intro + this._sortChunks().map(function (chunk) { | ||
return chunk.content; | ||
}).join('') + this.outro; | ||
}, | ||
@@ -590,17 +701,17 @@ trimLines: function trimLines() { | ||
var charIndex = this.original.length; | ||
var i = this.patches.length; | ||
var i = this.chunks.length; | ||
while (i--) { | ||
var patch = this.patches[i]; | ||
var chunk = this.chunks[i]; | ||
if (charIndex > patch.end) { | ||
var _slice = this.original.slice(patch.end, charIndex); | ||
if (charIndex > chunk.end) { | ||
var _slice = this.original.slice(chunk.end, charIndex); | ||
var _match = rx.exec(_slice); | ||
if (_match) { | ||
this.patch(charIndex - _match[0].length, charIndex, ''); | ||
this.chunk(charIndex - _match[0].length, charIndex, ''); | ||
} | ||
if (!_match || _match[0].length < _slice.length) { | ||
// there is non-whitespace after the patch | ||
// there is non-whitespace after the chunk | ||
return this; | ||
@@ -610,6 +721,6 @@ } | ||
patch.content = patch.content.replace(rx, ''); | ||
if (patch.content) return this; | ||
chunk.content = chunk.content.replace(rx, ''); | ||
if (chunk.content) return this; | ||
charIndex = patch.start; | ||
charIndex = chunk.start; | ||
} | ||
@@ -620,3 +731,3 @@ | ||
var match = rx.exec(slice); | ||
if (match) this.patch(charIndex - match[0].length, charIndex, ''); | ||
if (match) this.chunk(charIndex - match[0].length, charIndex, ''); | ||
@@ -633,13 +744,13 @@ return this; | ||
for (var i = 0; i < this.patches.length; i += 1) { | ||
var patch = this.patches[i]; | ||
for (var i = 0; i < this.chunks.length; i += 1) { | ||
var chunk = this.chunks[i]; | ||
if (charIndex < patch.start) { | ||
var _slice2 = this.original.slice(charIndex, patch.start); | ||
if (charIndex < chunk.start) { | ||
var _slice2 = this.original.slice(charIndex, chunk.start); | ||
var _match2 = rx.exec(_slice2); | ||
if (_match2) this.patch(charIndex, charIndex + _match2[0].length, ''); | ||
if (_match2) this.chunk(charIndex, charIndex + _match2[0].length, ''); | ||
if (!_match2 || _match2[0].length < _slice2.length) { | ||
// there is non-whitespace before the patch | ||
// there is non-whitespace before the chunk | ||
return this; | ||
@@ -649,6 +760,6 @@ } | ||
patch.content = patch.content.replace(rx, ''); | ||
if (patch.content) return this; | ||
chunk.content = chunk.content.replace(rx, ''); | ||
if (chunk.content) return this; | ||
charIndex = patch.end; | ||
charIndex = chunk.end; | ||
} | ||
@@ -659,3 +770,3 @@ | ||
var match = rx.exec(slice); | ||
if (match) this.patch(charIndex, charIndex + match[0].length, ''); | ||
if (match) this.chunk(charIndex, charIndex + match[0].length, ''); | ||
@@ -756,3 +867,3 @@ return this; | ||
var prefix = i > 0 ? getSemis(source.separator) || ',' : ''; | ||
var mappings = undefined; | ||
var mappings = void 0; | ||
@@ -857,3 +968,3 @@ // we don't bother encoding sources without a filename | ||
if (!this.intro) { | ||
var source = undefined; | ||
var source = void 0; | ||
var i = 0; | ||
@@ -878,3 +989,3 @@ | ||
var source = undefined; | ||
var source = void 0; | ||
var i = this.sources.length - 1; | ||
@@ -881,0 +992,0 @@ |
@@ -5,3 +5,3 @@ { | ||
"author": "Rich Harris", | ||
"version": "0.10.2", | ||
"version": "0.11.0-alpha", | ||
"repository": "https://github.com/rich-harris/magic-string", | ||
@@ -19,3 +19,3 @@ "main": "dist/magic-string.cjs.js", | ||
"es6-promise": "^3.0.2", | ||
"eslint": "^1.10.3", | ||
"eslint": "^2.0.0", | ||
"istanbul": "^0.4.1", | ||
@@ -25,3 +25,3 @@ "mocha": "^2.3.4", | ||
"resolve": "^1.1.6", | ||
"rollup": "^0.24.0", | ||
"rollup": "^0.25.0", | ||
"rollup-plugin-babel": "^2.3.5", | ||
@@ -28,0 +28,0 @@ "rollup-plugin-npm": "^1.2.0", |
@@ -1,2 +0,2 @@ | ||
import Patch from './Patch.js'; | ||
import Chunk from './Chunk.js'; | ||
import SourceMap from './utils/SourceMap.js'; | ||
@@ -11,2 +11,4 @@ import guessIndent from './utils/guessIndent.js'; | ||
export default function MagicString ( string, options = {} ) { | ||
const chunk = new Chunk( 0, string.length, string ); | ||
Object.defineProperties( this, { | ||
@@ -16,3 +18,4 @@ original: { writable: true, value: string }, | ||
intro: { writable: true, value: '' }, | ||
patches: { writable: true, value: [] }, | ||
chunks: { writable: true, value: [ chunk ] }, | ||
moves: { writable: true, value: [] }, | ||
filename: { writable: true, value: options.filename }, | ||
@@ -41,3 +44,3 @@ indentExclusionRanges: { writable: true, value: options.indentExclusionRanges }, | ||
cloned.patches = this.patches.map( patch => patch.clone() ); | ||
cloned.chunks = this.chunks.map( chunk => chunk.clone() ); | ||
@@ -76,3 +79,3 @@ if ( this.indentExclusionRanges ) { | ||
getMappings ( hires, sourceIndex, offsets, names ) { | ||
return encodeMappings( this.original, this.intro, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names ); | ||
return encodeMappings( this.original, this.intro, this.chunks, hires, this.sourcemapLocations, sourceIndex, offsets, names ); | ||
}, | ||
@@ -115,42 +118,51 @@ | ||
let chunkIndex; | ||
let charIndex = 0; | ||
let patchIndex = 0; | ||
const indentUntil = end => { | ||
while ( charIndex < end ) { | ||
for ( chunkIndex = 0; chunkIndex < this.chunks.length; chunkIndex += 1 ) { // can't cache this.chunks.length, it may change | ||
let chunk = this.chunks[ chunkIndex ]; | ||
const end = chunk.end; | ||
if ( chunk.edited ) { | ||
if ( !isExcluded[ charIndex ] ) { | ||
const char = this.original[ charIndex ]; | ||
chunk.content = chunk.content.replace( pattern, replacer ); | ||
if ( char === '\n' ) { | ||
shouldIndentNextCharacter = true; | ||
} else if ( char !== '\r' && shouldIndentNextCharacter ) { | ||
this.patches.splice( patchIndex, 0, new Patch( charIndex, charIndex, indentStr, '', false ) ); | ||
shouldIndentNextCharacter = false; | ||
patchIndex += 1; | ||
if ( chunk.content.length ) { | ||
shouldIndentNextCharacter = chunk.content[ chunk.content.length - 1 ] === '\n'; | ||
} | ||
} | ||
} else { | ||
charIndex = chunk.start; | ||
charIndex += 1; | ||
} | ||
}; | ||
while ( charIndex < end ) { | ||
if ( !isExcluded[ charIndex ] ) { | ||
const char = this.original[ charIndex ]; | ||
for ( ; patchIndex < this.patches.length; patchIndex += 1 ) { // can't cache this.patches.length, it may change | ||
const patch = this.patches[ patchIndex ]; | ||
if ( char === '\n' ) { | ||
shouldIndentNextCharacter = true; | ||
} else if ( char !== '\r' && shouldIndentNextCharacter ) { | ||
shouldIndentNextCharacter = false; | ||
indentUntil( patch.start ); | ||
const indentation = new Chunk( charIndex, charIndex, '' ).edit( indentStr, false ); | ||
const remainder = chunk.split( charIndex ); | ||
if ( !isExcluded[ charIndex ] ) { | ||
patch.content = patch.content.replace( pattern, replacer ); | ||
if ( charIndex === chunk.start ) { | ||
this.chunks.splice( chunkIndex, 0, indentation ); | ||
chunkIndex += 1; | ||
} else { | ||
this.chunks.splice( chunkIndex + 1, 0, indentation, remainder ); | ||
chunkIndex += 2; | ||
} | ||
if ( patch.content.length ) { | ||
shouldIndentNextCharacter = patch.content[ patch.content.length - 1 ] === '\n'; | ||
chunk = remainder; | ||
} | ||
} | ||
charIndex += 1; | ||
} | ||
} | ||
charIndex = patch.end; | ||
charIndex = chunk.end; | ||
} | ||
indentUntil( this.original.length ); | ||
this.outro = this.outro.replace( pattern, replacer ); | ||
@@ -162,7 +174,12 @@ | ||
insert ( index, content ) { | ||
if ( typeof content !== 'string' ) { | ||
throw new TypeError( 'inserted content must be a string' ); | ||
} | ||
if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' ); | ||
this.patch( index, index, content ); | ||
this._split( index ); | ||
let next = this.chunks.findIndex( chunk => chunk.end > index ); | ||
if ( !~next ) next = this.chunks.length; | ||
const newChunk = new Chunk( index, index, '' ).edit( content, false ); | ||
this.chunks.splice( next, 0, newChunk ); | ||
return this; | ||
@@ -180,2 +197,14 @@ }, | ||
move ( start, end, index ) { | ||
if ( index >= start && index <= end ) throw new Error( 'Cannot move a selection inside itself' ); | ||
this._split( start ); | ||
this._split( end ); | ||
this._split( index ); | ||
this.moves.push({ start, end, index }); | ||
return this; | ||
}, | ||
overwrite ( start, end, content, storeName ) { | ||
@@ -186,46 +215,19 @@ if ( typeof content !== 'string' ) { | ||
this.patch( start, end, content, storeName ); | ||
return this; | ||
}, | ||
this._split( start ); | ||
this._split( end ); | ||
patch ( start, end, content, storeName ) { | ||
const original = this.original.slice( start, end ); | ||
if ( storeName ) this.storedNames[ original ] = true; | ||
if ( storeName ) { | ||
const original = this.original.slice( start, end ); | ||
this.storedNames[ original ] = true; | ||
} | ||
let i = this.patches.length; | ||
while ( i-- ) { | ||
const previous = this.patches[i]; | ||
let firstIndex = this.chunks.findIndex( chunk => chunk.start === start && chunk.original.length ); | ||
let lastIndex = this.chunks.findIndex( chunk => chunk.start === end ); | ||
if ( !~firstIndex ) firstIndex = this.chunks.length; | ||
if ( !~lastIndex ) lastIndex = this.chunks.length; | ||
// TODO can we tidy this up? | ||
const newChunk = new Chunk( start, end, this.original.slice( start, end ) ).edit( content, storeName ); | ||
// if this completely covers previous patch, remove it | ||
if ( start !== end && start <= previous.start && end >= previous.end ) { | ||
// unless it's an insert at the start | ||
if ( previous.start === previous.end && previous.start === start ) break; | ||
// or it's an insert at the end | ||
if ( previous.start === previous.end && previous.end === end ) continue; | ||
this.patches.splice( i, 1 ); | ||
} | ||
// if it overlaps, throw error | ||
else if ( start < previous.end && end > previous.start ) { | ||
// special case – it's okay to remove overlapping ranges | ||
if ( !previous.content.length && !content.length ) { | ||
previous.start = Math.min( start, previous.start ); | ||
previous.end = Math.max( end, previous.end ); | ||
return; | ||
} | ||
throw new Error( `Cannot overwrite the same content twice: '${original}'` ); | ||
} | ||
// if this precedes previous patch, stop search | ||
else if ( start >= previous.end ) { | ||
break; | ||
} | ||
} | ||
const patch = new Patch( start, end, content, original, storeName ); | ||
this.patches.splice( i + 1, 0, patch ); | ||
return patch; | ||
this.chunks.splice( firstIndex, lastIndex - firstIndex, newChunk ); | ||
return this; | ||
}, | ||
@@ -241,7 +243,34 @@ | ||
remove ( start, end ) { | ||
if ( start < 0 || end > this.original.length ) { | ||
throw new Error( 'Character is out of bounds' ); | ||
while ( start < 0 ) start += this.original.length; | ||
while ( end < 0 ) end += this.original.length; | ||
if ( start === end ) return this; | ||
if ( start < 0 || end > this.original.length ) throw new Error( 'Character is out of bounds' ); | ||
if ( start > end ) throw new Error( 'end must be greater than start' ); | ||
let firstIndex = this.chunks.findIndex( chunk => chunk.start <= start && chunk.end > start ); | ||
let chunk = this.chunks[ firstIndex ]; | ||
// if the chunk contains `start`, split | ||
if ( chunk.start < start ) { | ||
if ( chunk.edited && chunk.content.length ) throw new Error( 'nope' ); | ||
this._split( start ); | ||
firstIndex += 1; | ||
} | ||
this.patch( start, end, '' ); | ||
let lastIndex = this.chunks.findIndex( chunk => chunk.start < end && chunk.end >= end ); | ||
chunk = this.chunks[ lastIndex ]; | ||
// if the chunk contains `end`, split | ||
if ( chunk.start < end ) { | ||
if ( chunk.edited && chunk.content.length ) throw new Error( 'nope' ); | ||
this._split( end ); | ||
} | ||
lastIndex += 1; | ||
const newChunk = new Chunk( start, end, this.original.slice( start, end ) ).edit( '', false ); | ||
this.chunks.splice( firstIndex, lastIndex - firstIndex, newChunk ); | ||
return this; | ||
@@ -263,36 +292,24 @@ }, | ||
let firstPatchIndex = 0; | ||
let lastPatchIndex = this.patches.length; | ||
let result = ''; | ||
while ( lastPatchIndex-- ) { | ||
const patch = this.patches[ lastPatchIndex ]; | ||
if ( end >= patch.start && end < patch.end ) throw new Error( `Cannot use replaced characters (${start}, ${end}) as slice anchors` ); | ||
// TODO handle moves | ||
// TODO this is weird, rewrite it | ||
if ( patch.start > end ) continue; | ||
break; | ||
} | ||
for ( let i = 0; i < this.chunks.length; i += 1 ) { | ||
const chunk = this.chunks[i]; | ||
for ( firstPatchIndex = 0; firstPatchIndex <= lastPatchIndex; firstPatchIndex += 1 ) { | ||
const patch = this.patches[ firstPatchIndex ]; | ||
if ( start > patch.start && start <= patch.end ) throw new Error( `Cannot use replaced characters (${start}, ${end}) as slice anchors` ); | ||
if ( chunk.end <= start ) continue; | ||
if ( chunk.start >= end ) break; | ||
if ( start <= patch.start ) { | ||
break; | ||
} | ||
} | ||
if ( chunk.start < start || chunk.end > end ) { | ||
if ( chunk.edited ) throw new Error( `Cannot use replaced characters (${start}, ${end}) as slice anchors` ); | ||
let result = ''; | ||
let lastIndex = start; | ||
const sliceStart = Math.max( start - chunk.start, 0 ); | ||
const sliceEnd = Math.min( chunk.content.length - ( chunk.end - end ), chunk.content.length ); | ||
for ( let i = firstPatchIndex; i <= lastPatchIndex; i += 1 ) { | ||
const patch = this.patches[i]; | ||
result += this.original.slice( lastIndex, patch.start ); | ||
result += patch.content; | ||
lastIndex = patch.end; | ||
result += chunk.content.slice( sliceStart, sliceEnd ); | ||
} else { | ||
result += chunk.content; | ||
} | ||
} | ||
result += this.original.slice( lastIndex, end ); | ||
return result; | ||
@@ -309,4 +326,41 @@ }, | ||
_sortChunks () { | ||
let chunks = this.chunks.slice(); | ||
// TODO there must be a better way than this... | ||
this.moves.forEach( ( move, i ) => { | ||
let firstIndex = chunks.findIndex( chunk => chunk.start === move.start ); | ||
let lastIndex = chunks.findIndex( ( chunk, i ) => i >= firstIndex && chunk.end === move.end ) + 1; | ||
if ( !lastIndex ) lastIndex = chunks.length; | ||
let insertionIndex = chunks.findIndex( chunk => chunk.start === move.index ); | ||
if ( !~insertionIndex ) insertionIndex = chunks.length; | ||
const num = lastIndex - firstIndex; | ||
if ( firstIndex < insertionIndex ) insertionIndex -= num; | ||
const toMove = chunks.splice( firstIndex, num ); | ||
chunks.splice.apply( chunks, [ insertionIndex, 0 ].concat( toMove ) ); | ||
}); | ||
return chunks; | ||
}, | ||
_split ( index ) { | ||
// TODO bisect | ||
for ( let i = 0; i < this.chunks.length; i += 1 ) { | ||
const chunk = this.chunks[i]; | ||
if ( chunk.start === index || chunk.end === index ) return; | ||
if ( chunk.start < index && chunk.end > index ) { | ||
const newChunk = chunk.split( index ); | ||
this.chunks.splice( i + 1, 0, newChunk ); | ||
return; | ||
} | ||
} | ||
}, | ||
toString () { | ||
return this.intro + this.slice( 0, this.original.length ) + this.outro; | ||
return this.intro + this._sortChunks().map( chunk => chunk.content ).join( '' ) + this.outro; | ||
}, | ||
@@ -329,17 +383,17 @@ | ||
let charIndex = this.original.length; | ||
let i = this.patches.length; | ||
let i = this.chunks.length; | ||
while ( i-- ) { | ||
const patch = this.patches[i]; | ||
const chunk = this.chunks[i]; | ||
if ( charIndex > patch.end ) { | ||
const slice = this.original.slice( patch.end, charIndex ); | ||
if ( charIndex > chunk.end ) { | ||
const slice = this.original.slice( chunk.end, charIndex ); | ||
const match = rx.exec( slice ); | ||
if ( match ) { | ||
this.patch( charIndex - match[0].length, charIndex, '' ); | ||
this.chunk( charIndex - match[0].length, charIndex, '' ); | ||
} | ||
if ( !match || match[0].length < slice.length ) { | ||
// there is non-whitespace after the patch | ||
// there is non-whitespace after the chunk | ||
return this; | ||
@@ -349,6 +403,6 @@ } | ||
patch.content = patch.content.replace( rx, '' ); | ||
if ( patch.content ) return this; | ||
chunk.content = chunk.content.replace( rx, '' ); | ||
if ( chunk.content ) return this; | ||
charIndex = patch.start; | ||
charIndex = chunk.start; | ||
} | ||
@@ -359,3 +413,3 @@ | ||
const match = rx.exec( slice ); | ||
if ( match ) this.patch( charIndex - match[0].length, charIndex, '' ); | ||
if ( match ) this.chunk( charIndex - match[0].length, charIndex, '' ); | ||
@@ -373,13 +427,13 @@ return this; | ||
for ( let i = 0; i < this.patches.length; i += 1 ) { | ||
const patch = this.patches[i]; | ||
for ( let i = 0; i < this.chunks.length; i += 1 ) { | ||
const chunk = this.chunks[i]; | ||
if ( charIndex < patch.start ) { | ||
const slice = this.original.slice( charIndex, patch.start ); | ||
if ( charIndex < chunk.start ) { | ||
const slice = this.original.slice( charIndex, chunk.start ); | ||
const match = rx.exec( slice ); | ||
if ( match ) this.patch( charIndex, charIndex + match[0].length, '' ); | ||
if ( match ) this.chunk( charIndex, charIndex + match[0].length, '' ); | ||
if ( !match || match[0].length < slice.length ) { | ||
// there is non-whitespace before the patch | ||
// there is non-whitespace before the chunk | ||
return this; | ||
@@ -389,6 +443,6 @@ } | ||
patch.content = patch.content.replace( rx, '' ); | ||
if ( patch.content ) return this; | ||
chunk.content = chunk.content.replace( rx, '' ); | ||
if ( chunk.content ) return this; | ||
charIndex = patch.end; | ||
charIndex = chunk.end; | ||
} | ||
@@ -399,3 +453,3 @@ | ||
const match = rx.exec( slice ); | ||
if ( match ) this.patch( charIndex, charIndex + match[0].length, '' ); | ||
if ( match ) this.chunk( charIndex, charIndex + match[0].length, '' ); | ||
@@ -402,0 +456,0 @@ return this; |
import { encode } from 'vlq'; | ||
export default function encodeMappings ( original, intro, patches, hires, sourcemapLocations, sourceIndex, offsets, names ) { | ||
export default function encodeMappings ( original, intro, chunks, hires, sourcemapLocations, sourceIndex, offsets, names ) { | ||
let rawLines = []; | ||
@@ -46,41 +46,42 @@ | ||
for ( let i = 0; i < patches.length; i += 1 ) { | ||
const patch = patches[i]; | ||
const addSegmentForPatch = patch.storeName || patch.start > originalCharIndex; | ||
for ( let i = 0; i < chunks.length; i += 1 ) { | ||
const chunk = chunks[i]; | ||
addSegmentsUntil( patch.start ); | ||
if ( chunk.edited ) { | ||
if ( i > 0 || chunk.content.length ) { | ||
rawSegments.push({ | ||
generatedCodeLine, | ||
generatedCodeColumn, | ||
sourceCodeLine, | ||
sourceCodeColumn, | ||
sourceCodeName: chunk.storeName ? names.indexOf( chunk.original ) : -1, | ||
sourceIndex | ||
}); | ||
} | ||
if ( addSegmentForPatch ) { | ||
rawSegments.push({ | ||
generatedCodeLine, | ||
generatedCodeColumn, | ||
sourceCodeLine, | ||
sourceCodeColumn, | ||
sourceCodeName: patch.storeName ? names.indexOf( patch.original ) : -1, | ||
sourceIndex | ||
}); | ||
} | ||
let lines = chunk.content.split( '\n' ); | ||
let lastLine = lines.pop(); | ||
let lines = patch.content.split( '\n' ); | ||
let lastLine = lines.pop(); | ||
if ( lines.length ) { | ||
generatedCodeLine += lines.length; | ||
rawLines[ generatedCodeLine ] = rawSegments = []; | ||
generatedCodeColumn = lastLine.length; | ||
} else { | ||
generatedCodeColumn += lastLine.length; | ||
} | ||
if ( lines.length ) { | ||
generatedCodeLine += lines.length; | ||
rawLines[ generatedCodeLine ] = rawSegments = []; | ||
generatedCodeColumn = lastLine.length; | ||
} else { | ||
generatedCodeColumn += lastLine.length; | ||
} | ||
lines = chunk.original.split( '\n' ); | ||
lastLine = lines.pop(); | ||
lines = patch.original.split( '\n' ); | ||
lastLine = lines.pop(); | ||
if ( lines.length ) { | ||
sourceCodeLine += lines.length; | ||
sourceCodeColumn = lastLine.length; | ||
if ( lines.length ) { | ||
sourceCodeLine += lines.length; | ||
sourceCodeColumn = lastLine.length; | ||
} else { | ||
sourceCodeColumn += lastLine.length; | ||
} | ||
} else { | ||
sourceCodeColumn += lastLine.length; | ||
addSegmentsUntil( chunk.end ); | ||
} | ||
originalCharIndex = patch.end; | ||
originalCharIndex = chunk.end; | ||
} | ||
@@ -87,0 +88,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
281585
2992