-
Notifications
You must be signed in to change notification settings - Fork 261
Expand file tree
/
Copy pathcodePreview.tsx
More file actions
125 lines (112 loc) · 4.6 KB
/
codePreview.tsx
File metadata and controls
125 lines (112 loc) · 4.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
'use client';
import { useExtensionWithDependency } from "@/hooks/useExtensionWithDependency";
import { useSyntaxHighlightingExtension } from "@/hooks/useSyntaxHighlightingExtension";
import { useThemeNormalized } from "@/hooks/useThemeNormalized";
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 { useMemo, useRef } from "react";
const markDecoration = Decoration.mark({
class: "cm-searchMatch-selected"
});
interface CodePreviewProps {
content: string,
language: string,
ranges: SearchResultRange[],
lineOffset: number,
}
export const CodePreview = ({
content,
language,
ranges,
lineOffset,
}: CodePreviewProps) => {
const editorRef = useRef<ReactCodeMirrorRef>(null);
const { theme } = useThemeNormalized();
const syntaxHighlighting = useSyntaxHighlightingExtension(language, editorRef.current?.view);
const rangeHighlighting = useExtensionWithDependency(editorRef.current?.view ?? null, () => {
return [
StateField.define<DecorationSet>({
create(editorState: EditorState) {
const document = editorState.doc;
const decorations = ranges
.sort((a, b) => {
return a.Start.ByteOffset - b.Start.ByteOffset;
})
.filter(({ Start, End }) => {
const startLine = Start.LineNumber - lineOffset;
const endLine = End.LineNumber - lineOffset;
if (
startLine < 1 ||
endLine < 1 ||
startLine > document.lines ||
endLine > document.lines
) {
return false;
}
return true;
})
.map(({ Start, End }) => {
const startLine = Start.LineNumber - lineOffset;
const endLine = End.LineNumber - lineOffset;
const from = document.line(startLine).from + Start.Column - 1;
const to = document.line(endLine).from + End.Column - 1;
return markDecoration.range(from, to);
});
return Decoration.set(decorations);
},
update(highlights: DecorationSet, _transaction: Transaction) {
return highlights;
},
provide: (field) => EditorView.decorations.from(field),
}),
];
}, [ranges, lineOffset]);
const extensions = useMemo(() => {
return [
syntaxHighlighting,
EditorView.lineWrapping,
lineOffsetExtension(lineOffset),
rangeHighlighting,
];
}, [syntaxHighlighting, lineOffset, rangeHighlighting]);
return (
<CodeMirror
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}
/>
)
}