Skip to content

Improve rendering performance of search results #52

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"recommendations": [
"dbaeumer.vscode-eslint"
]
}
7 changes: 6 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,10 @@
"files.associations": {
"*.json": "jsonc",
"index.json": "json"
}
},
"eslint.workingDirectories": [
{
"pattern": "./packages/*/"
}
]
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Fixed issue with GitLab sub-projects not being included recursively. ([#54](https://github.com/sourcebot-dev/sourcebot/pull/54))
- Fixed slow rendering performance when rendering a large number of results. ([#52](https://github.com/sourcebot-dev/sourcebot/pull/52))

## [2.1.1] - 2024-10-25

Expand Down
1 change: 1 addition & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@replit/codemirror-vim": "^6.2.1",
"@tanstack/react-query": "^5.53.3",
"@tanstack/react-table": "^8.20.5",
"@tanstack/react-virtual": "^3.10.8",
"@uiw/react-codemirror": "^4.23.0",
"class-variance-authority": "^0.7.0",
"client-only": "^0.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,4 @@ export const CodePreviewPanel = ({
onSelectedMatchIndexChange={onSelectedMatchIndexChange}
/>
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const FilterPanel = ({
);

onFilterChanged(filteredMatches);
}, [matches, repos, languages]);
}, [matches, repos, languages, onFilterChanged]);

return (
<div className="p-3 flex flex-col gap-3">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
'use client';

import { useExtensionWithDependency } from "@/hooks/useExtensionWithDependency";
import { useSyntaxHighlightingExtension } from "@/hooks/useSyntaxHighlightingExtension";
import { useThemeNormalized } from "@/hooks/useThemeNormalized";
import { getSyntaxHighlightingExtension } from "@/hooks/useSyntaxHighlightingExtension";
import { lineOffsetExtension } from "@/lib/extensions/lineOffsetExtension";
import { SearchResultRange } from "@/lib/types";
import CodeMirror, { Decoration, DecorationSet, EditorState, EditorView, ReactCodeMirrorRef, StateField, Transaction } from "@uiw/react-codemirror";
import { defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
import { EditorState, StateField, Transaction } from "@codemirror/state";
import { defaultLightThemeOption, oneDarkHighlightStyle, oneDarkTheme } from "@uiw/react-codemirror";
import { Decoration, DecorationSet, EditorView, lineNumbers } from "@codemirror/view";
import { useMemo, useRef } from "react";
import { LightweightCodeMirror, CodeMirrorRef } from "./lightweightCodeMirror";
import { useThemeNormalized } from "@/hooks/useThemeNormalized";

const markDecoration = Decoration.mark({
class: "cm-searchMatch-selected"
Expand All @@ -25,13 +28,22 @@ export const CodePreview = ({
ranges,
lineOffset,
}: CodePreviewProps) => {
const editorRef = useRef<ReactCodeMirrorRef>(null);
const editorRef = useRef<CodeMirrorRef>(null);
const { theme } = useThemeNormalized();

const syntaxHighlighting = useSyntaxHighlightingExtension(language, editorRef.current?.view);

const rangeHighlighting = useExtensionWithDependency(editorRef.current?.view ?? null, () => {
const extensions = useMemo(() => {
return [
EditorView.editable.of(false),
...(theme === 'dark' ? [
syntaxHighlighting(oneDarkHighlightStyle),
oneDarkTheme,
] : [
syntaxHighlighting(defaultHighlightStyle),
defaultLightThemeOption,
]),
lineNumbers(),
lineOffsetExtension(lineOffset),
getSyntaxHighlightingExtension(language),
StateField.define<DecorationSet>({
create(editorState: EditorState) {
const document = editorState.doc;
Expand Down Expand Up @@ -61,7 +73,8 @@ export const CodePreview = ({
const from = document.line(startLine).from + Start.Column - 1;
const to = document.line(endLine).from + End.Column - 1;
return markDecoration.range(from, to);
});
})
.sort((a, b) => a.from - b.from);

return Decoration.set(decorations);
},
Expand All @@ -70,56 +83,15 @@ export const CodePreview = ({
},
provide: (field) => EditorView.decorations.from(field),
}),
];
}, [ranges, lineOffset]);

const extensions = useMemo(() => {
return [
syntaxHighlighting,
EditorView.lineWrapping,
lineOffsetExtension(lineOffset),
rangeHighlighting,
];
}, [syntaxHighlighting, lineOffset, rangeHighlighting]);
]
}, [language, lineOffset, ranges, theme]);

return (
<CodeMirror
<LightweightCodeMirror
ref={editorRef}
readOnly={true}
editable={false}
value={content}
theme={theme === "dark" ? "dark" : "light"}
basicSetup={{
lineNumbers: true,
syntaxHighlighting: true,

// Disable all this other stuff...
... {
foldGutter: false,
highlightActiveLineGutter: false,
highlightSpecialChars: false,
history: false,
drawSelection: false,
dropCursor: false,
allowMultipleSelections: false,
indentOnInput: false,
bracketMatching: false,
closeBrackets: false,
autocompletion: false,
rectangularSelection: false,
crosshairCursor: false,
highlightActiveLine: false,
highlightSelectionMatches: false,
closeBracketsKeymap: false,
defaultKeymap: false,
searchKeymap: false,
historyKeymap: false,
foldKeymap: false,
completionKeymap: false,
lintKeymap: false,
}
}}
extensions={extensions}
/>
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const FileMatch = ({
return (
<div
tabIndex={0}
className="cursor-pointer p-1 focus:ring-inset focus:ring-4 bg-white dark:bg-[#282c34]"
className="cursor-pointer focus:ring-inset focus:ring-4 bg-white dark:bg-[#282c34]"
onKeyDown={(e) => {
if (e.key !== "Enter") {
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
'use client';

import { getRepoCodeHostInfo } from "@/lib/utils";
import { useCallback, useMemo, useState } from "react";
import { useCallback, useMemo } from "react";
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";

const MAX_MATCHES_TO_PREVIEW = 3;
export const MAX_MATCHES_TO_PREVIEW = 3;

interface FileMatchContainerProps {
file: SearchResultFile;
onOpenFile: () => void;
onMatchIndexChanged: (matchIndex: number) => void;
showAllMatches: boolean;
onShowAllMatchesButtonClicked: () => void;
}

export const FileMatchContainer = ({
file,
onOpenFile,
onMatchIndexChanged,
showAllMatches,
onShowAllMatchesButtonClicked,
}: FileMatchContainerProps) => {

const [showAll, setShowAll] = useState(false);
const matchCount = useMemo(() => {
return file.ChunkMatches.length;
}, [file]);
Expand All @@ -33,12 +36,12 @@ export const FileMatchContainer = ({
return a.ContentStart.LineNumber - b.ContentStart.LineNumber;
});

if (!showAll) {
if (!showAllMatches) {
return sortedMatches.slice(0, MAX_MATCHES_TO_PREVIEW);
}

return sortedMatches;
}, [file, showAll]);
}, [file, showAllMatches]);

const fileNameRange = useMemo(() => {
for (const match of matches) {
Expand Down Expand Up @@ -79,10 +82,6 @@ export const FileMatchContainer = ({
return matchCount > MAX_MATCHES_TO_PREVIEW;
}, [matchCount]);

const onShowMoreMatches = useCallback(() => {
setShowAll(!showAll);
}, [showAll]);

const onOpenMatch = useCallback((index: number) => {
const matchIndex = matches.slice(0, index).reduce((acc, match) => {
return acc + match.Ranges.length;
Expand All @@ -94,8 +93,9 @@ export const FileMatchContainer = ({

return (
<div>
{/* Title */}
<div
className="sticky 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 z-10"
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"
onClick={() => {
onOpenFile();
}}
Expand Down Expand Up @@ -132,6 +132,8 @@ export const FileMatchContainer = ({
</div>
</div>
</div>

{/* Matches */}
{matches.map((match, index) => (
<div
key={index}
Expand All @@ -148,6 +150,8 @@ export const FileMatchContainer = ({
)}
</div>
))}

{/* Show more button */}
{isMoreContentButtonVisible && (
<div
tabIndex={0}
Expand All @@ -156,15 +160,15 @@ export const FileMatchContainer = ({
if (e.key !== "Enter") {
return;
}
onShowMoreMatches();
onShowAllMatchesButtonClicked();
}}
onClick={onShowMoreMatches}
onClick={onShowAllMatchesButtonClicked}
>
<p
className="text-blue-500 cursor-pointer text-sm flex flex-row items-center gap-2"
>
{showAll ? <DoubleArrowUpIcon className="w-3 h-3" /> : <DoubleArrowDownIcon className="w-3 h-3" />}
{showAll ? `Show fewer matches` : `Show ${matchCount - MAX_MATCHES_TO_PREVIEW} more matches`}
{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`}
</p>
</div>
)}
Expand Down
Loading