Skip to content

Commit f4776fb

Browse files
committed
Revisualize on custom regex change
1 parent 1e74640 commit f4776fb

File tree

8 files changed

+83
-8
lines changed

8 files changed

+83
-8
lines changed

packages/common/src/types/ScopeProvider.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@ export interface ScopeProvider {
9494
* @returns A {@link Disposable} which will stop the callback from running
9595
*/
9696
onDidChangeScopeInfo(callback: ScopeTypeInfoEventCallback): Disposable;
97+
98+
/**
99+
* Determine the level of support for the iteration scope of {@link scopeType}
100+
* in {@link editor}, as determined by its language id.
101+
* @param editor The editor to check
102+
* @param scopeType The scope type to check
103+
* @returns The level of support for the iteration scope of {@link scopeType}
104+
* in {@link editor}
105+
*/
106+
getScopeInfo: (
107+
scopeType: ScopeType,
108+
) => ScopeTypeInfo;
97109
}
98110

99111
interface ScopeRangeConfigBase {

packages/common/src/util/Disposer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import { Disposable } from "../ide/types/ide.types";
99
export class Disposer implements Disposable {
1010
private disposables: Disposable[] = [];
1111

12+
constructor(...disposables: Disposable[]) {
13+
this.push(...disposables)
14+
}
15+
1216
public push(...disposables: Disposable[]) {
1317
this.disposables.push(...disposables);
1418
}

packages/cursorless-engine/src/cursorlessEngine.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ function createScopeProvider(
151151
getScopeSupport: supportChecker.getScopeSupport,
152152
getIterationScopeSupport: supportChecker.getIterationScopeSupport,
153153
onDidChangeScopeSupport: supportWatcher.onDidChangeScopeSupport,
154+
getScopeInfo: infoProvider.getScopeTypeInfo,
154155
onDidChangeScopeInfo: infoProvider.onDidChangeScopeInfo,
155156
};
156157
}

packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export class ScopeInfoProvider {
3232
);
3333

3434
this.onDidChangeScopeInfo = this.onDidChangeScopeInfo.bind(this);
35+
this.getScopeTypeInfo = this.getScopeTypeInfo.bind(this);
3536
this.updateScopeTypeInfos();
3637
}
3738

packages/cursorless-engine/src/scopeProviders/ScopeSupportWatcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export class ScopeSupportWatcher {
3939
// dirty-state changes.
4040
ide().onDidChangeTextDocument(this.debouncer.run),
4141
languageDefinitions.onDidChangeDefinition(this.debouncer.run),
42-
this.scopeInfoProvider.onDidChangeScopeInfo(this.debouncer.run),
42+
this.scopeInfoProvider.onDidChangeScopeInfo(() => this.onChange()),
4343
this.debouncer,
4444
);
4545

packages/cursorless-vscode/src/ScopeVisualizerCommandApi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Disposable, ScopeType } from "@cursorless/common";
22

33
export type VisualizerScopeTypeListener = (
4-
scopeType: ScopeType | undefined,
4+
scopeType: ScopeType | undefined, visualizationType: VisualizationType | undefined,
55
) => void;
66

