Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Conversation

@lloiser
Copy link
Contributor

@lloiser lloiser commented Feb 8, 2018

This PR improves the handling of descriptions in diagnostic messages …

* 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
@lloiser
Copy link
Contributor Author

lloiser commented Feb 8, 2018

In my opinion there are still some issues when I compare it to the linter-ui-default package:

linter-ui-default
linter-ui-default

atom-ide-ui
atom-ide-ui

@Goom11
Copy link
Contributor

Goom11 commented Feb 8, 2018

This is awesome!! Thanks for doing this!

cc: @matthewwithanm, @hansonw
LGTM but y'all know this code better

@Arcanemagus
Copy link
Contributor

remove from table, only show in datatip/popup

Ideally the fact that a message has a description should be showing in the table, it just should be collapsed by default. linter-ui-plus has a great interface for this, it just never implemented tooltips.

When #40 was filed descriptions always rendered their full contents with no way to collapse them. Currently there is a toggle that controls that behavior, the bug remaining there is that the first part of the description is still rendered when that option is disabled, but at least it is cut off.


This PR should not be marked as fixing #45, there are other parts of that that this doesn't cover. For example the url field is still ignored, even though it's one of the more useful bits of information from a message.

@lloiser
Copy link
Contributor Author

lloiser commented Feb 9, 2018

Oh, I totally misunderstood #40 then. Sry...
I will see if I can get this expandable description into the table too.

@lloiser lloiser changed the title Diagnostics: handling of descriptions in messages WIP: Diagnostics: handling of descriptions in messages Feb 9, 2018
@lloiser
Copy link
Contributor Author

lloiser commented Feb 18, 2018

Whoa... it took me some time to get my head around rxjs. Hopefully I did not too bad 😆
The build failed because npm test died unexpectedly ... I unfortunately cannot retry the build.

@codecov-io
Copy link

codecov-io commented Feb 21, 2018

Codecov Report

Merging #164 into master will decrease coverage by <.01%.
The diff coverage is 0%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master    #164      +/-   ##
=========================================
- Coverage    1.95%   1.94%   -0.01%     
=========================================
  Files         368     369       +1     
  Lines       14315   14363      +48     
=========================================
  Hits          280     280              
- Misses      14035   14083      +48
Impacted Files Coverage Δ
...atom-ide-diagnostics/lib/services/LinterAdapter.js 0% <ø> (ø) ⬆️
...om-ide-diagnostics-ui/lib/ui/DiagnosticsMessage.js 0% <ø> (ø) ⬆️
.../pkg/atom-ide-diagnostics/lib/redux/createStore.js 0% <ø> (ø) ⬆️
modules/nuclide-commons/memoizeUntilChanged.js 0% <ø> (ø) ⬆️
...m-ide-ui/pkg/atom-ide-diagnostics-ui/lib/gutter.js 0% <0%> (ø) ⬆️
...ide-ui/pkg/atom-ide-diagnostics/lib/redux/Epics.js 0% <0%> (ø) ⬆️
...e-ui/pkg/atom-ide-diagnostics/lib/redux/Actions.js 0% <0%> (ø) ⬆️
...atom-ide-diagnostics-ui/lib/ui/DiagnosticsTable.js 0% <0%> (ø) ⬆️
...-ui/pkg/atom-ide-diagnostics/lib/redux/Reducers.js 0% <0%> (ø) ⬆️
...tom-ide-diagnostics-ui/lib/getDiagnosticDatatip.js 0% <0%> (ø) ⬆️
... and 4 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 094459b...3189a42. Read the comment docs.

@lloiser lloiser changed the title WIP: Diagnostics: handling of descriptions in messages Diagnostics: handling of descriptions in messages Feb 22, 2018
@lloiser
Copy link
Contributor Author

lloiser commented Mar 8, 2018

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
@lloiser
Copy link
Contributor Author

lloiser commented Mar 29, 2018

Sry that I have to mention you. Is there any reason why this hasn't been reviewed yet? @matthewwithanm, @hansonw, @Goom11

