Skip to content

implement 'short paint' scope type #737

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 43 commits into from
Jul 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
5a9d592
implement 'small paint' scope type
AndrewDant Jun 5, 2022
6caf71c
additional test with white space on each side
AndrewDant Jun 5, 2022
b12268c
Merge remote-tracking branch 'origin/main' into small-paint
AndrewDant Jun 10, 2022
aa18213
updates to get small paint working besides removal range
AndrewDant Jun 20, 2022
dc864a9
stage refactors recommended during pair programming
AndrewDant Jun 20, 2022
d173bc9
Merge remote-tracking branch 'origin/main' into small-paint
AndrewDant Jun 20, 2022
87498e2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 21, 2022
cf6cc31
revert some refactors after merging
AndrewDant Jun 21, 2022
079642a
Merge remote-tracking branch 'AndrewDant/small-paint' into small-paint
AndrewDant Jun 21, 2022
b13f110
Merge remote-tracking branch 'origin/main' into small-paint
AndrewDant Jun 22, 2022
3506d29
Updates small paint to stop on any surrounding pairs' delimiter
AndrewDant Jun 23, 2022
b085811
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 23, 2022
59012b4
some additional small paint tests
AndrewDant Jun 23, 2022
7c87e2e
Merge remote-tracking branch 'AndrewDant/small-paint' into small-paint
AndrewDant Jun 23, 2022
e66d129
Merge branch 'main' into small-paint
AndreasArvidsson Jun 23, 2022
a0b62d7
rename small paint stage
AndrewDant Jun 27, 2022
5ce21ca
remove unnecessary changed
AndrewDant Jun 27, 2022
3bad85d
Merge branch 'main' into small-paint
AndrewDant Jun 27, 2022
00080e4
update default phrase from small paint to short paint
AndrewDant Jun 27, 2022
4f3ab2f
Merge remote-tracking branch 'AndrewDant/small-paint' into small-paint
AndrewDant Jun 27, 2022
bb3d2c2
update short paint every behavior
AndrewDant Jun 27, 2022
3d2fdca
update short paint to call process surrounding pair directly
AndrewDant Jun 28, 2022
fa6d7c2
fix short paint stage capitalization
AndrewDant Jun 28, 2022
23cf6bd
fix short paint modifier capitalization
AndrewDant Jun 28, 2022
831604c
Merge branch 'main' into small-paint
AndrewDant Jun 28, 2022
c66e219
update small paint every tests and scope type in tests
AndrewDant Jun 28, 2022
df27e7d
refactor short paint
AndrewDant Jun 28, 2022
4111578
Merge remote-tracking branch 'AndrewDant/small-paint' into small-paint
AndrewDant Jun 28, 2022
c8c1db9
Switch to strong containment implementation
pokey Jun 28, 2022
18c33c4
Merge branch 'main' into small-paint
AndreasArvidsson Jun 28, 2022
a0feece
fix bug with paint selection type
AndrewDant Jun 28, 2022
a887b04
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 28, 2022
9f51cce
fix a reference to short paint stage name
AndrewDant Jun 28, 2022
e69e682
remove outdated tests
AndrewDant Jun 28, 2022
61523c3
boundedNonWhitespaceStage -> BoundedNonWhitespaceStage
pokey Jun 29, 2022
03c0500
Cleanup
pokey Jun 29, 2022
23bc193
paint and short paint tests
AndrewDant Jun 30, 2022
8d19fac
Merge remote-tracking branch 'AndrewDant/small-paint' into small-paint
AndrewDant Jun 30, 2022
ca690e4
better change every paint pair tests
AndrewDant Jun 30, 2022
436b0df
Merge branch 'main' into small-paint
AndreasArvidsson Jun 30, 2022
65c4181
Merge branch 'main' into small-paint
AndrewDant Jul 1, 2022
d111942
Merge branch 'main' into small-paint
pokey Jul 3, 2022
be1a56c
Merge branch 'main' into small-paint
pokey Jul 3, 2022
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
1 change: 1 addition & 0 deletions cursorless-talon/src/modifiers/containing_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"file": "document",
"line": "line",
"paint": "nonWhitespaceSequence",
"short paint": "boundedNonWhitespaceSequence",
"link": "url",
"token": "token",
}
Expand Down
7 changes: 7 additions & 0 deletions src/processTargets/getModifierStage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import {
UrlStage,
} from "./modifiers/scopeTypeStages/RegexStage";
import TokenStage from "./modifiers/scopeTypeStages/TokenStage";
import BoundedNonWhitespaceSequenceStage, {
BoundedNonWhitespaceSequenceModifier,
} from "./modifiers/BoundedNonWhitespaceStage";
import SurroundingPairStage from "./modifiers/SurroundingPairStage";
import { ModifierStage } from "./PipelineStages.types";