77
export interface ScopeVisualizer {

packages/cursorless-vscode/src/extension.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,15 @@ import {
3535
import { KeyboardCommands } from "./keyboard/KeyboardCommands";
3636
import { registerCommands } from "./registerCommands";
3737
import { ReleaseNotes } from "./ReleaseNotes";
38+
import { ScopeTreeProvider } from "./ScopeTreeProvider";
3839
import {
3940
ScopeVisualizer,
41+
VisualizerScopeTypeListener as ScopeVisualizerListener,
4042
VisualizationType,
41-
VisualizerScopeTypeListener,
4243
} from "./ScopeVisualizerCommandApi";
4344
import { StatusBarItem } from "./StatusBarItem";
4445
import { vscodeApi } from "./vscodeApi";
45-
import { ScopeTreeProvider } from "./ScopeTreeProvider";
46+
import { revisualizeOnCustomRegexChange } from "./revisualizeOnCustomRegexChange";
4647

4748
/**
4849
* Extension entrypoint called by VSCode on Cursorless startup.
@@ -97,6 +98,9 @@ export async function activate(
9798
const statusBarItem = StatusBarItem.create("cursorless.showQuickPick");
9899
const keyboardCommands = KeyboardCommands.create(context, statusBarItem);
99100
const scopeVisualizer = createScopeVisualizer(normalizedIde, scopeProvider);
101+
context.subscriptions.push(
102+
revisualizeOnCustomRegexChange(scopeVisualizer, scopeProvider),
103+
);
100104
ScopeTreeProvider.create(
101105
vscodeApi,
102106
context,
@@ -178,7 +182,7 @@ function createScopeVisualizer(
178182
let scopeVisualizer: VscodeScopeVisualizer | undefined;
179183
let currentScopeType: ScopeType | undefined;
180184

181-
const listeners: VisualizerScopeTypeListener[] = [];
185+
const listeners: ScopeVisualizerListener[] = [];
182186

183187
return {
184188
start(scopeType: ScopeType, visualizationType: VisualizationType) {
@@ -191,21 +195,21 @@ function createScopeVisualizer(
191195
);
192196
scopeVisualizer.start();
193197
currentScopeType = scopeType;
194-
listeners.forEach((listener) => listener(scopeType));
198+
listeners.forEach((listener) => listener(scopeType, visualizationType));
195199
},
196200

197201
stop() {
198202
scopeVisualizer?.dispose();
199203
scopeVisualizer = undefined;
200204
currentScopeType = undefined;
201-
listeners.forEach((listener) => listener(undefined));
205+
listeners.forEach((listener) => listener(undefined, undefined));
202206
},
203207

204208
get scopeType() {
205209
return currentScopeType;
206210
},
207211

208-
onDidChangeScopeType(listener: VisualizerScopeTypeListener): Disposable {
212+
onDidChangeScopeType(listener: ScopeVisualizerListener): Disposable {
209213
listeners.push(listener);
210214

211215
return {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import {
2+
Disposable,
3+
Disposer, ScopeProvider, ScopeTypeInfo
4+
} from "@cursorless/common";
5+
import { ScopeVisualizer, VisualizationType } from "./ScopeVisualizerCommandApi";
6+
import { isEqual } from "lodash";
7+
8+
/**
9+
* Attempts to ensure that the scope visualizer is still visualizing the same
10+
* scope type after the user changes one of their custom regexes. Because custom
11+
* regexes don't have a unique identifier, we have to do some guesswork to
12+
* figure out which custom regex the user changed. This function look for a
13+
* custom regex with the same spoken form as the one that was changed, and if it
14+
* finds one, it starts visualizing that one instead.
15+
*
16+
* @param scopeVisualizer The scope visualizer to listen to
17+
* @param scopeProvider Provides scope information
18+
* @returns A {@link Disposable} which will stop the callback from running
19+
*/
20+
export function revisualizeOnCustomRegexChange(
21+
scopeVisualizer: ScopeVisualizer,
22+
scopeProvider: ScopeProvider
23+
): Disposable {
24+
let currentRegexScopeInfo: ScopeTypeInfo | undefined;
25+
let currentVisualizationType: VisualizationType | undefined;
26+
27+
return new Disposer(
28+
scopeVisualizer.onDidChangeScopeType((scopeType, visualizationType) => {
29+
currentRegexScopeInfo =
30+
scopeType?.type === "customRegex"
31+
? scopeProvider.getScopeInfo(scopeType)
32+
: undefined;
33+
currentVisualizationType = visualizationType;
34+
}),
35+
36+
scopeProvider.onDidChangeScopeInfo((scopeInfos) => {
37+
if (currentRegexScopeInfo != null &&
38+
!scopeInfos.some((scopeInfo) => isEqual(scopeInfo.scopeType, currentRegexScopeInfo!.scopeType)
39+
)) {
40+
const replacement = scopeInfos.find(
41+
(scopeInfo) => scopeInfo.scopeType.type === "customRegex" &&
42+
isEqual(scopeInfo.spokenForm, currentRegexScopeInfo!.spokenForm)
43+
);
44+
if (replacement != null) {
45+
scopeVisualizer.start(
46+
replacement.scopeType,
47+
currentVisualizationType!
48+
);
49+
}
50+
}
51+
})
52+
);
53+
}

0 commit comments

Comments
 (0)