Skip to content

Commit 8adabf6

Browse files
committed
add offset support
1 parent b11e1b2 commit 8adabf6

File tree

4 files changed

+78
-14
lines changed

4 files changed

+78
-14
lines changed

web/public/schemas.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

web/src/components/Docs/SchemaClass.tsx

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import React, { useContext, useMemo } from "react";
2-
import { Link } from "react-router-dom";
2+
import { Link, useNavigate } from "react-router-dom";
33
import styled from "styled-components";
44
import * as api from "./api";
55
import { SchemaTypeView, MetadataTags } from "./SchemaType";
66
import { ReferencedBy } from "./ReferencedBy";
77
import { KindIcon } from "./utils/components";
88
import { DeclarationsContext } from "./DeclarationsContext";
9-
import { useSearchWords } from "./utils/filtering";
9+
import { useSearchWords, useSearchOffsets } from "./utils/filtering";
1010
import {
1111
CommonGroupMembers,
1212
CommonGroupSignature,
@@ -41,6 +41,21 @@ const FieldRow = styled.div<{ $highlighted?: boolean }>`
4141
const FieldSignature = styled.div`
4242
font-weight: 600;
4343
font-size: 16px;
44+
display: flex;
45+
align-items: baseline;
46+
gap: 6px;
47+
`;
48+
49+
const FieldOffset = styled.span`
50+
font-size: 14px;
51+
font-weight: 500;
52+
color: ${(props) => props.theme.textDim};
53+
font-variant-numeric: tabular-nums;
54+
cursor: pointer;
55+
56+
&:hover {
57+
color: ${(props) => props.theme.highlight};
58+
}
4459
`;
4560

4661
const CollapsedFieldsLink = styled(Link)`
@@ -55,9 +70,13 @@ const CollapsedFieldsLink = styled(Link)`
5570
}
5671
`;
5772

58-
export const ModuleBadge: React.FC<{ module: string }> = ({ module }) => (
59-
<StyledModuleBadge>{module}</StyledModuleBadge>
60-
);
73+
export const ModuleBadge: React.FC<{ module: string }> = ({ module }) => {
74+
const { root } = useContext(DeclarationsContext);
75+
const navigate = useNavigate();
76+
return (
77+
<StyledModuleBadge onClick={() => navigate(`${root}?search=${encodeURIComponent(`module:${module}`)}`)}>{module}</StyledModuleBadge>
78+
);
79+
};
6180

6281
const StyledModuleBadge = styled.span`
6382
font-size: 14px;
@@ -69,6 +88,11 @@ const StyledModuleBadge = styled.span`
6988
border-radius: 6px;
7089
margin-left: 8px;
7190
vertical-align: middle;
91+
cursor: pointer;
92+
93+
&:hover {
94+
border-color: ${(props) => props.theme.highlight};
95+
}
7296
`;
7397

7498
export function matchesWords(name: string, words: string[]): boolean {
@@ -83,17 +107,21 @@ export const SchemaClassView: React.FC<{
83107
}> = ({ className, style, declaration }) => {
84108
const { root } = useContext(DeclarationsContext);
85109
const searchWords = useSearchWords();
110+
const searchOffsets = useSearchOffsets();
86111

87-
const nameMatches = searchWords.length === 0 || matchesWords(declaration.name, searchWords);
88-
const collapseNonMatching = searchWords.length > 0 && !nameMatches;
112+
const isSearching = searchWords.length > 0 || searchOffsets.length > 0;
113+
const nameMatches = searchWords.length > 0 && matchesWords(declaration.name, searchWords);
114+
const collapseNonMatching = isSearching && !nameMatches;
89115

90116
const { matchingFields, hiddenCount } = useMemo(() => {
91117
if (!collapseNonMatching) {
92118
return { matchingFields: declaration.fields, hiddenCount: 0 };
93119
}
94-
const matching = declaration.fields.filter((f) => matchesWords(f.name, searchWords));
120+
const matching = declaration.fields.filter((f) =>
121+
(searchWords.length > 0 && matchesWords(f.name, searchWords)) || (searchOffsets.length > 0 && searchOffsets.includes(f.offset)),
122+
);
95123
return { matchingFields: matching, hiddenCount: declaration.fields.length - matching.length };
96-
}, [declaration.fields, searchWords, collapseNonMatching]);
124+
}, [declaration.fields, searchWords, searchOffsets, collapseNonMatching]);
97125

98126
return (
99127
<CommonGroupWrapper className={className} style={style}>
@@ -120,7 +148,7 @@ export const SchemaClassView: React.FC<{
120148
{(matchingFields.length > 0 || hiddenCount > 0) && (
121149
<ClassMembers>
122150
{matchingFields.map((field) => (
123-
<SchemaFieldView key={field.name} field={field} highlighted={searchWords.length > 0 && matchesWords(field.name, searchWords)} />
151+
<SchemaFieldView key={field.name} field={field} highlighted={(searchWords.length > 0 && matchesWords(field.name, searchWords)) || (searchOffsets.length > 0 && searchOffsets.includes(field.offset))} />
124152
))}
125153
{hiddenCount > 0 && (
126154
<CollapsedFieldsLink to={`${root}/${declaration.module}/${declaration.name}`}>
@@ -135,10 +163,14 @@ export const SchemaClassView: React.FC<{
135163
};
136164

137165
function SchemaFieldView({ field, highlighted }: { field: api.SchemaField; highlighted: boolean }) {
166+
const { root } = useContext(DeclarationsContext);
167+
const navigate = useNavigate();
168+
const offsetHex = `0x${field.offset.toString(16).toUpperCase()}`;
138169
return (
139170
<FieldRow $highlighted={highlighted}>
140171
<FieldSignature>
141172
<KindIcon kind="field" size="small" />
173+
<FieldOffset onClick={() => navigate(`${root}?search=${encodeURIComponent(`offset:${offsetHex}`)}`)}>{offsetHex}</FieldOffset>
142174
{field.name}: <SchemaTypeView type={field.type} />
143175
</FieldSignature>
144176
<MetadataTags metadata={field.metadata} />

web/src/components/Docs/utils/filtering.tsx

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,41 @@ export function useFilteredData(declarations: api.Declaration[]) {
2020
}, [declarations, search, module, scope]);
2121
}
2222

23+
function isFilterPrefix(word: string): boolean {
24+
return word.startsWith("module:") || word.startsWith("offset:");
25+
}
26+
2327
export function useSearchWords(): string[] {
2428
const { search } = useContext(SearchContext);
2529
return useMemo(
26-
() => search ? search.toLowerCase().split(" ").filter((x) => !x.startsWith("module:")) : [],
30+
() => search ? search.toLowerCase().split(" ").filter((x) => !isFilterPrefix(x)) : [],
2731
[search],
2832
);
2933
}
3034

35+
export function useSearchOffsets(): number[] {
36+
const { search } = useContext(SearchContext);
37+
return useMemo(() => {
38+
if (!search) return [];
39+
return search.toLowerCase().split(" ")
40+
.filter((x) => x.startsWith("offset:"))
41+
.map((x) => parseOffset(x.replace(/^offset:/, "")))
42+
.filter((x): x is number => x !== null);
43+
}, [search]);
44+
}
45+
46+
function parseOffset(value: string): number | null {
47+
const trimmed = value.trim();
48+
if (trimmed === "") return null;
49+
const n = trimmed.startsWith("0x") ? parseInt(trimmed, 16) : parseInt(trimmed, 10);
50+
return Number.isNaN(n) ? null : n;
51+
}
52+
3153
export function doSearch(declarations: api.Declaration[], words: string[]): api.Declaration[] {
3254
const moduleWords = words.filter((x) => x.startsWith("module:")).map((x) => x.replace(/^module:/, ""));
33-
const nameWords = words.filter((x) => !x.startsWith("module:"));
55+
const offsetValues = words.filter((x) => x.startsWith("offset:")).map((x) => parseOffset(x.replace(/^offset:/, "")));
56+
const offsets = offsetValues.filter((x): x is number => x !== null);
57+
const nameWords = words.filter((x) => !isFilterPrefix(x));
3458

3559
function filterModule(declaration: api.Declaration): boolean {
3660
if (moduleWords.length === 0) return true;
@@ -44,9 +68,17 @@ export function doSearch(declarations: api.Declaration[], words: string[]): api.
4468
return nameWords.every((word) => lower.includes(word));
4569
}
4670

71+
function matchesOffset(declaration: api.Declaration): boolean {
72+
if (offsets.length === 0) return false;
73+
if (declaration.kind !== "class") return false;
74+
return declaration.fields.some((f) => offsets.includes(f.offset));
75+
}
76+
4777
return declarations.filter((declaration) => {
4878
if (!filterModule(declaration)) return false;
4979

80+
if (offsets.length > 0) return matchesOffset(declaration);
81+
5082
if (matchesName(declaration.name)) return true;
5183

5284
if (declaration.kind === "class") {

web/src/components/layout/NavBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function NavBarWithSearch({ baseUrl }: { baseUrl: string }) {
3131
/>
3232
</NavBarSidebarCell>
3333
<NavBarContentCell>
34-
<NavBarSearchBox baseUrl={baseUrl} placeholder="Search... (module:name to filter)" />
34+
<NavBarSearchBox baseUrl={baseUrl} placeholder="Search... (module: or offset: to filter)" />
3535
<HomeBrandLink to="/schemas">Source 2 Schemas</HomeBrandLink>
3636
<NavBarThemeSwitcher />
3737
</NavBarContentCell>

0 commit comments

Comments
 (0)