diff --git a/src/vs/editor/contrib/snippet/common/snippetParser.ts b/src/vs/editor/contrib/snippet/common/snippetParser.ts index 026247a5e1bb5355e646fd973ab2fee96d6c458d..e06dc9621dd57cea97d07ab098946512957b22a7 100644 --- a/src/vs/editor/contrib/snippet/common/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/common/snippetParser.ts @@ -151,6 +151,13 @@ export class Text extends Marker { len(): number { return this.string.length; } + with(string: string): Text { + if (this.string !== string) { + return new Text(string); + } else { + return this; + } + } } export class Placeholder extends Marker { @@ -160,6 +167,9 @@ export class Placeholder extends Marker { toString() { return Marker.toString(this.defaultValue); } + with(defaultValue: Marker[]): Placeholder { + return new Placeholder(this.name, defaultValue); + } } export class Variable extends Marker { @@ -169,14 +179,17 @@ export class Variable extends Marker { constructor(public name: string = '', public defaultValue: Marker[]) { super(); } - get isDefined(): boolean { return this.resolvedValue !== undefined; } - toString() { return this.isDefined ? this.resolvedValue : Marker.toString(this.defaultValue); } + with(defaultValue: Marker[]): Variable { + let ret = new Variable(this.name, defaultValue); + ret.resolvedValue = this.resolvedValue; + return ret; + } } export function walk(marker: Marker[], visitor: (marker: Marker) => boolean): void { const stack = [...marker]; @@ -234,26 +247,41 @@ export class TextmateSnippet { return map; } - adjustIndentation(normalizer: (whitespace: string) => string): void { + withIndentation(normalizer: (whitespace: string) => string): TextmateSnippet { + // create a new snippet because this can be + // different for each and every cursor + const newMarker = [...this.marker]; + TextmateSnippet._adjustIndentation(newMarker, normalizer); + return new TextmateSnippet(newMarker); + } - walk(this.marker, candidate => { + private static _adjustIndentation(marker: Marker[], normalizer: (whitespace: string) => string): void { + for (let i = 0; i < marker.length; i++) { + const candidate = marker[i]; if (candidate instanceof Text) { //check for newline characters and adjust indent let regex = /\r\n|\r|\n/g; let match: RegExpMatchArray; - while (match = regex.exec(candidate.string)) { + let value = candidate.string; + while (match = regex.exec(value)) { let pos = regex.lastIndex; - let whitespace = getLeadingWhitespace(candidate.string, pos); + let whitespace = getLeadingWhitespace(value, pos); let normalized = normalizer(whitespace); if (whitespace !== normalized) { - candidate.string = candidate.string.substr(0, pos) + value = value.substr(0, pos) + normalized - + candidate.string.substr(pos + whitespace.length); + + value.substr(pos + whitespace.length); + + marker[i] = candidate.with(value); } } + } else if (candidate instanceof Placeholder || candidate instanceof Variable) { + // recurse with a copied array + let children = [...candidate.defaultValue]; + TextmateSnippet._adjustIndentation(children, normalizer); + marker[i] = candidate.with(children); } - return true; - }); + } } } diff --git a/src/vs/editor/contrib/snippet/test/common/snippetParser.test.ts b/src/vs/editor/contrib/snippet/test/common/snippetParser.test.ts index 60e200e8102904e229d4035809ca3b648e806d07..01e98e161232bedab1e65514b3aac2af77775eae 100644 --- a/src/vs/editor/contrib/snippet/test/common/snippetParser.test.ts +++ b/src/vs/editor/contrib/snippet/test/common/snippetParser.test.ts @@ -363,21 +363,24 @@ suite('SnippetParser', () => { assert.equal(snippet.offset(array[0]), 3); }); - test('TextmateSnippet#adjustIndentation', () => { + test('TextmateSnippet#withIndentation', () => { let snippet = SnippetParser.parse('foo\n bar'); assert.equal(Marker.toString(snippet.marker), 'foo\n bar'); - snippet.adjustIndentation(s => s.replace(/ /, '\t')); - assert.equal(Marker.toString(snippet.marker), 'foo\n\tbar'); + let newSnippet = snippet.withIndentation(s => s.replace(/ /, '\t')); + assert.equal(Marker.toString(snippet.marker), 'foo\n bar'); + assert.equal(Marker.toString(newSnippet.marker), 'foo\n\tbar'); snippet = SnippetParser.parse('foo\r\n bar\r\n far'); assert.equal(Marker.toString(snippet.marker), 'foo\r\n bar\r\n far'); - snippet.adjustIndentation(s => s.replace(/ /, '\t')); - assert.equal(Marker.toString(snippet.marker), 'foo\r\n\tbar\r\n\tfar'); + newSnippet = snippet.withIndentation(s => s.replace(/ /, '\t')); + assert.equal(Marker.toString(snippet.marker), 'foo\r\n bar\r\n far'); + assert.equal(Marker.toString(newSnippet.marker), 'foo\r\n\tbar\r\n\tfar'); snippet = SnippetParser.parse('foo${1:bar\r far\r boo}'); assert.equal(Marker.toString(snippet.marker), 'foobar\r far\r boo'); - snippet.adjustIndentation(s => s.replace(/ /, '\t')); - assert.equal(Marker.toString(snippet.marker), 'foobar\r\tfar\r\tboo'); + newSnippet = snippet.withIndentation(s => s.replace(/ /, '\t')); + assert.equal(Marker.toString(snippet.marker), 'foobar\r far\r boo'); + assert.equal(Marker.toString(newSnippet.marker), 'foobar\r\tfar\r\tboo'); }); });