ui: Open table command

This is crudest way of opening the table -> just query the table and open this as the result of any other query.

The next step would be to open it as a sqlTable.

Bug: 380055911
Change-Id: Id9419e775820e98cdf1e2a00342971b90062ef5c
diff --git a/CHANGELOG b/CHANGELOG
index 2a33ef5..600160b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -31,7 +31,8 @@
   Trace Processor:
     *
   UI:
-    *
+    * Introduced `Open table:` command which would open any Perfetto Standard
+      Library table in a new tab.
   SDK:
     *
 
diff --git a/ui/src/plugins/dev.perfetto.SqlModules/index.ts b/ui/src/plugins/dev.perfetto.SqlModules/index.ts
index f2fde8d..a1ee8c2 100644
--- a/ui/src/plugins/dev.perfetto.SqlModules/index.ts
+++ b/ui/src/plugins/dev.perfetto.SqlModules/index.ts
@@ -15,18 +15,48 @@
 import {assetSrc} from '../../base/assets';
 import {assertExists} from '../../base/logging';
 import {PerfettoPlugin} from '../../public/plugin';
+import {Trace} from '../../public/trace';
 import {SqlModules} from './sql_modules';
 import {SQL_MODULES_DOCS_SCHEMA, SqlModulesImpl} from './sql_modules_impl';
+import {PromptOption} from '../../public/omnibox';
+import {addQueryResultsTab} from '../../public/lib/query_table/query_result_tab';
 
 export default class implements PerfettoPlugin {
   static readonly id = 'dev.perfetto.SqlModules';
   private sqlModules?: SqlModules;
 
-  async onTraceLoad() {
+  async onTraceLoad(ctx: Trace) {
     const resp = await fetch(assetSrc('stdlib_docs.json'));
     const json = await resp.json();
     const docs = SQL_MODULES_DOCS_SCHEMA.parse(json);
-    this.sqlModules = new SqlModulesImpl(docs);
+    const sqlModules = new SqlModulesImpl(docs);
+    this.sqlModules = sqlModules;
+
+    ctx.commands.registerCommand({
+      id: 'perfetto.OpenSqlModulesTable',
+      name: 'Open table...',
+      callback: async () => {
+        const tables = sqlModules.listTables();
+        const promptOptions: PromptOption[] = tables.map((s) => ({
+          key: s,
+          displayName: s,
+        }));
+        const tableName = await ctx.omnibox.prompt(
+          'Choose a table...',
+          promptOptions,
+        );
+        if (tableName === undefined) {
+          return;
+        }
+        const module = sqlModules.getModuleForTable(tableName);
+        module &&
+          addQueryResultsTab(ctx, {
+            query: `INCLUDE PERFETTO MODULE ${module.includeKey};
+           SELECT * FROM ${tableName}`,
+            title: tableName,
+          });
+      },
+    });
   }
 
   getSqlModules() {
diff --git a/ui/src/plugins/dev.perfetto.SqlModules/sql_modules.ts b/ui/src/plugins/dev.perfetto.SqlModules/sql_modules.ts
index 9f0503a..e3c11dc 100644
--- a/ui/src/plugins/dev.perfetto.SqlModules/sql_modules.ts
+++ b/ui/src/plugins/dev.perfetto.SqlModules/sql_modules.ts
@@ -20,7 +20,7 @@
 
   // Returns Perfetto SQL table/view if it was loaded in one of the Perfetto
   // SQL module.
-  getTable(tableName: string): SqlTable | undefined;
+  getModuleForTable(tableName: string): SqlModule | undefined;
 }
 
 // Handles the access to a specific Perfetto SQL Package. Package consists of
@@ -29,7 +29,7 @@
   readonly name: string;
   readonly modules: SqlModule[];
   listTables(): string[];
-  getTable(tableName: string): SqlTable | undefined;
+  getModuleForTable(tableName: string): SqlModule | undefined;
 }
 
 // Handles the access to a specific Perfetto SQL module.
@@ -39,6 +39,7 @@
   readonly functions: SqlFunction[];
   readonly tableFunctions: SqlTableFunction[];
   readonly macros: SqlMacro[];
+  getTable(tableName: string): SqlTable | undefined;
 }
 
 // The definition of Perfetto SQL table/view.
diff --git a/ui/src/plugins/dev.perfetto.SqlModules/sql_modules_impl.ts b/ui/src/plugins/dev.perfetto.SqlModules/sql_modules_impl.ts
index ee6fe79..2e3ab1c 100644
--- a/ui/src/plugins/dev.perfetto.SqlModules/sql_modules_impl.ts
+++ b/ui/src/plugins/dev.perfetto.SqlModules/sql_modules_impl.ts
@@ -33,16 +33,12 @@
   }
 
   listTables(): string[] {
-    const tables: string[] = [];
-    for (const stdlibPackage of this.packages) {
-      tables.concat(stdlibPackage.listTables());
-    }
-    return tables;
+    return this.packages.flatMap((p) => p.listTables());
   }
 
-  getTable(tableName: string): SqlTable | undefined {
+  getModuleForTable(tableName: string): SqlModule | undefined {
     for (const stdlibPackage of this.packages) {
-      const maybeTable = stdlibPackage.getTable(tableName);
+      const maybeTable = stdlibPackage.getModuleForTable(tableName);
       if (maybeTable) {
         return maybeTable;
       }
@@ -62,12 +58,11 @@
       this.modules.push(new StdlibModuleImpl(moduleJson));
     }
   }
-
-  getTable(tableName: string): SqlTable | undefined {
+  getModuleForTable(tableName: string): SqlModule | undefined {
     for (const module of this.modules) {
       for (const dataObj of module.dataObjects) {
         if (dataObj.name == tableName) {
-          return dataObj;
+          return module;
         }
       }
     }
@@ -99,6 +94,14 @@
     );
     this.macros = docs.macros.map((json) => new StdlibMacroImpl(json));
   }
+  getTable(tableName: string): SqlTable | undefined {
+    for (const obj of this.dataObjects) {
+      if (obj.name == tableName) {
+        return obj;
+      }
+    }
+    return undefined;
+  }
 }
 
 class StdlibMacroImpl implements SqlMacro {