@@ -7,7 +7,6 @@ import * as vscode from 'vscode';
77//import * as nls from 'vscode-nls';
88import * as util from 'util' ;
99
10- const trackers = new Set < string > ( ) ;
1110
1211const PATTERN = 'listening on.* (https?://\\S+|[0-9]+)' ; // matches "listening on port 3000" or "Now listening on: https://localhost:5001"
1312const URI_FORMAT = 'http://localhost:%s' ;
@@ -20,78 +19,145 @@ interface ServerReadyAction {
2019 webRoot ?: string ;
2120}
2221
23- export function activate ( context : vscode . ExtensionContext ) {
22+ class ServerReadyDetector extends vscode . Disposable {
23+ static detectors = new Map < vscode . DebugSession , ServerReadyDetector > ( ) ;
2424
25- context . subscriptions . push ( vscode . debug . registerDebugConfigurationProvider ( '*' , {
26- resolveDebugConfiguration ( _folder : vscode . WorkspaceFolder | undefined , debugConfiguration : vscode . DebugConfiguration ) {
27- const args : ServerReadyAction = debugConfiguration . serverReadyAction ;
28- if ( debugConfiguration . type && args ) {
29- startTrackerForType ( context , debugConfiguration . type ) ;
25+ private hasFired = false ;
26+ private regexp : RegExp ;
27+ private disposables : vscode . Disposable [ ] = [ ] ;
28+
29+ static start ( session : vscode . DebugSession ) : ServerReadyDetector | undefined {
30+ if ( session . configuration . serverReadyAction ) {
31+ let detector = ServerReadyDetector . detectors . get ( session ) ;
32+ if ( ! detector ) {
33+ detector = new ServerReadyDetector ( session ) ;
34+ ServerReadyDetector . detectors . set ( session , detector ) ;
3035 }
31- return debugConfiguration ;
36+ return detector ;
3237 }
33- } ) ) ;
34- }
38+ return undefined ;
39+ }
3540
36- function startTrackerForType ( context : vscode . ExtensionContext , type : string ) {
41+ static stop ( session : vscode . DebugSession ) : void {
42+ let detector = ServerReadyDetector . detectors . get ( session ) ;
43+ if ( detector ) {
44+ ServerReadyDetector . detectors . delete ( session ) ;
45+ detector . dispose ( ) ;
46+ }
47+ }
48+
49+ private constructor ( private session : vscode . DebugSession ) {
50+ super ( ( ) => this . internalDispose ( ) ) ;
51+
52+ this . regexp = new RegExp ( session . configuration . serverReadyAction . pattern || PATTERN ) ;
53+ }
54+
55+ private internalDispose ( ) {
56+ this . disposables . forEach ( d => d . dispose ( ) ) ;
57+ this . disposables = [ ] ;
58+ }
3759
38- if ( ! trackers . has ( type ) ) {
39- trackers . add ( type ) ;
60+ trackTerminals ( ) {
61+ // TODO: listen only on the Terminal associated with the debug session
62+ vscode . window . terminals . forEach ( terminal => {
63+ this . disposables . push ( terminal . onDidWriteData ( s => {
64+ this . detectPattern ( s ) ;
65+ } ) ) ;
66+ } ) ;
67+ }
4068
41- // scan debug console output for a PORT message
42- context . subscriptions . push ( vscode . debug . registerDebugAdapterTrackerFactory ( type , {
43- createDebugAdapterTracker ( session : vscode . DebugSession ) {
69+ detectPattern ( s : string ) : void {
70+
71+ if ( ! this . hasFired ) {
72+ const result = this . regexp . exec ( s ) ;
73+ if ( result && result . length === 2 ) {
74+ this . openExternalWithString ( this . session , result [ 1 ] ) ;
75+ this . hasFired = true ;
76+ this . internalDispose ( ) ;
77+ }
78+ }
79+ }
80+
81+ private openExternalWithString ( session : vscode . DebugSession , portOrUriString : string ) {
82+
83+ if ( portOrUriString ) {
84+ if ( / ^ [ 0 - 9 ] + $ / . test ( portOrUriString ) ) {
4485 const args : ServerReadyAction = session . configuration . serverReadyAction ;
45- if ( args ) {
46- const regexp = new RegExp ( args . pattern || PATTERN ) ;
47- let hasFired = false ;
48- return {
49- onDidSendMessage : m => {
50- if ( ! hasFired && m . type === 'event' && m . event === 'output' && m . body . output ) {
51- const result = regexp . exec ( m . body . output ) ;
52- if ( result && result . length === 2 ) {
53- openExternalWithString ( session , result [ 1 ] ) ;
54- hasFired = true ;
55- }
56- }
57- }
58- } ;
59- }
60- return undefined ;
86+ portOrUriString = util . format ( args . uriFormat || URI_FORMAT , portOrUriString ) ;
6187 }
62- } ) ) ;
88+ this . openExternalWithUri ( session , portOrUriString ) ;
89+ }
6390 }
64- }
6591
66- function openExternalWithString ( session : vscode . DebugSession , portOrUriString : string ) {
92+ private openExternalWithUri ( session : vscode . DebugSession , uri : string ) {
6793
68- if ( portOrUriString ) {
69- if ( / ^ [ 0 - 9 ] + $ / . test ( portOrUriString ) ) {
70- const args : ServerReadyAction = session . configuration . serverReadyAction ;
71- portOrUriString = util . format ( args . uriFormat || URI_FORMAT , portOrUriString ) ;
94+ const args : ServerReadyAction = session . configuration . serverReadyAction ;
95+ switch ( args . action || 'openExternally' ) {
96+ case 'openExternally' :
97+ vscode . env . openExternal ( vscode . Uri . parse ( uri ) ) ;
98+ break ;
99+ case 'debugWithChrome' :
100+ vscode . debug . startDebugging ( session . workspaceFolder , {
101+ type : 'chrome' ,
102+ name : 'Chrome Debug' ,
103+ request : 'launch' ,
104+ url : uri ,
105+ webRoot : args . webRoot || WEB_ROOT
106+ } ) ;
107+ break ;
108+ default :
109+ // not supported
110+ break ;
72111 }
73- openExternalWithUri ( session , portOrUriString ) ;
74112 }
75113}
76114
77- function openExternalWithUri ( session : vscode . DebugSession , uri : string ) {
78-
79- const args : ServerReadyAction = session . configuration . serverReadyAction ;
80- switch ( args . action || 'openExternally' ) {
81- case 'openExternally' :
82- vscode . env . openExternal ( vscode . Uri . parse ( uri ) ) ;
83- break ;
84- case 'debugWithChrome' :
85- vscode . debug . startDebugging ( session . workspaceFolder , {
86- type : 'chrome' ,
87- name : 'Chrome Debug' ,
88- request : 'launch' ,
89- url : uri ,
90- webRoot : args . webRoot || WEB_ROOT
91- } ) ;
92- break ;
93- default :
94- // not supported
95- break ;
96- }
115+ export function activate ( context : vscode . ExtensionContext ) {
116+
117+ context . subscriptions . push ( vscode . debug . onDidChangeActiveDebugSession ( session => {
118+ if ( session && session . configuration . serverReadyAction ) {
119+ const detector = ServerReadyDetector . start ( session ) ;
120+ if ( detector ) {
121+ detector . trackTerminals ( ) ;
122+ }
123+ }
124+ } ) ) ;
125+
126+ context . subscriptions . push ( vscode . debug . onDidTerminateDebugSession ( session => {
127+ ServerReadyDetector . stop ( session ) ;
128+ } ) ) ;
129+
130+ const trackers = new Set < string > ( ) ;
131+
132+ context . subscriptions . push ( vscode . debug . registerDebugConfigurationProvider ( '*' , {
133+ resolveDebugConfiguration ( _folder : vscode . WorkspaceFolder | undefined , debugConfiguration : vscode . DebugConfiguration ) {
134+ if ( debugConfiguration . type && debugConfiguration . serverReadyAction ) {
135+ if ( ! trackers . has ( debugConfiguration . type ) ) {
136+ trackers . add ( debugConfiguration . type ) ;
137+ startTrackerForType ( context , debugConfiguration . type ) ;
138+ }
139+ }
140+ return debugConfiguration ;
141+ }
142+ } ) ) ;
143+ }
144+
145+ function startTrackerForType ( context : vscode . ExtensionContext , type : string ) {
146+
147+ // scan debug console output for a PORT message
148+ context . subscriptions . push ( vscode . debug . registerDebugAdapterTrackerFactory ( type , {
149+ createDebugAdapterTracker ( session : vscode . DebugSession ) {
150+ const detector = ServerReadyDetector . start ( session ) ;
151+ if ( detector ) {
152+ return {
153+ onDidSendMessage : m => {
154+ if ( m . type === 'event' && m . event === 'output' && m . body . output ) {
155+ detector . detectPattern ( m . body . output ) ;
156+ }
157+ }
158+ } ;
159+ }
160+ return undefined ;
161+ }
162+ } ) ) ;
97163}
0 commit comments