11import {
2- CustomRegexScopeType ,
32 Disposable ,
4- FileSystem ,
3+ Disposer ,
54 ScopeType ,
6- SimpleScopeTypeType ,
7- SurroundingPairName ,
85 SurroundingPairScopeType ,
9- isSimpleScopeType ,
106 simpleScopeTypeTypes ,
117 surroundingPairNames ,
128} from "@cursorless/common" ;
139import { pull } from "lodash" ;
14- import { ScopeTypeInfo , ScopeTypeInfoEventCallback } from ".." ;
15- import { Debouncer } from "../core/Debouncer" ;
1610import { homedir } from "os" ;
1711import * as path from "path" ;
12+ import { ScopeTypeInfo , ScopeTypeInfoEventCallback } from ".." ;
13+ import { CustomSpokenForms } from "../CustomSpokenForms" ;
14+
15+ import { SpokenFormGenerator } from "../generateSpokenForm" ;
1816import { scopeTypeToString } from "./scopeTypeToString" ;
19- import {
20- CustomRegexSpokenFormEntry ,
21- PairedDelimiterSpokenFormEntry ,
22- SimpleScopeTypeTypeSpokenFormEntry ,
23- getSpokenFormEntries ,
24- } from "./getSpokenFormEntries" ;
2517
2618export const spokenFormsPath = path . join (
2719 homedir ( ) ,
@@ -33,31 +25,20 @@ export const spokenFormsPath = path.join(
3325 * Maintains a list of all scope types and notifies listeners when it changes.
3426 */
3527export class ScopeInfoProvider {
36- private disposables : Disposable [ ] = [ ] ;
37- private debouncer = new Debouncer ( ( ) => this . onChange ( ) , 250 ) ;
28+ private disposer = new Disposer ( ) ;
3829 private listeners : ScopeTypeInfoEventCallback [ ] = [ ] ;
39- private simpleScopeTypeSpokenFormMap ?: Record < SimpleScopeTypeType , string [ ] > ;
40- private pairedDelimiterSpokenFormMap ?: Record < SurroundingPairName , string [ ] > ;
41- private customRegexSpokenFormMap ?: Record < string , string [ ] > ;
4230 private scopeInfos ! : ScopeTypeInfo [ ] ;
4331
44- private constructor ( fileSystem : FileSystem ) {
45- this . disposables . push (
46- fileSystem . watch ( spokenFormsPath , this . debouncer . run ) ,
47- this . debouncer ,
32+ constructor (
33+ private customSpokenForms : CustomSpokenForms ,
34+ private spokenFormGenerator : SpokenFormGenerator ,
35+ ) {
36+ this . disposer . push (
37+ customSpokenForms . onDidChangeCustomSpokenForms ( ( ) => this . onChange ( ) ) ,
4838 ) ;
4939
5040 this . onDidChangeScopeInfo = this . onDidChangeScopeInfo . bind ( this ) ;
51- }
52-
53- static create ( fileSystem : FileSystem ) {
54- const obj = new ScopeInfoProvider ( fileSystem ) ;
55- obj . init ( ) ;
56- return obj ;
57- }
58-
59- private async init ( ) {
60- await this . updateScopeTypeInfos ( ) ;
41+ this . updateScopeTypeInfos ( ) ;
6142 }
6243
6344 /**
@@ -69,7 +50,6 @@ export class ScopeInfoProvider {
6950 * @returns A {@link Disposable} which will stop the callback from running
7051 */
7152 onDidChangeScopeInfo ( callback : ScopeTypeInfoEventCallback ) : Disposable {
72- this . updateScopeTypeInfos ( ) . then ( ( ) => callback ( this . getScopeTypeInfos ( ) ) ) ;
7353 callback ( this . getScopeTypeInfos ( ) ) ;
7454
7555 this . listeners . push ( callback ) ;
@@ -82,76 +62,36 @@ export class ScopeInfoProvider {
8262 }
8363
8464 private async onChange ( ) {
85- await this . updateScopeTypeInfos ( ) ;
65+ this . updateScopeTypeInfos ( ) ;
8666
8767 this . listeners . forEach ( ( listener ) => listener ( this . scopeInfos ) ) ;
8868 }
8969
90- private async updateScopeTypeInfos ( ) : Promise < void > {
91- const update = ( ) => {
92- const scopeTypes : ScopeType [ ] = [
93- ...simpleScopeTypeTypes
94- // Ignore instance pseudo-scope for now
95- // Skip "string" because we use surrounding pair for that
96- . filter (
97- ( scopeTypeType ) =>
98- scopeTypeType !== "instance" && scopeTypeType !== "string" ,
99- )
100- . map ( ( scopeTypeType ) => ( {
101- type : scopeTypeType ,
102- } ) ) ,
103-
104- ...surroundingPairNames . map (
105- ( surroundingPairName ) : SurroundingPairScopeType => ( {
106- type : "surroundingPair" ,
107- delimiter : surroundingPairName ,
108- } ) ,
109- ) ,
110-
111- ...( this . customRegexSpokenFormMap == null
112- ? [ ]
113- : Object . keys ( this . customRegexSpokenFormMap )
114- ) . map (
115- ( regex ) : CustomRegexScopeType => ( { type : "customRegex" , regex } ) ,
116- ) ,
117- ] ;
118-
119- this . scopeInfos = scopeTypes . map ( ( scopeType ) =>
120- this . getScopeTypeInfo ( scopeType ) ,
121- ) ;
122- } ;
123-
124- update ( ) ;
125-
126- return this . updateSpokenFormMaps ( ) . then ( update ) ;
127- }
128-
129- private async updateSpokenFormMaps ( ) {
130- const entries = await getSpokenFormEntries ( ) ;
131-
132- this . simpleScopeTypeSpokenFormMap = Object . fromEntries (
133- entries
134- . filter (
135- ( entry ) : entry is SimpleScopeTypeTypeSpokenFormEntry =>
136- entry . type === "simpleScopeTypeType" ,
137- )
138- . map ( ( { id, spokenForms } ) => [ id , spokenForms ] as const ) ,
139- ) ;
140- this . customRegexSpokenFormMap = Object . fromEntries (
141- entries
142- . filter (
143- ( entry ) : entry is CustomRegexSpokenFormEntry =>
144- entry . type === "customRegex" ,
145- )
146- . map ( ( { id, spokenForms } ) => [ id , spokenForms ] as const ) ,
147- ) ;
148- this . pairedDelimiterSpokenFormMap = Object . fromEntries (
149- entries
70+ private updateScopeTypeInfos ( ) : void {
71+ const scopeTypes : ScopeType [ ] = [
72+ ...simpleScopeTypeTypes
73+ // Ignore instance pseudo-scope for now
74+ // Skip "string" because we use surrounding pair for that
15075 . filter (
151- ( entry ) : entry is PairedDelimiterSpokenFormEntry =>
152- entry . type === "pairedDelimiter ",
76+ ( scopeTypeType ) =>
77+ scopeTypeType !== "instance" && scopeTypeType !== "string ",
15378 )
154- . map ( ( { id, spokenForms } ) => [ id , spokenForms ] as const ) ,
79+ . map ( ( scopeTypeType ) => ( {
80+ type : scopeTypeType ,
81+ } ) ) ,
82+
83+ ...surroundingPairNames . map (
84+ ( surroundingPairName ) : SurroundingPairScopeType => ( {
85+ type : "surroundingPair" ,
86+ delimiter : surroundingPairName ,
87+ } ) ,
88+ ) ,
89+
90+ ...this . customSpokenForms . getCustomRegexScopeTypes ( ) ,
91+ ] ;
92+
93+ this . scopeInfos = scopeTypes . map ( ( scopeType ) =>
94+ this . getScopeTypeInfo ( scopeType ) ,
15595 ) ;
15696 }
15797
@@ -162,38 +102,11 @@ export class ScopeInfoProvider {
162102 getScopeTypeInfo ( scopeType : ScopeType ) : ScopeTypeInfo {
163103 return {
164104 scopeType,
165- spokenForms : this . getSpokenForms ( scopeType ) ,
105+ spokenForm : this . spokenFormGenerator . scopeType ( scopeType ) ,
166106 humanReadableName : scopeTypeToString ( scopeType ) ,
167107 isLanguageSpecific : isLanguageSpecific ( scopeType ) ,
168108 } ;
169109 }
170-
171- getSpokenForms ( scopeType : ScopeType ) : string [ ] | undefined {
172- if ( isSimpleScopeType ( scopeType ) ) {
173- return this . simpleScopeTypeSpokenFormMap ?. [ scopeType . type ] ;
174- }
175-
176- if ( scopeType . type === "surroundingPair" ) {
177- return this . pairedDelimiterSpokenFormMap ?. [ scopeType . delimiter ] ;
178- }
179-
180- if ( scopeType . type === "customRegex" ) {
181- return this . customRegexSpokenFormMap ?. [ scopeType . regex ] ;
182- }
183-
184- return undefined ;
185- }
186-
187- dispose ( ) : void {
188- this . disposables . forEach ( ( { dispose } ) => {
189- try {
190- dispose ( ) ;
191- } catch ( e ) {
192- // do nothing; some of the VSCode disposables misbehave, and we don't
193- // want that to prevent us from disposing the rest of the disposables
194- }
195- } ) ;
196- }
197110}
198111
199112/**
0 commit comments