Skip to content

Commit 053a0cd

Browse files
committed
(GH-494) Add Progress Bar for long lived operations
Previously during long running operations for the Node Graph and Puppet Resource features, the user is left wondering "What's going on?" as there's no visual indicator. This commit: * Adds a non-deterministic message box (by default) when long lived operations start and then removes the message box once completed. This uses the native VSCode API 'vscode.window.withProgress' * Adds two new settings to change the progress display behaviour; 'puppet.notification.nodeGraph' and 'puppet.notification.puppetResource'. These settings can have the values of messagebox, statusbar or none, which changes the notification type used for each type of operation. * Updates the README with the new animated gif files and notes the new settings.
1 parent ff7085c commit 053a0cd

File tree

7 files changed

+159
-55
lines changed

7 files changed

+159
-55
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ The information returned will be pasted into the open editor window where your c
197197

198198
![puppet_resource](https://raw.githubusercontent.com/lingua-pupuli/puppet-vscode/master/docs/assets/puppet_resource.gif)
199199

200+
You can change the notification message from the default message box, to a status bar notification or none at all, using the `puppet.notification.puppetResource` configuration setting.
201+
200202
#### Puppet Node Graph preview
201203

202204
You can preview the [node graph](https://puppet.com/blog/visualize-your-infrastructure-models) of a manifest while you edit your Puppet Code.
@@ -209,6 +211,8 @@ The node graph will appear next to the manifest
209211

210212
![puppet_node_graph](https://raw.githubusercontent.com/lingua-pupuli/puppet-vscode/master/docs/assets/puppet_node_graph.gif)
211213

214+
You can change the notification message from the default message box, to a status bar notification or none at all, using the `puppet.notification.nodeGraph` configuration setting.
215+
212216
### Puppet Development Kit Support
213217

214218
You can use the [Puppet Development Kit](https://puppet.com/blog/develop-modules-faster-new-puppet-development-kit) inside VS Code from the command palette. To use any of the above commands, open the command palette and start typing a command. You can also use the right-click context menu or the editor menu to reach these commands.

docs/assets/puppet_node_graph.gif

-1.34 MB
Loading

docs/assets/puppet_resource.gif

-47.7 KB
Loading

package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,26 @@
398398
"agent"
399399
]
400400
},
401+
"puppet.notification.nodeGraph": {
402+
"type": "string",
403+
"default": "messagebox",
404+
"description": "The type of notification used when a node graph is being generated. Default value of messagebox",
405+
"enum": [
406+
"messagebox",
407+
"statusbar",
408+
"none"
409+
]
410+
},
411+
"puppet.notification.puppetResource": {
412+
"type": "string",
413+
"default": "messagebox",
414+
"description": "The type of notification used when a running Puppet Resouce. Default value of messagebox",
415+
"enum": [
416+
"messagebox",
417+
"statusbar",
418+
"none"
419+
]
420+
},
401421
"puppet.editorService.modulePath": {
402422
"description": "**DEPRECATED** Please use puppet.editorService.puppet.modulePath instead"
403423
},

src/feature/NodeGraphFeature.ts

Lines changed: 65 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import * as path from 'path';
66
import { IFeature } from "../feature";
77
import { ILogger } from "../logging";
88
import { ConnectionStatus } from '../interfaces';
9-
import { CompileNodeGraphRequest } from '../messages';
9+
import { CompileNodeGraphRequest, CompileNodeGraphResponse } from '../messages';
1010
import { reporter } from '../telemetry/telemetry';
1111
import * as viz from 'viz.js';
1212
import { ConnectionHandler } from "../handler";
13+
import { ISettings, SettingsFromWorkspace } from '../settings';
1314

1415
const PuppetNodeGraphToTheSideCommandId: string = 'extension.puppetShowNodeGraphToSide';
1516

@@ -47,6 +48,7 @@ class NodeGraphWebViewProvider implements vscode.Disposable {
4748
this.parentFeature.onProviderWebPanelDisposed(this);
4849
});
4950

51+
this.webPanel.webview.html = "Generating...";
5052
this.updateAsync();
5153
}
5254

@@ -67,40 +69,74 @@ class NodeGraphWebViewProvider implements vscode.Disposable {
6769
const requestData = {
6870
external: this.docUri.toString()
6971
};
70-
return this.connectionHandler.languageClient
72+
73+
// Calculate where the progress message should go, if at all.
74+
const currentSettings:ISettings = SettingsFromWorkspace();
75+
var notificationType = vscode.ProgressLocation.Notification;
76+
if (currentSettings.notification !== undefined && currentSettings.notification.nodeGraph !== undefined) {
77+
switch (currentSettings.notification.nodeGraph.toLowerCase()) {
78+
case "messagebox": notificationType = vscode.ProgressLocation.Notification; break;
79+
case "statusbar": notificationType = vscode.ProgressLocation.Window; break;
80+
case "none": notificationType = undefined; break;
81+
default: break; // Default is already set
82+
}
83+
}
84+
85+
if (notificationType !== undefined) {
86+
return vscode.window.withProgress({
87+
location: notificationType,
88+
title: "Puppet",
89+
cancellable: false
90+
}, (progress) => {
91+
progress.report({message: "Generating Node Graph"});
92+
return this.connectionHandler.languageClient
93+
.sendRequest(CompileNodeGraphRequest.type, requestData)
94+
.then(
95+
(compileResult) => {
96+
return this.responseToHTMLString(compileResult);
97+
});
98+
});
99+
}
100+
else {
101+
return this.connectionHandler.languageClient
71102
.sendRequest(CompileNodeGraphRequest.type, requestData)
72103
.then(
73104
(compileResult) => {
105+
return this.responseToHTMLString(compileResult);
106+
});
107+
}
108+
}
74109

