-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add auto-refresh capabilities for memory inspector windows #115
Conversation
You definitely raise a very interesting point! My reasoning for a dedicated boolean flag (auto refresh enabled) was that I wanted to give the user the option to disable the auto-refresh without the need to change (and lose) the value to something "invalid" (like -1). So in my mind auto refresh and refresh on stop were definitely not mutually exclusive. I don't know if there really is a use case for Curiously, the |
70acba8
to
f58701c
Compare
(force-pushed to solve merge conflict) |
@colin-grant-work , @martin-fleck-at , a couple of very good points. Let's not rush into merging this one before the release and spend a little more though on it. I'll get back to this next week. |
I suggest that we allow it - or the new preference that combines it with auto-refresh - to be configured on a per-view basis, as you've done in your PR. Then there can't be a conflict: the options would be encapsulated in a single piece of configuration, and that configuration would configurable in the usual places (globally and per view).
I think we have a good model for this in the |
Ah yes, I somehow misinterpreted your previous statement, having all as a per-view basis that absolutely makes sense. Thank you for the clarification! |
Just throwing that one out there as well: Currently we always refresh the memory inspector when the view state (active/focussed or visible) changes and the view is visible. That can result in quite a few refreshes as during debugging the code editor usually gets focus so clicking between the code editor and the memory inspector also triggers a refresh every time. So while we are at it: Should we also introduce a |
Or maybe a not on focus change :-). But yes, I think if that's currently happening as part of the default behavior (and if it's something that we can prevent from happening), then it would make sense to include it among the options. |
I agree that we should try to combine the different triggers into a single user visible setting. I think however that a simple drop-down may not be sufficient. It would rather need to be a drop-down with checkbox items (or similar) to selectively allow adding/removing triggers.
Is this like in always fetching data from the debug adapter? I am not entirely sure that switching tabs should do a full re-read from the target system. Appreciate though that this may be a result of the WebView not preserving the previous state when hidden for example due to a tab-switch. And that also a debug adapter needs to do its part in caching values where sensible. On |
f58701c
to
9322f7a
Compare
@jreineckearm @colin-grant-work I pushed an update to the current branch and updated the description. We now have the following options:
All options are now view-local, i.e., scoped per view. This made it necessary for new events to be sent to the webview (e.g., stopped on a debug session and view state change of a webview). I think in the long run, it is definitely a good idea to have that information within the webview so we can be more fine-grained and smarter with memory updates. The refactoring of the session tracking could also serve nicely as a basis for #97. As discussed before those options are currently mutually exclusive (enum) but there might be a use case where you actually want to combine them (i.e., they become toggles) - as mentioned by @jreineckearm. Unfortunately, I lack the real world application for this use case, so I'd highly value your input on how we should best proceed on this. I think having agreement on that before merging the PR is key as we are already breaking the previous [1] Note I also found a very small UX issue in the settings sub-group rendering which may also break settings #119. |
9322f7a
to
dc631e7
Compare
I'll take a look at this tomorrow. |
package.json
Outdated
"editor/title": [ | ||
{ | ||
"command": "memory-inspector.show", | ||
"group": "navigation", | ||
"when": "memory-inspector.canRead && (resourceLangId === c || resourceLangId === cpp)" | ||
} |
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.
Is this related to the functionality elsewhere in the PR? It looks like the aim is to expose the Memory Inspector functionality more prominently, but the resourceLangId
is only a so-so proxy for 'is a source file of a source file for a debug session that can read memory'.
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.
You are correct, it is not strictly part of this PR and so I only mentioned it as a "minor" in the PR description
Add title toolbar item for C/C++ file to access open memory inspector
Mainly because I used it a lot to open a memory inspector from a source file. I understand that the condition is far from complete in this regard but since we are only opening an empty memory inspector, I don't see the harm. However, I understand if you want me to move this out of this PR as wel.
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 suppose that if I would put it anywhere, I would put it (if possible) on the toolbar of the debug view rather than the file, since the 'memory' and the 'file' don't have a clear correspondence. For now, perhaps separate from this PR so that it can be discussed separately.
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.
Agree that this is one we should address together with other entry points proposed in #52 in separate PRs.
I believe there was a discussion around source editors already.
cf8befb
to
96d8750
Compare
@colin-grant-work Thank you very much for your feedback! It took me a while to get back to this but I pushed an update now and replied to your comments - some of which may still need some broader discussion but I'm not sure they all need to happen as part of this task. |
My apologies for only coming back to this now! I kind of like the idea of condensing the possible triggers to reduce complexity. But I am a little concerned about losing the ability to mix triggers because of flattening them into an enum. Especially if we need to add more in future. I still have no good idea how to realize this instead. My initial reaction would have been to have a kind of dropdown where you can toggle check marks to indicate which triggers are active. But I couldn't find that pattern yet in VS Code configurations. Often, such things seem to be realized by a bunch of checkboxes.... which we worst case have to accept for now. Let me do some more digging if I find something in the design guidelines... Regarding I think One original thought about the Will have a chat with Martin about this again tomorrow. But it feels like we need to keep the options separate and revisit if we can handle the |
I had another look at applying the auto-save settings scheme to the window refresh. Unfortunately, there are some differences because of which I don't think they apply one-to-one to the memory window updates:
I believe we should keep the trigger/event configuration on the level it was before rather than trying to condense them into a flat list. Should the settings be only global or also local? I believe also local. Even if I am worried about the advanced window settings getting more and more these days. But this could be handled by a redesign of the in-window settings that distinguishes between common settings and expert settings. |
I'm not sure exactly which state you're referring to when you say
Do you just mean that you would like to have two configurations (or three, more likely):
And not any form of If so, it sounds like you have strong feelings about it, and I can live with that arrangement. |
- Rework 'refreshOnStop' boolean setting to 'autoRefresh' enumerable -- On Stop (previously: 'on' for 'refreshOnStop' -- On Focus (previously: always implicit on view state change) -- After Delay (new) -- Off (previously: 'off' for 'refreshOnStop') - On Stop -- Rework global setting to be local for each Memory Inspector -- Listen to debug session stopped event and propagate to view - On Focus -- Rework implicit refresh update to option in setting -- Listen to view state changes and propagate to view - After Delay -- New option to explicitly define a delay when re-fetching the memory -- Minimum: 500ms, default: 500, input step size: 250ms Refactoring: - Split debug session tracking into dedicated class with session events -- Convert debug events into session events with additional data - Split context tracking from memory provider into dedicated class - Move manifest to common for default values and avoid duplication -- Align 'Min' with 'Minimal' value from manifest Minor: - Add title toolbar item for C/C++ file to access open memory inspector - Improve debugging experience by using inline source maps - Align creation of option enums to use const objects - Additionally guard 'body' on debug responses for safety - Avoid functional React state update where unnecessary Fixes eclipse-cdt-cloud#91
- Improve wording in setting descriptions - Move shared types into common area instead of using type imports - Fix 'fetchMemory' not returning the correct promise
Also remove the show memory inspector toolbar item for C/C++ files
96d8750
to
b6da870
Compare
@colin-grant-work @jreineckearm I rebased the PR and added another commit which separates the options again. We now have |
@colin-grant-work , @martin-fleck-at : and apologies again for taking a while to come back to this.
Correct (
Correct, we should carefully review if we can improve the window to deal with this without introducing another option. For example by tracking a
Well...kind of. I'd love to get these enhancements in to test them with users. :-) |
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.
Tested the changes, works nicely and behavior is as expected. Awesome work, @martin-fleck-at !
package.json
Outdated
], | ||
"default": "on", | ||
"description": "Refresh memory views when debugger stops" | ||
"description": "Refresh Memory Inspectors when the debugger stops" |
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.
"description": "Refresh Memory Inspectors when the debugger stops" | |
"description": "Refresh Memory Inspector windows when the debugger stops" |
package.json
Outdated
"Do not automatically refresh after the configured delay" | ||
], | ||
"default": "off", | ||
"description": "Refresh Memory Inspectors after the configured delay" |
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.
Let's include the link to the other preference here as well. That'll be easier for users to click on and make the connection more obvious.
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.
Good idea, I'll add the link!
src/common/messaging.ts
Outdated
export interface ViewState { | ||
active: boolean; | ||
visible: boolean; |
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 interface is passed around, but I don't see that its values are ever used?
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.
Yeah, good catch! It is a remnant of the code we had for the focus change, i.e., the code that is currently commented:
// activate the code below if you want to refresh the memory when the view becomes active (focussed)
// const viewStateChange: Change<ViewState> = { from: from.viewState, to: current.viewState };
// if (hasChangedTo(viewStateChange, 'active', true)) {
// this.fetchMemory();
// }
I'll remove the view state related code and remove the commented code as well. We'll always have the PR to look up any history, it does not need to be in the submitted change.
src/plugin/memory-provider.ts
Outdated
} | ||
contributedTracker?.onWillReceiveMessage?.(message); | ||
} | ||
onWillStartSession: () => contributedTracker?.onWillStartSession?.(), |
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 that this is now equivalent to return contributedTracker
since we don't actually do any work of our own here.
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 had to adapt the return type of the function but you are of course correct. I'd even say that it is more correct now as we simply return undefined if we do not have an adapter tracker instead of an empty adapter tracker that never does anything. I really like this.
src/plugin/memory-provider.ts
Outdated
// We only send out a custom event if we don't expect the client to handle the memory event | ||
// since our client is VS Code we can assume that they will always support this but better to be safe | ||
const offset = response?.offset ? (args.offset ?? 0) + response.offset : args.offset; | ||
const count = response?.bytesWritten ?? stringToBytesMemory(args.data).length; | ||
this._onDidWriteMemory.fire({ memoryReference: args.memoryReference, offset, count }); | ||
// if our custom handler is active, let's fire the event ourselves | ||
this.sessionTracker['fireSessionEvent'](session, 'memory-written', { memoryReference: args.memoryReference, offset, count }); |
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 bracket access of fireSessionEvent
here is a bit suspicious.
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 was not sure whether to make the method public out of habit but since this is a VS Code extension and not a framework, there is a lower risk of exposing "internal" methods. I made the method public since we actually use it from somewhere else, thanks!
src/plugin/memory-webview-main.ts
Outdated
let memoryOptions = initialOptions; | ||
const disposables = [ | ||
this.messenger.onNotification(readyType, () => { | ||
this.setInitialSettings(participant, panel.title); | ||
this.setSessionContext(participant, this.memoryProvider.createContext()); | ||
this.refresh(participant, options); | ||
}, { sender: participant }), | ||
this.messenger.onRequest(setOptionsType, o => { | ||
options = { ...options, ...o }; | ||
}, { sender: participant }), | ||
this.messenger.onNotification(readyType, () => this.initialize(participant, panel, memoryOptions), { sender: participant }), | ||
this.messenger.onRequest(setOptionsType, newOptions => { memoryOptions = { ...memoryOptions, ...newOptions }; }, { sender: participant }), |
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'm not sure I understand what's going on here. We reassign the memoryOptions
when a setOptions
message is requested, but we only use it when we call this.initialize
in response to the readyType
notification, which should only happen once and should happen before the view sends a setOptions
message. Under what circumstances would we used the reassigned value of memoryOptions
?
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.
Hmm... it's been too long and I don't really remember why I did that. May have had something to do with the view state changes that may happen earlier? Not sure but I can no longer produce any issues with the previous code, so I restored it.
@@ -52,6 +53,7 @@ export interface OptionsWidgetProps | |||
|
|||
interface OptionsWidgetState { | |||
isTitleEditing: boolean; | |||
isEnablingPeriodicRefresh: boolean; |
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.
It looks like this should really be an event handler: when the value is changed to not off, we want to focus the interval input. Alternatively, it isn't necessary: the interval input is always available, so the user can focus it any time they want. This is different from the title editing input, whose appearance is controlled by a state value and into which we want to pass focus as soon as it appears - i.e. in the other case, a state change is the event we're responding to.
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 agree with you, I thought it was nice UX but it might be a bit much, especially if the user already configured the delay to what they want once. I therefore removed the auto-focus on the input again.
src/webview/memory-webview-view.tsx
Outdated
// activate the code below if you want to refresh the memory when the view becomes active (focussed) | ||
// const viewStateChange: Change<ViewState> = { from: from.viewState, to: current.viewState }; | ||
// if (hasChangedTo(viewStateChange, 'active', true)) { | ||
// this.fetchMemory(); | ||
// } |
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.
It looks like this is where the view state would be accessed. Given that we've decided not to include this as an option available to user, do we want to include this code?
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.
Initially I wanted to keep it until we all agreed on the options we want to have. As we did that now, I removed all view state-related code to not pollute our code base, thank you for catching that!
@colin-grant-work Thank you very much for your feedback! Just as a heads-up, we'll look into it early next week, as many of us are out of office this week. Thanks! |
- Improve settings descriptions in package.json - Get rid of obsolete ViewState and view state-related code - Properly return provider result for contributed trackers - Make 'fireSessionEvent' public as we access it from other classes - Avoid auto-focusing the delay input field if we change options
@colin-grant-work @jreineckearm I merged the master into this and resolved some conflicts and then added the PR feedback on top of that. It would be great if someone could have another look to see if everything still works properly. Thank you! |
Still works as expected after today's updates. |
Extend Auto Refresh capabilities with 'After Delay' and 'On Focus'
Rework 'refreshOnStop' boolean setting to 'autoRefresh' enumerable
-- On Stop (previously: 'on' for 'refreshOnStop')
-- On Focus (previously: always implicit on view state change)
-- After Delay (new)
-- Off (previously: 'off' for 'refreshOnStop')
On Stop
-- Rework global setting to be local for each Memory Inspector
-- Listen to debug session stopped event and propagate to view
On Focus
-- Rework implicit refresh update to option in setting
-- Listen to view state changes and propagate to view
After Delay
-- New option to explicitly define a delay when re-fetching the memory
-- Minimum: 500ms, default: 500, input step size: 250ms
Refactoring:
-- Convert debug events into session events with additional data
-- Align 'Min' with 'Minimal' value from manifest
Minor:
Fixes #91
How to test
(Note: Options have been replaced by enum and look a little bit different now)
Review checklist
Reminder for reviewers