Procházet zdrojové kódy

fix(app): preserve shadowed command owners (#39044)

Brendan Allan před 1 měsícem
rodič
revize
09afffeead

+ 22 - 7
packages/app/src/context/command.test.ts

@@ -1,5 +1,11 @@
 import { describe, expect, test } from "bun:test"
-import { commandPaletteOptions, resolveKeybindOption, upsertCommandRegistration, type CommandOption } from "./command"
+import {
+  activeCommandRegistrations,
+  addCommandRegistration,
+  commandPaletteOptions,
+  resolveKeybindOption,
+  type CommandOption,
+} from "./command"
 
 const paletteOptions: CommandOption[] = [
   { id: "settings.open", title: "Open settings" },
@@ -15,22 +21,31 @@ describe("commandPaletteOptions", () => {
   })
 })
 
-describe("upsertCommandRegistration", () => {
-  test("replaces keyed registrations", () => {
+describe("command registrations", () => {
+  test("shadows keyed registrations while retaining the previous owner", () => {
     const one = () => [{ id: "one", title: "One" }]
     const two = () => [{ id: "two", title: "Two" }]
 
-    const next = upsertCommandRegistration([{ key: "layout", options: one }], { key: "layout", options: two })
+    const registrations = addCommandRegistration([{ key: "layout", options: one }], {
+      key: "layout",
+      options: two,
+    })
+    const active = activeCommandRegistrations(registrations)
 
-    expect(next).toHaveLength(1)
-    expect(next[0]?.options).toBe(two)
+    expect(registrations).toHaveLength(2)
+    expect(active).toHaveLength(1)
+    expect(active[0]?.options).toBe(two)
+
+    const restored = activeCommandRegistrations(registrations.filter((entry) => entry.options !== two))
+    expect(restored).toHaveLength(1)
+    expect(restored[0]?.options).toBe(one)
   })
 
   test("keeps unkeyed registrations additive", () => {
     const one = () => [{ id: "one", title: "One" }]
     const two = () => [{ id: "two", title: "Two" }]
 
-    const next = upsertCommandRegistration([{ options: one }], { options: two })
+    const next = activeCommandRegistrations(addCommandRegistration([{ options: one }], { options: two }))
 
     expect(next).toHaveLength(2)
     expect(next[0]?.options).toBe(two)

+ 14 - 5
packages/app/src/context/command.tsx

@@ -114,9 +114,18 @@ export type CommandRegistration = {
   options: Accessor<CommandOption[]>
 }
 
-export function upsertCommandRegistration(registrations: CommandRegistration[], entry: CommandRegistration) {
-  if (entry.key === undefined) return [entry, ...registrations]
-  return [entry, ...registrations.filter((x) => x.key !== entry.key)]
+export function addCommandRegistration(registrations: CommandRegistration[], entry: CommandRegistration) {
+  return [entry, ...registrations]
+}
+
+export function activeCommandRegistrations(registrations: CommandRegistration[]) {
+  const keys = new Set<string>()
+  return registrations.filter((entry) => {
+    if (entry.key === undefined) return true
+    if (keys.has(entry.key)) return false
+    keys.add(entry.key)
+    return true
+  })
 }
 
 export function parseKeybind(config: string): Keybind[] {
@@ -281,7 +290,7 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
       const seen = new Set<string>()
       const all: CommandOption[] = []
 
-      for (const reg of store.registrations) {
+      for (const reg of activeCommandRegistrations(store.registrations)) {
         for (const opt of reg.options()) {
           if (seen.has(opt.id)) {
             if (import.meta.env.DEV && !warnedDuplicates.has(opt.id)) {
@@ -425,7 +434,7 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
         key: id,
         options,
       }
-      setStore("registrations", (arr) => upsertCommandRegistration(arr, entry))
+      setStore("registrations", (arr) => addCommandRegistration(arr, entry))
       onCleanup(() => {
         setStore("registrations", (arr) => arr.filter((x) => x !== entry))
       })