Skip to content

Commit c7b2f44

Browse files
authored
Highlight Relevant Context (#91)
1 parent 1bc0200 commit c7b2f44

6 files changed

Lines changed: 69 additions & 99 deletions

File tree

client/src/webview/script.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export function getScript(vscode: VSCodeApi, document: Document, window: Window)
3030
let diagnostics: LJDiagnostic[] | undefined;
3131
let showAllDiagnostics = false;
3232
let currentFile: string;
33-
const expandedErrors = new Set<number>();
3433
let stateMachine: LJStateMachine;
3534
let context: LJContext;
3635
let errorAtCursor: RefinementMismatchError;
@@ -121,6 +120,17 @@ export function getScript(vscode: VSCodeApi, document: Document, window: Window)
121120
return;
122121
}
123122

123+
const diagnosticContextButton = target.closest?.('.diagnostic-context-btn');
124+
if (diagnosticContextButton) {
125+
e.preventDefault();
126+
e.stopPropagation();
127+
128+
const revealTarget = getDiagnosticRevealTargetFromKey(diagnosticContextButton.getAttribute('data-diagnostic-target'));
129+
if (!revealTarget) return;
130+
revealContextForDiagnostic(revealTarget);
131+
return;
132+
}
133+
124134
// derivation expansion click
125135
const derivableNode = target.closest?.('.derivable-node');
126136
if (derivableNode) {
@@ -198,21 +208,6 @@ export function getScript(vscode: VSCodeApi, document: Document, window: Window)
198208
return;
199209
}
200210

201-
// toggle show more/less for errors
202-
if (target.classList.contains('show-more-button')) {
203-
e.stopPropagation();
204-
const errorIndex = parseInt(target.getAttribute('data-error-index') || '-1', 10);
205-
if (errorIndex >= 0) {
206-
if (expandedErrors.has(errorIndex)) {
207-
expandedErrors.delete(errorIndex);
208-
} else {
209-
expandedErrors.add(errorIndex);
210-
}
211-
updateView();
212-
}
213-
return;
214-
}
215-
216211
// highlight variable
217212
const highlightButton = target.closest?.('.highlight-var-btn');
218213
if (highlightButton) {
@@ -318,7 +313,7 @@ export function getScript(vscode: VSCodeApi, document: Document, window: Window)
318313
switch (selectedTab) {
319314
case 'diagnostics':
320315
root.innerHTML = diagnostics
321-
? renderDiagnosticsView(diagnostics, showAllDiagnostics, currentFile, expandedErrors)
316+
? renderDiagnosticsView(diagnostics, showAllDiagnostics, currentFile)
322317
: renderLoading();
323318
break;
324319
case 'fsm': {
@@ -360,4 +355,15 @@ export function getScript(vscode: VSCodeApi, document: Document, window: Window)
360355
}, 1800);
361356
}
362357

358+
function revealContextForDiagnostic(target: DiagnosticRevealTarget) {
359+
selectedTab = 'context';
360+
vscode.postMessage({
361+
type: 'openFile',
362+
filePath: target.file,
363+
line: target.position.lineEnd,
364+
character: target.position.colEnd,
365+
});
366+
updateView();
367+
}
368+
363369
}

