diff --git a/package.json b/package.json
index 14e19e69..9da554e9 100644
--- a/package.json
+++ b/package.json
@@ -105,6 +105,7 @@
"worker-loader": "^2.0.0"
},
"dependencies": {
+ "@slimsag/react-shortcuts": "^1.2.1",
"@sourcegraph/codeintellify": "^3.9.0",
"@sourcegraph/extensions-client-common": "^10.0.0",
"@sourcegraph/react-loading-spinner": "0.0.6",
diff --git a/src/libs/code_intelligence/extensions.tsx b/src/libs/code_intelligence/extensions.tsx
index dd63d9f8..b0f03a3e 100644
--- a/src/libs/code_intelligence/extensions.tsx
+++ b/src/libs/code_intelligence/extensions.tsx
@@ -25,6 +25,7 @@ import * as H from 'history'
import { isErrorLike } from '../../shared/backend/errors'
import { createExtensionsContextController, createMessageTransports } from '../../shared/backend/extensions'
import { GlobalDebug } from '../../shared/components/GlobalDebug'
+import { ShortcutProvider } from '../../shared/components/ShortcutProvider'
import { sourcegraphUrl } from '../../shared/util/context'
import { getGlobalDebugMount } from '../github/extensions'
import { MountGetter } from './code_intelligence'
@@ -111,11 +112,13 @@ export function initializeExtensions(
const { extensionsContextController, extensionsController } = createControllers(documents)
render(
- ,
+
+
+ ,
getCommandPaletteMount()
)
diff --git a/src/libs/github/extensions.tsx b/src/libs/github/extensions.tsx
index bc5f765b..54dff167 100644
--- a/src/libs/github/extensions.tsx
+++ b/src/libs/github/extensions.tsx
@@ -8,6 +8,7 @@ import { ContributableMenu } from 'sourcegraph/module/protocol'
import * as React from 'react'
import { render } from 'react-dom'
import { GlobalDebug } from '../../shared/components/GlobalDebug'
+import { ShortcutProvider } from '../../shared/components/ShortcutProvider'
export function getCommandPaletteMount(): HTMLElement {
const headerElem = document.querySelector('div.HeaderMenu>div:last-child')
@@ -51,11 +52,13 @@ export function injectExtensionsGlobalComponents({
extensionsContextController: Controller
}): void {
render(
- ,
+
+
+ ,
getCommandPaletteMount()
)
diff --git a/src/shared/components/GlobalDebug.tsx b/src/shared/components/GlobalDebug.tsx
index 0d8f3a87..090606d5 100644
--- a/src/shared/components/GlobalDebug.tsx
+++ b/src/shared/components/GlobalDebug.tsx
@@ -6,6 +6,7 @@ import * as H from 'history'
import MenuDownIcon from 'mdi-react/MenuDownIcon'
import * as React from 'react'
import { sourcegraphUrl } from '../util/context'
+import { ShortcutProvider } from './ShortcutProvider'
interface Props {
location: H.Location
@@ -28,13 +29,17 @@ export const GlobalDebug: React.SFC = props =>
-
- void }>}
- caretIcon={MenuDownIcon as React.ComponentType<{ className: string; onClick?: () => void }>}
- extensionsController={props.extensionsController}
- link={ExtensionLink}
- />
+
+ void }>
+ }
+ caretIcon={MenuDownIcon as React.ComponentType<{ className: string; onClick?: () => void }>}
+ extensionsController={props.extensionsController}
+ link={ExtensionLink}
+ />
+
diff --git a/src/shared/components/ShortcutProvider.tsx b/src/shared/components/ShortcutProvider.tsx
new file mode 100644
index 00000000..2b2e41b7
--- /dev/null
+++ b/src/shared/components/ShortcutProvider.tsx
@@ -0,0 +1,42 @@
+import { Context, ContextProvider, ProviderProps, ShortcutManager } from '@slimsag/react-shortcuts'
+import * as React from 'react'
+
+/**
+ * Describes the variable this file injects into the `global` object. It is
+ * heavily prefixed to avoid collisions.
+ */
+interface GlobalContext {
+ /** The singleton ShortcutManager object. */
+ browserExtensionShortcutManager?: ShortcutManager
+}
+
+// This ShortcutProvider is derived from the default @shopify/react-shortcuts
+// implementation:
+//
+// https://github.com/Shopify/quilt/blob/master/packages/react-shortcuts/src/ShortcutProvider/ShortcutProvider.tsx
+//
+// We cannot use the default implementation above because it assumes the
+// application is rendered via a single React component in order to create the
+// ShortcutManager singleton. In our case, there are multiple React components
+// on the page and they each need to use a single ShortcutManager, so we must
+// manage it ourselves here. If we did not do this, we would have multiple
+// ShortcutManagers and each would register their own conflicting document
+// event handlers.
+export class ShortcutProvider extends React.Component {
+ public componentDidMount(): void {
+ const globals = global as GlobalContext
+ if (!globals.browserExtensionShortcutManager) {
+ globals.browserExtensionShortcutManager = new ShortcutManager()
+ globals.browserExtensionShortcutManager.setup()
+ }
+ }
+
+ public render(): JSX.Element | null {
+ const globals = global as GlobalContext
+ const context: Context = {
+ shortcutManager: globals.browserExtensionShortcutManager,
+ }
+
+ return {this.props.children}
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index f6e16363..13382de7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1306,6 +1306,13 @@
dependencies:
prop-types "^15.6.2"
+"@slimsag/react-shortcuts@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@slimsag/react-shortcuts/-/react-shortcuts-1.2.1.tgz#a3ea054a057137de8636bc9fabac61369103e597"
+ integrity sha512-dUTQjBX1yjnHbqkTL5ditqtx5R6QhcXI3lCnjOy8e5XUOr5LOtVpol1mufU2lAb7DtEYrSVRQpUeNmZrTEgLcg==
+ dependencies:
+ prop-types "^15.6.2"
+
"@sourcegraph/codeintellify@^3.9.0":
version "3.9.0"
resolved "https://registry.yarnpkg.com/@sourcegraph/codeintellify/-/codeintellify-3.9.0.tgz#1e3f002558cc367e8c5ee9b0a54e0c47c6ee8761"