-
Notifications
You must be signed in to change notification settings - Fork 77
Diagnostics: handling of descriptions in messages #164
Diagnostics: handling of descriptions in messages #164
Conversation
* remove from table, only show in datatip/popup * handle string and function types * parse markdown using marked
Added unnecessary line break between "Reference" and "<path>" if diagnostic message contains a reference
|
In my opinion there are still some issues when I compare it to the
|
|
This is awesome!! Thanks for doing this! cc: @matthewwithanm, @hansonw |
Ideally the fact that a message has a When #40 was filed This PR should not be marked as fixing #45, there are other parts of that that this doesn't cover. For example the |
|
Oh, I totally misunderstood #40 then. Sry... |
|
Whoa... it took me some time to get my head around |
|
uhm.. I'm basically done with the PR, so it would nice if anybody can review it 😁 |
modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsPopup.js
|
Sry that I have to mention you. Is there any reason why this hasn't been reviewed yet? @matthewwithanm, @hansonw, @Goom11 |
|
really sorry about letting this slip, @lloiser! thanks so much for doing this. there's not a good reason we haven't reviewed it—just that it's a pretty big contribution and we've been busy. i'll review today ❤️ |
matthewwithanm
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks so much for putting the time into this, @lloiser ❤️
i left a few comments. lmk what you think 😊
| autoVisibility, | ||
| onShowTracesChange: setShowTraces, | ||
| onFilterByActiveTextEditorChange: setFilterByActiveTextEditor, | ||
| onSelectMessage: setSelectedMessage, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a little confusing since it makes it seem that setting the selected message is a global concern, when it isn't. it took me a couple read throughs to understand that we needed this because descriptions are a global concern. i think it would be cleared up some if you renamed setSelectedMessage() here to handleSelectMessage(). that way it's obvious that it's responding to an event and not updating state.
| updater.fetchDescriptions([message], true); | ||
| } | ||
| }; | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't actually need to create a new function every time here. instead, we can just have a single function that closes over the state like this:
const setSelectedMessage = (message: DiagnosticMessage) => {
const updater = this._model.state.diagnosticUpdater;
if (updater == null) return;
updater.fetchDescriptions([message], true);
}| if (updater && showTraces) { | ||
| updater.fetchDescriptions(messages, false); | ||
| } | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a couple things here:
- this is a subscription that's never cleaned up.
- this function is just for getting a stream of states—it really shouldn't have side effects like this.
- fetching a description is now a potentially async (and uncancellable) operation. i don't think we can do this for every single message every time we get messages.
i'm not sure exactly what a good way to do this is though. 🤔@hansonw do you have any ideas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I could easily add some cleanup code to
this._subscriptions - It was rather easy to add it in here because it already contained all the nice streams to hook into :) but it could be moved somewhere else too.
- it only fetches them if the check box is checked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least how it works in linter-ui-default and linter-ui-plus is that they are only fetched when requested by the user if they are a Promise, since there really is no way to know what is coming back from it.
The idea behind descriptions was that they could be a long form description for what is going on... to the point where the entire help web page could be loaded in there if the provider author really wanted. I don't know of any providers that take advantage of this, but there are ones that provide quite large text in there (linter-perlcritic being one of them).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, i'm still not sure how to resolve this…if the checkbox is checked, this is going to do some potentially expensive async operation for every diagnostic, every time the diagnostics are updated. that last bit is pretty important because the diagnostics can be updated fairly frequently (e.g. if one is added).
it seems like maybe there's a disconnect about what "description" was intended for and what Nuclide's doing with it. if it truly is designed to be something that's only pulled, then maybe we should have another field for pushable, longer-form summaries? or we could expect summaries to be in a subject + newline + details format and the toggle would affect that…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I left Nuclide, diagnostics were just records with static information. There was no notion of fetching any additional information about diagnostics after the fact. So I don't think I have much to add here, sorry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I somehow like and dislike the idea of having 2 "descriptions":
- pro: it would make the diagnostics table as simple as it was before
- pro: no async loading of many descriptions because they are not rendered in the table anymore
- con: there would be 2 descriptions which is kind of hard to explain to a user why the diagnostics datatip description is different to the description in the table (but I'm no UI/UX expert 😬 )
I'm open to do as you wish, I just want the description to be part of the UI somewhere :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any updates on this? pending PRs are quite tedious...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reverted the code responsible for showing the description in the table. It's only visible in the datatip. How does this sound to you?
| ) => { | ||
| const {description} = props; | ||
| if (description != null) { | ||
| const __html = marked(description); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you use domPurify here?
also, since marked() is potentially expensive, we should probably make this PureComponent
| goToLocation, | ||
| codeActionsForMessage, | ||
| descriptions, | ||
| ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you replace this bind call with an arrow function? i think it would be a lot easier to read if it were more explicit.
| ), | ||
| ) | ||
| .map(descriptions => | ||
| Actions.setDescriptions(new Map(descriptions), keepDescriptions), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you add a comment here explaining that (and why) we only do this once we get all of the descriptions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only reason was that having to load and dispatch a lot of descriptions updates the diagnostics table really often and (maybe) affects performance.
| ) | ||
| .catch(err => { | ||
| getLogger('atom-ide-diagnostics').error( | ||
| `Error fetching description for ${messages[0].filePath}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this catch should be on the individual description observable? otherwise the path won't necessarily be the one that errored, right?
| selectMessage: (message: DiagnosticMessage) => { | ||
| this._selectMessage(message); | ||
| globalState.onSelectMessage(message); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm, it's a bummer we're making this function every time we see an update, even if globalState.onSelectMessage hasn't changed. i guess we could do something like selectMessage: createSelectMessage(globalState.onSelectMessage), where
const createSelectMessage = memoizeUntilChanged(
onSelectMessage => message => {
this._model.setState({selectedMessage: message});
onSelectMessage(message);
}
);(then we would just get rid of _selectMessage().) what do you think?
| goToLocation: gotoLine, | ||
| codeActionsForMessage, | ||
| })), | ||
| Observable.zip( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think you want combineLatest() here? otherwise you're depending on getting exactly one code action to every description.
* fix some minor rxjs issues * avoid unnecessary react props changes by memoizing props * use `domPurify` for description markdown
|
I have noticed that since 792630d the description is not displayed anymore for selected messages in the diagnostics table. Are there any plans to add it back in? If not then I can remove the |
|
@lloiser nope! it wasn't a great user experience. |
modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessageText.js
It now is only visible in the datatip.
|
@matthewwithanm as I already said above I reverted the changes in the table. The description is only visible in the datatip now. |
|
Sorry, missed it! I'll check it out tomorrow ❤️ |
|
@lloiser we still show descriptions in the table when also, do we have a memory leak here? it seems like |
|
As I said, the diagnostics table does the same as before: takes the |
Whenever the list of messages changes, check if the messages of the descriptions are still valid.
|
@matthewwithanm now with memory leak prevention 😄 |
| }); | ||
| }); | ||
| }); | ||
| if (descriptions.size === newDescriptions.size) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't it possible for them to be the same size but still have changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't think of any case where this can happen...
matthewwithanm
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for sticking with us! ❤️
one quick q
|
okay, so i'm testing this out, trying to merge and not seeing the descriptions 🤔 probably i'm just doing something dumb, but if you do this and then hover the gutter, they should be populated, right? atom.packages.serviceHub.consume('linter-indie', '2.0.0', x => { window.linter = x({name: 'Test'}); });
// then…
linter.setAllMessages([{
severity: 'info',
location: {
file: '/path/to/file.js',
position: [[5, 0]],
},
excerpt: 'This is an error message on a file',
description: () => Promise.resolve('blah')
}]); |
|
You are right, in the gutter it does not work but when you hover the code in the editor |
|
@matthewwithanm ping ... (sry for being annoying 😬) |
|
@lloiser no need to apologize! sorry this has taken so long. this is an important part of our workflows at facebook and we just need to be extra careful (: |
Summary: Imported from facebookarchive/atom-ide-ui#164, this supports linter messages with async descriptions. Reviewed By: hansonw Differential Revision: D8825841 fbshipit-source-id: 622acf765285a21f39d643f11682e589945425e9
Summary: Imported from #164, this supports linter messages with async descriptions. Reviewed By: hansonw Differential Revision: D8825841 fbshipit-source-id: 622acf765285a21f39d643f11682e589945425e9


This PR improves the handling of descriptions in diagnostic messages …