Expand Down Expand Up @@ -89,6 +92,10 @@ const getContainingScopeStage = (
return new NonWhitespaceSequenceStage(
modifier as NonWhitespaceSequenceModifier
);
case "boundedNonWhitespaceSequence":
return new BoundedNonWhitespaceSequenceStage(
modifier as BoundedNonWhitespaceSequenceModifier
);
case "url":
return new UrlStage(modifier as UrlModifier);
case "surroundingPair":
Expand Down
77 changes: 77 additions & 0 deletions src/processTargets/modifiers/BoundedNonWhitespaceStage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Target } from "../../typings/target.types";
import {
ContainingScopeModifier,
EveryScopeModifier,
} from "../../typings/targetDescriptor.types";
import { ProcessedTargetsContext } from "../../typings/Types";
import { ModifierStage } from "../PipelineStages.types";
import { TokenTarget } from "../targets";
import getModifierStage from "../getModifierStage";
import { processSurroundingPair } from "./surroundingPair";
import { NoContainingScopeError } from "../../errors";

export type BoundedNonWhitespaceSequenceModifier = (
| ContainingScopeModifier
| EveryScopeModifier
) & {
scopeType: { type: "boundedNonWhitespaceSequence" };
};

/**
* Intersection of NonWhitespaceSequenceStage and a surrounding pair
* Expand the target until reaching a white space or surrounding pair.
* If there is no surrounding pair defaults to the non white space sequence
*/
export default class BoundedNonWhitespaceSequenceStage
implements ModifierStage
{
constructor(private modifier: BoundedNonWhitespaceSequenceModifier) {}

run(context: ProcessedTargetsContext, target: Target): Target[] {
const paintStage = getModifierStage({
type: this.modifier.type,
scopeType: { type: "nonWhitespaceSequence" },
});

const paintTargets = paintStage.run(context, target);

const pairInfo = processSurroundingPair(
context,
target.editor,
target.contentRange,
{
type: "surroundingPair",
delimiter: "any",
requireStrongContainment: true,
}
);

if (pairInfo == null) {
return paintTargets;
}

const targets = paintTargets.flatMap((paintTarget) => {
const contentRange = paintTarget.contentRange.intersection(
pairInfo.interiorRange
);

if (contentRange == null || contentRange.isEmpty) {
return [];
}

return [
new TokenTarget({
editor: target.editor,
isReversed: target.isReversed,
contentRange,
}),
];
});

if (targets.length === 0) {
throw new NoContainingScopeError(this.modifier.scopeType.type);
}

return targets;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ class RegexStage implements ModifierStage {
for (let i = start.line; i <= end.line; ++i) {
this.getMatchesForLine(editor, i).forEach((range) => {
// Regex match and selection intersects
if (range.end.isAfterOrEqual(start) && range.end.isBeforeOrEqual(end)) {
if (
range.end.isAfterOrEqual(start) &&
range.start.isBeforeOrEqual(end)
) {
targets.push(this.getTargetFromRange(target, range));
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { SurroundingPairScopeType } from "../../../typings/targetDescriptor.types";
import { findOppositeDelimiter } from "./findOppositeDelimiter";
import { getSurroundingPairOffsets } from "./getSurroundingPairOffsets";
import {
SurroundingPairOffsets,
DelimiterOccurrence,
Offsets,
PossibleDelimiterOccurrence,
DelimiterOccurrence,
SurroundingPairOffsets,
} from "./types";
import { findOppositeDelimiter } from "./findOppositeDelimiter";
import { weaklyContains } from "./weaklyContains";

/**
* Looks for a surrounding pair where one of its delimiters contains the entire selection.
Expand All @@ -28,7 +30,7 @@ export function findDelimiterPairAdjacentToSelection(
initialIndex: number,
delimiterOccurrences: PossibleDelimiterOccurrence[],
selectionOffsets: Offsets,
forceDirection: "left" | "right" | undefined,
scopeType: SurroundingPairScopeType,
bailOnUnmatchedAdjacent: boolean = false
): SurroundingPairOffsets | null {
const indicesToTry = [initialIndex + 1, initialIndex];
Expand All @@ -38,8 +40,7 @@ export function findDelimiterPairAdjacentToSelection(

if (
delimiterOccurrence != null &&
delimiterOccurrence.offsets.start <= selectionOffsets.start &&
delimiterOccurrence.offsets.end >= selectionOffsets.end
weaklyContains(delimiterOccurrence.offsets, selectionOffsets)
) {
const { delimiterInfo } = delimiterOccurrence;

Expand All @@ -48,14 +49,23 @@ export function findDelimiterPairAdjacentToSelection(
delimiterOccurrences,
index,
delimiterInfo,
forceDirection
scopeType.forceDirection
);

if (possibleMatch != null) {
return getSurroundingPairOffsets(
const surroundingPairOffsets = getSurroundingPairOffsets(
delimiterOccurrence as DelimiterOccurrence,
possibleMatch
);

if (
!scopeType.requireStrongContainment ||
(surroundingPairOffsets.leftDelimiter.start <
selectionOffsets.start &&
surroundingPairOffsets.rightDelimiter.end > selectionOffsets.end)
) {
return surroundingPairOffsets;
}
} else if (bailOnUnmatchedAdjacent) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { sortedIndexBy } from "lodash";
import { SimpleSurroundingPairName } from "../../../typings/targetDescriptor.types";
import {
SimpleSurroundingPairName,
SurroundingPairScopeType,
} from "../../../typings/targetDescriptor.types";
import { findDelimiterPairAdjacentToSelection } from "./findDelimiterPairAdjacentToSelection";
import { findDelimiterPairContainingSelection } from "./findDelimiterPairContainingSelection";
import {
Expand Down Expand Up @@ -30,7 +33,7 @@ import {
* @returns
*/
export function findSurroundingPairCore(
forceDirection: "left" | "right" | undefined,
scopeType: SurroundingPairScopeType,
delimiterOccurrences: PossibleDelimiterOccurrence[],
acceptableDelimiters: SimpleSurroundingPairName[],
selectionOffsets: Offsets,
Expand All @@ -57,7 +60,7 @@ export function findSurroundingPairCore(
initialIndex,
delimiterOccurrences,
selectionOffsets,
forceDirection,
scopeType,
bailOnUnmatchedAdjacent
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Range, TextDocument, TextEditor } from "vscode";
import { SyntaxNode } from "web-tree-sitter";
import {
SimpleSurroundingPairName,
SurroundingPairDirection,
SurroundingPairScopeType,
} from "../../../typings/targetDescriptor.types";
import { getNodeRange } from "../../../util/nodeSelectors";
import { isContainedInErrorNode } from "../../../util/treeSitterUtils";
Expand Down Expand Up @@ -62,7 +62,7 @@ export function findSurroundingPairParseTreeBased(
selection: Range,
node: SyntaxNode,
delimiters: SimpleSurroundingPairName[],
forceDirection: "left" | "right" | undefined
scopeType: SurroundingPairScopeType
) {
const document: TextDocument = editor.document;

Expand All @@ -88,7 +88,7 @@ export function findSurroundingPairParseTreeBased(
individualDelimiters,
delimiters,
selectionOffsets,
forceDirection,
scopeType,
};

// Walk up the parse tree from parent to parent until we find a node whose
Expand Down Expand Up @@ -149,7 +149,7 @@ interface Context {
*/
selectionOffsets: Offsets;

forceDirection: SurroundingPairDirection | undefined;
scopeType: SurroundingPairScopeType;
}

/**
Expand All @@ -170,7 +170,7 @@ function findSurroundingPairContainedInNode(
individualDelimiters,
delimiters,
selectionOffsets,
forceDirection,
scopeType,
} = context;

/**
Expand Down Expand Up @@ -213,7 +213,7 @@ function findSurroundingPairContainedInNode(
// approach might not always work, but seems to work in the
// languages we've tried.
let side =
delimiterInfo.side === "unknown" && forceDirection == null
delimiterInfo.side === "unknown" && scopeType.forceDirection == null
? inferDelimiterSide(delimiterNode)
: delimiterInfo.side;

Expand All @@ -227,7 +227,7 @@ function findSurroundingPairContainedInNode(

// Just run core algorithm once we have our list of delimiters.
return findSurroundingPairCore(
forceDirection,
scopeType,
delimiterOccurrences,
delimiters,
selectionOffsets,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { escapeRegExp, findLast, uniq } from "lodash";
import { Range, TextDocument, TextEditor } from "vscode";
import {
SimpleSurroundingPairName,
SurroundingPairDirection,
SurroundingPairName,
SurroundingPairScopeType,
} from "../../../typings/targetDescriptor.types";
import { getDocumentRange } from "../../../util/range";
import { matchAll } from "../../../util/regex";
Expand Down Expand Up @@ -70,7 +70,7 @@ export function findSurroundingPairTextBased(
range: Range,
allowableRange: Range | null,
delimiters: SimpleSurroundingPairName[],
forceDirection: "left" | "right" | undefined
scopeType: SurroundingPairScopeType
) {
const document: TextDocument = editor.document;
const fullRange = allowableRange ?? getDocumentRange(document);
Expand Down Expand Up @@ -106,7 +106,7 @@ export function findSurroundingPairTextBased(
* Context to pass to nested call
*/
const context: Context = {
forceDirection,
scopeType,
delimiterRegex,
delimiters,
delimiterTextToDelimiterInfoMap,
Expand Down Expand Up @@ -196,7 +196,7 @@ function getDelimiterRegex(individualDelimiters: IndividualDelimiter[]) {
* Context to pass to nested call
*/
interface Context {
forceDirection: SurroundingPairDirection | undefined;
scopeType: SurroundingPairScopeType;
delimiterTextToDelimiterInfoMap: {
[k: string]: IndividualDelimiter;
};
Expand Down Expand Up @@ -229,11 +229,12 @@ function getDelimiterPairOffsets(
isAtEndOfFullRange: boolean
): SurroundingPairOffsets | null {
const {
forceDirection,
scopeType,
delimiterTextToDelimiterInfoMap,
delimiterRegex,
delimiters,
} = context;
const { forceDirection } = scopeType;

// XXX: The below is a bit wasteful when there are multiple targets, because
// this whole function gets run once per target, so we're re-running this
Expand Down Expand Up @@ -290,7 +291,7 @@ function getDelimiterPairOffsets(

// Then just run core algorithm
const surroundingPair = findSurroundingPairCore(
forceDirection,
scopeType,
delimiterOccurrences,
delimiters,
selectionOffsets,
Expand Down
17 changes: 9 additions & 8 deletions src/processTargets/modifiers/surroundingPair/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,22 @@ import { findSurroundingPairTextBased } from "./findSurroundingPairTextBased";
* smallest pair of delimiters which contains the selection.
*
* @param context Context to be leveraged by modifier
* @param selection The selection to process
* @param modifier The surrounding pair modifier information
* @param editor The editor containing the range
* @param range The range to process
* @param scopeType The surrounding pair modifier information
* @returns The new selection expanded to the containing surrounding pair or
* `null` if none was found
*/
export function processSurroundingPair(
context: ProcessedTargetsContext,
editor: TextEditor,
range: Range,
modifier: SurroundingPairScopeType
scopeType: SurroundingPairScopeType
): SurroundingPairInfo | null {
const document = editor.document;
const delimiters = complexDelimiterMap[
modifier.delimiter as ComplexSurroundingPairName
] ?? [modifier.delimiter];
scopeType.delimiter as ComplexSurroundingPairName
] ?? [scopeType.delimiter];

let node: SyntaxNode | null;
let textFragmentExtractor: TextFragmentExtractor;
Expand All @@ -53,7 +54,7 @@ export function processSurroundingPair(
range,
null,
delimiters,
modifier.forceDirection
scopeType
);
} else {
throw err;
Expand All @@ -73,7 +74,7 @@ export function processSurroundingPair(
range,
textFragmentRange,
delimiters,
modifier.forceDirection
scopeType
);

if (surroundingRange != null) {
Expand All @@ -89,6 +90,6 @@ export function processSurroundingPair(
range,
node,
delimiters,
modifier.forceDirection
scopeType
);
}
13 changes: 13 additions & 0 deletions src/processTargets/modifiers/surroundingPair/weaklyContains.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Offsets } from "./types";

/**
* Determines whether {@link offsets1} weakly contains {@link offsets2}, which
* defined as the boundaries of {@link offsets1} being inside or equal to the
* boundaries of {@link offsets2}.
* @param offsets1 The first set of offsets
* @param offsets2 The second set of offsets
* @returns `true` if {@link offsets1} weakly contains {@link offsets2}
*/
export function weaklyContains(offsets1: Offsets, offsets2: Offsets) {
return offsets1.start <= offsets2.start && offsets1.end >= offsets2.end;
}
Loading