11import * as path from "path" ;
22
3+ import { VSBuffer } from "vs/base/common/buffer" ;
34import { Emitter , Event } from "vs/base/common/event" ;
5+ import { IDisposable } from "vs/base/common/lifecycle" ;
6+ import { Schemas } from "vs/base/common/network" ;
47import { OS } from "vs/base/common/platform" ;
5- import { URI } from "vs/base/common/uri" ;
8+ import { URI , UriComponents } from "vs/base/common/uri" ;
69import { IServerChannel } from "vs/base/parts/ipc/common/ipc" ;
710import { IDiagnosticInfo } from "vs/platform/diagnostics/common/diagnosticsService" ;
811import { IEnvironmentService } from "vs/platform/environment/common/environment" ;
912import { FileDeleteOptions , FileOverwriteOptions , FileType , IStat , IWatchOptions , FileOpenOptions } from "vs/platform/files/common/files" ;
13+ import { ILogService } from "vs/platform/log/common/log" ;
1014import { IRemoteAgentEnvironment } from "vs/platform/remote/common/remoteAgentEnvironment" ;
15+ import { DiskFileSystemProvider } from "vs/workbench/services/files/node/diskFileSystemProvider" ;
16+
17+ /**
18+ * Extend the file provider to allow unwatching.
19+ */
20+ class Watcher extends DiskFileSystemProvider {
21+ public readonly watches = new Map < number , IDisposable > ( ) ;
22+
23+ public dispose ( ) : void {
24+ this . watches . forEach ( ( w ) => w . dispose ( ) ) ;
25+ this . watches . clear ( ) ;
26+ super . dispose ( ) ;
27+ }
28+
29+ public _watch ( req : number , resource : URI , opts : IWatchOptions ) : void {
30+ this . watches . set ( req , this . watch ( resource , opts ) ) ;
31+ }
32+
33+ public unwatch ( req : number ) : void {
34+ this . watches . get ( req ) ! . dispose ( ) ;
35+ this . watches . delete ( req ) ;
36+ }
37+ }
1138
1239/**
1340 * See: src/vs/platform/remote/common/remoteAgentFileSystemChannel.ts.
1441 */
1542export class FileProviderChannel implements IServerChannel {
16- public listen ( _context : any , event : string ) : Event < any > {
43+ private readonly provider : DiskFileSystemProvider ;
44+ private readonly watchers = new Map < string , Watcher > ( ) ;
45+
46+ public constructor ( private readonly logService : ILogService ) {
47+ this . provider = new DiskFileSystemProvider ( this . logService ) ;
48+ }
49+
50+ public listen ( _ : unknown , event : string , args ?: any ) : Event < any > {
1751 switch ( event ) {
52+ // This is where the actual file changes are sent. The watch method just
53+ // adds things that will fire here. That means we have to split up
54+ // watchers based on the session otherwise sessions would get events for
55+ // other sessions. There is also no point in having the watcher unless
56+ // something is listening. I'm not sure there is a different way to
57+ // dispose, anyway.
1858 case "filechange" :
19- // TODO: not sure what to do here yet
20- return new Emitter ( ) . event ;
59+ const session = args [ 0 ] ;
60+ const emitter = new Emitter ( {
61+ onFirstListenerAdd : ( ) => {
62+ const provider = new Watcher ( this . logService ) ;
63+ this . watchers . set ( session , provider ) ;
64+ provider . onDidChangeFile ( ( events ) => {
65+ emitter . fire ( events . map ( ( event ) => ( {
66+ ...event ,
67+ resource : event . resource . with ( { scheme : Schemas . vscodeRemote } ) ,
68+ } ) ) ) ;
69+ } ) ;
70+ provider . onDidErrorOccur ( ( event ) => emitter . fire ( event ) ) ;
71+ } ,
72+ onLastListenerRemove : ( ) => {
73+ this . watchers . get ( session ) ! . dispose ( ) ;
74+ this . watchers . delete ( session ) ;
75+ } ,
76+ } ) ;
77+
78+ return emitter . event ;
2179 }
2280
2381 throw new Error ( `Invalid listen "${ event } "` ) ;
2482 }
2583
2684 public call ( _ : unknown , command : string , args ?: any ) : Promise < any > {
27- console . log ( "got call" , command , args ) ;
2885 switch ( command ) {
2986 case "stat" : return this . stat ( args [ 0 ] ) ;
3087 case "open" : return this . open ( args [ 0 ] , args [ 1 ] ) ;
3188 case "close" : return this . close ( args [ 0 ] ) ;
32- case "read" : return this . read ( args [ 0 ] , args [ 1 ] , args [ 2 ] , args [ 3 ] , args [ 4 ] ) ;
89+ case "read" : return this . read ( args [ 0 ] , args [ 1 ] , args [ 2 ] ) ;
3390 case "write" : return this . write ( args [ 0 ] , args [ 1 ] , args [ 2 ] , args [ 3 ] , args [ 4 ] ) ;
3491 case "delete" : return this . delete ( args [ 0 ] , args [ 1 ] ) ;
3592 case "mkdir" : return this . mkdir ( args [ 0 ] ) ;
3693 case "readdir" : return this . readdir ( args [ 0 ] ) ;
3794 case "rename" : return this . rename ( args [ 0 ] , args [ 1 ] , args [ 2 ] ) ;
3895 case "copy" : return this . copy ( args [ 0 ] , args [ 1 ] , args [ 2 ] ) ;
39- case "watch" : return this . watch ( args [ 0 ] , args [ 1 ] ) ;
40- case "unwatch" : return this . unwatch ( args [ 0 ] ) , args [ 1 ] ;
96+ case "watch" : return this . watch ( args [ 0 ] , args [ 1 ] , args [ 2 ] , args [ 3 ] ) ;
97+ case "unwatch" : return this . unwatch ( args [ 0 ] , args [ 1 ] ) ;
4198 }
4299
43100 throw new Error ( `Invalid call "${ command } "` ) ;
44101 }
45102
46- private async stat ( resource : URI ) : Promise < IStat > {
47- throw new Error ( "not implemented" ) ;
103+ private async stat ( resource : UriComponents ) : Promise < IStat > {
104+ return this . provider . stat ( URI . from ( resource ) ) ;
48105 }
49106
50- private async open ( resource : URI , opts : FileOpenOptions ) : Promise < number > {
51- throw new Error ( "not implemented" ) ;
107+ private async open ( resource : UriComponents , opts : FileOpenOptions ) : Promise < number > {
108+ return this . provider . open ( URI . from ( resource ) , opts ) ;
52109 }
53110
54111 private async close ( fd : number ) : Promise < void > {
55- throw new Error ( "not implemented" ) ;
112+ return this . provider . close ( fd ) ;
56113 }
57114
58- private async read ( fd : number , pos : number , data : Uint8Array , offset : number , length : number ) : Promise < number > {
59- throw new Error ( "not implemented" ) ;
115+ private async read ( fd : number , pos : number , length : number ) : Promise < [ VSBuffer , number ] > {
116+ const buffer = VSBuffer . alloc ( length ) ;
117+ const bytesRead = await this . provider . read ( fd , pos , buffer . buffer , 0 , length ) ;
118+ return [ buffer , bytesRead ] ;
60119 }
61120
62- private async write ( fd : number , pos : number , data : Uint8Array , offset : number , length : number ) : Promise < number > {
63- throw new Error ( "not implemented" ) ;
121+ private write ( fd : number , pos : number , buffer : VSBuffer , offset : number , length : number ) : Promise < number > {
122+ return this . provider . write ( fd , pos , buffer . buffer , offset , length ) ;
64123 }
65124
66- private async delete ( resource : URI , opts : FileDeleteOptions ) : Promise < void > {
67- throw new Error ( "not implemented" ) ;
125+ private async delete ( resource : UriComponents , opts : FileDeleteOptions ) : Promise < void > {
126+ return this . provider . delete ( URI . from ( resource ) , opts ) ;
68127 }
69128
70- private async mkdir ( resource : URI ) : Promise < void > {
71- throw new Error ( "not implemented" ) ;
129+ private async mkdir ( resource : UriComponents ) : Promise < void > {
130+ return this . provider . mkdir ( URI . from ( resource ) ) ;
72131 }
73132
74- private async readdir ( resource : URI ) : Promise < [ string , FileType ] [ ] > {
75- throw new Error ( "not implemented" ) ;
133+ private async readdir ( resource : UriComponents ) : Promise < [ string , FileType ] [ ] > {
134+ return this . provider . readdir ( URI . from ( resource ) ) ;
76135 }
77136
78- private async rename ( resource : URI , target : URI , opts : FileOverwriteOptions ) : Promise < void > {
79- throw new Error ( "not implemented" ) ;
137+ private async rename ( resource : UriComponents , target : UriComponents , opts : FileOverwriteOptions ) : Promise < void > {
138+ return this . provider . rename ( URI . from ( resource ) , URI . from ( target ) , opts ) ;
80139 }
81140
82- private copy ( resource : URI , target : URI , opts : FileOverwriteOptions ) : Promise < void > {
83- throw new Error ( "not implemented" ) ;
141+ private copy ( resource : UriComponents , target : UriComponents , opts : FileOverwriteOptions ) : Promise < void > {
142+ return this . provider . copy ( URI . from ( resource ) , URI . from ( target ) , opts ) ;
84143 }
85144
86- private watch ( resource : URI , opts : IWatchOptions ) : Promise < void > {
87- throw new Error ( "not implemented" ) ;
145+ private async watch ( session : string , req : number , resource : UriComponents , opts : IWatchOptions ) : Promise < void > {
146+ this . watchers . get ( session ) ! . _watch ( req , URI . from ( resource ) , opts ) ;
88147 }
89148
90- private unwatch ( resource : URI ) : void {
91- throw new Error ( "not implemented" ) ;
149+ private async unwatch ( session : string , req : number ) : Promise < void > {
150+ this . watchers . get ( session ) ! . unwatch ( req ) ;
92151 }
93152}
94153
@@ -112,16 +171,18 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
112171 }
113172
114173 private async getEnvironmentData ( ) : Promise < IRemoteAgentEnvironment > {
174+ // TODO: this `with` stuff feels a bit jank.
175+ // Maybe it should already come in like this instead.
115176 return {
116177 pid : process . pid ,
117- appRoot : URI . file ( this . environment . appRoot ) ,
118- appSettingsHome : this . environment . appSettingsHome ,
119- settingsPath : this . environment . machineSettingsHome ,
120- logsPath : URI . file ( this . environment . logsPath ) ,
121- extensionsPath : URI . file ( this . environment . extensionsPath ) ,
122- extensionHostLogsPath : URI . file ( path . join ( this . environment . logsPath , "extension-host" ) ) , // TODO
123- globalStorageHome : URI . file ( this . environment . globalStorageHome ) ,
124- userHome : URI . file ( this . environment . userHome ) ,
178+ appRoot : URI . file ( this . environment . appRoot ) . with ( { scheme : Schemas . vscodeRemote } ) ,
179+ appSettingsHome : this . environment . appSettingsHome . with ( { scheme : Schemas . vscodeRemote } ) ,
180+ settingsPath : this . environment . machineSettingsHome . with ( { scheme : Schemas . vscodeRemote } ) ,
181+ logsPath : URI . file ( this . environment . logsPath ) . with ( { scheme : Schemas . vscodeRemote } ) ,
182+ extensionsPath : URI . file ( this . environment . extensionsPath ) . with ( { scheme : Schemas . vscodeRemote } ) ,
183+ extensionHostLogsPath : URI . file ( path . join ( this . environment . logsPath , "extension-host" ) ) . with ( { scheme : Schemas . vscodeRemote } ) , // TODO
184+ globalStorageHome : URI . file ( this . environment . globalStorageHome ) . with ( { scheme : Schemas . vscodeRemote } ) ,
185+ userHome : URI . file ( this . environment . userHome ) . with ( { scheme : Schemas . vscodeRemote } ) ,
125186 extensions : [ ] , // TODO
126187 os : OS ,
127188 } ;
0 commit comments