Skip to content

Commit

Permalink
implement (ap|pre)pend(Left|Right) – closes #109, #110
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich-Harris committed Nov 24, 2016
1 parent 114f739 commit a2c0569
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 58 deletions.
12 changes: 10 additions & 2 deletions src/Chunk.js
Expand Up @@ -18,10 +18,14 @@ export default function Chunk ( start, end, content ) {
}

Chunk.prototype = {
append ( content ) {
appendLeft ( content ) {
this.outro += content;
},

appendRight ( content ) {
this.intro = this.intro + content;
},

clone () {
const chunk = new Chunk( this.start, this.end, this.original );

Expand Down Expand Up @@ -65,7 +69,11 @@ Chunk.prototype = {
return this;
},

prepend ( content ) {
prependLeft ( content ) {
this.outro = content + this.outro;
},

prependRight ( content ) {
this.intro = content + this.intro;
},

Expand Down
121 changes: 91 additions & 30 deletions src/MagicString.js
Expand Up @@ -7,6 +7,11 @@ import isObject from './utils/isObject.js';
import getLocator from './utils/getLocator.js';
import Stats from './utils/Stats.js';

const warned = {
insertLeft: false,
insertRight: false
};

export default function MagicString ( string, options = {} ) {
const chunk = new Chunk( 0, string.length, string );

Expand Down Expand Up @@ -46,6 +51,44 @@ MagicString.prototype = {
return this;
},

appendLeft ( index, content ) {
if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );

if ( DEBUG ) this.stats.time( 'insertLeft' );

this._split( index );

const chunk = this.byEnd[ index ];

if ( chunk ) {
chunk.appendLeft( content );
} else {
this.intro += content;
}

if ( DEBUG ) this.stats.timeEnd( 'insertLeft' );
return this;
},

appendRight ( index, content ) {
if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );

if ( DEBUG ) this.stats.time( 'insertLeft' );

this._split( index );

const chunk = this.byStart[ index ];

if ( chunk ) {
chunk.appendRight( content );
} else {
this.outro += content;
}

if ( DEBUG ) this.stats.timeEnd( 'insertLeft' );
return this;
},

clone () {
const cloned = new MagicString( this.original, { filename: this.filename });

Expand Down Expand Up @@ -173,10 +216,10 @@ MagicString.prototype = {
shouldIndentNextCharacter = false;

if ( charIndex === chunk.start ) {
chunk.prepend( indentStr );
chunk.prependRight( indentStr );
} else {
const rhs = chunk.split( charIndex );
rhs.prepend( indentStr );
rhs.prependRight( indentStr );

this.byStart[ charIndex ] = rhs;
this.byEnd[ charIndex ] = chunk;
Expand Down Expand Up @@ -204,41 +247,21 @@ MagicString.prototype = {
},

insertLeft ( index, content ) {
if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );

if ( DEBUG ) this.stats.time( 'insertLeft' );

this._split( index );

const chunk = this.byEnd[ index ];

if ( chunk ) {
chunk.append( content );
} else {
this.intro += content;
if ( !warned.insertLeft ) {
console.warn( 'magicString.insertLeft(...) is deprecated. Use magicString.appendLeft(...) instead' ); // eslint-disable-line no-console
warned.insertLeft = true;
}

if ( DEBUG ) this.stats.timeEnd( 'insertLeft' );
return this;
return this.appendLeft( index, content );
},

insertRight ( index, content ) {
if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );

if ( DEBUG ) this.stats.time( 'insertRight' );

this._split( index );

const chunk = this.byStart[ index ];

if ( chunk ) {
chunk.prepend( content );
} else {
this.outro += content;
if ( !warned.insertRight ) {
console.warn( 'magicString.insertRight(...) is deprecated. Use magicString.prependRight(...) instead' ); // eslint-disable-line no-console
warned.insertRight = true;
}

if ( DEBUG ) this.stats.timeEnd( 'insertRight' );
return this;
return this.prependRight( index, content );
},

move ( start, end, index ) {
Expand Down Expand Up @@ -338,6 +361,44 @@ MagicString.prototype = {
return this;
},

prependLeft ( index, content ) {
if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );

if ( DEBUG ) this.stats.time( 'insertRight' );

this._split( index );

const chunk = this.byEnd[ index ];

if ( chunk ) {
chunk.prependLeft( content );
} else {
this.intro = content + this.intro;
}

if ( DEBUG ) this.stats.timeEnd( 'insertRight' );
return this;
},

prependRight ( index, content ) {
if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );

if ( DEBUG ) this.stats.time( 'insertRight' );

this._split( index );

const chunk = this.byStart[ index ];

if ( chunk ) {
chunk.prependRight( content );
} else {
this.outro = content + this.outro;
}

if ( DEBUG ) this.stats.timeEnd( 'insertRight' );
return this;
},

remove ( start, end ) {
while ( start < 0 ) start += this.original.length;
while ( end < 0 ) end += this.original.length;
Expand Down
112 changes: 86 additions & 26 deletions test/MagicString.js
Expand Up @@ -38,6 +38,78 @@ describe( 'MagicString', () => {
});
});

describe( '(ap|pre)pend(Left|Right)', () => {
it( 'preserves intended order', () => {
const s = new MagicString( '0123456789' );

s.insertLeft( 5, 'A' );
s.insertRight( 5, 'a' );
s.insertRight( 5, 'b' );
s.insertLeft( 5, 'B' );
s.insertLeft( 5, 'C' );
s.insertRight( 5, 'c' );

assert.equal( s.toString(), '01234ABCcba56789' );
assert.equal( s.slice(0, 5) , '01234ABC' );
assert.equal( s.slice(5), 'cba56789' );

s.prependLeft( 5, '<' );
s.prependLeft( 5, '{' );
assert.equal( s.toString(), '01234{<ABCcba56789' );

s.appendRight( 5, '>' );
s.appendRight( 5, '}' );
assert.equal( s.toString(), '01234{<ABCcba>}56789' );

s.appendLeft(5, '('); // appendLeft is a synonym for insertLeft
s.appendLeft(5, '['); // appendLeft is a synonym for insertLeft
assert.equal( s.toString(), '01234{<ABC([cba>}56789' );

s.prependRight(5, ')'); // prependRight is a synonym for insertRight
s.prependRight(5, ']'); // prependRight is a synonym for insertRight
assert.equal( s.toString(), '01234{<ABC([])cba>}56789' );

assert.equal( s.slice(0, 5), '01234{<ABC([' );
assert.equal( s.slice(5), '])cba>}56789' );
});

it( 'preserves intended order at beginning of string', () => {
const s = new MagicString( 'x' );

s.appendLeft( 0, '1' );
s.prependLeft( 0, '2' );
s.appendLeft( 0, '3' );
s.prependLeft( 0, '4' );

assert.equal( s.toString(), '4213x' );
});

it( 'preserves intended order at end of string', () => {
const s = new MagicString( 'x' );

s.appendRight( 1, '1' );
s.prependRight( 1, '2' );
s.appendRight( 1, '3' );
s.prependRight( 1, '4' );

assert.equal( s.toString(), 'x4213' );
});
});

describe( 'appendLeft', () => {
it( 'should return this', () => {
const s = new MagicString( 'abcdefghijkl' );
assert.strictEqual( s.appendLeft( 0, 'a' ), s );
});
});

describe( 'appendRight', () => {
it( 'should return this', () => {
const s = new MagicString( 'abcdefghijkl' );
assert.strictEqual( s.appendRight( 0, 'a' ), s );
});
});

describe( 'clone', () => {
it( 'should clone a magic string', () => {
const s = new MagicString( 'abcdefghijkl' );
Expand Down Expand Up @@ -481,32 +553,6 @@ describe( 'MagicString', () => {
// });
});

describe( 'insertLeft', () => {
it( 'inserts repeatedly in correct order', () => {
const s = new MagicString( 'ab' );
assert.equal( s.insertLeft(1, '1').toString(), 'a1b' );
assert.equal( s.insertLeft(1, '2').toString(), 'a12b' );
});

it( 'should return this', () => {
const s = new MagicString( 'abcdefghijkl' );
assert.strictEqual( s.insertLeft( 0, 'a' ), s );
});
});

describe( 'insertRight', () => {
it( 'inserts repeatedly in correct order', () => {
const s = new MagicString( 'ab' );
assert.equal( s.insertRight(1, '1').toString(), 'a1b' );
assert.equal( s.insertRight(1, '2').toString(), 'a21b' );
});

it( 'should return this', () => {
const s = new MagicString( 'abcdefghijkl' );
assert.strictEqual( s.insertLeft( 0, 'a' ), s );
});
});

describe( 'move', () => {
it( 'moves content from the start', () => {
const s = new MagicString( 'abcdefghijkl' );
Expand Down Expand Up @@ -750,6 +796,20 @@ describe( 'MagicString', () => {
});
});

describe( 'prependLeft', () => {
it( 'should return this', () => {
const s = new MagicString( 'abcdefghijkl' );
assert.strictEqual( s.prependLeft( 0, 'a' ), s );
});
});

describe( 'prependRight', () => {
it( 'should return this', () => {
const s = new MagicString( 'abcdefghijkl' );
assert.strictEqual( s.prependRight( 0, 'a' ), s );
});
});

describe( 'remove', () => {
it( 'should remove characters from the original string', () => {
const s = new MagicString( 'abcdefghijkl' );
Expand Down

0 comments on commit a2c0569

Please sign in to comment.