client/src/webview/styles.ts

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export function getStyles(): string {
161161
}
162162
.diagnostic-item {
163163
background-color: var(--vscode-textCodeBlock-background);
164-
padding: 0.5rem 3rem 0.5rem 1rem;
164+
padding: 0.5rem 5rem 0.5rem 1rem;
165165
margin-bottom: 1rem;
166166
border-radius: 4px;
167167
position: relative;
@@ -176,10 +176,10 @@ export function getStyles(): string {
176176
line-height: 1;
177177
pointer-events: none;
178178
}
179-
.copy-diagnostic-btn {
179+
.copy-diagnostic-btn,
180+
.diagnostic-context-btn {
180181
position: absolute;
181182
top: 0.5rem;
182-
right: 0.5rem;
183183
display: inline-flex;
184184
align-items: center;
185185
justify-content: center;
@@ -193,12 +193,20 @@ export function getStyles(): string {
193193
opacity: 0.65;
194194
transition: background-color 0.16s ease, border-color 0.16s ease, opacity 0.16s ease, transform 0.16s ease;
195195
}
196-
.copy-diagnostic-btn:hover {
196+
.copy-diagnostic-btn {
197+
right: 0.5rem;
198+
}
199+
.diagnostic-context-btn {
200+
right: 2.5rem;
201+
}
202+
.copy-diagnostic-btn:hover,
203+
.diagnostic-context-btn:hover {
197204
background: var(--vscode-editor-background);
198205
border-color: var(--vscode-widget-border);
199206
opacity: 1;
200207
}
201-
.copy-diagnostic-btn:disabled {
208+
.copy-diagnostic-btn:disabled,
209+
.diagnostic-context-btn:disabled {
202210
opacity: 0.8;
203211
cursor: default;
204212
}
@@ -370,20 +378,6 @@ export function getStyles(): string {
370378
button:hover {
371379
background-color: var(--vscode-button-hoverBackground);
372380
}
373-
.show-more-button {
374-
display: block;
375-
width: 100%;
376-
margin: 0.5rem auto;
377-
padding: 0.5rem;
378-
background-color: transparent;
379-
border: none;
380-
color: var(--vscode-foreground);
381-
opacity: 0.7;
382-
font-size: 1rem;
383-
}
384-
.show-more-button:hover {
385-
background-color: var(--vscode-editor-background);
386-
}
387381
.underline-button {
388382
color: var(--vscode-foreground);
389383
text-align: center;
@@ -400,9 +394,6 @@ export function getStyles(): string {
400394
.underline-button:hover {
401395
background: none;
402396
}
403-
.extra-content {
404-
margin-top: 1rem;
405-
}
406397
.tooltip:hover::after {
407398
content: attr(data-tooltip);
408399
position: absolute;
@@ -548,8 +539,10 @@ export function getStyles(): string {
548539
word-break: normal;
549540
}
550541
551-
.context-variables-table th:first-child,
552-
.translation-table th:first-child {
542+
.context-variable-relevant td:first-child {
543+
border-left: 2px solid var(--vscode-errorForeground);
544+
}
545+
.context-variables-table th:first-child {
553546
padding-left: calc(0.75rem + 0.8rem);
554547
}
555548
.context-aliases-table td:last-child,

client/src/webview/views/context/variables.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { renderToggleSection, renderHighlightButton, renderDiagnosticRevealButto
66

77
export function renderContextVariables(variables: LJVariable[], isExpanded: boolean, errorAtCursor?: RefinementMismatchError): string {
88
const expected = errorAtCursor ? errorAtCursor.expected.value : undefined;
9+
const relevantNames = new Set(Object.keys(errorAtCursor?.translationTable || {}));
910
return /*html*/`
1011
<div class="context-section">
1112
${renderToggleSection('Variables', 'context-vars', isExpanded)}
@@ -23,12 +24,15 @@ export function renderContextVariables(variables: LJVariable[], isExpanded: bool
2324
</tr>
2425
</thead>
2526
<tbody>
26-
${variables.map(variable => /*html*/`
27-
<tr>
28-
<td>${renderHighlightButton(variable.position!, getSimpleName(variable.name))}</td>
27+
${variables.map(variable => {
28+
const displayName = getSimpleName(variable.name);
29+
const isRelevant = relevantNames.has(variable.name);
30+
return /*html*/`
31+
<tr class="${isRelevant ? 'context-variable-relevant' : ''}">
32+
<td>${renderHighlightButton(variable.position!, displayName)}</td>
2933
<td><code>${renderHighlightedInlineExpression(variable.refinement)}</code></td>
3034
</tr>
31-
`).join('')}
35+
`}).join('')}
3236
${errorAtCursor ? renderFailingRefinement(errorAtCursor, expected!) : ''}
3337
</tbody>
3438
</table>

client/src/webview/views/diagnostics/diagnostics.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ export function renderDiagnosticsView(
99
diagnostics: LJDiagnostic[],
1010
showAll: boolean,
1111
currentFile: string | undefined,
12-
expandedErrors: Set<number>,
1312
): string {
1413
const fileDiagnostics = diagnostics.filter(diagnostic => diagnostic.file?.toLowerCase() === currentFile?.toLowerCase() || !diagnostic.file);
1514
const displayDiagnostics = showAll ? diagnostics : fileDiagnostics;
@@ -35,7 +34,7 @@ export function renderDiagnosticsView(
3534
`
3635
}
3736
<div class="content">
38-
${renderErrors(errors, expandedErrors)}
37+
${renderErrors(errors)}
3938
${renderWarnings(warnings)}
4039
${displayDiagnostics.length > 0 && hiddenErrors > 0 ? /*html*/`
4140
<p class="more-indicator">(+${hiddenErrors} error${hiddenErrors !== 1 ? 's' : ''})</p>

client/src/webview/views/diagnostics/errors.ts

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { renderDiagnosticDataAttributes, renderExpressionSection, renderDiagnosticHeader, renderSection, renderCustomSection, renderTranslationTable, renderLocation, } from "../sections";
1+
import { renderDiagnosticDataAttributes, renderExpressionSection, renderDiagnosticHeader, renderCustomSection, renderLocation, renderDiagnosticContextButton } from "../sections";
22
import { renderDerivationNode } from "./derivation-nodes";
33
import type {
44
ArgumentMismatchError,
@@ -11,19 +11,18 @@ import type {
1111
StateConflictError,
1212
StateRefinementError,
1313
SyntaxError,
14-
TranslationTable,
1514
} from "../../../types/diagnostics";
1615
import { renderCopyDiagnosticButton } from "./diagnostics";
1716

18-
export function renderErrors(errors: LJError[], expandedErrors: Set<number>): string {
17+
export function renderErrors(errors: LJError[]): string {
1918
return /*html*/`
2019
<ul>
2120
${errors.map((error, index) => {
22-
const isExpanded = expandedErrors.has(index);
2321
return /*html*/`
2422
<li class="diagnostic-item error-item" ${renderDiagnosticDataAttributes(error)}>
23+
${renderDiagnosticContextAction(error)}
2524
${renderCopyDiagnosticButton('error', index)}
26-
${renderError(error, index, isExpanded)}
25+
${renderError(error)}
2726
</li>
2827
`;
2928
}).join("")}
@@ -62,25 +61,15 @@ const errorContentRenderers: ErrorRendererMap = {
6261
'custom-error': (_: CustomError) => "",
6362
};
6463

65-
export function renderError(error: LJError, errorIndex: number, isExpanded: boolean): string {
64+
export function renderError(error: LJError): string {
6665
const message = error.type === 'refinement-error' || error.type === 'state-refinement-error' ? error.customMessage : error.message;
6766
const header = renderDiagnosticHeader(error.title, message || '');
6867
const content = (errorContentRenderers[error.type] as (error: LJError) => string)?.(error) || '';
6968
const location = renderLocation(error);
70-
const extra = renderExtra(error, errorIndex, isExpanded);
71-
return /*html*/`${header}${content}${location}${extra}`;
69+
return /*html*/`${header}${content}${location}`;
7270
}
7371

74-
function renderExtra(error: LJError, errorIndex: number, isExpanded: boolean): string {
75-
const button = /*html*/`
76-
<button class="show-more-button" data-error-index="${errorIndex}" title="Toggle show extra information about the diagnostic">
77-
${isExpanded ? '↑' : '↓'}
78-
</button>
79-
`;
80-
81-
let extra = "";
82-
if (Object.prototype.hasOwnProperty.call(error, 'translationTable')) {
83-
extra += renderTranslationTable((error as any).translationTable as TranslationTable);
84-
}
85-
return extra ? isExpanded ? /*html*/`${button}<div class="extra-content">${extra}</div>` : button : "";
72+
function renderDiagnosticContextAction(error: LJError): string {
73+
if (error.type !== 'refinement-error' && error.type !== 'state-refinement-error') return "";
74+
return renderDiagnosticContextButton(error.position);
8675
}

client/src/webview/views/sections.ts

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import type { LJDiagnostic, PlacementInCode, SourcePosition, TranslationTable } from "../../types/diagnostics";
1+
import type { LJDiagnostic, SourcePosition } from "../../types/diagnostics";
22
import { escapeHtml } from "../utils";
33
import { renderHighlightedExpression, renderHighlightedInlineExpression } from "../highlighting";
44
import { getDiagnosticRevealTarget, getDiagnosticRevealTargetKey } from "../diagnostic-reveal";
5-
import { renderCodicon } from "../icons";
5+
import { renderCodicon, renderCodiconButton } from "../icons";
66

77
export const renderMainHeader = (title: string, selectedTab: NavTab): string => /*html*/`
88
<div class="header">
@@ -40,36 +40,6 @@ export const renderLocation = (diagnostic: LJDiagnostic): string => {
4040
return renderCustomSection("Location", /*html*/`<pre>${renderLocationLink(diagnostic.position)}</pre>`);
4141
};
4242

43-
export function renderTranslationTable(translationTable: TranslationTable): string {
44-
const entries = Object.entries(translationTable).sort((a, b) => a[0].localeCompare(b[0])); // sort by variable name
45-
if (entries.length === 0) return '';
46-
47-
return /*html*/`
48-
<div class="translation-table">
49-
<h3>Context Variables</h3>
50-
<table>
51-
<thead>
52-
<tr>
53-
<th>Name</th>
54-
<th>Source</th>
55-
</tr>
56-
</thead>
57-
<tbody>
58-
${entries.map(([variable, placement]: [string, PlacementInCode]) => {
59-
if (!placement.position) return ''
60-
return /*html*/`
61-
<tr>
62-
<td>${renderHighlightButton(placement.position, variable)}</td>
63-
<td><code>${renderHighlightedInlineExpression(placement.text)}</code></td>
64-
</tr>
65-
`;
66-
}).join('')}
67-
</tbody>
68-
</table>
69-
</div>
70-
`;
71-
}
72-
7343
export function renderHighlightButton(position: SourcePosition, content: string, error: boolean = false): string {
7444
return /*html*/`
7545
<button
@@ -85,6 +55,15 @@ export function renderHighlightButton(position: SourcePosition, content: string,
8555
`;
8656
}
8757

58+
export function renderDiagnosticContextButton(position?: SourcePosition | null): string {
59+
if (!position?.file) return "";
60+
return renderCodiconButton("symbol-variable", {
61+
className: "diagnostic-context-btn",
62+
title: "View related context",
63+
attributes: `data-diagnostic-target="${getDiagnosticRevealTargetKey({ file: position.file, position })}"`,
64+
});
65+
}
66+
8867
export function renderDiagnosticRevealButton(position: SourcePosition, content: string): string {
8968
if (!position.file) return `<code>${content}</code>`
9069
return /*html*/`

0 commit comments

Comments
 (0)