Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 81 additions & 9 deletions lib/elements/analysis_results_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ class AnalysisResultsController {
final DElement toggle;
bool _flashHidden;

final StreamController<AnalysisIssue> _onClickController =
final StreamController<LineInfo> _onClickController =
StreamController.broadcast();

Stream<AnalysisIssue> get onIssueClick => _onClickController.stream;
Stream<LineInfo> get onItemClicked => _onClickController.stream;

AnalysisResultsController(this.flash, this.message, this.toggle) {
// Show issues by default, but hide the flash element (otherwise an empty
Expand Down Expand Up @@ -70,29 +70,89 @@ class AnalysisResultsController {
message.text = '$amount ${amount == 1 ? 'issue' : 'issues'}';

flash.clearChildren();
for (var elem in issues.map(_issueElement)) {
for (var issue in issues) {
var elem = _issueElement(issue);
flash.add(elem);

for (var diagnostic in issue.diagnosticMessages) {
var diagnosticElement = _diagnosticElement(diagnostic);
flash.add(diagnosticElement);
}
}
}

Element _issueElement(AnalysisIssue issue) {
var message = issue.message;
if (issue.message.endsWith('.')) {
message = message.substring(0, message.length - 1);
}
message = _stripPeriod(message);

var elem = DivElement()..classes.add('issue');
var elem = DivElement()..classes.addAll(['issue', 'clickable']);

elem.children.add(SpanElement()
..text = issue.kind
..classes.addAll(_classesForType[issue.kind]));

elem.children.add(SpanElement()
var columnElem = DivElement()..classes.add('issue-column');

var messageSpan = DivElement()
..text = '$message - line ${issue.line}'
..classes.add('message');
columnElem.children.add(messageSpan);

// Add the correction, if any.
if (issue.correction != null && issue.correction.isNotEmpty) {
var correctionMessage = _stripPeriod(issue.correction);
columnElem.children.add(DivElement()
..text = correctionMessage
..classes.add('message'));
}

// Add a link to the documentation
if (issue.url != null && issue.url.isNotEmpty) {
columnElem.children.add(AnchorElement()
..href = issue.url
..text = ' Open Documentation'
..target = '_blank'
..classes.add('issue-anchor'));
}

elem.children.add(columnElem);

elem.onClick.listen((_) {
_onClickController.add(LineInfo(
line: issue.line,
charStart: issue.charStart,
charLength: issue.charLength));
});

return elem;
}

String _stripPeriod(String s) {
if (s.endsWith('.')) {
s = s.substring(0, s.length - 1);
}
return s;
}

Element _diagnosticElement(DiagnosticMessage diagnosticMessage) {
var message = diagnosticMessage.message;
if (message.endsWith('.')) {
message = message.substring(0, message.length - 1);
}

var elem = DivElement()..classes.addAll(['issue', 'clickable']);

elem.children.add(SpanElement()..classes.add('issue-indent'));

elem.children.add(SpanElement()
..text = message
..classes.add('message'));

elem.onClick.listen((_) {
_onClickController.add(issue);
_onClickController.add(LineInfo(
line: diagnosticMessage.line,
charStart: diagnosticMessage.charStart,
charLength: diagnosticMessage.charLength));
});

return elem;
Expand All @@ -118,3 +178,15 @@ class AnalysisResultsController {
toggle.text = _hideMsg;
}
}

class LineInfo {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm kind of surprised there isn't already a class with these properties in the codebase, but I can't find one. There's Position in /lib/editing/editor.dart, but it doesn't include length.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, given that Position is identical in structure to the interface Position from LSP, consider using the LSP interface Range instead. Although it might not be as convenient for your purposes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Location type is used in AnalysisError and DiagnosticMessage classes, so dart-services adds the line, charStart, and charLength to an AnalysisIssue protocol buffer message that the client gets. The client doesn't actually use the analysis_server_lib package at the moment.

We could add two new Postition and Range messages to our protocol buffer, but that might be out of scope here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to consider a slightly more specific name, like TextRange or SelectionRange or something. Just a thought.

final int line;
final int charStart;
final int charLength;

LineInfo({
this.line,
this.charStart,
this.charLength,
});
}
4 changes: 2 additions & 2 deletions lib/embed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,8 @@ class Embed {
DElement(querySelector('#issues')),
DElement(querySelector('#issues-message')),
DElement(querySelector('#issues-toggle')))
..onIssueClick.listen((issue) {
_jumpTo(issue.line, issue.charStart, issue.charLength, focus: true);
..onItemClicked.listen((item) {
_jumpTo(item.line, item.charStart, item.charLength, focus: true);
});

if (options.mode == EmbedMode.flutter || options.mode == EmbedMode.html) {
Expand Down
4 changes: 2 additions & 2 deletions lib/playground.dart
Original file line number Diff line number Diff line change
Expand Up @@ -600,8 +600,8 @@ class Playground implements GistContainer, GistController {
DElement(querySelector('#issues')),
DElement(querySelector('#issues-message')),
DElement(querySelector('#issues-toggle')))
..onIssueClick.listen((issue) {
_jumpTo(issue.line, issue.charStart, issue.charLength, focus: true);
..onItemClicked.listen((item) {
_jumpTo(item.line, item.charStart, item.charLength, focus: true);
});

_finishedInit();
Expand Down
27 changes: 25 additions & 2 deletions lib/scss/shared.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
@import 'package:dart_pad/scss/colors';
@import 'package:dart_pad/scss/variables';

a {
text-decoration: none;
cursor: pointer;
font-family: Roboto, sans-serif;
}

// Flashes
#flash-container > div + div {
// Adds space between the flashes in the flash container, if more than one is displayed.
Expand Down Expand Up @@ -69,16 +75,23 @@

// Issues
#issues {
padding: 8px;
overflow-y: auto;
max-height: 50vh;
}

#issues .issue {
display: flex;
flex-direction: row;
align-items: start;
line-height: 20px;
border-radius: 3px;
padding: 4px;
margin: 0;
}



.clickable {
cursor: pointer;
}

Expand All @@ -91,10 +104,20 @@
font-size: $embed-editor-font-size;
display: inline-block;
border-radius: 3px;
padding: 2px 5px;
padding: 0 5px 2px 5px;
min-width: 62px;
}

.issue-indent {
border: 1px;
padding: 2px 5px; // to match size of issuelabel
min-width: 62px;
}

.issue-anchor {
margin: 0 4px;
}

.issue .issuelabel.error {
color: $issue-error-color;
border: 1px solid $issue-error-color;
Expand Down
1 change: 1 addition & 0 deletions lib/services/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const nullSafetyServerUrl = 'https://nullsafety.api.dartpad.dev/';

// A URL to use while debugging.
// final serverUrl = 'http://127.0.0.1:8082/';
// const nullSafetyServerUrl = 'http://127.0.0.1:8082/';

const Duration serviceCallTimeout = Duration(seconds: 10);
const Duration longServiceCallTimeout = Duration(seconds: 60);
Expand Down
Loading