Skip to content

Commit b0b5ae6

Browse files
authored
Group errors by line number and show count (microsoft#7299)
* Group errors by line number and show count * address pr feedback * revert changes in render method * 2nd try * whitespace * i am determined to revert these changes. * simplify error sort * revert tsconfig change * format document and remove trailing whitespace
1 parent 1327954 commit b0b5ae6

File tree

1 file changed

+54
-16
lines changed

1 file changed

+54
-16
lines changed

webapp/src/errorList.tsx

+54-16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
import * as React from "react";
44
import * as sui from "./sui";
55

6+
type GroupedError = {
7+
error: pxtc.KsDiagnostic,
8+
count: number,
9+
index: number
10+
};
11+
612
export interface ErrorListProps {
713
onSizeChange: (state: pxt.editor.ErrorListState) => void;
814
listenToErrorChanges: (key: string, onErrorChanges: (errors: pxtc.KsDiagnostic[]) => void) => void;
@@ -41,7 +47,7 @@ export class ErrorList extends React.Component<ErrorListProps, ErrorListState> {
4147
}
4248

4349
render() {
44-
const {isCollapsed, errors, exception} = this.state;
50+
const { isCollapsed, errors, exception } = this.state;
4551
const errorsAvailable = !!errors?.length || !!exception;
4652
const collapseTooltip = lf("Collapse Error List");
4753

@@ -52,12 +58,12 @@ export class ErrorList extends React.Component<ErrorListProps, ErrorListState> {
5258
<div className="errorListHeader" role="button" aria-label={lf("{0} error list", isCollapsed ? lf("Expand") : lf("Collapse"))} onClick={this.onCollapseClick} onKeyDown={sui.fireClickOnEnter} tabIndex={0}>
5359
<h4>{lf("Problems")}</h4>
5460
<div className="ui red circular label countBubble">{exception ? 1 : errors.length}</div>
55-
<div className="toggleButton"><sui.Icon icon={`chevron ${isCollapsed ? 'up' : 'down'}`}/></div>
61+
<div className="toggleButton"><sui.Icon icon={`chevron ${isCollapsed ? 'up' : 'down'}`} /></div>
5662
</div>
5763
{!isCollapsed && <div className="errorListInner">
5864
{exception && <div className="debuggerSuggestion" role="button" onClick={this.props.startDebugger} onKeyDown={sui.fireClickOnEnter} tabIndex={0}>
5965
{lf("Debug this project")}
60-
<sui.Icon className="debug-icon" icon="icon bug"/>
66+
<sui.Icon className="debug-icon" icon="icon bug" />
6167
</div>}
6268
{errorListContent}
6369
</div>}
@@ -88,7 +94,7 @@ export class ErrorList extends React.Component<ErrorListProps, ErrorListState> {
8894
}
8995

9096
onErrorMessageClick(e: pxtc.LocationInfo, index: number) {
91-
pxt.tickEvent('errorlist.goto', {errorIndex: index}, { interactiveConsent: true });
97+
pxt.tickEvent('errorlist.goto', { errorIndex: index }, { interactiveConsent: true });
9298
this.props.goToError(e)
9399
}
94100

@@ -115,8 +121,9 @@ export class ErrorList extends React.Component<ErrorListProps, ErrorListState> {
115121
return `${error.messageText}-${error.fileName}-${error.line}-${error.column}`
116122
}
117123

124+
const grouped = groupErrors(errors);
118125
return <div className="ui selection list">
119-
{(errors).map((e, index) => <ErrorListItem key={errorKey(e)} index={index} error={e} revealError={this.onErrorMessageClick} />)}
126+
{grouped.map((e, index) => <ErrorListItem key={errorKey(e.error)} index={index} error={e} revealError={this.onErrorMessageClick} />)}
120127
</div>
121128
}
122129

@@ -129,7 +136,7 @@ export class ErrorList extends React.Component<ErrorListProps, ErrorListState> {
129136

130137
if (!location) return null;
131138

132-
return <ErrorListItem key={index} index={index} stackframe={sf} location={location} revealError={this.onErrorMessageClick}/>
139+
return <ErrorListItem key={index} index={index} stackframe={sf} location={location} revealError={this.onErrorMessageClick} />
133140
})}
134141
</div>
135142
</div>;
@@ -139,7 +146,7 @@ export class ErrorList extends React.Component<ErrorListProps, ErrorListState> {
139146
interface ErrorListItemProps {
140147
index: number;
141148
revealError: (location: pxtc.LocationInfo, index: number) => void;
142-
error?: pxtc.KsDiagnostic;
149+
error?: GroupedError;
143150
stackframe?: pxsim.StackFrameInfo;
144151
location?: pxtc.LocationInfo;
145152
}
@@ -156,22 +163,53 @@ class ErrorListItem extends React.Component<ErrorListItemProps, ErrorListItemSta
156163
}
157164

158165
render() {
159-
const {error, stackframe, location} = this.props
166+
const { error, stackframe, location } = this.props
160167

161-
const message = stackframe ? lf("at {0} (line {1})", stackframe.funcInfo.functionName, location.line + 1)
162-
: lf("Line {0}: {1}", error.endLine ? error.endLine + 1 : error.line + 1, error.messageText)
168+
const message = stackframe ?
169+
stackFrameMessageStringWithLineNumber(stackframe, location) :
170+
errorMessageStringWithLineNumber(error.error);
171+
const errorCount = stackframe ? 1 : error.count;
163172

164173
return <div className={`item ${stackframe ? 'stackframe' : ''}`} role="button"
165-
onClick={this.onErrorListItemClick}
166-
onKeyDown={sui.fireClickOnEnter}
167-
aria-label={lf("Go to {0}: {1}", stackframe ? '' : 'error', message)}
168-
tabIndex={0}>
169-
{message}
174+
onClick={this.onErrorListItemClick}
175+
onKeyDown={sui.fireClickOnEnter}
176+
aria-label={lf("Go to {0}: {1}", stackframe ? '' : 'error', message)}
177+
tabIndex={0}>
178+
{message} {(errorCount <= 1) ? null : <div className="ui gray circular label countBubble">{errorCount}</div>}
170179
</div>
171180
}
172181

173182
onErrorListItemClick() {
174-
const location = this.props.stackframe ? this.props.location : this.props.error
183+
const location = this.props.stackframe ? this.props.location : this.props.error.error
175184
this.props.revealError(location, this.props.index)
176185
}
177186
}
187+
188+
function errorMessageStringWithLineNumber(error: pxtc.KsDiagnostic) {
189+
return lf("Line {0}: {1}", error.endLine ? error.endLine + 1 : error.line + 1, error.messageText);
190+
}
191+
192+
function stackFrameMessageStringWithLineNumber(stackframe: pxsim.StackFrameInfo, location: pxtc.LocationInfo) {
193+
return lf("at {0} (line {1})", stackframe.funcInfo.functionName, location.line + 1);
194+
}
195+
196+
function groupErrors(errors: pxtc.KsDiagnostic[]) {
197+
const grouped = new Map<string, GroupedError>();
198+
let index = 0;
199+
for (const error of errors) {
200+
const key = errorMessageStringWithLineNumber(error);
201+
if (!grouped.has(key)) {
202+
grouped.set(key, {
203+
index: index++,
204+
count: 1,
205+
error
206+
});
207+
}
208+
else {
209+
grouped.get(key).count++;
210+
}
211+
}
212+
const sorted: GroupedError[] = [];
213+
grouped.forEach(value => sorted.push(value));
214+
return sorted.sort((a, b) => a.index - b.index);
215+
}

0 commit comments

Comments
 (0)