diff --git a/src/NavigationMap.ts b/src/NavigationMap.ts index b2af5dbf7d..4e5e5ba720 100644 --- a/src/NavigationMap.ts +++ b/src/NavigationMap.ts @@ -1,6 +1,7 @@ -import { TextDocumentChangeEvent, Range } from "vscode"; +import { TextDocumentChangeEvent, Range, TextDocument } from "vscode"; import { SymbolColor } from "./constants"; -import { Token } from "./Types"; +import { selectionWithEditorFromPositions } from "./selectionUtils"; +import { SelectionWithEditor, Token } from "./Types"; /** * Maps from (color, character) pairs to tokens @@ -64,9 +65,75 @@ export default class NavigationMap { this.map = {}; } - public getTokenForRange(range: Range) { - return Object.values(this.map).find( - (token) => token.range.intersection(range) != null + /** + * Given a selection returns a new selection which contains the tokens + * intersecting the given selection. Uses heuristics to tie break when the + * given selection is empty and abuts 2 adjacent tokens + * @param selection Selection to operate on + * @returns Modified selection + */ + public getTokenSelectionForSelection( + selection: SelectionWithEditor + ): SelectionWithEditor | null { + const range = selection.selection; + const tokens = range.isEmpty + ? this.getTokensForEmptyRange(selection.editor.document, range) + : this.getTokensForRange(selection.editor.document, range); + if (tokens.length < 1) { + return null; + } + const start = tokens[0].range.start; + const end = tokens[tokens.length - 1].range.end; + return selectionWithEditorFromPositions(selection, start, end); + } + + // Return tokens for overlapping ranges + private getTokensForRange(document: TextDocument, range: Range) { + const tokens = Object.values(this.map).filter((token) => { + if (token.editor.document !== document) { + return false; + } + const intersection = token.range.intersection(range); + return intersection != null && !intersection.isEmpty; + }); + tokens.sort((a, b) => a.startOffset - b.startOffset); + return tokens; + } + + // Returned single token for overlapping or adjacent range + private getTokensForEmptyRange(document: TextDocument, range: Range) { + const tokens = Object.values(this.map).filter( + (token) => + token.editor.document === document && + token.range.intersection(range) != null ); + + // If multiple matches sort and take the first + tokens.sort((a, b) => { + // First sort on alphanumeric + const aIsAlphaNum = isAlphaNum(a.text); + const bIsAlphaNum = isAlphaNum(b.text); + if (aIsAlphaNum && !bIsAlphaNum) { + return -1; + } + if (bIsAlphaNum && !aIsAlphaNum) { + return 1; + } + + // Second sort on length + const lengthDiff = b.text.length - a.text.length; + if (lengthDiff !== 0) { + return lengthDiff; + } + + // Lastly sort on start position. ie leftmost + return a.startOffset - b.startOffset; + }); + + return tokens.slice(0, 1); } } + +function isAlphaNum(text: string) { + return /^\w+$/.test(text); +} diff --git a/src/Types.ts b/src/Types.ts index c50b68d7a0..32cd031c65 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -71,6 +71,7 @@ export type Delimiter = | "backtickQuotes"; export type ScopeType = + | "attribute" | "argumentOrParameter" | "arrowFunction" | "class" @@ -90,7 +91,6 @@ export type ScopeType = | "string" | "type" | "value" - | "xmlAttribute" | "xmlElement" | "xmlBothTags" | "xmlEndTag" diff --git a/src/extension.ts b/src/extension.ts index 8ff9ec826a..57899e9317 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -90,7 +90,7 @@ export async function activate(context: vscode.ExtensionContext) { } else { if (await testCaseRecorder.start()) { vscode.window.showInformationMessage( - "Recording test cases for following commands" + `Recording test cases for following commands in:\n${testCaseRecorder.fixtureSubdirectory}` ); } } diff --git a/src/inferFullTargets.ts b/src/inferFullTargets.ts index 381626359d..888f5db96f 100644 --- a/src/inferFullTargets.ts +++ b/src/inferFullTargets.ts @@ -332,7 +332,10 @@ function inferRangeStartTarget( prototypeTargets: Target[], actionPreferences: ActionPreferences ): PrimitiveTarget { - const mark = target.mark ?? CURSOR_MARK; + const mark = + target.mark ?? + (target.selectionType === "token" ? CURSOR_MARK_TOKEN : CURSOR_MARK); + prototypeTargets = hasContent(target) ? [] : prototypeTargets; const selectionType = @@ -375,13 +378,17 @@ export function inferRangeEndTarget( ? [] : possiblePrototypeTargetsIncludingStartTarget; + const startMark = extractAttributeFromList( + possiblePrototypeTargetsIncludingStartTarget, + "mark" + ); + const mark = target.mark ?? - extractAttributeFromList( - possiblePrototypeTargetsIncludingStartTarget, - "mark" - ) ?? - CURSOR_MARK; + (startMark != null && startMark.type !== CURSOR_MARK.type + ? startMark + : null) ?? + (target.selectionType === "token" ? CURSOR_MARK_TOKEN : CURSOR_MARK); const selectionType = target.selectionType ?? diff --git a/src/languages/cpp.ts b/src/languages/cpp.ts index 1890c30782..f4899ccab4 100644 --- a/src/languages/cpp.ts +++ b/src/languages/cpp.ts @@ -84,7 +84,7 @@ const nodeMatchers: Partial> = { value: valueMatcher("*[declarator][value]", "*[value]", "assignment_expression[right]", "optional_parameter_declaration[default_value]"), collectionItem: argumentMatcher("initializer_list"), argumentOrParameter: argumentMatcher("parameter_list", "argument_list"), - xmlAttribute: "attribute" + attribute: "attribute" }; export default createPatternMatchers(nodeMatchers); diff --git a/src/languages/typescript.ts b/src/languages/typescript.ts index aa7270357a..cc8b87b8e6 100644 --- a/src/languages/typescript.ts +++ b/src/languages/typescript.ts @@ -154,7 +154,7 @@ const nodeMatchers: Partial> = { ), argumentOrParameter: argumentMatcher("formal_parameters", "arguments"), // XML, JSX - xmlAttribute: ["jsx_attribute"], + attribute: ["jsx_attribute"], xmlElement: ["jsx_element", "jsx_self_closing_element"], xmlBothTags: getTags, xmlStartTag: getStartTag, diff --git a/src/processTargets.ts b/src/processTargets.ts index dd85741f85..f759551770 100644 --- a/src/processTargets.ts +++ b/src/processTargets.ts @@ -24,6 +24,8 @@ import { SelectionWithEditor, Target, TypedSelection, + Position as TargetPosition, + InsideOutsideType, } from "./Types"; export default function processTargets( @@ -199,19 +201,15 @@ function getSelectionsFromMark( return context.sourceMark; case "cursorToken": { - const tokens = context.currentSelections.map((selection) => { - const token = context.navigationMap.getTokenForRange( - selection.selection - ); - if (token == null) { - throw new Error("Couldn't find mark under cursor"); + const tokenSelections = context.currentSelections.map((selection) => { + const tokenSelection = + context.navigationMap.getTokenSelectionForSelection(selection); + if (tokenSelection == null) { + throw new Error("Couldn't find token in selection"); } - return token; + return tokenSelection; }); - return tokens.map((token) => ({ - selection: new Selection(token.range.start, token.range.end), - editor: token.editor, - })); + return tokenSelections; } case "decoratedSymbol": @@ -334,6 +332,15 @@ function transformSelection( const activeIndex = modifier.active < 0 ? modifier.active + pieces.length : modifier.active; + if ( + anchorIndex < 0 || + activeIndex < 0 || + anchorIndex >= pieces.length || + activeIndex >= pieces.length + ) { + throw new Error("Subtoken index out of range"); + } + const isReversed = activeIndex < anchorIndex; const anchor = selection.selection.start.translate( @@ -461,6 +468,8 @@ function createTypedSelection( selectionContext: getTokenSelectionContext( selection, modifier, + position, + insideOutsideType, selectionContext ), }; @@ -600,6 +609,8 @@ function performPositionAdjustment( function getTokenSelectionContext( selection: SelectionWithEditor, modifier: Modifier, + position: TargetPosition, + insideOutsideType: InsideOutsideType, selectionContext: SelectionContext ): SelectionContext { if (!isSelectionContextEmpty(selectionContext)) { @@ -611,32 +622,38 @@ function getTokenSelectionContext( const document = selection.editor.document; const { start, end } = selection.selection; - - const startLine = document.lineAt(start); - const leadingText = startLine.text.slice(0, start.character); - const leadingDelimiters = leadingText.match(/\s+$/); - const leadingDelimiterRange = - leadingDelimiters != null - ? new Range( - start.line, - start.character - leadingDelimiters[0].length, - start.line, - start.character - ) - : null; - const endLine = document.lineAt(end); - const trailingText = endLine.text.slice(end.character); - const trailingDelimiters = trailingText.match(/^\s+/); - const trailingDelimiterRange = - trailingDelimiters != null - ? new Range( - end.line, - end.character, - end.line, - end.character + trailingDelimiters[0].length - ) - : null; + let leadingDelimiterRange, trailingDelimiterRange; + + // Position start/end of has no delimiter + if (position !== "before" || insideOutsideType !== "inside") { + const startLine = document.lineAt(start); + const leadingText = startLine.text.slice(0, start.character); + const leadingDelimiters = leadingText.match(/\s+$/); + leadingDelimiterRange = + leadingDelimiters != null + ? new Range( + start.line, + start.character - leadingDelimiters[0].length, + start.line, + start.character + ) + : null; + } + + if (position !== "after" || insideOutsideType !== "inside") { + const trailingText = endLine.text.slice(end.character); + const trailingDelimiters = trailingText.match(/^\s+/); + trailingDelimiterRange = + trailingDelimiters != null + ? new Range( + end.line, + end.character, + end.line, + end.character + trailingDelimiters[0].length + ) + : null; + } const isInDelimitedList = (leadingDelimiterRange != null || trailingDelimiterRange != null) && diff --git a/src/test/suite/fixtures/recorded/compoundTargets/takePastEndOfToken.yml b/src/test/suite/fixtures/recorded/compoundTargets/takePastEndOfToken.yml new file mode 100644 index 0000000000..99c1363520 --- /dev/null +++ b/src/test/suite/fixtures/recorded/compoundTargets/takePastEndOfToken.yml @@ -0,0 +1,26 @@ +spokenForm: take past end of token +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: {type: primitive} + end: {type: primitive, position: after, insideOutsideType: inside, selectionType: token} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: {} +initialState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 8} +finalState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 11} + thatMark: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 11} +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: cursorToken}, selectionType: token, position: after, modifier: {type: identity}, insideOutsideType: inside}}] diff --git a/src/test/suite/fixtures/recorded/compoundTargets/takePastStartOfToken.yml b/src/test/suite/fixtures/recorded/compoundTargets/takePastStartOfToken.yml new file mode 100644 index 0000000000..0b6d248283 --- /dev/null +++ b/src/test/suite/fixtures/recorded/compoundTargets/takePastStartOfToken.yml @@ -0,0 +1,26 @@ +spokenForm: take past start of token +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: {type: primitive} + end: {type: primitive, position: before, insideOutsideType: inside, selectionType: token} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: {} +initialState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 8} +finalState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 6} + thatMark: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 6} +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: cursorToken}, selectionType: token, position: before, modifier: {type: identity}, insideOutsideType: inside}}] diff --git a/src/test/suite/fixtures/recorded/compoundTargets/takePastTrap.yml b/src/test/suite/fixtures/recorded/compoundTargets/takePastTrap.yml new file mode 100644 index 0000000000..464eea0521 --- /dev/null +++ b/src/test/suite/fixtures/recorded/compoundTargets/takePastTrap.yml @@ -0,0 +1,31 @@ +spokenForm: take past trap +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: {type: primitive} + end: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: t} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.t: + start: {line: 0, character: 6} + end: {line: 0, character: 11} +initialState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} +finalState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 11} + thatMark: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 11} +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: t}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}}] diff --git a/src/test/suite/fixtures/recorded/compoundTargets/takeTokenPastTrap.yml b/src/test/suite/fixtures/recorded/compoundTargets/takeTokenPastTrap.yml new file mode 100644 index 0000000000..da86ce7fc4 --- /dev/null +++ b/src/test/suite/fixtures/recorded/compoundTargets/takeTokenPastTrap.yml @@ -0,0 +1,31 @@ +spokenForm: take token past trap +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: {type: primitive, selectionType: token} + end: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: t} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.t: + start: {line: 0, character: 6} + end: {line: 0, character: 11} +initialState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} +finalState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 11} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 11} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: cursorToken}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: t}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}}] diff --git a/src/test/suite/fixtures/recorded/languages/cpp/takeAttribute.yml b/src/test/suite/fixtures/recorded/languages/cpp/takeAttribute.yml index 2eca2cebec..92d42b1bb5 100644 --- a/src/test/suite/fixtures/recorded/languages/cpp/takeAttribute.yml +++ b/src/test/suite/fixtures/recorded/languages/cpp/takeAttribute.yml @@ -4,7 +4,7 @@ command: actionName: setSelection partialTargets: - type: primitive - modifier: {type: containingScope, scopeType: xmlAttribute} + modifier: {type: containingScope, scopeType: attribute} extraArgs: [] marks: {} initialState: @@ -20,4 +20,4 @@ finalState: thatMark: - anchor: {line: 0, character: 0} active: {line: 0, character: 13} -fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: xmlAttribute}, insideOutsideType: inside}] +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: attribute}, insideOutsideType: inside}] diff --git a/src/test/suite/fixtures/recorded/languages/jsx/takeAttribute.yml b/src/test/suite/fixtures/recorded/languages/jsx/takeAttribute.yml index 8f4cbd3f99..15d46f1517 100644 --- a/src/test/suite/fixtures/recorded/languages/jsx/takeAttribute.yml +++ b/src/test/suite/fixtures/recorded/languages/jsx/takeAttribute.yml @@ -4,7 +4,7 @@ command: actionName: setSelection partialTargets: - type: primitive - modifier: {type: containingScope, scopeType: xmlAttribute} + modifier: {type: containingScope, scopeType: attribute} extraArgs: [] marks: {} initialState: @@ -28,4 +28,4 @@ finalState: thatMark: - anchor: {line: 2, character: 7} active: {line: 2, character: 15} -fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: xmlAttribute}, insideOutsideType: inside}] +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: attribute}, insideOutsideType: inside}] diff --git a/src/test/suite/fixtures/recorded/languages/jsx/takeEveryAttribute.yml b/src/test/suite/fixtures/recorded/languages/jsx/takeEveryAttribute.yml index 03de9e8900..09027e3f4c 100644 --- a/src/test/suite/fixtures/recorded/languages/jsx/takeEveryAttribute.yml +++ b/src/test/suite/fixtures/recorded/languages/jsx/takeEveryAttribute.yml @@ -4,7 +4,7 @@ command: actionName: setSelection partialTargets: - type: primitive - modifier: {type: containingScope, scopeType: xmlAttribute, includeSiblings: true} + modifier: {type: containingScope, scopeType: attribute, includeSiblings: true} extraArgs: [] marks: {} initialState: @@ -32,4 +32,4 @@ finalState: active: {line: 2, character: 15} - anchor: {line: 2, character: 16} active: {line: 2, character: 26} -fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: xmlAttribute, includeSiblings: true}, insideOutsideType: inside}] +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: attribute, includeSiblings: true}, insideOutsideType: inside}] diff --git a/src/test/suite/fixtures/recorded/positions/chuckPastAfterLook.yml b/src/test/suite/fixtures/recorded/positions/chuckPastAfterLook.yml new file mode 100644 index 0000000000..dca4f3429e --- /dev/null +++ b/src/test/suite/fixtures/recorded/positions/chuckPastAfterLook.yml @@ -0,0 +1,32 @@ +spokenForm: chuck past after look +languageId: typescript +command: + actionName: delete + partialTargets: + - type: range + start: {type: primitive} + end: + type: primitive + position: after + mark: {type: decoratedSymbol, symbolColor: default, character: l} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.l: + start: {line: 0, character: 0} + end: {line: 0, character: 5} +initialState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} +finalState: + documentContents: hethere + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} + thatMark: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: outside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: l}, selectionType: token, position: after, modifier: {type: identity}, insideOutsideType: outside}}] diff --git a/src/test/suite/fixtures/recorded/positions/chuckPastBeforeTrap.yml b/src/test/suite/fixtures/recorded/positions/chuckPastBeforeTrap.yml new file mode 100644 index 0000000000..0a2c17f5a1 --- /dev/null +++ b/src/test/suite/fixtures/recorded/positions/chuckPastBeforeTrap.yml @@ -0,0 +1,32 @@ +spokenForm: chuck past before trap +languageId: typescript +command: + actionName: delete + partialTargets: + - type: range + start: {type: primitive} + end: + type: primitive + position: before + mark: {type: decoratedSymbol, symbolColor: default, character: t} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.t: + start: {line: 0, character: 6} + end: {line: 0, character: 11} +initialState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 8} +finalState: + documentContents: helloere + selections: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 5} + thatMark: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 5} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: outside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: t}, selectionType: token, position: before, modifier: {type: identity}, insideOutsideType: outside}}] diff --git a/src/test/suite/fixtures/recorded/positions/chuckPastEndOfLine.yml b/src/test/suite/fixtures/recorded/positions/chuckPastEndOfLine.yml new file mode 100644 index 0000000000..ec5ec8b38e --- /dev/null +++ b/src/test/suite/fixtures/recorded/positions/chuckPastEndOfLine.yml @@ -0,0 +1,26 @@ +spokenForm: chuck past end of line +languageId: typescript +command: + actionName: delete + partialTargets: + - type: range + start: {type: primitive} + end: {type: primitive, position: after, insideOutsideType: inside, selectionType: line} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: {} +initialState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} +finalState: + documentContents: he + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} + thatMark: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: outside}, end: {type: primitive, mark: {type: cursor}, selectionType: line, position: after, modifier: {type: identity}, insideOutsideType: inside}}] diff --git a/src/test/suite/fixtures/recorded/positions/chuckPastEndOfLook.yml b/src/test/suite/fixtures/recorded/positions/chuckPastEndOfLook.yml new file mode 100644 index 0000000000..12bef1e266 --- /dev/null +++ b/src/test/suite/fixtures/recorded/positions/chuckPastEndOfLook.yml @@ -0,0 +1,33 @@ +spokenForm: chuck past end of look +languageId: typescript +command: + actionName: delete + partialTargets: + - type: range + start: {type: primitive} + end: + type: primitive + position: after + insideOutsideType: inside + mark: {type: decoratedSymbol, symbolColor: default, character: l} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.l: + start: {line: 0, character: 0} + end: {line: 0, character: 5} +initialState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} +finalState: + documentContents: he there + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} + thatMark: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: outside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: l}, selectionType: token, position: after, modifier: {type: identity}, insideOutsideType: inside}}] diff --git a/src/test/suite/fixtures/recorded/positions/chuckPastStartOfTrap.yml b/src/test/suite/fixtures/recorded/positions/chuckPastStartOfTrap.yml new file mode 100644 index 0000000000..bacb8543da --- /dev/null +++ b/src/test/suite/fixtures/recorded/positions/chuckPastStartOfTrap.yml @@ -0,0 +1,33 @@ +spokenForm: chuck past start of trap +languageId: typescript +command: + actionName: delete + partialTargets: + - type: range + start: {type: primitive} + end: + type: primitive + position: before + insideOutsideType: inside + mark: {type: decoratedSymbol, symbolColor: default, character: t} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.t: + start: {line: 0, character: 6} + end: {line: 0, character: 11} +initialState: + documentContents: hello there + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 8} +finalState: + documentContents: hello ere + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} + thatMark: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: outside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: t}, selectionType: token, position: before, modifier: {type: identity}, insideOutsideType: inside}}] diff --git a/src/test/suite/fixtures/recorded/subtoken/takeFirstChar.yml b/src/test/suite/fixtures/recorded/subtoken/takeFirstChar.yml new file mode 100644 index 0000000000..4a78aa7dd7 --- /dev/null +++ b/src/test/suite/fixtures/recorded/subtoken/takeFirstChar.yml @@ -0,0 +1,24 @@ +spokenForm: take first char +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: primitive + selectionType: token + modifier: {type: subpiece, pieceType: character, anchor: 0, active: 0} + extraArgs: [] +marks: {} +initialState: + documentContents: //aa + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} +finalState: + documentContents: //aa + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 3} + thatMark: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 3} +fullTargets: [{type: primitive, mark: {type: cursorToken}, selectionType: token, position: contents, modifier: {type: subpiece, pieceType: character, anchor: 0, active: 0}, insideOutsideType: inside}] diff --git a/src/test/suite/fixtures/recorded/subtoken/takeFirstChar2.yml b/src/test/suite/fixtures/recorded/subtoken/takeFirstChar2.yml new file mode 100644 index 0000000000..7d8c305754 --- /dev/null +++ b/src/test/suite/fixtures/recorded/subtoken/takeFirstChar2.yml @@ -0,0 +1,24 @@ +spokenForm: take first char +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: primitive + selectionType: token + modifier: {type: subpiece, pieceType: character, anchor: 0, active: 0} + extraArgs: [] +marks: {} +initialState: + documentContents: aa// + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} +finalState: + documentContents: aa// + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 1} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 1} +fullTargets: [{type: primitive, mark: {type: cursorToken}, selectionType: token, position: contents, modifier: {type: subpiece, pieceType: character, anchor: 0, active: 0}, insideOutsideType: inside}] diff --git a/src/test/suite/fixtures/recorded/subtoken/takeFirstChar3.yml b/src/test/suite/fixtures/recorded/subtoken/takeFirstChar3.yml new file mode 100644 index 0000000000..6d2196f033 --- /dev/null +++ b/src/test/suite/fixtures/recorded/subtoken/takeFirstChar3.yml @@ -0,0 +1,24 @@ +spokenForm: take first char +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: primitive + selectionType: token + modifier: {type: subpiece, pieceType: character, anchor: 0, active: 0} + extraArgs: [] +marks: {} +initialState: + documentContents: ///** + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} +finalState: + documentContents: ///** + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 1} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 1} +fullTargets: [{type: primitive, mark: {type: cursorToken}, selectionType: token, position: contents, modifier: {type: subpiece, pieceType: character, anchor: 0, active: 0}, insideOutsideType: inside}] diff --git a/src/test/suite/fixtures/recorded/subtoken/takeFirstChar4.yml b/src/test/suite/fixtures/recorded/subtoken/takeFirstChar4.yml new file mode 100644 index 0000000000..aba9d12811 --- /dev/null +++ b/src/test/suite/fixtures/recorded/subtoken/takeFirstChar4.yml @@ -0,0 +1,24 @@ +spokenForm: take first char +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: primitive + selectionType: token + modifier: {type: subpiece, pieceType: character, anchor: 0, active: 0} + extraArgs: [] +marks: {} +initialState: + documentContents: //*** + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} +finalState: + documentContents: //*** + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 3} + thatMark: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 3} +fullTargets: [{type: primitive, mark: {type: cursorToken}, selectionType: token, position: contents, modifier: {type: subpiece, pieceType: character, anchor: 0, active: 0}, insideOutsideType: inside}] diff --git a/src/test/suite/fixtures/recorded/subtoken/takeFirstChar5.yml b/src/test/suite/fixtures/recorded/subtoken/takeFirstChar5.yml new file mode 100644 index 0000000000..419385b1c4 --- /dev/null +++ b/src/test/suite/fixtures/recorded/subtoken/takeFirstChar5.yml @@ -0,0 +1,24 @@ +spokenForm: take first char +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: primitive + selectionType: token + modifier: {type: subpiece, pieceType: character, anchor: 0, active: 0} + extraArgs: [] +marks: {} +initialState: + documentContents: //** + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} +finalState: + documentContents: //** + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 1} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 1} +fullTargets: [{type: primitive, mark: {type: cursorToken}, selectionType: token, position: contents, modifier: {type: subpiece, pieceType: character, anchor: 0, active: 0}, insideOutsideType: inside}] diff --git a/src/test/suite/fixtures/recorded/subtoken/takeFirstWord.yml b/src/test/suite/fixtures/recorded/subtoken/takeFirstWord.yml new file mode 100644 index 0000000000..f05574a7ca --- /dev/null +++ b/src/test/suite/fixtures/recorded/subtoken/takeFirstWord.yml @@ -0,0 +1,24 @@ +spokenForm: take first word +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: primitive + selectionType: token + modifier: {type: subpiece, pieceType: word, anchor: 0, active: 0} + extraArgs: [] +marks: {} +initialState: + documentContents: ;helloThere; + selections: + - anchor: {line: 0, character: 11} + active: {line: 0, character: 11} +finalState: + documentContents: ;helloThere; + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 6} + thatMark: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 6} +fullTargets: [{type: primitive, mark: {type: cursorToken}, selectionType: token, position: contents, modifier: {type: subpiece, pieceType: word, anchor: 0, active: 0}, insideOutsideType: inside}] diff --git a/src/test/suite/fixtures/recorded/subtoken/takeSecondCharLookPastEndOfToken.yml b/src/test/suite/fixtures/recorded/subtoken/takeSecondCharLookPastEndOfToken.yml new file mode 100644 index 0000000000..114ccb0d63 --- /dev/null +++ b/src/test/suite/fixtures/recorded/subtoken/takeSecondCharLookPastEndOfToken.yml @@ -0,0 +1,33 @@ +spokenForm: take second char look past end of token +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: + type: primitive + selectionType: token + modifier: {type: subpiece, pieceType: character, anchor: 1, active: 1} + mark: {type: decoratedSymbol, symbolColor: default, character: l} + end: {type: primitive, position: after, insideOutsideType: inside, selectionType: token} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.l: + start: {line: 0, character: 4} + end: {line: 0, character: 9} +initialState: + documentContents: " hello there" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: " hello there" + selections: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 9} + thatMark: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 9} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: l}, selectionType: token, position: contents, modifier: {type: subpiece, pieceType: character, anchor: 1, active: 1}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: l}, selectionType: token, position: after, modifier: {type: identity}, insideOutsideType: inside}}] diff --git a/src/test/suite/fixtures/recorded/subtoken/takeSecondCharLookPastSecondCharTrap.yml b/src/test/suite/fixtures/recorded/subtoken/takeSecondCharLookPastSecondCharTrap.yml new file mode 100644 index 0000000000..219b635e34 --- /dev/null +++ b/src/test/suite/fixtures/recorded/subtoken/takeSecondCharLookPastSecondCharTrap.yml @@ -0,0 +1,40 @@ +spokenForm: take second char look past second char trap +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: range + start: + type: primitive + selectionType: token + modifier: {type: subpiece, pieceType: character, anchor: 1, active: 1} + mark: {type: decoratedSymbol, symbolColor: default, character: l} + end: + type: primitive + selectionType: token + modifier: {type: subpiece, pieceType: character, anchor: 1, active: 1} + mark: {type: decoratedSymbol, symbolColor: default, character: t} + excludeStart: false + excludeEnd: false + extraArgs: [] +marks: + default.l: + start: {line: 0, character: 4} + end: {line: 0, character: 9} + default.t: + start: {line: 0, character: 10} + end: {line: 0, character: 15} +initialState: + documentContents: " hello there" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: " hello there" + selections: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 12} + thatMark: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 12} +fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: l}, selectionType: token, position: contents, modifier: {type: subpiece, pieceType: character, anchor: 1, active: 1}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: t}, selectionType: token, position: contents, modifier: {type: subpiece, pieceType: character, anchor: 1, active: 1}, insideOutsideType: inside}}] diff --git a/src/test/suite/fixtures/recorded/subtoken/takeSecondWord.yml b/src/test/suite/fixtures/recorded/subtoken/takeSecondWord.yml new file mode 100644 index 0000000000..c0a700246f --- /dev/null +++ b/src/test/suite/fixtures/recorded/subtoken/takeSecondWord.yml @@ -0,0 +1,24 @@ +spokenForm: take second word +languageId: typescript +command: + actionName: setSelection + partialTargets: + - type: primitive + selectionType: token + modifier: {type: subpiece, pieceType: word, anchor: 1, active: 1} + extraArgs: [] +marks: {} +initialState: + documentContents: ;helloThere; + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} +finalState: + documentContents: ;helloThere; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 11} + thatMark: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 11} +fullTargets: [{type: primitive, mark: {type: cursorToken}, selectionType: token, position: contents, modifier: {type: subpiece, pieceType: word, anchor: 1, active: 1}, insideOutsideType: inside}]