diff --git a/src/inferFullTargets.ts b/src/inferFullTargets.ts index fe6ec586cb..381626359d 100644 --- a/src/inferFullTargets.ts +++ b/src/inferFullTargets.ts @@ -290,36 +290,16 @@ export function inferSingleNonListTarget( function inferRangeStartSelectionType( context: InferenceContext, - endTarget: PartialPrimitiveTarget, prototypeTargets: Target[], inferredMark: Mark ): SelectionType { - if ( - endTarget.position !== "before" && - endTarget.position !== "after" && - endTarget.selectionType != null - ) { - return endTarget.selectionType; - } - return getPrimitiveSelectionType(context, inferredMark, prototypeTargets); } function inferRangeStartModifier( target: PartialPrimitiveTarget, - endTarget: PartialPrimitiveTarget, prototypeTargets: Target[] ): Modifier { - if ( - endTarget.position !== "before" && - endTarget.position !== "after" && - endTarget.modifier != null && - target.mark == null && - endTarget.modifier.type === "containingScope" - ) { - return endTarget.modifier; - } - return ( extractAttributeFromList(prototypeTargets, "modifier") ?? { type: "identity", @@ -357,13 +337,12 @@ function inferRangeStartTarget( const selectionType = target.selectionType ?? - inferRangeStartSelectionType(context, endTarget, prototypeTargets, mark); + inferRangeStartSelectionType(context, prototypeTargets, mark); const position: Position = target.position ?? "contents"; const modifier = - target.modifier ?? - inferRangeStartModifier(target, endTarget, prototypeTargets); + target.modifier ?? inferRangeStartModifier(target, prototypeTargets); const insideOutsideType = target.insideOutsideType ?? diff --git a/src/test/suite/fixtures/recorded/inference/takeAfterVestPastAir.yml b/src/test/suite/fixtures/recorded/inference/takeAfterVestPastAir.yml new file mode 100644 index 0000000000..6e9e2809a6 --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/takeAfterVestPastAir.yml @@ -0,0 +1,45 @@ +spokenForm: take after vest past air +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: + type: primitive + position: after + mark: {type: decoratedSymbol, symbolColor: default, character: v} + end: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: a} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.v: + start: {line: 1, character: 6} + end: {line: 1, character: 11} + default.a: + start: {line: 3, character: 6} + end: {line: 3, character: 11} +initialState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 11} + active: {line: 3, character: 11} + thatMark: + - anchor: {line: 1, character: 11} + active: {line: 3, character: 11} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: v}, selectionType: token, position: after, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}}] \ No newline at end of file diff --git a/src/test/suite/fixtures/recorded/inference/takeAfterVestPastBeforeAir.yml b/src/test/suite/fixtures/recorded/inference/takeAfterVestPastBeforeAir.yml new file mode 100644 index 0000000000..99b0484ed8 --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/takeAfterVestPastBeforeAir.yml @@ -0,0 +1,46 @@ +spokenForm: take after vest past before air +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: + type: primitive + position: after + mark: {type: decoratedSymbol, symbolColor: default, character: v} + end: + type: primitive + position: before + mark: {type: decoratedSymbol, symbolColor: default, character: a} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.v: + start: {line: 1, character: 6} + end: {line: 1, character: 11} + default.a: + start: {line: 3, character: 6} + end: {line: 3, character: 11} +initialState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 11} + active: {line: 3, character: 6} + thatMark: + - anchor: {line: 1, character: 11} + active: {line: 3, character: 6} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: v}, selectionType: token, position: after, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: before, modifier: {type: identity}, insideOutsideType: inside}}] \ No newline at end of file diff --git a/src/test/suite/fixtures/recorded/inference/takeAirPastEndOfLine.yml b/src/test/suite/fixtures/recorded/inference/takeAirPastEndOfLine.yml index 1badbb4638..0865c16bfe 100644 --- a/src/test/suite/fixtures/recorded/inference/takeAirPastEndOfLine.yml +++ b/src/test/suite/fixtures/recorded/inference/takeAirPastEndOfLine.yml @@ -13,8 +13,8 @@ command: extraArgs: [] marks: default.a: - start: {line: 1, character: 6} - end: {line: 1, character: 11} + start: {line: 3, character: 6} + end: {line: 3, character: 11} initialState: documentContents: | @@ -22,8 +22,8 @@ initialState: const value = "Hello world"; selections: - - anchor: {line: 3, character: 6} - active: {line: 3, character: 6} + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} finalState: documentContents: | @@ -31,9 +31,9 @@ finalState: const value = "Hello world"; selections: - - anchor: {line: 1, character: 6} - active: {line: 1, character: 28} + - anchor: {line: 3, character: 6} + active: {line: 3, character: 28} thatMark: - - anchor: {line: 1, character: 6} - active: {line: 1, character: 28} + - anchor: {line: 3, character: 6} + active: {line: 3, character: 28} fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: line, position: after, modifier: {type: identity}, insideOutsideType: inside}}] \ No newline at end of file diff --git a/src/test/suite/fixtures/recorded/inference/takeHarpAndStringEach.yml b/src/test/suite/fixtures/recorded/inference/takeHarpAndStringEach.yml new file mode 100644 index 0000000000..5cf594e1bc --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/takeHarpAndStringEach.yml @@ -0,0 +1,46 @@ +spokenForm: take harp and string each +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: list + elements: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: h} + - type: primitive + modifier: {type: containingScope, scopeType: string} + mark: {type: decoratedSymbol, symbolColor: default, character: e} + extraArgs: [] +marks: + default.h: + start: {line: 1, character: 15} + end: {line: 1, character: 20} + default.e: + start: {line: 3, character: 15} + end: {line: 3, character: 20} +initialState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 15} + active: {line: 1, character: 20} + - anchor: {line: 3, character: 14} + active: {line: 3, character: 27} + thatMark: + - anchor: {line: 1, character: 15} + active: {line: 1, character: 20} + - anchor: {line: 3, character: 14} + active: {line: 3, character: 27} +fullTargets: [{type: list, elements: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: h}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: e}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: string}, insideOutsideType: inside}]}] \ No newline at end of file diff --git a/src/test/suite/fixtures/recorded/inference/takeHarpPastStringEach.yml b/src/test/suite/fixtures/recorded/inference/takeHarpPastStringEach.yml new file mode 100644 index 0000000000..428221c6dd --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/takeHarpPastStringEach.yml @@ -0,0 +1,45 @@ +spokenForm: take harp past string each +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: h} + end: + type: primitive + modifier: {type: containingScope, scopeType: string} + mark: {type: decoratedSymbol, symbolColor: default, character: e} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.h: + start: {line: 1, character: 15} + end: {line: 1, character: 20} + default.e: + start: {line: 3, character: 15} + end: {line: 3, character: 20} +initialState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 15} + active: {line: 3, character: 27} + thatMark: + - anchor: {line: 1, character: 15} + active: {line: 3, character: 27} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: h}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: e}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: string}, insideOutsideType: inside}}] \ No newline at end of file diff --git a/src/test/suite/fixtures/recorded/inference/takeLinePastAir.yml b/src/test/suite/fixtures/recorded/inference/takeLinePastAir.yml new file mode 100644 index 0000000000..941b040b0b --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/takeLinePastAir.yml @@ -0,0 +1,39 @@ +spokenForm: take line past air +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: {type: primitive, selectionType: line} + end: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: a} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.a: + start: {line: 3, character: 6} + end: {line: 3, character: 11} +initialState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 6} + active: {line: 1, character: 6} +finalState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 0} + active: {line: 3, character: 28} + thatMark: + - anchor: {line: 1, character: 0} + active: {line: 3, character: 28} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: cursor}, selectionType: line, position: contents, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: line, position: contents, modifier: {type: identity}, insideOutsideType: inside}}] \ No newline at end of file diff --git a/src/test/suite/fixtures/recorded/inference/takeLineVestPastAir.yml b/src/test/suite/fixtures/recorded/inference/takeLineVestPastAir.yml new file mode 100644 index 0000000000..9175d6d4af --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/takeLineVestPastAir.yml @@ -0,0 +1,45 @@ +spokenForm: take line vest past air +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: + type: primitive + selectionType: line + mark: {type: decoratedSymbol, symbolColor: default, character: v} + end: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: a} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.v: + start: {line: 1, character: 6} + end: {line: 1, character: 11} + default.a: + start: {line: 3, character: 6} + end: {line: 3, character: 11} +initialState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 0} + active: {line: 3, character: 28} + thatMark: + - anchor: {line: 1, character: 0} + active: {line: 3, character: 28} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: v}, selectionType: line, position: contents, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: line, position: contents, modifier: {type: identity}, insideOutsideType: inside}}] \ No newline at end of file diff --git a/src/test/suite/fixtures/recorded/inference/takePastLineAir.yml b/src/test/suite/fixtures/recorded/inference/takePastLineAir.yml new file mode 100644 index 0000000000..e95934d261 --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/takePastLineAir.yml @@ -0,0 +1,40 @@ +spokenForm: take past line air +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: {type: primitive} + end: + type: primitive + selectionType: line + mark: {type: decoratedSymbol, symbolColor: default, character: a} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.a: + start: {line: 3, character: 6} + end: {line: 3, character: 11} +initialState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 6} + active: {line: 1, character: 6} +finalState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 6} + active: {line: 3, character: 28} + thatMark: + - anchor: {line: 1, character: 6} + active: {line: 3, character: 28} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: line, position: contents, modifier: {type: identity}, insideOutsideType: inside}}] \ No newline at end of file diff --git a/src/test/suite/fixtures/recorded/inference/takeStringHarpPastEach.yml b/src/test/suite/fixtures/recorded/inference/takeStringHarpPastEach.yml new file mode 100644 index 0000000000..d679eb2254 --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/takeStringHarpPastEach.yml @@ -0,0 +1,45 @@ +spokenForm: take string harp past each +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: + type: primitive + modifier: {type: containingScope, scopeType: string} + mark: {type: decoratedSymbol, symbolColor: default, character: h} + end: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: e} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.h: + start: {line: 1, character: 15} + end: {line: 1, character: 20} + default.e: + start: {line: 3, character: 15} + end: {line: 3, character: 20} +initialState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 14} + active: {line: 3, character: 27} + thatMark: + - anchor: {line: 1, character: 14} + active: {line: 3, character: 27} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: h}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: string}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: e}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: string}, insideOutsideType: inside}}] \ No newline at end of file diff --git a/src/test/suite/fixtures/recorded/inference/takeVestAndLineAir.yml b/src/test/suite/fixtures/recorded/inference/takeVestAndLineAir.yml new file mode 100644 index 0000000000..5253e223b2 --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/takeVestAndLineAir.yml @@ -0,0 +1,46 @@ +spokenForm: take vest and line air +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: list + elements: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: v} + - type: primitive + selectionType: line + mark: {type: decoratedSymbol, symbolColor: default, character: a} + extraArgs: [] +marks: + default.v: + start: {line: 1, character: 6} + end: {line: 1, character: 11} + default.a: + start: {line: 3, character: 6} + end: {line: 3, character: 11} +initialState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 6} + active: {line: 1, character: 11} + - anchor: {line: 3, character: 0} + active: {line: 3, character: 28} + thatMark: + - anchor: {line: 1, character: 6} + active: {line: 1, character: 11} + - anchor: {line: 3, character: 0} + active: {line: 3, character: 28} +fullTargets: [{type: list, elements: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: v}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: line, position: contents, modifier: {type: identity}, insideOutsideType: inside}]}] \ No newline at end of file diff --git a/src/test/suite/fixtures/recorded/inference/takeVestPastAfterAir.yml b/src/test/suite/fixtures/recorded/inference/takeVestPastAfterAir.yml new file mode 100644 index 0000000000..b6ba6a8302 --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/takeVestPastAfterAir.yml @@ -0,0 +1,45 @@ +spokenForm: take vest past after air +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: v} + end: + type: primitive + position: after + mark: {type: decoratedSymbol, symbolColor: default, character: a} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.v: + start: {line: 1, character: 6} + end: {line: 1, character: 11} + default.a: + start: {line: 3, character: 6} + end: {line: 3, character: 11} +initialState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 6} + active: {line: 3, character: 11} + thatMark: + - anchor: {line: 1, character: 6} + active: {line: 3, character: 11} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: v}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: after, modifier: {type: identity}, insideOutsideType: inside}}] \ No newline at end of file diff --git a/src/test/suite/fixtures/recorded/inference/takeVestPastBeforeAir.yml b/src/test/suite/fixtures/recorded/inference/takeVestPastBeforeAir.yml new file mode 100644 index 0000000000..13bd5379a5 --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/takeVestPastBeforeAir.yml @@ -0,0 +1,45 @@ +spokenForm: take vest past before air +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: v} + end: + type: primitive + position: before + mark: {type: decoratedSymbol, symbolColor: default, character: a} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.v: + start: {line: 1, character: 6} + end: {line: 1, character: 11} + default.a: + start: {line: 3, character: 6} + end: {line: 3, character: 11} +initialState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 6} + active: {line: 3, character: 6} + thatMark: + - anchor: {line: 1, character: 6} + active: {line: 3, character: 6} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: v}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: before, modifier: {type: identity}, insideOutsideType: inside}}] \ No newline at end of file diff --git a/src/test/suite/fixtures/recorded/inference/takeVestPastLineAir.yml b/src/test/suite/fixtures/recorded/inference/takeVestPastLineAir.yml new file mode 100644 index 0000000000..08a171b768 --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/takeVestPastLineAir.yml @@ -0,0 +1,45 @@ +spokenForm: take vest past line air +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: v} + end: + type: primitive + selectionType: line + mark: {type: decoratedSymbol, symbolColor: default, character: a} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.v: + start: {line: 1, character: 6} + end: {line: 1, character: 11} + default.a: + start: {line: 3, character: 6} + end: {line: 3, character: 11} +initialState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: | + + const value = "Hello world"; + + const value = "Hello world"; + selections: + - anchor: {line: 1, character: 6} + active: {line: 3, character: 28} + thatMark: + - anchor: {line: 1, character: 6} + active: {line: 3, character: 28} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: v}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: line, position: contents, modifier: {type: identity}, insideOutsideType: inside}}] \ No newline at end of file