提交 038703dc 编写于 作者: J Johannes Rieken

renamed and make TextSnippet extend Marker, #27543

上级 b28e0e34
......@@ -134,8 +134,19 @@ export abstract class Marker {
parent: Marker;
protected _adopt(child: Marker): void {
child.parent = this;
private _children: Marker[] = [];
set children(marker: Marker[]) {
this._children = [];
for (const m of marker) {
m.parent = this;
this._children.push(m);
}
// Object.freeze(this._children);
}
get children(): Marker[] {
return this._children;
}
toString() {
......@@ -176,15 +187,15 @@ export class Placeholder extends Marker {
}
}
constructor(public index: string = '', public defaultValue: Marker[]) {
constructor(public index: string = '', children: Marker[]) {
super();
defaultValue.forEach(this._adopt, this);
this.children = children;
}
get isFinalTabstop() {
return this.index === '0';
}
toString() {
return Marker.toString(this.defaultValue);
return Marker.toString(this.children);
}
}
......@@ -192,9 +203,9 @@ export class Variable extends Marker {
resolvedValue: string;
constructor(public name: string = '', public defaultValue: Marker[]) {
constructor(public name: string = '', children: Marker[]) {
super();
defaultValue.forEach(this._adopt, this);
this.children = children;
}
get isDefined(): boolean {
return this.resolvedValue !== undefined;
......@@ -207,7 +218,7 @@ export class Variable extends Marker {
}
}
toString() {
return this.isDefined ? this.resolvedValue : Marker.toString(this.defaultValue);
return this.isDefined ? this.resolvedValue : Marker.toString(this.children);
}
}
export function walk(marker: Marker[], visitor: (marker: Marker) => boolean): void {
......@@ -219,36 +230,38 @@ export function walk(marker: Marker[], visitor: (marker: Marker) => boolean): vo
break;
}
if (marker instanceof Placeholder || marker instanceof Variable) {
stack.unshift(...marker.defaultValue);
stack.unshift(...marker.children);
}
}
}
export class TextmateSnippet {
export class TextmateSnippet extends Marker {
readonly marker: Marker[];
readonly placeholders: Placeholder[];
private _placeholders: Placeholder[];
constructor(marker: Marker[]) {
this.marker = marker;
this.placeholders = [];
// fill in placeholders
walk(marker, candidate => {
if (candidate instanceof Placeholder) {
this.placeholders.push(candidate);
}
return true;
});
super();
this.children = marker;
}
Object.freeze(this.marker);
Object.freeze(this.placeholders);
get placeholders(): Placeholder[] {
if (!this._placeholders) {
// fill in placeholders
this._placeholders = [];
walk(this.children, candidate => {
if (candidate instanceof Placeholder) {
this.placeholders.push(candidate);
}
return true;
});
}
return this._placeholders;
}
offset(marker: Marker): number {
let pos = 0;
let found = false;
walk(this.marker, candidate => {
walk(this.children, candidate => {
if (candidate === marker) {
found = true;
return false;
......@@ -263,7 +276,7 @@ export class TextmateSnippet {
return pos;
}
len(marker: Marker): number {
fullLen(marker: Marker): number {
let ret = 0;
walk([marker], marker => {
ret += marker.len();
......@@ -285,22 +298,31 @@ export class TextmateSnippet {
}
get text() {
return Marker.toString(this.marker);
return Marker.toString(this.children);
}
resolveVariables(resolver: { resolve(name: string): string }): this {
walk(this.marker, candidate => {
walk(this.children, candidate => {
if (candidate instanceof Variable) {
candidate.resolvedValue = resolver.resolve(candidate.name);
if (candidate.isDefined) {
// remove default value from resolved variable
candidate.defaultValue.length = 0;
candidate.children = [];
}
}
return true;
});
return this;
}
replace(marker: Marker, others: Marker[]): void {
const { parent } = marker;
const idx = parent.children.indexOf(marker);
const newChildren = parent.children.slice(0);
newChildren.splice(idx, 1, ...others);
parent.children = newChildren;
this._placeholders = undefined;
}
}
export class SnippetParser {
......@@ -349,17 +371,17 @@ export class SnippetParser {
// fill in default values for repeated placeholders
// like `${1:foo}and$1` becomes ${1:foo}and${1:foo}
if (!placeholderDefaultValues.has(thisMarker.index)) {
placeholderDefaultValues.set(thisMarker.index, thisMarker.defaultValue);
} else if (thisMarker.defaultValue.length === 0) {
thisMarker.defaultValue = placeholderDefaultValues.get(thisMarker.index).slice(0);
placeholderDefaultValues.set(thisMarker.index, thisMarker.children);
} else if (thisMarker.children.length === 0) {
thisMarker.children = placeholderDefaultValues.get(thisMarker.index).slice(0);
}
if (thisMarker.defaultValue.length > 0) {
walk(thisMarker.defaultValue, placeholderDefaultValues);
if (thisMarker.children.length > 0) {
walk(thisMarker.children, placeholderDefaultValues);
}
} else if (thisMarker instanceof Variable) {
walk(thisMarker.defaultValue, placeholderDefaultValues);
walk(thisMarker.children, placeholderDefaultValues);
} else if (i > 0 && thisMarker instanceof Text && marker[i - 1] instanceof Text) {
(<Text>marker[i - 1]).string += (<Text>marker[i]).string;
......
......@@ -65,7 +65,7 @@ export class OneSnippet {
// create a decoration for each placeholder
for (const placeholder of this._snippet.placeholders) {
const placeholderOffset = this._snippet.offset(placeholder);
const placeholderLen = this._snippet.len(placeholder);
const placeholderLen = this._snippet.fullLen(placeholder);
const range = Range.fromPositions(
model.getPositionAt(this._offset + placeholderOffset),
model.getPositionAt(this._offset + placeholderOffset + placeholderLen)
......
......@@ -153,13 +153,13 @@ suite('SnippetParser', () => {
assertTextAndMarker('foo${1:bar\\}${2:foo}}', 'foobar}foo', Text, Placeholder);
let [, placeholder] = new SnippetParser(true, false).parse('foo${1:bar\\}${2:foo}}');
let { defaultValue } = (<Placeholder>placeholder);
let { children } = (<Placeholder>placeholder);
assert.equal((<Placeholder>placeholder).index, '1');
assert.ok(defaultValue[0] instanceof Text);
assert.equal(defaultValue[0].toString(), 'bar}');
assert.ok(defaultValue[1] instanceof Placeholder);
assert.equal(defaultValue[1].toString(), 'foo');
assert.ok(children[0] instanceof Text);
assert.equal(children[0].toString(), 'bar}');
assert.ok(children[1] instanceof Placeholder);
assert.equal(children[1].toString(), 'foo');
});
test('Parser, placeholder', () => {
......@@ -225,17 +225,17 @@ suite('SnippetParser', () => {
const placeholder = <Placeholder>marker[1];
assert.equal(placeholder, false);
assert.equal(placeholder.index, '1');
assert.equal(placeholder.defaultValue.length, 3);
assert.ok(placeholder.defaultValue[0] instanceof Text);
assert.ok(placeholder.defaultValue[1] instanceof Variable);
assert.ok(placeholder.defaultValue[2] instanceof Text);
assert.equal(placeholder.defaultValue[0].toString(), ' ');
assert.equal(placeholder.defaultValue[1].toString(), '');
assert.equal(placeholder.defaultValue[2].toString(), ' ');
const nestedVariable = <Variable>placeholder.defaultValue[1];
assert.equal(placeholder.children.length, 3);
assert.ok(placeholder.children[0] instanceof Text);
assert.ok(placeholder.children[1] instanceof Variable);
assert.ok(placeholder.children[2] instanceof Text);
assert.equal(placeholder.children[0].toString(), ' ');
assert.equal(placeholder.children[1].toString(), '');
assert.equal(placeholder.children[2].toString(), ' ');
const nestedVariable = <Variable>placeholder.children[1];
assert.equal(nestedVariable.name, 'TM_SELECTED_TEXT');
assert.equal(nestedVariable.defaultValue.length, 0);
assert.equal(nestedVariable.children.length, 0);
marker = new SnippetParser().parse('$TM_SELECTED_TEXT');
assert.equal(marker.length, 1);
......@@ -268,13 +268,13 @@ suite('SnippetParser', () => {
const [p1, , p2, , p3] = new SnippetParser().parse('{{first}}-{{2:}}-{{second}}-{{1:}}');
assert.equal((<Placeholder>p1).index, 'first');
assert.equal(Marker.toString((<Placeholder>p1).defaultValue), 'first');
assert.equal(Marker.toString((<Placeholder>p1).children), 'first');
assert.equal((<Placeholder>p2).index, '2');
assert.equal(Marker.toString((<Placeholder>p2).defaultValue), '');
assert.equal(Marker.toString((<Placeholder>p2).children), '');
assert.equal((<Placeholder>p3).index, 'second');
assert.equal(Marker.toString((<Placeholder>p3).defaultValue), 'second');
assert.equal(Marker.toString((<Placeholder>p3).children), 'second');
});
test('Parser, default placeholder values', () => {
......@@ -284,12 +284,12 @@ suite('SnippetParser', () => {
const [, p1, , p2] = new SnippetParser().parse('errorContext: `${1:err}`, error:$1');
assert.equal((<Placeholder>p1).index, '1');
assert.equal((<Placeholder>p1).defaultValue.length, '1');
assert.equal((<Text>(<Placeholder>p1).defaultValue[0]), 'err');
assert.equal((<Placeholder>p1).children.length, '1');
assert.equal((<Text>(<Placeholder>p1).children[0]), 'err');
assert.equal((<Placeholder>p2).index, '1');
assert.equal((<Placeholder>p2).defaultValue.length, '1');
assert.equal((<Text>(<Placeholder>p2).defaultValue[0]), 'err');
assert.equal((<Placeholder>p2).children.length, '1');
assert.equal((<Text>(<Placeholder>p2).children[0]), 'err');
});
......@@ -309,8 +309,8 @@ suite('SnippetParser', () => {
test('marker#len', () => {
function assertLen(template: string, ...lengths: number[]): void {
const { marker } = SnippetParser.parse(template);
walk(marker, m => {
const { children } = SnippetParser.parse(template);
walk(children, m => {
const expected = lengths.shift();
assert.equal(m.len(), expected);
return true;
......@@ -335,15 +335,15 @@ suite('SnippetParser', () => {
assert.equal(first.index, '1');
assert.equal(second.index, '2');
assert.ok(second.parent === first);
assert.ok(first.parent === undefined);
assert.ok(first.parent === snippet);
snippet = SnippetParser.parse('${VAR:default${1:value}}$0');
assert.equal(snippet.placeholders.length, 2);
[first] = snippet.placeholders;
assert.equal(first.index, '1');
assert.ok(snippet.marker[0] instanceof Variable);
assert.ok(first.parent === snippet.marker[0]);
assert.ok(snippet.children[0] instanceof Variable);
assert.ok(first.parent === snippet.children[0]);
});
test('TextmateSnippet#enclosingPlaceholders', function () {
......@@ -356,13 +356,13 @@ suite('SnippetParser', () => {
test('TextmateSnippet#offset', () => {
let snippet = SnippetParser.parse('te$1xt');
assert.equal(snippet.offset(snippet.marker[0]), 0);
assert.equal(snippet.offset(snippet.marker[1]), 2);
assert.equal(snippet.offset(snippet.marker[2]), 2);
assert.equal(snippet.offset(snippet.children[0]), 0);
assert.equal(snippet.offset(snippet.children[1]), 2);
assert.equal(snippet.offset(snippet.children[2]), 2);
snippet = SnippetParser.parse('${TM_SELECTED_TEXT:def}');
assert.equal(snippet.offset(snippet.marker[0]), 0);
assert.equal(snippet.offset((<Variable>snippet.marker[0]).defaultValue[0]), 0);
assert.equal(snippet.offset(snippet.children[0]), 0);
assert.equal(snippet.offset((<Variable>snippet.children[0]).children[0]), 0);
// forgein marker
assert.equal(snippet.offset(new Text('foo')), -1);
......@@ -386,4 +386,18 @@ suite('SnippetParser', () => {
placeholders = snippet.placeholders;
assert.equal(placeholders.length, 3);
});
test('TextmateSnippet#replace', function () {
let snippet = SnippetParser.parse('aaa${1:bbb${2:ccc}}$0');
assert.equal(snippet.placeholders.length, 3);
const [, second] = snippet.placeholders;
assert.equal(second.index, '2');
let nested = SnippetParser.parse('ddd$1eee$0');
snippet.replace(second, nested.children);
assert.equal(snippet.text, 'aaabbbdddeee');
assert.equal(snippet.placeholders.length, 4);
});
});
......@@ -144,14 +144,14 @@ export class EditorAccessor implements emmet.Editor {
return SnippetParser.escape(marker.string);
} else if (marker instanceof Placeholder) {
if (marker.defaultValue.length > 0) {
return `\${${marker.index}:${marker.defaultValue.map(toSnippetString).join('')}}`;
if (marker.children.length > 0) {
return `\${${marker.index}:${marker.children.map(toSnippetString).join('')}}`;
} else {
return `\$${marker.index}`;
}
} else if (marker instanceof Variable) {
if (marker.defaultValue.length > 0) {
return `\${${marker.name}:${marker.defaultValue.map(toSnippetString).join('')}}`;
if (marker.children.length > 0) {
return `\${${marker.name}:${marker.children.map(toSnippetString).join('')}}`;
} else {
return `\$${marker.name}`;
}
......
......@@ -169,13 +169,13 @@ function _rewriteBogousVariables(snippet: ISnippet): boolean {
return SnippetParser.escape(marker.string);
} else if (marker instanceof Placeholder) {
if (marker.defaultValue.length > 0) {
return `\${${marker.index}:${marker.defaultValue.map(fixBogousVariables).join('')}}`;
if (marker.children.length > 0) {
return `\${${marker.index}:${marker.children.map(fixBogousVariables).join('')}}`;
} else {
return `\$${marker.index}`;
}
} else if (marker instanceof Variable) {
if (marker.defaultValue.length === 0 && !EditorSnippetVariableResolver.VariableNames[marker.name]) {
if (marker.children.length === 0 && !EditorSnippetVariableResolver.VariableNames[marker.name]) {
// a 'variable' without a default value and not being one of our supported
// variables is automatically turing into a placeholder. This is to restore
// a bug we had before. So `${foo}` becomes `${N:foo}`
......@@ -183,8 +183,8 @@ function _rewriteBogousVariables(snippet: ISnippet): boolean {
placeholders.set(marker.name, index);
return `\${${index++}:${marker.name}}`;
} else if (marker.defaultValue.length > 0) {
return `\${${marker.name}:${marker.defaultValue.map(fixBogousVariables).join('')}}`;
} else if (marker.children.length > 0) {
return `\${${marker.name}:${marker.children.map(fixBogousVariables).join('')}}`;
} else {
return `\$${marker.name}`;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册