mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-11 20:05:25 +00:00
refactor code nav callbacks into symbolHoverPopup
This commit is contained in:
parent
29994d6011
commit
2767c2b088
6 changed files with 157 additions and 270 deletions
|
|
@ -3,7 +3,6 @@
|
|||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { SymbolHoverPopup } from "@/ee/features/codeNav/components/symbolHoverPopup";
|
||||
import { symbolHoverTargetsExtension } from "@/ee/features/codeNav/components/symbolHoverPopup/symbolHoverTargetsExtension";
|
||||
import { SymbolDefinition } from "@/ee/features/codeNav/components/symbolHoverPopup/useHoveredOverSymbolInfo";
|
||||
import { useHasEntitlement } from "@/features/entitlements/useHasEntitlement";
|
||||
import { useCodeMirrorLanguageExtension } from "@/hooks/useCodeMirrorLanguageExtension";
|
||||
import { useCodeMirrorTheme } from "@/hooks/useCodeMirrorTheme";
|
||||
|
|
@ -11,14 +10,10 @@ import { useKeymapExtension } from "@/hooks/useKeymapExtension";
|
|||
import { useNonEmptyQueryParam } from "@/hooks/useNonEmptyQueryParam";
|
||||
import { search } from "@codemirror/search";
|
||||
import CodeMirror, { EditorSelection, EditorView, ReactCodeMirrorRef, SelectionRange, ViewUpdate } from "@uiw/react-codemirror";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { EditorContextMenu } from "../../../components/editorContextMenu";
|
||||
import { useBrowseNavigation } from "../../hooks/useBrowseNavigation";
|
||||
import { BrowseHighlightRange, HIGHLIGHT_RANGE_QUERY_PARAM } from "../../hooks/utils";
|
||||
import { useBrowseState } from "../../hooks/useBrowseState";
|
||||
import { rangeHighlightingExtension } from "./rangeHighlightingExtension";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
import { createAuditAction } from "@/ee/features/audit/actions";
|
||||
|
||||
interface PureCodePreviewPanelProps {
|
||||
path: string;
|
||||
|
|
@ -40,9 +35,6 @@ export const PureCodePreviewPanel = ({
|
|||
const [currentSelection, setCurrentSelection] = useState<SelectionRange>();
|
||||
const keymapExtension = useKeymapExtension(editorRef?.view);
|
||||
const hasCodeNavEntitlement = useHasEntitlement("code-nav");
|
||||
const { updateBrowseState } = useBrowseState();
|
||||
const { navigateToPath } = useBrowseNavigation();
|
||||
const captureEvent = useCaptureEvent();
|
||||
|
||||
const highlightRangeQuery = useNonEmptyQueryParam(HIGHLIGHT_RANGE_QUERY_PARAM);
|
||||
const highlightRange = useMemo((): BrowseHighlightRange | undefined => {
|
||||
|
|
@ -134,72 +126,6 @@ export const PureCodePreviewPanel = ({
|
|||
});
|
||||
}, [editorRef, highlightRange]);
|
||||
|
||||
const onFindReferences = useCallback((symbolName: string) => {
|
||||
captureEvent('wa_find_references_pressed', {
|
||||
source: 'browse',
|
||||
});
|
||||
createAuditAction({
|
||||
action: "user.performed_find_references",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
})
|
||||
|
||||
updateBrowseState({
|
||||
selectedSymbolInfo: {
|
||||
repoName,
|
||||
symbolName,
|
||||
revisionName,
|
||||
language,
|
||||
},
|
||||
isBottomPanelCollapsed: false,
|
||||
activeExploreMenuTab: "references",
|
||||
})
|
||||
}, [captureEvent, updateBrowseState, repoName, revisionName, language]);
|
||||
|
||||
|
||||
// If we resolve multiple matches, instead of navigating to the first match, we should
|
||||
// instead popup the bottom sheet with the list of matches.
|
||||
const onGotoDefinition = useCallback((symbolName: string, symbolDefinitions: SymbolDefinition[]) => {
|
||||
captureEvent('wa_goto_definition_pressed', {
|
||||
source: 'browse',
|
||||
});
|
||||
createAuditAction({
|
||||
action: "user.performed_goto_definition",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
})
|
||||
|
||||
if (symbolDefinitions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (symbolDefinitions.length === 1) {
|
||||
const symbolDefinition = symbolDefinitions[0];
|
||||
const { fileName, repoName } = symbolDefinition;
|
||||
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
highlightRange: symbolDefinition.range,
|
||||
})
|
||||
} else {
|
||||
updateBrowseState({
|
||||
selectedSymbolInfo: {
|
||||
symbolName,
|
||||
repoName,
|
||||
revisionName,
|
||||
language,
|
||||
},
|
||||
activeExploreMenuTab: "definitions",
|
||||
isBottomPanelCollapsed: false,
|
||||
})
|
||||
}
|
||||
}, [captureEvent, navigateToPath, revisionName, updateBrowseState, repoName, language]);
|
||||
|
||||
const theme = useCodeMirrorTheme();
|
||||
|
||||
return (
|
||||
|
|
@ -223,11 +149,12 @@ export const PureCodePreviewPanel = ({
|
|||
)}
|
||||
{editorRef && hasCodeNavEntitlement && (
|
||||
<SymbolHoverPopup
|
||||
source="preview"
|
||||
editorRef={editorRef}
|
||||
revisionName={revisionName}
|
||||
language={language}
|
||||
onFindReferences={onFindReferences}
|
||||
onGotoDefinition={onGotoDefinition}
|
||||
fileName={path}
|
||||
repoName={repoName}
|
||||
/>
|
||||
)}
|
||||
</CodeMirror>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
'use client';
|
||||
|
||||
import { useBrowseNavigation } from "@/app/[domain]/browse/hooks/useBrowseNavigation";
|
||||
import { EditorContextMenu } from "@/app/[domain]/components/editorContextMenu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { SymbolHoverPopup } from "@/ee/features/codeNav/components/symbolHoverPopup";
|
||||
import { symbolHoverTargetsExtension } from "@/ee/features/codeNav/components/symbolHoverPopup/symbolHoverTargetsExtension";
|
||||
import { useHasEntitlement } from "@/features/entitlements/useHasEntitlement";
|
||||
import { SearchResultChunk } from "@/features/search";
|
||||
import { useCodeMirrorLanguageExtension } from "@/hooks/useCodeMirrorLanguageExtension";
|
||||
import { useCodeMirrorTheme } from "@/hooks/useCodeMirrorTheme";
|
||||
import { useKeymapExtension } from "@/hooks/useKeymapExtension";
|
||||
import { useCodeMirrorLanguageExtension } from "@/hooks/useCodeMirrorLanguageExtension";
|
||||
import { gutterWidthExtension } from "@/lib/extensions/gutterWidthExtension";
|
||||
import { highlightRanges, searchResultHighlightExtension } from "@/lib/extensions/searchResultHighlightExtension";
|
||||
import { search } from "@codemirror/search";
|
||||
|
|
@ -16,13 +20,6 @@ import { Scrollbar } from "@radix-ui/react-scroll-area";
|
|||
import CodeMirror, { ReactCodeMirrorRef, SelectionRange } from '@uiw/react-codemirror';
|
||||
import { ArrowDown, ArrowUp } from "lucide-react";
|
||||
import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useBrowseNavigation } from "@/app/[domain]/browse/hooks/useBrowseNavigation";
|
||||
import { SymbolHoverPopup } from "@/ee/features/codeNav/components/symbolHoverPopup";
|
||||
import { symbolHoverTargetsExtension } from "@/ee/features/codeNav/components/symbolHoverPopup/symbolHoverTargetsExtension";
|
||||
import { useHasEntitlement } from "@/features/entitlements/useHasEntitlement";
|
||||
import { SymbolDefinition } from "@/ee/features/codeNav/components/symbolHoverPopup/useHoveredOverSymbolInfo";
|
||||
import { createAuditAction } from "@/ee/features/audit/actions";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
|
||||
export interface CodePreviewFile {
|
||||
content: string;
|
||||
|
|
@ -59,8 +56,6 @@ export const CodePreview = ({
|
|||
const languageExtension = useCodeMirrorLanguageExtension(file?.language ?? '', editorRef?.view);
|
||||
const [currentSelection, setCurrentSelection] = useState<SelectionRange>();
|
||||
|
||||
const captureEvent = useCaptureEvent();
|
||||
|
||||
const extensions = useMemo(() => {
|
||||
return [
|
||||
keymapExtension,
|
||||
|
|
@ -115,81 +110,6 @@ export const CodePreview = ({
|
|||
onSelectedMatchIndexChange((prev) => prev + 1);
|
||||
}, [onSelectedMatchIndexChange]);
|
||||
|
||||
const onGotoDefinition = useCallback((symbolName: string, symbolDefinitions: SymbolDefinition[]) => {
|
||||
captureEvent('wa_goto_definition_pressed', {
|
||||
source: 'preview',
|
||||
});
|
||||
createAuditAction({
|
||||
action: "user.performed_goto_definition",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
})
|
||||
|
||||
if (symbolDefinitions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (symbolDefinitions.length === 1) {
|
||||
const symbolDefinition = symbolDefinitions[0];
|
||||
const { fileName, repoName } = symbolDefinition;
|
||||
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName: file.revision,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
highlightRange: symbolDefinition.range,
|
||||
})
|
||||
} else {
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName: file.revision,
|
||||
path: file.filepath,
|
||||
pathType: 'blob',
|
||||
setBrowseState: {
|
||||
selectedSymbolInfo: {
|
||||
symbolName,
|
||||
repoName,
|
||||
revisionName: file.revision,
|
||||
language: file.language,
|
||||
},
|
||||
activeExploreMenuTab: "definitions",
|
||||
isBottomPanelCollapsed: false,
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [captureEvent, file.filepath, file.language, file.revision, navigateToPath, repoName]);
|
||||
|
||||
const onFindReferences = useCallback((symbolName: string) => {
|
||||
captureEvent('wa_find_references_pressed', {
|
||||
source: 'preview',
|
||||
});
|
||||
createAuditAction({
|
||||
action: "user.performed_find_references",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
})
|
||||
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName: file.revision,
|
||||
path: file.filepath,
|
||||
pathType: 'blob',
|
||||
setBrowseState: {
|
||||
selectedSymbolInfo: {
|
||||
repoName,
|
||||
symbolName,
|
||||
revisionName: file.revision,
|
||||
language: file.language,
|
||||
},
|
||||
activeExploreMenuTab: "references",
|
||||
isBottomPanelCollapsed: false,
|
||||
}
|
||||
})
|
||||
}, [captureEvent, file.filepath, file.language, file.revision, navigateToPath, repoName]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex flex-row bg-accent items-center justify-between pr-3 py-0.5 mt-7">
|
||||
|
|
@ -286,11 +206,12 @@ export const CodePreview = ({
|
|||
|
||||
{editorRef && hasCodeNavEntitlement && (
|
||||
<SymbolHoverPopup
|
||||
source="preview"
|
||||
editorRef={editorRef}
|
||||
language={file.language}
|
||||
revisionName={file.revision}
|
||||
onFindReferences={onFindReferences}
|
||||
onGotoDefinition={onGotoDefinition}
|
||||
fileName={file.filepath}
|
||||
repoName={repoName}
|
||||
/>
|
||||
)}
|
||||
</CodeMirror>
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ export const ExploreMenu = ({
|
|||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" align="center">
|
||||
{isGlobalSearchEnabled ? "Search in current repository only" : "Search all repositories"}
|
||||
Search all repositories
|
||||
<KeyboardShortcutHint
|
||||
shortcut="⇧ A"
|
||||
className="ml-2"
|
||||
|
|
|
|||
|
|
@ -1,42 +1,50 @@
|
|||
import { useBrowseNavigation } from "@/app/[domain]/browse/hooks/useBrowseNavigation";
|
||||
import { KeyboardShortcutHint } from "@/app/components/keyboardShortcutHint";
|
||||
import { useToast } from "@/components/hooks/use-toast";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { LoadingButton } from "@/components/ui/loading-button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { createAuditAction } from "@/ee/features/audit/actions";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
import { computePosition, flip, offset, shift, VirtualElement } from "@floating-ui/react";
|
||||
import { ReactCodeMirrorRef } from "@uiw/react-codemirror";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { SymbolDefinition, useHoveredOverSymbolInfo } from "./useHoveredOverSymbolInfo";
|
||||
import { SymbolDefinitionPreview } from "./symbolDefinitionPreview";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
import { useToast } from "@/components/hooks/use-toast";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { KeyboardShortcutHint } from "@/app/components/keyboardShortcutHint";
|
||||
import { SymbolDefinitionPreview } from "./symbolDefinitionPreview";
|
||||
import { useHoveredOverSymbolInfo } from "./useHoveredOverSymbolInfo";
|
||||
|
||||
interface SymbolHoverPopupProps {
|
||||
editorRef: ReactCodeMirrorRef;
|
||||
language: string;
|
||||
revisionName: string;
|
||||
onFindReferences: (symbolName: string) => void;
|
||||
onGotoDefinition: (symbolName: string, symbolDefinitions: SymbolDefinition[]) => void;
|
||||
repoName: string;
|
||||
fileName: string;
|
||||
source: 'browse' | 'preview' | 'chat';
|
||||
}
|
||||
|
||||
export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
|
||||
editorRef,
|
||||
revisionName,
|
||||
language,
|
||||
onFindReferences,
|
||||
onGotoDefinition: _onGotoDefinition,
|
||||
repoName,
|
||||
fileName,
|
||||
source,
|
||||
}) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [isSticky, setIsSticky] = useState(false);
|
||||
const { toast } = useToast();
|
||||
const { navigateToPath } = useBrowseNavigation();
|
||||
const captureEvent = useCaptureEvent();
|
||||
|
||||
const symbolInfo = useHoveredOverSymbolInfo({
|
||||
editorRef,
|
||||
isSticky,
|
||||
revisionName,
|
||||
language,
|
||||
repoName,
|
||||
});
|
||||
|
||||
// Positions the popup relative to the symbol
|
||||
|
|
@ -77,13 +85,121 @@ export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
|
|||
}
|
||||
}, [symbolInfo, editorRef]);
|
||||
|
||||
// Multiple symbol definitions can exist for the same symbol, but we can only navigate
|
||||
// and display a preview of one. If the symbol definition exists in the current file,
|
||||
// then we use that one, otherwise we fallback to the first definition in the list.
|
||||
const previewedSymbolDefinition = useMemo(() => {
|
||||
if (!symbolInfo?.symbolDefinitions || symbolInfo.symbolDefinitions.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const matchingDefinition = symbolInfo.symbolDefinitions.find(
|
||||
(definition) => (
|
||||
definition.fileName === fileName && definition.repoName === repoName
|
||||
)
|
||||
);
|
||||
|
||||
if (matchingDefinition) {
|
||||
return matchingDefinition;
|
||||
}
|
||||
|
||||
return symbolInfo.symbolDefinitions[0];
|
||||
}, [fileName, repoName, symbolInfo?.symbolDefinitions]);
|
||||
|
||||
const onGotoDefinition = useCallback(() => {
|
||||
if (!symbolInfo || !symbolInfo.symbolDefinitions) {
|
||||
if (
|
||||
!symbolInfo ||
|
||||
!symbolInfo.symbolDefinitions ||
|
||||
!previewedSymbolDefinition
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
_onGotoDefinition(symbolInfo.symbolName, symbolInfo.symbolDefinitions);
|
||||
}, [symbolInfo, _onGotoDefinition]);
|
||||
captureEvent('wa_goto_definition_pressed', {
|
||||
source,
|
||||
});
|
||||
|
||||
createAuditAction({
|
||||
action: "user.performed_goto_definition",
|
||||
metadata: {
|
||||
message: symbolInfo.symbolName,
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
fileName,
|
||||
repoName,
|
||||
revisionName,
|
||||
language,
|
||||
range: highlightRange,
|
||||
} = previewedSymbolDefinition;
|
||||
|
||||
navigateToPath({
|
||||
// Always navigate to the preview symbol definition.
|
||||
repoName,
|
||||
revisionName,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
highlightRange,
|
||||
// If there are multiple definitions, we should open the Explore panel with the definitions.
|
||||
...(symbolInfo.symbolDefinitions.length > 1 ? {
|
||||
setBrowseState: {
|
||||
selectedSymbolInfo: {
|
||||
symbolName: symbolInfo.symbolName,
|
||||
repoName,
|
||||
revisionName,
|
||||
language,
|
||||
},
|
||||
activeExploreMenuTab: "definitions",
|
||||
isBottomPanelCollapsed: false,
|
||||
}
|
||||
} : {}),
|
||||
});
|
||||
}, [
|
||||
captureEvent,
|
||||
previewedSymbolDefinition,
|
||||
navigateToPath,
|
||||
source,
|
||||
symbolInfo
|
||||
]);
|
||||
|
||||
const onFindReferences = useCallback((symbolName: string) => {
|
||||
captureEvent('wa_find_references_pressed', {
|
||||
source,
|
||||
});
|
||||
|
||||
createAuditAction({
|
||||
action: "user.performed_find_references",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
})
|
||||
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
setBrowseState: {
|
||||
selectedSymbolInfo: {
|
||||
symbolName,
|
||||
repoName,
|
||||
revisionName,
|
||||
language,
|
||||
},
|
||||
activeExploreMenuTab: "references",
|
||||
isBottomPanelCollapsed: false,
|
||||
}
|
||||
})
|
||||
}, [
|
||||
captureEvent,
|
||||
fileName,
|
||||
language,
|
||||
navigateToPath,
|
||||
repoName,
|
||||
revisionName,
|
||||
source
|
||||
]);
|
||||
|
||||
// @todo: We should probably make the behaviour s.t., the ctrl / cmd key needs to be held
|
||||
// down to navigate to the definition. We should also only show the underline when the key
|
||||
|
|
@ -147,9 +263,9 @@ export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
|
|||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
Loading...
|
||||
</div>
|
||||
) : symbolInfo.symbolDefinitions && symbolInfo.symbolDefinitions.length > 0 ? (
|
||||
) : previewedSymbolDefinition ? (
|
||||
<SymbolDefinitionPreview
|
||||
symbolDefinition={symbolInfo.symbolDefinitions[0]}
|
||||
symbolDefinition={previewedSymbolDefinition}
|
||||
/>
|
||||
) : (
|
||||
<p className="text-sm font-medium text-muted-foreground">No hover info found</p>
|
||||
|
|
@ -160,13 +276,13 @@ export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
|
|||
<TooltipTrigger asChild>
|
||||
<LoadingButton
|
||||
loading={symbolInfo.isSymbolDefinitionsLoading}
|
||||
disabled={!symbolInfo.symbolDefinitions || symbolInfo.symbolDefinitions.length === 0}
|
||||
disabled={!previewedSymbolDefinition}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onGotoDefinition}
|
||||
>
|
||||
{
|
||||
!symbolInfo.isSymbolDefinitionsLoading && (!symbolInfo.symbolDefinitions || symbolInfo.symbolDefinitions.length === 0) ?
|
||||
!symbolInfo.isSymbolDefinitionsLoading && !previewedSymbolDefinition ?
|
||||
"No definition found" :
|
||||
`Go to ${symbolInfo.symbolDefinitions && symbolInfo.symbolDefinitions.length > 1 ? "definitions" : "definition"}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ interface UseHoveredOverSymbolInfoProps {
|
|||
isSticky: boolean;
|
||||
revisionName: string;
|
||||
language: string;
|
||||
repoName: string;
|
||||
}
|
||||
|
||||
export type SymbolDefinition = {
|
||||
|
|
@ -19,6 +20,7 @@ export type SymbolDefinition = {
|
|||
language: string;
|
||||
fileName: string;
|
||||
repoName: string;
|
||||
revisionName: string;
|
||||
range: SourceRange;
|
||||
}
|
||||
|
||||
|
|
@ -37,6 +39,7 @@ export const useHoveredOverSymbolInfo = ({
|
|||
isSticky,
|
||||
revisionName,
|
||||
language,
|
||||
repoName,
|
||||
}: UseHoveredOverSymbolInfoProps): HoveredOverSymbolInfo | undefined => {
|
||||
const mouseOverTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const mouseOutTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
|
@ -50,12 +53,13 @@ export const useHoveredOverSymbolInfo = ({
|
|||
}, [symbolElement]);
|
||||
|
||||
const { data: symbolDefinitions, isLoading: isSymbolDefinitionsLoading } = useQuery({
|
||||
queryKey: ["definitions", symbolName, revisionName, language, domain],
|
||||
queryKey: ["definitions", symbolName, revisionName, language, domain, repoName],
|
||||
queryFn: () => unwrapServiceError(
|
||||
findSearchBasedSymbolDefinitions({
|
||||
symbolName: symbolName!,
|
||||
language,
|
||||
revisionName,
|
||||
repoName,
|
||||
})
|
||||
),
|
||||
select: ((data) => {
|
||||
|
|
@ -66,6 +70,7 @@ export const useHoveredOverSymbolInfo = ({
|
|||
language: file.language,
|
||||
fileName: file.fileName,
|
||||
repoName: file.repository,
|
||||
revisionName: revisionName,
|
||||
range: match.range,
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import { useBrowseNavigation } from "@/app/[domain]/browse/hooks/useBrowseNavigation";
|
||||
import { PathHeader } from "@/app/[domain]/components/pathHeader";
|
||||
import { SymbolHoverPopup } from '@/ee/features/codeNav/components/symbolHoverPopup';
|
||||
import { symbolHoverTargetsExtension } from "@/ee/features/codeNav/components/symbolHoverPopup/symbolHoverTargetsExtension";
|
||||
import { SymbolDefinition } from '@/ee/features/codeNav/components/symbolHoverPopup/useHoveredOverSymbolInfo';
|
||||
import { useHasEntitlement } from "@/features/entitlements/useHasEntitlement";
|
||||
import { useCodeMirrorLanguageExtension } from "@/hooks/useCodeMirrorLanguageExtension";
|
||||
import { useCodeMirrorTheme } from "@/hooks/useCodeMirrorTheme";
|
||||
|
|
@ -12,15 +10,13 @@ import { useKeymapExtension } from "@/hooks/useKeymapExtension";
|
|||
import { cn } from "@/lib/utils";
|
||||
import { Range } from "@codemirror/state";
|
||||
import { Decoration, DecorationSet, EditorView } from '@codemirror/view';
|
||||
import { CodeHostType } from "@sourcebot/db";
|
||||
import CodeMirror, { ReactCodeMirrorRef, StateField } from '@uiw/react-codemirror';
|
||||
import isEqual from "fast-deep-equal/react";
|
||||
import { ChevronDown, ChevronRight } from "lucide-react";
|
||||
import { forwardRef, memo, Ref, useCallback, useImperativeHandle, useMemo, useState } from "react";
|
||||
import { FileReference } from "../../types";
|
||||
import { createCodeFoldingExtension } from "./codeFoldingExtension";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
import { CodeHostType } from "@sourcebot/db";
|
||||
import { createAuditAction } from "@/ee/features/audit/actions";
|
||||
import isEqual from "fast-deep-equal/react";
|
||||
|
||||
const lineDecoration = Decoration.line({
|
||||
attributes: { class: "cm-range-border-radius chat-lineHighlight" },
|
||||
|
|
@ -74,7 +70,6 @@ const ReferencedFileSourceListItem = ({
|
|||
}: ReferencedFileSourceListItemProps, forwardedRef: Ref<ReactCodeMirrorRef>) => {
|
||||
const theme = useCodeMirrorTheme();
|
||||
const [editorRef, setEditorRef] = useState<ReactCodeMirrorRef | null>(null);
|
||||
const captureEvent = useCaptureEvent();
|
||||
|
||||
useImperativeHandle(
|
||||
forwardedRef,
|
||||
|
|
@ -84,7 +79,6 @@ const ReferencedFileSourceListItem = ({
|
|||
const hasCodeNavEntitlement = useHasEntitlement("code-nav");
|
||||
|
||||
const languageExtension = useCodeMirrorLanguageExtension(language, editorRef?.view);
|
||||
const { navigateToPath } = useBrowseNavigation();
|
||||
|
||||
const getReferenceAtPos = useCallback((x: number, y: number, view: EditorView): FileReference | undefined => {
|
||||
const pos = view.posAtCoords({ x, y });
|
||||
|
|
@ -217,83 +211,6 @@ const ReferencedFileSourceListItem = ({
|
|||
codeFoldingExtension,
|
||||
]);
|
||||
|
||||
const onGotoDefinition = useCallback((symbolName: string, symbolDefinitions: SymbolDefinition[]) => {
|
||||
if (symbolDefinitions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
captureEvent('wa_goto_definition_pressed', {
|
||||
source: 'chat',
|
||||
});
|
||||
createAuditAction({
|
||||
action: "user.performed_goto_definition",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
});
|
||||
|
||||
if (symbolDefinitions.length === 1) {
|
||||
const symbolDefinition = symbolDefinitions[0];
|
||||
const { fileName, repoName } = symbolDefinition;
|
||||
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName: revision,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
highlightRange: symbolDefinition.range,
|
||||
})
|
||||
} else {
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName: revision,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
setBrowseState: {
|
||||
selectedSymbolInfo: {
|
||||
symbolName,
|
||||
repoName,
|
||||
revisionName: revision,
|
||||
language: language,
|
||||
},
|
||||
activeExploreMenuTab: "definitions",
|
||||
isBottomPanelCollapsed: false,
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}, [captureEvent, navigateToPath, revision, repoName, fileName, language]);
|
||||
|
||||
const onFindReferences = useCallback((symbolName: string) => {
|
||||
captureEvent('wa_find_references_pressed', {
|
||||
source: 'chat',
|
||||
});
|
||||
createAuditAction({
|
||||
action: "user.performed_find_references",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
});
|
||||
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName: revision,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
setBrowseState: {
|
||||
selectedSymbolInfo: {
|
||||
symbolName,
|
||||
repoName,
|
||||
revisionName: revision,
|
||||
language: language,
|
||||
},
|
||||
activeExploreMenuTab: "references",
|
||||
isBottomPanelCollapsed: false,
|
||||
}
|
||||
})
|
||||
|
||||
}, [captureEvent, fileName, language, navigateToPath, repoName, revision]);
|
||||
|
||||
const ExpandCollapseIcon = useMemo(() => {
|
||||
return isExpanded ? ChevronDown : ChevronRight;
|
||||
}, [isExpanded]);
|
||||
|
|
@ -341,11 +258,12 @@ const ReferencedFileSourceListItem = ({
|
|||
>
|
||||
{editorRef && hasCodeNavEntitlement && (
|
||||
<SymbolHoverPopup
|
||||
source="chat"
|
||||
editorRef={editorRef}
|
||||
revisionName={revision}
|
||||
language={language}
|
||||
onFindReferences={onFindReferences}
|
||||
onGotoDefinition={onGotoDefinition}
|
||||
repoName={repoName}
|
||||
fileName={fileName}
|
||||
/>
|
||||
)}
|
||||
</CodeMirror>
|
||||
|
|
|
|||
Loading…
Reference in a new issue