@@ -42,6 +42,8 @@ import {DurationPrecision, TimestampFormat} from '../../public/timeline';
42
42
import { getTimeSpanOfSelectionOrVisibleWindow } from '../../public/utils' ;
43
43
import { Workspace } from '../../public/workspace' ;
44
44
import { showModal } from '../../widgets/modal' ;
45
+ import { assertExists } from '../../base/logging' ;
46
+ import { Setting } from '../../public/settings' ;
45
47
46
48
const QUICKSAVE_LOCALSTORAGE_KEY = 'quicksave' ;
47
49
@@ -129,9 +131,14 @@ function getOrPromptForTimestamp(tsRaw: unknown): time | undefined {
129
131
return promptForTimestamp ( 'Enter a timestamp' ) ;
130
132
}
131
133
134
+ const macroSchema = z . record ( z . array ( commandInvocationSchema ) ) ;
135
+ type MacroConfig = z . infer < typeof macroSchema > ;
136
+
132
137
export default class CoreCommands implements PerfettoPlugin {
133
138
static readonly id = 'dev.perfetto.CoreCommands' ;
134
139
140
+ static macrosSetting : Setting < MacroConfig > | undefined = undefined ;
141
+
135
142
static onActivate ( ctx : AppImpl ) {
136
143
if ( ctx . sidebar . enabled ) {
137
144
ctx . commands . registerCommand ( {
@@ -144,13 +151,10 @@ export default class CoreCommands implements PerfettoPlugin {
144
151
} ) ;
145
152
}
146
153
147
- // Use shared commandInvocationSchema where 'id' is the command's unique identifier
148
- const macroSchema = z . record ( z . array ( commandInvocationSchema ) ) ;
149
- type MacroConfig = z . infer < typeof macroSchema > ;
150
154
const macroSettingsEditor = new JsonSettingsEditor < MacroConfig > ( {
151
155
schema : macroSchema ,
152
156
} ) ;
153
- const setting = ctx . settings . register ( {
157
+ CoreCommands . macrosSetting = ctx . settings . register ( {
154
158
id : 'perfetto.CoreCommands#UserDefinedMacros' ,
155
159
name : 'Macros' ,
156
160
description :
@@ -161,27 +165,6 @@ export default class CoreCommands implements PerfettoPlugin {
161
165
render : ( setting ) => macroSettingsEditor . render ( setting ) ,
162
166
} ) ;
163
167
164
- // Register both user-defined macros from settings and extra macros from google3
165
- const allMacros = {
166
- ...setting . get ( ) ,
167
- ...ctx . extraMacros . reduce ( ( acc , macro ) => ( { ...acc , ...macro } ) , { } ) ,
168
- } as MacroConfig ;
169
- for ( const [ macroName , commands ] of Object . entries ( allMacros ) ) {
170
- ctx . commands . registerCommand ( {
171
- id : `dev.perfetto.UserMacro.${ macroName } ` ,
172
- name : macroName ,
173
- callback : async ( ) => {
174
- // Macros could run multiple commands, some of which might prompt the
175
- // user in an optional way. But macros should be self-contained
176
- // so we disable prompts during their execution.
177
- using _ = ctx . omnibox . disablePrompts ( ) ;
178
- for ( const command of commands ) {
179
- await ctx . commands . runCommand ( command . id , ...command . args ) ;
180
- }
181
- } ,
182
- } ) ;
183
- }
184
-
185
168
const input = document . createElement ( 'input' ) ;
186
169
input . classList . add ( 'trace_file' ) ;
187
170
input . setAttribute ( 'type' , 'file' ) ;
@@ -232,6 +215,23 @@ export default class CoreCommands implements PerfettoPlugin {
232
215
}
233
216
234
217
async onTraceLoad ( ctx : TraceImpl ) : Promise < void > {
218
+ const app = AppImpl . instance ;
219
+
220
+ // Rgister macros from settings first.
221
+ registerMacros ( ctx , assertExists ( CoreCommands . macrosSetting ) . get ( ) ) ;
222
+
223
+ // Register the macros from extras at onTraceReady (the latest time
224
+ // possible).
225
+ ctx . onTraceReady . addListener ( async ( _ ) => {
226
+ // Await the promise: we've tried to be async as long as possible but
227
+ // now we need the extras to be loaded.
228
+ await app . extraLoadingPromise ;
229
+ registerMacros (
230
+ ctx ,
231
+ app . extraMacros . reduce ( ( acc , macro ) => ( { ...acc , ...macro } ) , { } ) ,
232
+ ) ;
233
+ } ) ;
234
+
235
235
ctx . commands . registerCommand ( {
236
236
id : 'dev.perfetto.RunQueryAllProcesses' ,
237
237
name : 'Run query: All processes' ,
@@ -866,3 +866,21 @@ async function openWithLegacyUi(file: File) {
866
866
}
867
867
return await openInOldUIWithSizeCheck ( file ) ;
868
868
}
869
+
870
+ function registerMacros ( trace : TraceImpl , config : MacroConfig ) {
871
+ for ( const [ macroName , commands ] of Object . entries ( config ) ) {
872
+ trace . commands . registerCommand ( {
873
+ id : `dev.perfetto.UserMacro.${ macroName } ` ,
874
+ name : macroName ,
875
+ callback : async ( ) => {
876
+ // Macros could run multiple commands, some of which might prompt the
877
+ // user in an optional way. But macros should be self-contained
878
+ // so we disable prompts during their execution.
879
+ using _ = trace . omnibox . disablePrompts ( ) ;
880
+ for ( const command of commands ) {
881
+ await trace . commands . runCommand ( command . id , ...command . args ) ;
882
+ }
883
+ } ,
884
+ } ) ;
885
+ }
886
+ }
0 commit comments