Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(ap|pre)pend(Left|Right) #112

Merged
merged 2 commits into from
Nov 24, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ Adds the specified character index (with respect to the original string) to sour

Appends the specified content to the end of the string. Returns `this`.

### s.appendLeft( index, content )

Appends the specified `content` at the `index` in the original string. If a range *ending* with `index` is subsequently moved, the insert will be moved with it. Returns `this`. See also `s.prependLeft(...)`.

### s.appendRight( index, content )

Appends the specified `content` at the `index` in the original string. If a range *starting* with `index` is subsequently moved, the insert will be moved with it. Returns `this`. See also `s.prependRight(...)`.

### s.clone()

Does what you'd expect.
Expand Down Expand Up @@ -120,11 +128,11 @@ The `options` argument can have an `exclude` property, which is an array of `[st

### s.insertLeft( index, content )

Inserts the specified `content` at the `index` in the original string. If a range *ending* with `index` is subsequently moved, the insert will be moved with it. Returns `this`.
**DEPRECATED** since 0.17 – use `s.appendLeft(...)` instead

### s.insertRight( index, content )

Inserts the specified `content` at the `index` in the original string. If a range *starting* with `index` is subsequently moved, the insert will be moved with it. Returns `this`.
**DEPRECATED** since 0.17 – use `s.prependRight(...)` instead

### s.locate( index )

Expand All @@ -146,6 +154,14 @@ Replaces the characters from `start` to `end` with `content`. The same restricti

Prepends the string with the specified content. Returns `this`.

### s.prependLeft ( index, content )

Same as `s.appendLeft(...)`, except that the inserted content will go *before* any previous appends or prepends at `index`

### s.prependRight ( index, content )

Same as `s.appendRight(...)`, except that the inserted content will go *before* any previous appends or prepends at `index`

### s.remove( start, end )

Removes the characters from `start` to `end` (of the original string, **not** the generated string). Removing the same content twice, or making removals that partially overlap, will cause an error. Returns `this`.
Expand Down
12 changes: 10 additions & 2 deletions src/Chunk.js
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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