diff --git a/packages/web/src/app/[domain]/browse/layout.tsx b/packages/web/src/app/[domain]/browse/layout.tsx
index d8c7efd2..d7c9f145 100644
--- a/packages/web/src/app/[domain]/browse/layout.tsx
+++ b/packages/web/src/app/[domain]/browse/layout.tsx
@@ -10,6 +10,7 @@ import { useBrowseParams } from "./hooks/useBrowseParams";
import { FileSearchCommandDialog } from "./components/fileSearchCommandDialog";
import { useDomain } from "@/hooks/useDomain";
import { SearchBar } from "../components/searchBar";
+import escapeStringRegexp from "escape-string-regexp";
interface LayoutProps {
children: React.ReactNode;
@@ -30,7 +31,7 @@ export default function Layout({
diff --git a/packages/web/src/app/components/keyboardShortcutHint.tsx b/packages/web/src/app/components/keyboardShortcutHint.tsx
index 0bbff3c0..8e0e4703 100644
--- a/packages/web/src/app/components/keyboardShortcutHint.tsx
+++ b/packages/web/src/app/components/keyboardShortcutHint.tsx
@@ -1,13 +1,15 @@
+import { cn } from '@/lib/utils'
import React from 'react'
interface KeyboardShortcutHintProps {
shortcut: string
label?: string
+ className?: string
}
-export function KeyboardShortcutHint({ shortcut, label }: KeyboardShortcutHintProps) {
+export function KeyboardShortcutHint({ shortcut, label, className }: KeyboardShortcutHintProps) {
return (
-
+
unwrapServiceError(
findSearchBasedSymbolReferences({
symbolName: selectedSymbolInfo.symbolName,
language: selectedSymbolInfo.language,
revisionName: selectedSymbolInfo.revisionName,
+ repoName: isGlobalSearchEnabled ? undefined : selectedSymbolInfo.repoName
})
),
});
@@ -56,16 +62,25 @@ export const ExploreMenu = ({
isPending: isDefinitionsResponsePending,
isLoading: isDefinitionsResponseLoading,
} = useQuery({
- queryKey: ["definitions", selectedSymbolInfo.symbolName, selectedSymbolInfo.repoName, selectedSymbolInfo.revisionName, selectedSymbolInfo.language, domain],
+ queryKey: ["definitions", selectedSymbolInfo.symbolName, selectedSymbolInfo.repoName, selectedSymbolInfo.revisionName, selectedSymbolInfo.language, domain, isGlobalSearchEnabled],
queryFn: () => unwrapServiceError(
findSearchBasedSymbolDefinitions({
symbolName: selectedSymbolInfo.symbolName,
language: selectedSymbolInfo.language,
revisionName: selectedSymbolInfo.revisionName,
+ repoName: isGlobalSearchEnabled ? undefined : selectedSymbolInfo.repoName
})
),
});
+ useHotkeys('shift+a', () => {
+ setIsGlobalSearchEnabled(!isGlobalSearchEnabled);
+ }, {
+ enableOnFormTags: true,
+ enableOnContentEditable: true,
+ description: "Search all repositories",
+ });
+
const isPending = isReferencesResponsePending || isDefinitionsResponsePending;
const isLoading = isReferencesResponseLoading || isDefinitionsResponseLoading;
const isError = isDefinitionsResponseError || isReferencesResponseError;
@@ -98,29 +113,52 @@ export const ExploreMenu = ({
-
-
+
+
-
- Search Based
-
-
-
- Symbol references and definitions found using a best-guess search heuristic.
-
-
+
+ Search Based
+
+
+
+ Symbol references and definitions found using a best-guess search heuristic.
+
+
+
+
+
+
+
+
+
+
+
+ {isGlobalSearchEnabled ? "Search in current repository only" : "Search all repositories"}
+
+
+
+
{
+ execute: async ({ symbol, language, repository }) => {
// @todo: make revision configurable.
const revision = "HEAD";
@@ -35,6 +36,7 @@ export const findSymbolReferencesTool = tool({
symbolName: symbol,
language,
revisionName: "HEAD",
+ repoName: repository,
});
if (isServiceError(response)) {
@@ -63,8 +65,9 @@ export const findSymbolDefinitionsTool = tool({
inputSchema: z.object({
symbol: z.string().describe("The symbol to find definitions of"),
language: z.string().describe("The programming language of the symbol"),
+ repository: z.string().describe("The repository to scope the search to").optional(),
}),
- execute: async ({ symbol, language }) => {
+ execute: async ({ symbol, language, repository }) => {
// @todo: make revision configurable.
const revision = "HEAD";
@@ -72,6 +75,7 @@ export const findSymbolDefinitionsTool = tool({
symbolName: symbol,
language,
revisionName: revision,
+ repoName: repository,
});
if (isServiceError(response)) {
diff --git a/packages/web/src/features/codeNav/api.ts b/packages/web/src/features/codeNav/api.ts
index d721dbe9..83e0a887 100644
--- a/packages/web/src/features/codeNav/api.ts
+++ b/packages/web/src/features/codeNav/api.ts
@@ -8,6 +8,7 @@ import { withOptionalAuthV2 } from "@/withAuthV2";
import { SearchResponse } from "../search/types";
import { FindRelatedSymbolsRequest, FindRelatedSymbolsResponse } from "./types";
import { QueryIR } from '../search/ir';
+import escapeStringRegexp from "escape-string-regexp";
// The maximum number of matches to return from the search API.
const MAX_REFERENCE_COUNT = 1000;
@@ -18,6 +19,7 @@ export const findSearchBasedSymbolReferences = async (props: FindRelatedSymbolsR
symbolName,
language,
revisionName = "HEAD",
+ repoName,
} = props;
const languageFilter = getExpandedLanguageFilter(language);
@@ -40,6 +42,11 @@ export const findSearchBasedSymbolReferences = async (props: FindRelatedSymbolsR
}
},
languageFilter,
+ ...(repoName ? [{
+ repo: {
+ regexp: `^${escapeStringRegexp(repoName)}$`,
+ }
+ }]: [])
]
}
}
@@ -67,6 +74,7 @@ export const findSearchBasedSymbolDefinitions = async (props: FindRelatedSymbols
symbolName,
language,
revisionName = "HEAD",
+ repoName
} = props;
const languageFilter = getExpandedLanguageFilter(language);
@@ -93,6 +101,11 @@ export const findSearchBasedSymbolDefinitions = async (props: FindRelatedSymbols
}
},
languageFilter,
+ ...(repoName ? [{
+ repo: {
+ regexp: `^${escapeStringRegexp(repoName)}$`,
+ }
+ }]: [])
]
}
}
diff --git a/packages/web/src/features/codeNav/types.ts b/packages/web/src/features/codeNav/types.ts
index b1dace76..4df8f685 100644
--- a/packages/web/src/features/codeNav/types.ts
+++ b/packages/web/src/features/codeNav/types.ts
@@ -4,7 +4,16 @@ import { rangeSchema, repositoryInfoSchema } from "../search/types";
export const findRelatedSymbolsRequestSchema = z.object({
symbolName: z.string(),
language: z.string(),
+ /**
+ * Optional revision name to scope search to.
+ * If not provided, the search will be scoped to HEAD.
+ */
revisionName: z.string().optional(),
+ /**
+ * Optional repository name to scope search to.
+ * If not provided, the search will be across all repositories.
+ */
+ repoName: z.string().optional(),
});
export type FindRelatedSymbolsRequest = z.infer;
diff --git a/packages/web/src/features/search/fileSourceApi.ts b/packages/web/src/features/search/fileSourceApi.ts
index 3ea4aa73..a3945f23 100644
--- a/packages/web/src/features/search/fileSourceApi.ts
+++ b/packages/web/src/features/search/fileSourceApi.ts
@@ -6,6 +6,7 @@ import { search } from "./searchApi";
import { sew } from "@/actions";
import { withOptionalAuthV2 } from "@/withAuthV2";
import { QueryIR } from './ir';
+import escapeStringRegexp from "escape-string-regexp";
// @todo (bkellam) #574 : We should really be using `git show :` to fetch file contents here.
// This will allow us to support permalinks to files at a specific revision that may not be indexed
@@ -18,7 +19,7 @@ export const getFileSource = async ({ fileName, repository, branch }: FileSource
children: [
{
repo: {
- regexp: `^${repository}$`,
+ regexp: `^${escapeStringRegexp(repository)}$`,
},
},
{