Skip to content

Add support for 'all' modifier for items within a collection #597

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

Closed
Closed
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
32 changes: 22 additions & 10 deletions cursorless-talon/src/modifiers/containing_scope.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from typing import Any
from talon import Module, app
from talon import Module, Context, app
from ..csv_overrides import init_csv_and_watch_changes

mod = Module()

ctx = Context()

mod.list("cursorless_scope_type", desc="Supported scope types")

Expand Down Expand Up @@ -46,17 +46,29 @@
"end tag": "xmlEndTag",
}

mod.list("select_multiple_modifiers", desc="modifiers for multiple selections")
multiple_modifiers = {"every", "all"}
ctx.lists["user.select_multiple_modifiers"] = multiple_modifiers
Comment on lines +49 to +51
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue that these terms should be csv-configurable



@mod.capture(rule="[every] {user.cursorless_scope_type}")
@mod.capture(rule="[{user.select_multiple_modifiers}] {user.cursorless_scope_type}")
def cursorless_containing_scope(m) -> dict[str, dict[str, Any]]:
"""Expand to containing scope"""
return {
"modifier": {
"type": "containingScope",
"scopeType": m.cursorless_scope_type,
"includeSiblings": m[0] == "every",
"""Expand to every scope"""
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely open to putting this into another file, my python is a little weak as is my understanding of how it interacts with Talon. Just lmk.

if m[0] in multiple_modifiers:
return {
"modifier": {
"type": "everyScope",
"scopeType": m.cursorless_scope_type,
"contiguousRange": m[0] == "all" and m[1] == "collectionItem",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we only support this one for collectionItem?

}
}
else:
return {
"modifier": {
"type": "containingScope",
"scopeType": m.cursorless_scope_type,
}
}
}


# NOTE: Please do not change these dicts. Use the CSVs for customization.
Expand Down
8 changes: 8 additions & 0 deletions docs/user/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Note: If you'd like to customize any of the spoken forms, please see the [docume
- [Modifiers](#modifiers)
- [Syntactic scopes](#syntactic-scopes)
- [`"every"`](#every)
- [`"all"`](#all)
- [Sub-token modifiers](#sub-token-modifiers)
- [`"word"`](#word)
- [`"char"`](#char)
Expand Down Expand Up @@ -200,6 +201,13 @@ The command `"every"` can be used to select a syntactic element and all of its m

For example, the command `take every key [blue] air` will select every key in the map/object/dict including the token with a blue hat over the letter 'a'.

##### `"all"`

The command `"all"` can be used to select items within a collection and return a single selection. This differs `"every"` in that is can only be used on collection items, not keys or values, as it returns a single selection.

- `"take all item air"`
- `"take all item"` (if cursor is currently within a key)

##### Sub-token modifiers

###### `"word"`
Expand Down
3 changes: 1 addition & 2 deletions src/actions/WrapWithSnippet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ export default class WrapWithSnippet implements Action {
? undefined
: {
type: "containingScope",
scopeType: defaultScopeType,
includeSiblings: false,
scopeType: defaultScopeType
},
},
];
Expand Down
69 changes: 41 additions & 28 deletions src/languages/getNodeMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import go from "./go";
import { patternMatchers as ruby } from "./ruby";
import { UnsupportedLanguageError } from "../errors";
import { SupportedLanguageId } from "./constants";
import { Selection } from "vscode";


export function getNodeMatcher(
languageId: string,
scopeType: ScopeType,
includeSiblings: boolean
): NodeMatcher {
const matchers = languageMatchers[languageId as SupportedLanguageId];

Expand All @@ -41,13 +42,47 @@ export function getNodeMatcher(
return notSupported;
}

if (includeSiblings) {
return matcherIncludeSiblings(matcher);
}

return matcher;
}

export function getNodeMatcherWithSiblings(
languageId: string,
scopeType: ScopeType,
contiguousRange: boolean
): NodeMatcher {
return (
selection: SelectionWithEditor,
node: SyntaxNode
): NodeMatcherValue[] | null => {

const matcher = getNodeMatcher(languageId, scopeType);
let matches = matcher(selection, node);
if (matches == null) {
return null;
}
matches = matches.flatMap((match) =>
iterateNearestIterableAncestor(
match.node,
selectionWithEditorFromRange(selection, match.selection.selection),
matcher
)
) as NodeMatcherValue[];
if (matches.length > 0) {
if (matches.length >= 2 && contiguousRange) {
const start = matches[0].selection.selection;
const end = matches[matches.length - 1].selection.selection;

matches[0].selection.selection = start.union(end) as Selection;

return [matches[0]] as NodeMatcherValue[];
} else {
return matches;
}
}
return null;
};
}

const languageMatchers: Record<
SupportedLanguageId,
Record<ScopeType, NodeMatcher>
Expand Down Expand Up @@ -75,29 +110,6 @@ const languageMatchers: Record<
xml: html,
};

function matcherIncludeSiblings(matcher: NodeMatcher): NodeMatcher {
return (
selection: SelectionWithEditor,
node: SyntaxNode
): NodeMatcherValue[] | null => {
let matches = matcher(selection, node);
if (matches == null) {
return null;
}
matches = matches.flatMap((match) =>
iterateNearestIterableAncestor(
match.node,
selectionWithEditorFromRange(selection, match.selection.selection),
matcher
)
) as NodeMatcherValue[];
if (matches.length > 0) {
return matches;
}
return null;
};
}

function iterateNearestIterableAncestor(
node: SyntaxNode,
selection: SelectionWithEditor,
Expand All @@ -115,3 +127,4 @@ function iterateNearestIterableAncestor(
}
return [];
}

6 changes: 3 additions & 3 deletions src/languages/getTextFragmentExtractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function constructDefaultTextFragmentExtractor(
languageId: SupportedLanguageId,
stringTextFragmentExtractor?: TextFragmentExtractor
): TextFragmentExtractor {
const commentNodeMatcher = getNodeMatcher(languageId, "comment", false);
const commentNodeMatcher = getNodeMatcher(languageId, "comment");
stringTextFragmentExtractor =
stringTextFragmentExtractor ??
constructDefaultStringTextFragmentExtractor(languageId);
Expand Down Expand Up @@ -54,7 +54,7 @@ function constructDefaultTextFragmentExtractor(
function constructDefaultStringTextFragmentExtractor(
languageId: SupportedLanguageId
): TextFragmentExtractor {
const stringNodeMatcher = getNodeMatcher(languageId, "string", false);
const stringNodeMatcher = getNodeMatcher(languageId, "string");

return (node: SyntaxNode, selection: SelectionWithEditor) => {
if (stringNodeMatcher(selection, node) != null) {
Expand Down Expand Up @@ -82,7 +82,7 @@ function constructDefaultStringTextFragmentExtractor(
function constructHackedStringTextFragmentExtractor(
languageId: SupportedLanguageId
) {
const stringNodeMatcher = getNodeMatcher(languageId, "string", false);
const stringNodeMatcher = getNodeMatcher(languageId, "string");

return (node: SyntaxNode, selection: SelectionWithEditor) => {
if (stringNodeMatcher(selection, node) != null) {
Expand Down
25 changes: 18 additions & 7 deletions src/processTargets/modifiers/processModifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { SUBWORD_MATCHER } from "../../core/constants";
import { selectionWithEditorFromRange } from "../../util/selectionUtils";
import {
ContainingScopeModifier,
EveryScopeModifier,
HeadModifier,
NodeMatcher,
PrimitiveTarget,
Expand All @@ -16,7 +17,7 @@ import {
TailModifier,
} from "../../typings/Types";
import { processSurroundingPair } from "./surroundingPair";
import { getNodeMatcher } from "../../languages/getNodeMatcher";
import { getNodeMatcher, getNodeMatcherWithSiblings } from "../../languages/getNodeMatcher";

export type SelectionWithEditorWithContext = {
selection: SelectionWithEditor;
Expand Down Expand Up @@ -45,6 +46,7 @@ export default function (
result = [{ selection, context: {} }];
break;

case "everyScope":
case "containingScope":
result = processScopeType(context, selection, modifier);
break;
Expand Down Expand Up @@ -81,13 +83,22 @@ export default function (
function processScopeType(
context: ProcessedTargetsContext,
selection: SelectionWithEditor,
modifier: ContainingScopeModifier
modifier: ContainingScopeModifier | EveryScopeModifier,
): SelectionWithEditorWithContext[] | null {
const nodeMatcher = getNodeMatcher(
selection.editor.document.languageId,
modifier.scopeType,
modifier.includeSiblings ?? false
);
let nodeMatcher;
if ("contiguousRange" in modifier) {
nodeMatcher = getNodeMatcherWithSiblings(
selection.editor.document.languageId,
modifier.scopeType,
modifier.contiguousRange
);
} else {
nodeMatcher = getNodeMatcher(
selection.editor.document.languageId,
modifier.scopeType
);
}

const node: SyntaxNode | null = context.getNodeAtLocation(
new Location(selection.editor.document.uri, selection.selection)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ command:
action: remove
targets:
- type: primitive
modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: true}
modifier: {type: everyScope, scopeType: argumentOrParameter, contiguousRange: false}
mark: {type: decoratedSymbol, symbolColor: default, character: m}
initialState:
documentContents: "function myFunk(value: string, name: string, age: number) { };"
Expand All @@ -24,4 +24,4 @@ finalState:
thatMark:
- anchor: {line: 0, character: 16}
active: {line: 0, character: 16}
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: m}, selectionType: token, position: contents, insideOutsideType: outside, modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: true}, isImplicit: false}]
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: m}, selectionType: token, position: contents, insideOutsideType: outside, modifier: {type: everyScope, scopeType: argumentOrParameter, contiguousRange: false}, isImplicit: false}]
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ command:
action: cutToClipboard
targets:
- type: primitive
modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: true}
modifier: {type: everyScope, scopeType: argumentOrParameter, contiguousRange: false}
mark: {type: decoratedSymbol, symbolColor: default, character: m}
initialState:
documentContents: "function myFunk(value: string, name: string, age: number) { };"
Expand All @@ -24,4 +24,4 @@ finalState:
thatMark:
- anchor: {line: 0, character: 16}
active: {line: 0, character: 16}
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: m}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: true}, isImplicit: false}]
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: m}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: everyScope, scopeType: argumentOrParameter, contiguousRange: false}, isImplicit: false}]
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ command:
action: moveToTarget
targets:
- type: primitive
modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: true}
modifier: {type: everyScope, scopeType: argumentOrParameter, contiguousRange: false}
mark: {type: decoratedSymbol, symbolColor: default, character: m}
- {type: primitive, isImplicit: true}
initialState:
Expand All @@ -31,4 +31,4 @@ finalState:
sourceMark:
- anchor: {line: 0, character: 16}
active: {line: 0, character: 16}
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: m}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: true}, isImplicit: false}, {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}, isImplicit: true}]
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: m}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: everyScope, scopeType: argumentOrParameter, contiguousRange: false}, isImplicit: false}, {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}, isImplicit: true}]
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ command:
action: clearAndSetSelection
targets:
- type: primitive
modifier: {type: containingScope, scopeType: collectionItem, includeSiblings: true}
modifier: {type: everyScope, scopeType: collectionItem, contiguousRange: false}
initialState:
documentContents: |-

