@@ -6,179 +6,110 @@ import * as path from 'path';
66import { IFeature } from "../feature" ;
77import { ILogger } from "../logging" ;
88import { IConnectionManager } from '../connection' ;
9- import { ConnectionStatus } from '../interfaces' ;
10- import { CompileNodeGraphRequest } from '../messages' ;
11- import { reporter } from '../telemetry/telemetry' ;
12- import * as viz from 'viz.js' ;
139
1410const PuppetNodeGraphToTheSideCommandId : string = 'extension.puppetShowNodeGraphToSide' ;
1511
16- class NodeGraphContentProvider implements vscode . TextDocumentContentProvider {
17- private onDidChangeEvent = new vscode . EventEmitter < vscode . Uri > ( ) ;
18- private waiting : boolean = false ;
12+ class NodeGraphWebViewProvider implements vscode . Disposable {
1913 private connectionManager : IConnectionManager = undefined ;
20- private shownLanguageServerNotAvailable = false ;
14+ private docUri : vscode . Uri = undefined ;
15+ private webPanel : vscode . WebviewPanel = undefined ;
16+ private parentFeature : NodeGraphFeature = undefined ;
2117
22- constructor ( connectionManager :IConnectionManager ) {
18+ constructor (
19+ documentUri :vscode . Uri ,
20+ connectionManager :IConnectionManager ,
21+ parent : NodeGraphFeature )
22+ {
23+ this . docUri = documentUri ;
2324 this . connectionManager = connectionManager ;
25+ this . parentFeature = parent ;
2426 }
2527
26- public provideTextDocumentContent ( uri : vscode . Uri ) : Thenable < string > {
27- const sourceUri = vscode . Uri . parse ( uri . query ) ;
28-
29- return vscode . workspace . openTextDocument ( sourceUri ) . then ( document => {
30- const initialData = {
31- previewUri : uri . toString ( ) ,
32- source : sourceUri . toString ( )
33- } ;
34-
35- if ( ( this . connectionManager . status !== ConnectionStatus . RunningLoaded ) && ( this . connectionManager . status !== ConnectionStatus . RunningLoading ) ) {
36- if ( this . shownLanguageServerNotAvailable ) {
37- vscode . window . showInformationMessage ( "The Puppet Node Graph Preview is not available as the Editor Service is not ready" ) ;
38- this . shownLanguageServerNotAvailable = true ;
39- }
40- return "The Puppet Node Graph Preview is not available as the Editor Service is not ready" ;
41- }
28+ public isSameUri ( value : vscode . Uri ) : boolean {
29+ return value . toString ( ) === this . docUri . toString ( ) ;
30+ }
4231
43- // Use the language server to render the document
44- return this . connectionManager . languageClient
45- . sendRequest ( CompileNodeGraphRequest . type , sourceUri )
46- . then (
47- ( compileResult ) => {
48-
49- var svgContent = '' ;
50- if ( compileResult . dotContent !== null ) {
51- var styling = `
52- bgcolor = "transparent"
53- color = "white"
54- rankdir = "TB"
55- node [ shape="box" penwidth="2" color="#e0e0e0" style="rounded,filled" fontname="Courier New" fillcolor=black, fontcolor="white"]
56- edge [ style="bold" color="#f0f0f0" penwith="2" ]
57-
58- label = ""` ;
59-
60- var graphContent = compileResult . dotContent ;
61- if ( graphContent === undefined ) { graphContent = '' ; }
62- // vis.jz sees backslashes as escape characters, however they are not in the DOT language. Instead
63- // we should escape any backslash coming from a valid DOT file in preparation to be rendered
64- graphContent = graphContent . replace ( / \\ / g, "\\\\" ) ;
65- graphContent = graphContent . replace ( `label = "vscode"` , styling ) ;
66-
67- svgContent = viz ( graphContent , "svg" ) ;
68- }
69-
70- var errorContent = `<div style='font-size: 1.5em'>${ compileResult . error } </div>` ;
71- if ( ( compileResult . error === undefined ) || ( compileResult . error === null ) ) { errorContent = '' ; }
72-
73- if ( reporter ) {
74- reporter . sendTelemetryEvent ( PuppetNodeGraphToTheSideCommandId ) ;
75- }
76-
77- return `
78- ${ errorContent }
79- <div id="graphviz_svg_div">
80- ${ svgContent }
81- </div>` ;
82- } ) ;
32+ public show ( ) : void {
33+ if ( this . webPanel !== undefined ) { return ; }
34+ this . webPanel = vscode . window . createWebviewPanel (
35+ 'nodeGraph' , // Identifies the type of the webview. Used internally
36+ `Node Graph '${ path . basename ( this . docUri . fsPath ) } '` , // Title of the panel displayed to the user
37+ vscode . ViewColumn . Beside , // Editor column to show the new webview panel in.
38+ { }
39+ ) ;
40+
41+ this . webPanel . onDidDispose ( ( ) => {
42+ this . parentFeature . onProviderWebPanelDisposed ( this ) ;
8343 } ) ;
44+
45+ this . update ( ) ;
8446 }
8547
86- get onDidChange ( ) : vscode . Event < vscode . Uri > {
87- return this . onDidChangeEvent . event ;
48+ public update ( ) : void {
49+ this . webPanel . webview . html = this . getHTMLContent ( ) ;
8850 }
8951
90- public update ( uri : vscode . Uri ) {
91- if ( ! this . waiting ) {
92- this . waiting = true ;
93- setTimeout ( ( ) => {
94- this . waiting = false ;
95- this . onDidChangeEvent . fire ( uri ) ;
96- } , 300 ) ;
97- }
52+ public getHTMLContent ( ) : string {
53+ return '<html><body>Node Graph Preview - ' + ( new Date ( ) . toUTCString ( ) ) + '</body></html>' ;
54+ }
55+
56+ public dispose ( ) : any {
57+ this . webPanel . dispose ( ) ;
58+ return undefined ;
9859 }
9960}
10061
10162export class NodeGraphFeature implements IFeature {
102- private provider : NodeGraphContentProvider ;
63+ private acceptedLangId : string = undefined ;
64+ private providers : NodeGraphWebViewProvider [ ] = undefined ;
65+ private connectionManager : IConnectionManager = undefined ;
66+
67+ public onProviderWebPanelDisposed ( provider : NodeGraphWebViewProvider ) : void {
68+ // If the panel gets disposed then the user closed the tab.
69+ // Remove the provider object and dispose of it.
70+ const index = this . providers . indexOf ( provider , 0 ) ;
71+ if ( index > - 1 ) {
72+ this . providers . splice ( index , 1 ) ;
73+ provider . dispose ( ) ;
74+ }
75+ }
10376
10477 constructor (
10578 langID : string ,
10679 connectionManager : IConnectionManager ,
10780 logger : ILogger ,
10881 context : vscode . ExtensionContext
10982 ) {
83+ this . acceptedLangId = langID ;
84+ this . providers = [ ] ;
85+ this . connectionManager = connectionManager ;
86+
11087 context . subscriptions . push ( vscode . commands . registerCommand ( PuppetNodeGraphToTheSideCommandId ,
111- uri => this . showNodeGraph ( uri , true ) )
112- ) ;
88+ ( ) => {
89+ if ( ! vscode . window . activeTextEditor ) { return ; }
90+ if ( vscode . window . activeTextEditor . document . languageId !== this . acceptedLangId ) { return ; }
91+
92+ let resource = vscode . window . activeTextEditor . document . uri ;
93+ let provider = new NodeGraphWebViewProvider ( resource , this . connectionManager , this ) ;
94+ this . providers . push ( provider ) ;
95+ provider . show ( ) ;
96+ }
97+ ) ) ;
11398 logger . debug ( "Registered " + PuppetNodeGraphToTheSideCommandId + " command" ) ;
11499
115- this . provider = new NodeGraphContentProvider ( connectionManager ) ;
116- vscode . workspace . registerTextDocumentContentProvider ( langID , this . provider ) ;
117- logger . debug ( "Registered Node Graph Text Document provider" ) ;
118-
100+ // Subscribe to save events and fire updates
119101 context . subscriptions . push ( vscode . workspace . onDidSaveTextDocument ( document => {
120- if ( this . isNodeGraphFile ( document ) ) {
121- const uri = this . getNodeGraphUri ( document . uri ) ;
122- this . provider . update ( uri ) ;
123- }
102+ this . providers . forEach ( ( item ) => {
103+ if ( item . isSameUri ( document . uri ) ) { item . update ( ) ; }
104+ } ) ;
124105 } ) ) ;
125106 logger . debug ( "Registered onDidSaveTextDocument for node graph event handler" ) ;
126107 }
127108
128- private isNodeGraphFile ( document : vscode . TextDocument ) {
129- return document . languageId === 'puppet'
130- && document . uri . scheme !== 'puppet' ; // prevent processing of own documents
131- }
132-
133- private getNodeGraphUri ( uri : vscode . Uri ) {
134- if ( uri . scheme === 'puppet' ) {
135- return uri ;
136- }
137-
138- return uri . with ( {
139- scheme : 'puppet' ,
140- path : uri . fsPath + '.rendered' ,
141- query : uri . toString ( )
142- } ) ;
143- }
144-
145- private getViewColumn ( sideBySide : boolean ) : vscode . ViewColumn | undefined {
146- const active = vscode . window . activeTextEditor ;
147- if ( ! active ) {
148- return vscode . ViewColumn . One ;
149- }
150-
151- if ( ! sideBySide ) {
152- return active . viewColumn ;
153- }
154-
155- switch ( active . viewColumn ) {
156- case vscode . ViewColumn . One :
157- return vscode . ViewColumn . Two ;
158- case vscode . ViewColumn . Two :
159- return vscode . ViewColumn . Three ;
160- }
161-
162- return active . viewColumn ;
163- }
164-
165- private showNodeGraph ( uri ?: vscode . Uri , sideBySide : boolean = false ) {
166- let resource = uri ;
167- if ( ! ( resource instanceof vscode . Uri ) ) {
168- if ( vscode . window . activeTextEditor ) {
169- // we are relaxed and don't check for puppet files
170- // TODO: Should we? Probably
171- resource = vscode . window . activeTextEditor . document . uri ;
172- }
173- }
174-
175- const thenable = vscode . commands . executeCommand ( 'vscode.previewHtml' ,
176- this . getNodeGraphUri ( resource ) ,
177- this . getViewColumn ( sideBySide ) ,
178- `Node Graph '${ path . basename ( resource . fsPath ) } '` ) ;
179-
180- return thenable ;
109+ public dispose ( ) : any {
110+ // Dispose of any providers and then clear any references to them
111+ this . providers . forEach ( ( item ) => { item . dispose ( ) ; } ) ;
112+ this . providers = [ ] ;
113+ return undefined ;
181114 }
182-
183- public dispose ( ) : any { return undefined ; }
184115}
0 commit comments