Wire IDE symbol navigation panel
This commit is contained in:
@@ -106,7 +106,7 @@ const checks = [
|
|||||||
{
|
{
|
||||||
name: "module mode",
|
name: "module mode",
|
||||||
url: `${baseUrl}/editor?lang=ru&project=${projectId}&mode=module&routine=${routine}`,
|
url: `${baseUrl}/editor?lang=ru&project=${projectId}&mode=module&routine=${routine}`,
|
||||||
mustInclude: ["data-ide-workspace", "data-left-navigation-panel", "data-right-context-inspector", "data-open-objects-bar", "data-open-document-pin", "data-open-document-close", "data-fallback-tree-search", "data-fallback-tree-filters", "Alt+1 Alt+2 Alt+3", "Редактор BSL", "Код модуля не загружен", "Выберите реальный модуль", "Основная конфигурация", "Расширение: <Имя>", "SFERA", "Среды"]
|
mustInclude: ["data-ide-workspace", "data-active-mode=\"module\"", "data-left-navigation-panel", "data-right-context-inspector", "data-open-objects-bar", "data-open-document-pin", "data-open-document-close", "data-fallback-tree-search", "data-fallback-tree-filters", "data-fast-bsl-editor", "data-symbol-navigation-panel", "symbol-search-input", "Alt+1 Alt+2 Alt+3", "Основная конфигурация", "SFERA", "Среды"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "form mode",
|
name: "form mode",
|
||||||
|
|||||||
@@ -186,16 +186,13 @@ try {
|
|||||||
await restoredModuleTabs.first().click();
|
await restoredModuleTabs.first().click();
|
||||||
}
|
}
|
||||||
await page.locator("[data-fast-bsl-editor]").waitFor({ state: "visible", timeout: 15000 });
|
await page.locator("[data-fast-bsl-editor]").waitFor({ state: "visible", timeout: 15000 });
|
||||||
const symbolNavigationPanel = page.locator("[data-symbol-navigation-panel]");
|
await page.locator("[data-symbol-navigation-panel]").waitFor({ state: "visible", timeout: 15000 });
|
||||||
if ((await symbolNavigationPanel.count()) > 0) {
|
|
||||||
await symbolNavigationPanel.waitFor({ state: "visible", timeout: 15000 });
|
|
||||||
}
|
|
||||||
const symbolSearchInput = page.locator("#symbol-search-input");
|
const symbolSearchInput = page.locator("#symbol-search-input");
|
||||||
if ((await symbolSearchInput.count()) > 0) {
|
await symbolSearchInput.fill("demo");
|
||||||
await symbolSearchInput.fill("Проверить");
|
|
||||||
await symbolSearchInput.press("Enter");
|
await symbolSearchInput.press("Enter");
|
||||||
await page.locator("[data-symbol-result]").first().waitFor({ state: "visible", timeout: 15000 });
|
await page.locator("[data-symbol-result]").first().waitFor({ state: "visible", timeout: 15000 });
|
||||||
}
|
await page.locator('button[data-editor-action="symbol-definition-row"]').first().click();
|
||||||
|
await page.locator("[data-symbol-definition]").waitFor({ state: "visible", timeout: 15000 });
|
||||||
const findUsagesButton = page.locator('button[data-editor-action="find-usages"]');
|
const findUsagesButton = page.locator('button[data-editor-action="find-usages"]');
|
||||||
if ((await findUsagesButton.count()) > 0 && await findUsagesButton.isEnabled()) {
|
if ((await findUsagesButton.count()) > 0 && await findUsagesButton.isEnabled()) {
|
||||||
await findUsagesButton.click();
|
await findUsagesButton.click();
|
||||||
|
|||||||
@@ -2255,6 +2255,19 @@ function EditorPanel({
|
|||||||
value={monacoValue}
|
value={monacoValue}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<SymbolNavigationPanel
|
||||||
|
definition={symbolDefinition}
|
||||||
|
language={language}
|
||||||
|
message={symbolMessage}
|
||||||
|
onDefinition={handleDefinition}
|
||||||
|
onQueryChange={setSymbolQuery}
|
||||||
|
onReferences={handleReferences}
|
||||||
|
onSearch={handleSymbolSearch}
|
||||||
|
query={symbolQuery}
|
||||||
|
references={symbolReferences}
|
||||||
|
results={symbolResults}
|
||||||
|
state={symbolState}
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2262,28 +2275,56 @@ function EditorPanel({
|
|||||||
function SymbolNavigationPanel({
|
function SymbolNavigationPanel({
|
||||||
definition,
|
definition,
|
||||||
language,
|
language,
|
||||||
|
message,
|
||||||
onDefinition,
|
onDefinition,
|
||||||
|
onQueryChange,
|
||||||
onReferences,
|
onReferences,
|
||||||
|
onSearch,
|
||||||
|
query,
|
||||||
references,
|
references,
|
||||||
results
|
results,
|
||||||
|
state
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
definition: SymbolResult | null;
|
definition: SymbolResult | null;
|
||||||
language: UiLanguage;
|
language: UiLanguage;
|
||||||
|
message: string;
|
||||||
onDefinition: (lineageId?: string) => void;
|
onDefinition: (lineageId?: string) => void;
|
||||||
|
onQueryChange: (value: string) => void;
|
||||||
onReferences: (lineageId?: string) => void;
|
onReferences: (lineageId?: string) => void;
|
||||||
|
onSearch: () => void;
|
||||||
|
query: string;
|
||||||
references: SymbolReferences | null;
|
references: SymbolReferences | null;
|
||||||
results: SymbolResult[];
|
results: SymbolResult[];
|
||||||
|
state: "idle" | "loading" | "error";
|
||||||
}>) {
|
}>) {
|
||||||
const t = messages[language];
|
const t = messages[language];
|
||||||
const referenceRows = references?.references.slice(0, 8) ?? [];
|
const referenceRows = references?.references.slice(0, 8) ?? [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-t border-border p-3" data-symbol-navigation-panel>
|
<div className="max-h-56 shrink-0 overflow-auto border-t border-border bg-card p-3" data-symbol-navigation-panel>
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<div className="text-xs font-semibold uppercase text-muted-foreground">{t.referencesPanel}</div>
|
<label className="text-xs font-semibold uppercase text-muted-foreground" htmlFor="symbol-search-input">{t.referencesPanel}</label>
|
||||||
<Badge tone="neutral">{referenceRows.length}</Badge>
|
<input
|
||||||
|
className="h-8 min-w-48 flex-1 border border-border bg-background px-2 text-xs outline-none focus:border-primary"
|
||||||
|
id="symbol-search-input"
|
||||||
|
onChange={(event) => onQueryChange(event.target.value)}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
onSearch();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placeholder={language === "ru" ? "Найти символ" : "Find symbol"}
|
||||||
|
value={query}
|
||||||
|
/>
|
||||||
|
<ActionButton actionId="symbol-search-panel" disabled={state === "loading"} label={state === "loading" ? t.loading : t.search} onClick={onSearch} />
|
||||||
|
<Badge tone={state === "error" ? "danger" : referenceRows.length > 0 ? "success" : "neutral"}>{referenceRows.length}</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 space-y-2">
|
{message ? (
|
||||||
|
<div className={["mt-2 truncate text-xs", state === "error" ? "text-destructive" : "text-muted-foreground"].join(" ")} data-symbol-message>{message}</div>
|
||||||
|
) : null}
|
||||||
|
<div className="mt-3 grid gap-3 lg:grid-cols-[minmax(0,1fr)_minmax(280px,0.8fr)]">
|
||||||
|
<div className="min-w-0 space-y-2">
|
||||||
{results.length === 0 ? (
|
{results.length === 0 ? (
|
||||||
<div className="text-xs text-muted-foreground">{language === "ru" ? "Выполните поиск символа или откройте использования выбранного объекта." : "Search a symbol or open references for the selected object."}</div>
|
<div className="text-xs text-muted-foreground">{language === "ru" ? "Выполните поиск символа или откройте использования выбранного объекта." : "Search a symbol or open references for the selected object."}</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -2303,14 +2344,15 @@ function SymbolNavigationPanel({
|
|||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="min-w-0 space-y-2">
|
||||||
{definition ? (
|
{definition ? (
|
||||||
<div className="mt-3 border border-primary/30 bg-primary/5 p-2 text-xs" data-symbol-definition>
|
<div className="border border-primary/30 bg-primary/5 p-2 text-xs" data-symbol-definition>
|
||||||
<div className="font-medium">{definition.node.qualified_name}</div>
|
<div className="font-medium">{definition.node.qualified_name}</div>
|
||||||
<div className="mt-1 text-muted-foreground">{formatSourceLocation(definition.source, language)}</div>
|
<div className="mt-1 text-muted-foreground">{formatSourceLocation(definition.source, language)}</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{referenceRows.length > 0 ? (
|
{referenceRows.length > 0 ? (
|
||||||
<div className="mt-3 space-y-1" data-symbol-references>
|
<div className="space-y-1" data-symbol-references>
|
||||||
{referenceRows.map((reference) => (
|
{referenceRows.map((reference) => (
|
||||||
<div className="border border-border bg-muted/30 p-2 text-xs" key={reference.edge_id}>
|
<div className="border border-border bg-muted/30 p-2 text-xs" key={reference.edge_id}>
|
||||||
<div className="truncate font-medium">{reference.source?.qualified_name ?? reference.target?.qualified_name ?? reference.kind}</div>
|
<div className="truncate font-medium">{reference.source?.qualified_name ?? reference.target?.qualified_name ?? reference.kind}</div>
|
||||||
@@ -2320,6 +2362,8 @@ function SymbolNavigationPanel({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user