Expand Down Expand Up @@ -36,4 +36,4 @@ finalState:
active: {line: 2, character: 4}
- anchor: {line: 4, character: 4}
active: {line: 4, character: 4}
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: collectionItem, includeSiblings: true}}]
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: everyScope, scopeType: collectionItem, contiguousRange: false}}]
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ command:
action: clearAndSetSelection
targets:
- type: primitive
modifier: {type: containingScope, scopeType: collectionKey, includeSiblings: true}
modifier: {type: everyScope, scopeType: collectionKey, contiguousRange: false}
initialState:
documentContents: |-

Expand Down Expand Up @@ -36,4 +36,4 @@ finalState:
active: {line: 2, character: 4}
- anchor: {line: 4, character: 4}
active: {line: 4, character: 4}
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: collectionKey, includeSiblings: true}}]
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: everyScope, scopeType: collectionKey, contiguousRange: false}}]
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ command:
action: clearAndSetSelection
targets:
- type: primitive
modifier: {type: containingScope, scopeType: value, includeSiblings: true}
modifier: {type: everyScope, scopeType: value, contiguousRange: false}
initialState:
documentContents: |-

Expand Down Expand Up @@ -36,4 +36,4 @@ finalState:
active: {line: 2, character: 9}
- anchor: {line: 4, character: 9}
active: {line: 4, character: 9}
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: value, includeSiblings: true}}]
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: everyScope, scopeType: value, contiguousRange: false}}]
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ command:
action: setSelection
targets:
- type: primitive
modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: true}
modifier: {type: everyScope, scopeType: argumentOrParameter, contiguousRange: false}
initialState:
documentContents: |-
int f(int a, int b) {
Expand All @@ -30,4 +30,4 @@ finalState:
active: {line: 1, character: 14}
- anchor: {line: 1, character: 16}
active: {line: 1, character: 17}
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: true}, insideOutsideType: inside}]
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: everyScope, scopeType: argumentOrParameter, contiguousRange: false}, insideOutsideType: inside}]
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ command:
action: setSelection
targets:
- type: primitive
modifier: {type: containingScope, scopeType: collectionItem, includeSiblings: true}
modifier: {type: everyScope, scopeType: collectionItem, contiguousRange: false}
initialState:
documentContents: |-
int f(int a, int b) {
Expand All @@ -30,4 +30,4 @@ finalState:
active: {line: 1, character: 26}
- anchor: {line: 1, character: 28}
active: {line: 1, character: 29}
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: collectionItem, includeSiblings: true}, insideOutsideType: inside}]
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: everyScope, scopeType: collectionItem, contiguousRange: false}, insideOutsideType: inside}]
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ command:
action: setSelection
targets:
- type: primitive
modifier: {type: containingScope, scopeType: collectionItem, includeSiblings: true}
modifier: {type: everyScope, scopeType: collectionItem, contiguousRange: false}
initialState:
documentContents: "map[string]string{\"air\": \"bat\", \"cap\": \"drum\"}"
selections:
Expand All @@ -24,4 +24,4 @@ finalState:
active: {line: 0, character: 30}
- anchor: {line: 0, character: 32}
active: {line: 0, character: 45}
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: collectionItem, includeSiblings: true}}]
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: everyScope, scopeType: collectionItem, contiguousRange: false}}]
Loading