Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions data/fixtures/recorded/relativeScopes/changeNextCall.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
languageId: typescript
command:
version: 7
spokenForm: change next call
action:
name: clearAndSetSelection
target:
type: primitive
modifiers:
- type: relativeScope
scopeType: {type: functionCall}
offset: 1
length: 1
direction: forward
usePrePhraseSnapshot: false
initialState:
documentContents: aaa(bbb()); ccc();
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks: {}
finalState:
documentContents: aaa(bbb()); ;
selections:
- anchor: {line: 0, character: 12}
active: {line: 0, character: 12}
26 changes: 26 additions & 0 deletions data/fixtures/recorded/relativeScopes/changePreviousCall.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
languageId: typescript
command:
version: 7
spokenForm: change previous call
action:
name: clearAndSetSelection
target:
type: primitive
modifiers:
- type: relativeScope
scopeType: {type: functionCall}
offset: 1
length: 1
direction: backward
usePrePhraseSnapshot: false
initialState:
documentContents: aaa(); bbb(ccc());
selections:
- anchor: {line: 0, character: 17}
active: {line: 0, character: 17}
marks: {}
finalState:
documentContents: ; bbb(ccc());
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ function generateScopesExclusive(
}

/**
* Gets the interior scope range(s) within the containing scope of
* Gets the scope range(s) within the containing scope of
* {@link initialPosition} that should be used to exclude next / previous
* scopes.
*
Expand Down Expand Up @@ -194,114 +194,101 @@ function getExcludedInteriorRanges(
initialPosition: Position,
direction: Direction,
): Range[] {
const interiorTargets =
scopeHandler.scopeType?.type === "surroundingPair"
? getSurroundingPairInteriorTargets(
scopeHandler,
editor,
initialPosition,
direction,
)
: getLanguageInteriorTargets(
scopeHandlerFactory,
scopeHandler,
editor,
initialPosition,
direction,
);

// Interiors containing the initial position are excluded. This happens when
// you are in the body of an if statement and use `next state` and in that
// case we don't want to exclude scopes within the same interior.
return interiorTargets
.map((t) =>
t instanceof InteriorTarget ? t.fullInteriorRange : t.contentRange,
)
.filter((r) => !r.contains(initialPosition));
}

function getSurroundingPairInteriorTargets(
scopeHandler: ScopeHandler,
editor: TextEditor,
initialPosition: Position,
direction: Direction,
): Target[] {
const containingScope = getContainingScope(
const containingScopeTarget = getContainingScopeTarget(
scopeHandler,
editor,
initialPosition,
direction,
);

if (containingScope == null) {
// No containing scope, nothing to exclude.
if (containingScopeTarget == null) {
return [];
}

return containingScope
.getTargets(false)
.flatMap((t) => t.getInterior() ?? []);
}
const containingInteriorTargets = containingScopeTarget.getInterior();

function getLanguageInteriorTargets(
scopeHandlerFactory: ScopeHandlerFactory,
scopeHandler: ScopeHandler,
editor: TextEditor,
initialPosition: Position,
direction: Direction,
): Target[] {
// Containing target already has an interior. eg a surrounding pair scope.
if (containingInteriorTargets != null) {
return getFilteredInteriorRanges(
containingInteriorTargets,
initialPosition,
);
}

// Fallback to language specific interior scope handler
const interiorScopeHandler = scopeHandlerFactory.maybeCreate(
{ type: "interior" },
editor.document.languageId,
);

// No interior scope handler, nothing to exclude.
// For languages that hasn't defined the interior scope handler yet we default
// to NOT excluding anything.
if (interiorScopeHandler == null) {
return [];
}

const containingScope = getContainingScope(
scopeHandler,
editor,
initialPosition,
const containingPositions = getPositions(
containingScopeTarget.contentRange,
direction,
);

if (containingScope == null) {
return [];
}

const containingInitialPosition =
direction === "forward"
? containingScope.domain.start
: containingScope.domain.end;
const containingDistalPosition =
direction === "forward"
? containingScope.domain.end
: containingScope.domain.start;

const interiorScopes = interiorScopeHandler.generateScopes(
editor,
containingInitialPosition,
containingPositions.initial,
direction,
{
skipAncestorScopes: true,
distalPosition: containingDistalPosition,
distalPosition: containingPositions.distal,
},
);

return Array.from(interiorScopes).flatMap((s) => s.getTargets(false));
const interiorTargets = Array.from(interiorScopes).flatMap((s) =>
s.getTargets(false),
);

if (interiorTargets.length > 0) {
return getFilteredInteriorRanges(interiorTargets, initialPosition);
}

// This containing scope has no interior.
// Default to excluding the entire containing scope.
return [containingScopeTarget.contentRange];
}

function getPositions(range: Range, direction: Direction) {
return direction === "forward"
? { initial: range.start, distal: range.end }
: { initial: range.end, distal: range.start };
}

function getFilteredInteriorRanges(
interiorTargets: Target[],
initialPosition: Position,
) {
// Interiors containing the initial position are excluded. This happens when
// you are in the body of an if statement and use `next state` and in that
// case we don't want to exclude scopes within the same interior.
return interiorTargets
.map((t) =>
t instanceof InteriorTarget ? t.fullInteriorRange : t.contentRange,
)
.filter((r) => !r.contains(initialPosition));
}

function getContainingScope(
function getContainingScopeTarget(
scopeHandler: ScopeHandler,
editor: TextEditor,
initialPosition: Position,
direction: Direction,
): TargetScope | undefined {
return find(
): Target | undefined {
const containingScope = find(
scopeHandler.generateScopes(editor, initialPosition, direction, {
containment: "required",
allowAdjacentScopes: true,
skipAncestorScopes: true,
}),
);
return containingScope?.getTargets(false)[0];
}
Loading