75-
var svgContent = '';
76-
if (compileResult.dotContent !== null) {
77-
var styling = `
78-
bgcolor = "transparent"
79-
color = "white"
80-
rankdir = "TB"
81-
node [ shape="box" penwidth="2" color="#e0e0e0" style="rounded,filled" fontname="Courier New" fillcolor=black, fontcolor="white"]
82-
edge [ style="bold" color="#f0f0f0" penwith="2" ]
83-
84-
label = ""`;
85-
86-
var graphContent = compileResult.dotContent;
87-
if (graphContent === undefined) { graphContent = ''; }
88-
// vis.jz sees backslashes as escape characters, however they are not in the DOT language. Instead
89-
// we should escape any backslash coming from a valid DOT file in preparation to be rendered
90-
graphContent = graphContent.replace(/\\/g,"\\\\");
91-
graphContent = graphContent.replace(`label = "editorservices"`,styling);
92-
93-
svgContent = viz(graphContent,"svg");
94-
}
110+
private responseToHTMLString(compileResult: CompileNodeGraphResponse): string {
111+
var svgContent = '';
112+
if (compileResult.dotContent !== null) {
113+
var styling = `
114+
bgcolor = "transparent"
115+
color = "white"
116+
rankdir = "TB"
117+
node [ shape="box" penwidth="2" color="#e0e0e0" style="rounded,filled" fontname="Courier New" fillcolor=black, fontcolor="white"]
118+
edge [ style="bold" color="#f0f0f0" penwith="2" ]
119+
120+
label = ""`;
121+
122+
var graphContent = compileResult.dotContent;
123+
if (graphContent === undefined) { graphContent = ''; }
124+
// vis.jz sees backslashes as escape characters, however they are not in the DOT language. Instead
125+
// we should escape any backslash coming from a valid DOT file in preparation to be rendered
126+
graphContent = graphContent.replace(/\\/g,"\\\\");
127+
graphContent = graphContent.replace(`label = "editorservices"`,styling);
128+
129+
svgContent = viz(graphContent,"svg");
130+
}
95131

96-
var errorContent = `<div style='font-size: 1.5em'>${compileResult.error}</div>`;
97-
if ((compileResult.error === undefined) || (compileResult.error === null)) { errorContent = ''; }
132+
var errorContent = `<div style='font-size: 1.5em'>${compileResult.error}</div>`;
133+
if ((compileResult.error === undefined) || (compileResult.error === null)) { errorContent = ''; }
98134

99-
if (reporter) {
100-
reporter.sendTelemetryEvent(PuppetNodeGraphToTheSideCommandId);
101-
}
135+
if (reporter) {
136+
reporter.sendTelemetryEvent(PuppetNodeGraphToTheSideCommandId);
137+
}
102138

103-
const html: string = `<!DOCTYPE html>
139+
const html: string = `<!DOCTYPE html>
104140
<html lang="en">
105141
<head>
106142
<meta charset="UTF-8">
@@ -130,8 +166,7 @@ ${svgContent}
130166
</div>
131167
</body></html>`;
132168

133-
return html;
134-
});
169+
return html;
135170
}
136171

137172
public dispose(): any {

src/feature/PuppetResourceFeature.ts

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
import * as vscode from 'vscode';
44
import { IFeature } from '../feature';
55
import { ILogger } from '../logging';
6-
import { PuppetCommandStrings, PuppetResourceRequestParams, PuppetResourceRequest } from '../messages';
6+
import { PuppetCommandStrings, PuppetResourceRequestParams, PuppetResourceRequest, PuppetResourceResponse } from '../messages';
77
import { reporter } from '../telemetry/telemetry';
88
import { ConnectionStatus } from '../interfaces';
99
import { ConnectionHandler } from '../handler';
10+
import { ISettings, SettingsFromWorkspace } from '../settings';
1011

1112
export class PuppetResourceFeature implements IFeature {
1213
private _connectionHandler: ConnectionHandler;
@@ -42,36 +43,69 @@ export class PuppetResourceFeature implements IFeature {
4243
let requestParams = new RequestParams();
4344
requestParams.typename = moduleName;
4445

45-
thisCommand._connectionHandler.languageClient
46-
.sendRequest(PuppetResourceRequest.type, requestParams)
47-
.then(resourceResult => {
48-
if (resourceResult.error !== undefined && resourceResult.error.length > 0) {
49-
this.logger.error(resourceResult.error);
50-
return;
51-
}
52-
if (resourceResult.data === undefined || resourceResult.data.length === 0) {
53-
return;
54-
}
55-
56-
if (!editor) {
57-
return;
58-
}
59-
60-
var newPosition = new vscode.Position(0, 0);
61-
if (editor.selection.isEmpty) {
62-
const position = editor.selection.active;
63-
newPosition = position.with(position.line, 0);
64-
}
46+
// Calculate where the progress message should go, if at all.
47+
const currentSettings:ISettings = SettingsFromWorkspace();
48+
var notificationType = vscode.ProgressLocation.Notification;
49+
if (currentSettings.notification !== undefined && currentSettings.notification.puppetResource !== undefined) {
50+
switch (currentSettings.notification.puppetResource.toLowerCase()) {
51+
case "messagebox": notificationType = vscode.ProgressLocation.Notification; break;
52+
case "statusbar": notificationType = vscode.ProgressLocation.Window; break;
53+
case "none": notificationType = undefined; break;
54+
default: break; // Default is already set
55+
}
56+
}
6557

66-
this.editCurrentDocument(doc.uri, resourceResult.data, newPosition);
67-
if (reporter) {
68-
reporter.sendTelemetryEvent(PuppetCommandStrings.PuppetResourceCommandId);
69-
}
58+
if (notificationType !== undefined) {
59+
vscode.window.withProgress({
60+
location: notificationType,
61+
title: "Puppet",
62+
cancellable: false
63+
}, (progress) => {
64+
progress.report({message: `Gathering Puppet ${moduleName} Resources`});
65+
return thisCommand._connectionHandler.languageClient
66+
.sendRequest(PuppetResourceRequest.type, requestParams)
67+
.then(resourceResult => {
68+
this.respsonseToVSCodeEdit(resourceResult, editor, doc);
69+
}
70+
);
7071
});
72+
} else {
73+
thisCommand._connectionHandler.languageClient
74+
.sendRequest(PuppetResourceRequest.type, requestParams)
75+
.then(resourceResult => {
76+
this.respsonseToVSCodeEdit(resourceResult, editor, doc);
77+
}
78+
);
79+
}
7180
}
7281
});
7382
}
7483

84+
private respsonseToVSCodeEdit(resourceResult: PuppetResourceResponse, editor: vscode.TextEditor, doc: vscode.TextDocument) {
85+
if (resourceResult.error !== undefined && resourceResult.error.length > 0) {
86+
this.logger.error(resourceResult.error);
87+
return;
88+
}
89+
if (resourceResult.data === undefined || resourceResult.data.length === 0) {
90+
return;
91+
}
92+
93+
if (!editor) {
94+
return;
95+
}
96+
97+
var newPosition = new vscode.Position(0, 0);
98+
if (editor.selection.isEmpty) {
99+
const position = editor.selection.active;
100+
newPosition = position.with(position.line, 0);
101+
}
102+
103+
this.editCurrentDocument(doc.uri, resourceResult.data, newPosition);
104+
if (reporter) {
105+
reporter.sendTelemetryEvent(PuppetCommandStrings.PuppetResourceCommandId);
106+
}
107+
}
108+
75109
private pickPuppetResource(): Thenable<string | undefined> {
76110
let options: vscode.QuickPickOptions = {
77111
placeHolder: 'Enter a Puppet resource to interrogate',

src/settings.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,18 @@ export interface IPDKSettings {
6161
// Future Use
6262
}
6363

64+
export interface INotificationSettings {
65+
nodeGraph?: string;
66+
puppetResource?: string;
67+
}
68+
6469
export interface ISettings {
6570
editorService?: IEditorServiceSettings;
6671
format?: IFormatSettings;
6772
installDirectory?: string;
6873
installType?: PuppetInstallType;
6974
lint?: ILintSettings;
75+
notification?: INotificationSettings;
7076
pdk?: IPDKSettings;
7177
}
7278

@@ -160,6 +166,10 @@ export function DefaultWorkspaceSettings(): ISettings {
160166
lint: {
161167
enable: true,
162168
},
169+
notification: {
170+
nodeGraph: "messagebox",
171+
puppetResource: "messagebox"
172+
},
163173
pdk: {
164174
}
165175
};
@@ -176,6 +186,7 @@ export function SettingsFromWorkspace(): ISettings {
176186
installDirectory: workspaceConfig.get<string>("installDirectory", defaults.installDirectory),
177187
installType: workspaceConfig.get<PuppetInstallType>("installType", defaults.installType),
178188
lint: workspaceConfig.get<ILintSettings>("lint", defaults.lint),
189+
notification: workspaceConfig.get<INotificationSettings>("notification", defaults.notification),
179190
pdk: workspaceConfig.get<IPDKSettings>("pdk", defaults.pdk)
180191
};
181192

0 commit comments

Comments
 (0)