@matthewwithanm
Copy link
Contributor

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 ❤️

Copy link
Contributor

@matthewwithanm matthewwithanm left a 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,
Copy link
Contributor

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);
}
};
});
Copy link
Contributor

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);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

a couple things here:

  1. this is a subscription that's never cleaned up.
  2. this function is just for getting a stream of states—it really shouldn't have side effects like this.
  3. 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  1. I could easily add some cleanup code to this._subscriptions
  2. 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.
  3. it only fetches them if the check box is checked.

Copy link
Contributor

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).

Copy link
Contributor

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…

cc @hansonw and @nmote for some context and advice on this

Copy link
Contributor

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.

Copy link
Contributor Author

@lloiser lloiser May 11, 2018

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 :)

Copy link
Contributor Author

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...

Copy link
Contributor Author

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);
Copy link
Contributor

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,
),
Copy link
Contributor

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),
Copy link
Contributor

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?

Copy link
Contributor Author

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}`,
Copy link
Contributor

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);
},
Copy link
Contributor

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(
Copy link
Contributor

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.

lloiser added 2 commits April 1, 2018 17:39
* fix some minor rxjs issues
* avoid unnecessary react props changes by memoizing props
* use `domPurify` for description markdown
@lloiser
Copy link
Contributor Author

lloiser commented Apr 2, 2018

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 handleSelectMessage functions (and related code) I have added.

@matthewwithanm
Copy link
Contributor

@lloiser nope! it wasn't a great user experience.

@lloiser
Copy link
Contributor Author

lloiser commented Jul 8, 2018

@matthewwithanm as I already said above I reverted the changes in the table. The description is only visible in the datatip now.
The build failure is the same as on master currently. I guess this will be fixed with #273
Is there anything left for me to to?

@matthewwithanm
Copy link
Contributor

Sorry, missed it! I'll check it out tomorrow ❤️

@matthewwithanm
Copy link
Contributor

@lloiser we still show descriptions in the table when showTraces is true. what happens there with the async ones?

also, do we have a memory leak here? it seems like fetchDescriptions() is only ever called with keepDescriptions (should that be keepExisting?) === true. isn't that map just going to grow indefinitely?

@lloiser
Copy link
Contributor Author

lloiser commented Jul 9, 2018

As I said, the diagnostics table does the same as before: takes the message and splits on new lines and uses that as description. Only the datatip contains the description.
Yes you are right there is memory leak... I'm on it!

Whenever the list of messages changes, check if the messages of the 
descriptions are still valid.
@lloiser
Copy link
Contributor Author

lloiser commented Jul 9, 2018

@matthewwithanm now with memory leak prevention 😄

});
});
});
if (descriptions.size === newDescriptions.size) {
Copy link
Contributor

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?

Copy link
Contributor Author

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...

Copy link
Contributor

@matthewwithanm matthewwithanm left a 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

@matthewwithanm
Copy link
Contributor

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')
}]);

@lloiser
Copy link
Contributor Author

lloiser commented Jul 12, 2018

You are right, in the gutter it does not work but when you hover the code in the editor blah is shown.
Ah... I see it uses a completely different code path. Back to coding then :)

@lloiser
Copy link
Contributor Author

lloiser commented Jul 17, 2018

@matthewwithanm ping ...

(sry for being annoying 😬)

@matthewwithanm
Copy link
Contributor

@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 (:

@matthewwithanm matthewwithanm merged commit 9ab1156 into facebookarchive:master Jul 23, 2018
facebook-github-bot pushed a commit to facebookarchive/nuclide that referenced this pull request Jul 24, 2018
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
hansonw pushed a commit that referenced this pull request Jul 26, 2018
Summary: Imported from #164, this supports linter messages with async descriptions.

Reviewed By: hansonw

Differential Revision: D8825841

fbshipit-source-id: 622acf765285a21f39d643f11682e589945425e9
@lloiser lloiser deleted the ll-description branch August 23, 2018 20:41
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants