2024-09-26 03:12:20 +00:00
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import { getRepoCodeHostInfo } from "@/lib/utils";
|
2024-10-30 16:32:05 +00:00
|
|
|
import { useCallback, useMemo } from "react";
|
2024-09-26 03:12:20 +00:00
|
|
|
import Image from "next/image";
|
|
|
|
|
import { DoubleArrowDownIcon, DoubleArrowUpIcon, FileIcon } from "@radix-ui/react-icons";
|
|
|
|
|
import clsx from "clsx";
|
|
|
|
|
import { Separator } from "@/components/ui/separator";
|
|
|
|
|
import { SearchResultFile } from "@/lib/types";
|
|
|
|
|
import { FileMatch } from "./fileMatch";
|
|
|
|
|
|
2024-10-30 16:32:05 +00:00
|
|
|
export const MAX_MATCHES_TO_PREVIEW = 3;
|
2024-09-26 03:12:20 +00:00
|
|
|
|
|
|
|
|
interface FileMatchContainerProps {
|
|
|
|
|
file: SearchResultFile;
|
|
|
|
|
onOpenFile: () => void;
|
|
|
|
|
onMatchIndexChanged: (matchIndex: number) => void;
|
2024-10-30 16:32:05 +00:00
|
|
|
showAllMatches: boolean;
|
|
|
|
|
onShowAllMatchesButtonClicked: () => void;
|
2024-11-07 02:28:10 +00:00
|
|
|
isBranchFilteringEnabled: boolean;
|
2024-09-26 03:12:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const FileMatchContainer = ({
|
|
|
|
|
file,
|
|
|
|
|
onOpenFile,
|
|
|
|
|
onMatchIndexChanged,
|
2024-10-30 16:32:05 +00:00
|
|
|
showAllMatches,
|
|
|
|
|
onShowAllMatchesButtonClicked,
|
2024-11-07 02:28:10 +00:00
|
|
|
isBranchFilteringEnabled,
|
2024-09-26 03:12:20 +00:00
|
|
|
}: FileMatchContainerProps) => {
|
|
|
|
|
|
|
|
|
|
const matchCount = useMemo(() => {
|
|
|
|
|
return file.ChunkMatches.length;
|
|
|
|
|
}, [file]);
|
|
|
|
|
|
|
|
|
|
const matches = useMemo(() => {
|
|
|
|
|
const sortedMatches = file.ChunkMatches.sort((a, b) => {
|
|
|
|
|
return a.ContentStart.LineNumber - b.ContentStart.LineNumber;
|
|
|
|
|
});
|
|
|
|
|
|
2024-10-30 16:32:05 +00:00
|
|
|
if (!showAllMatches) {
|
2024-09-26 03:12:20 +00:00
|
|
|
return sortedMatches.slice(0, MAX_MATCHES_TO_PREVIEW);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sortedMatches;
|
2024-10-30 16:32:05 +00:00
|
|
|
}, [file, showAllMatches]);
|
2024-09-26 03:12:20 +00:00
|
|
|
|
|
|
|
|
const fileNameRange = useMemo(() => {
|
|
|
|
|
for (const match of matches) {
|
|
|
|
|
if (match.FileName && match.Ranges.length > 0) {
|
|
|
|
|
const range = match.Ranges[0];
|
|
|
|
|
return {
|
|
|
|
|
from: range.Start.Column - 1,
|
|
|
|
|
to: range.End.Column - 1,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}, [matches]);
|
|
|
|
|
|
|
|
|
|
const { repoIcon, repoName, repoLink } = useMemo(() => {
|
|
|
|
|
const info = getRepoCodeHostInfo(file.Repository);
|
|
|
|
|
if (info) {
|
|
|
|
|
return {
|
|
|
|
|
repoName: info.repoName,
|
|
|
|
|
repoLink: info.repoLink,
|
|
|
|
|
repoIcon: <Image
|
|
|
|
|
src={info.icon}
|
|
|
|
|
alt={info.costHostName}
|
2024-10-28 17:30:29 +00:00
|
|
|
className={`w-4 h-4 ${info.iconClassName}`}
|
2024-09-26 03:12:20 +00:00
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
repoName: file.Repository,
|
|
|
|
|
repoLink: undefined,
|
|
|
|
|
repoIcon: <FileIcon className="w-4 h-4" />
|
|
|
|
|
}
|
|
|
|
|
}, [file]);
|
|
|
|
|
|
|
|
|
|
const isMoreContentButtonVisible = useMemo(() => {
|
|
|
|
|
return matchCount > MAX_MATCHES_TO_PREVIEW;
|
|
|
|
|
}, [matchCount]);
|
|
|
|
|
|
|
|
|
|
const onOpenMatch = useCallback((index: number) => {
|
|
|
|
|
const matchIndex = matches.slice(0, index).reduce((acc, match) => {
|
|
|
|
|
return acc + match.Ranges.length;
|
|
|
|
|
}, 0);
|
|
|
|
|
onOpenFile();
|
|
|
|
|
onMatchIndexChanged(matchIndex);
|
|
|
|
|
}, [matches, onMatchIndexChanged, onOpenFile]);
|
|
|
|
|
|
2024-11-07 02:28:10 +00:00
|
|
|
const branches = useMemo(() => {
|
|
|
|
|
if (!file.Branches) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return file.Branches;
|
|
|
|
|
}, [file.Branches]);
|
|
|
|
|
|
2024-09-26 03:12:20 +00:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
2024-10-30 16:32:05 +00:00
|
|
|
{/* Title */}
|
2024-09-26 03:12:20 +00:00
|
|
|
<div
|
2024-10-30 16:32:05 +00:00
|
|
|
className="top-0 bg-cyan-200 dark:bg-cyan-900 primary-foreground px-2 py-0.5 flex flex-row items-center justify-between cursor-pointer"
|
2024-09-26 03:12:20 +00:00
|
|
|
onClick={() => {
|
|
|
|
|
onOpenFile();
|
|
|
|
|
}}
|
|
|
|
|
>
|
2024-10-28 17:30:29 +00:00
|
|
|
<div className="flex flex-row gap-2 items-center w-full overflow-hidden">
|
2024-09-26 03:12:20 +00:00
|
|
|
{repoIcon}
|
|
|
|
|
<span
|
|
|
|
|
className={clsx("font-medium", {
|
|
|
|
|
"cursor-pointer hover:underline": repoLink,
|
|
|
|
|
})}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
if (repoLink) {
|
|
|
|
|
window.open(repoLink, "_blank");
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{repoName}
|
|
|
|
|
</span>
|
2024-11-07 02:28:10 +00:00
|
|
|
{isBranchFilteringEnabled && branches.length > 0 && (
|
|
|
|
|
<span
|
|
|
|
|
className="text-xs font-semibold text-gray-500 dark:text-gray-400 mt-0.5"
|
|
|
|
|
title={branches.join(", ")}
|
|
|
|
|
>
|
|
|
|
|
{`@ ${branches[0]}`}
|
|
|
|
|
{branches.length > 1 && ` (+ ${branches.length - 1})`}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
2024-09-26 03:12:20 +00:00
|
|
|
<span>·</span>
|
2024-10-28 17:30:29 +00:00
|
|
|
<div className="flex-1 flex items-center overflow-hidden">
|
2024-11-07 02:28:10 +00:00
|
|
|
<span className="inline-block w-full truncate-start font-mono text-sm">
|
2024-10-28 17:30:29 +00:00
|
|
|
{!fileNameRange ?
|
|
|
|
|
file.FileName
|
|
|
|
|
: (
|
|
|
|
|
<>
|
|
|
|
|
{file.FileName.slice(0, fileNameRange.from)}
|
|
|
|
|
<span className="bg-yellow-200 dark:bg-blue-700">
|
|
|
|
|
{file.FileName.slice(fileNameRange.from, fileNameRange.to)}
|
|
|
|
|
</span>
|
|
|
|
|
{file.FileName.slice(fileNameRange.to)}
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2024-09-26 03:12:20 +00:00
|
|
|
</span>
|
2024-10-28 17:30:29 +00:00
|
|
|
</div>
|
2024-09-26 03:12:20 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-10-30 16:32:05 +00:00
|
|
|
|
|
|
|
|
{/* Matches */}
|
2024-09-26 03:12:20 +00:00
|
|
|
{matches.map((match, index) => (
|
|
|
|
|
<div
|
|
|
|
|
key={index}
|
|
|
|
|
>
|
|
|
|
|
<FileMatch
|
|
|
|
|
match={match}
|
|
|
|
|
file={file}
|
|
|
|
|
onOpen={() => {
|
|
|
|
|
onOpenMatch(index);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
{(index !== matches.length - 1 || isMoreContentButtonVisible) && (
|
|
|
|
|
<Separator className="dark:bg-gray-400" />
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
2024-10-30 16:32:05 +00:00
|
|
|
|
|
|
|
|
{/* Show more button */}
|
2024-09-26 03:12:20 +00:00
|
|
|
{isMoreContentButtonVisible && (
|
|
|
|
|
<div
|
|
|
|
|
tabIndex={0}
|
|
|
|
|
className="px-4 bg-accent p-0.5"
|
|
|
|
|
onKeyDown={(e) => {
|
|
|
|
|
if (e.key !== "Enter") {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-10-30 16:32:05 +00:00
|
|
|
onShowAllMatchesButtonClicked();
|
2024-09-26 03:12:20 +00:00
|
|
|
}}
|
2024-10-30 16:32:05 +00:00
|
|
|
onClick={onShowAllMatchesButtonClicked}
|
2024-09-26 03:12:20 +00:00
|
|
|
>
|
|
|
|
|
<p
|
|
|
|
|
className="text-blue-500 cursor-pointer text-sm flex flex-row items-center gap-2"
|
|
|
|
|
>
|
2024-10-30 16:32:05 +00:00
|
|
|
{showAllMatches ? <DoubleArrowUpIcon className="w-3 h-3" /> : <DoubleArrowDownIcon className="w-3 h-3" />}
|
|
|
|
|
{showAllMatches ? `Show fewer matches` : `Show ${matchCount - MAX_MATCHES_TO_PREVIEW} more matches`}
|
2024-09-26 03:12:20 +00:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|