1
1
import * as path from "path" ;
2
- import { VSBuffer } from "vs/base/common/buffer" ;
2
+ import { VSBuffer , VSBufferReadableStream } from "vs/base/common/buffer" ;
3
3
import { Emitter , Event } from "vs/base/common/event" ;
4
4
import { IDisposable } from "vs/base/common/lifecycle" ;
5
5
import { OS } from "vs/base/common/platform" ;
6
+ import { ReadableStreamEventPayload } from "vs/base/common/stream" ;
6
7
import { URI , UriComponents } from "vs/base/common/uri" ;
7
8
import { transformOutgoingURIs } from "vs/base/common/uriIpc" ;
8
9
import { IServerChannel } from "vs/base/parts/ipc/common/ipc" ;
9
10
import { IDiagnosticInfo } from "vs/platform/diagnostics/common/diagnostics" ;
10
11
import { IEnvironmentService } from "vs/platform/environment/common/environment" ;
11
12
import { ExtensionIdentifier , IExtensionDescription } from "vs/platform/extensions/common/extensions" ;
12
- import { FileDeleteOptions , FileOpenOptions , FileOverwriteOptions , FileType , IStat , IWatchOptions } from "vs/platform/files/common/files" ;
13
+ import { FileDeleteOptions , FileOpenOptions , FileOverwriteOptions , FileReadStreamOptions , FileType , FileWriteOptions , IStat , IWatchOptions } from "vs/platform/files/common/files" ;
14
+ import { createReadStream } from "vs/platform/files/common/io" ;
13
15
import { DiskFileSystemProvider } from "vs/platform/files/node/diskFileSystemProvider" ;
14
16
import { ILogService } from "vs/platform/log/common/log" ;
15
17
import product from "vs/platform/product/common/product" ;
16
- import { IRemoteAgentEnvironment } from "vs/platform/remote/common/remoteAgentEnvironment" ;
18
+ import { IRemoteAgentEnvironment , RemoteAgentConnectionContext } from "vs/platform/remote/common/remoteAgentEnvironment" ;
17
19
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry" ;
18
20
import { INodeProxyService } from "vs/server/src/common/nodeProxy" ;
19
21
import { getTranslations } from "vs/server/src/node/nls" ;
20
22
import { getUriTransformer , localRequire } from "vs/server/src/node/util" ;
23
+ import { IFileChangeDto } from "vs/workbench/api/common/extHost.protocol" ;
21
24
import { ExtensionScanner , ExtensionScannerInput } from "vs/workbench/services/extensions/node/extensionPoints" ;
22
25
23
26
/**
@@ -42,7 +45,7 @@ class Watcher extends DiskFileSystemProvider {
42
45
}
43
46
}
44
47
45
- export class FileProviderChannel implements IServerChannel , IDisposable {
48
+ export class FileProviderChannel implements IServerChannel < RemoteAgentConnectionContext > , IDisposable {
46
49
private readonly provider : DiskFileSystemProvider ;
47
50
private readonly watchers = new Map < string , Watcher > ( ) ;
48
51
@@ -53,48 +56,67 @@ export class FileProviderChannel implements IServerChannel, IDisposable {
53
56
this . provider = new DiskFileSystemProvider ( this . logService ) ;
54
57
}
55
58
56
- public listen ( context : any , event : string , args ?: any ) : Event < any > {
59
+ public listen ( context : RemoteAgentConnectionContext , event : string , args ?: any ) : Event < any > {
57
60
switch ( event ) {
58
- // This is where the actual file changes are sent. The watch method just
59
- // adds things that will fire here. That means we have to split up
60
- // watchers based on the session otherwise sessions would get events for
61
- // other sessions. There is also no point in having the watcher unless
62
- // something is listening. I'm not sure there is a different way to
63
- // dispose, anyway.
64
- case "filechange" :
65
- const session = args [ 0 ] ;
66
- const emitter = new Emitter ( {
67
- onFirstListenerAdd : ( ) => {
68
- const provider = new Watcher ( this . logService ) ;
69
- this . watchers . set ( session , provider ) ;
70
- const transformer = getUriTransformer ( context . remoteAuthority ) ;
71
- provider . onDidChangeFile ( ( events ) => {
72
- emitter . fire ( events . map ( ( event ) => ( {
73
- ...event ,
74
- resource : transformer . transformOutgoing ( event . resource ) ,
75
- } ) ) ) ;
76
- } ) ;
77
- provider . onDidErrorOccur ( ( event ) => emitter . fire ( event ) ) ;
78
- } ,
79
- onLastListenerRemove : ( ) => {
80
- this . watchers . get ( session ) ! . dispose ( ) ;
81
- this . watchers . delete ( session ) ;
82
- } ,
83
- } ) ;
84
-
85
- return emitter . event ;
61
+ case "filechange" : return this . filechange ( context , args [ 0 ] ) ;
62
+ case "readFileStream" : return this . readFileStream ( args [ 0 ] , args [ 1 ] ) ;
86
63
}
87
64
88
65
throw new Error ( `Invalid listen "${ event } "` ) ;
89
66
}
90
67
68
+ private filechange ( context : RemoteAgentConnectionContext , session : string ) : Event < IFileChangeDto [ ] > {
69
+ const emitter = new Emitter < IFileChangeDto [ ] > ( {
70
+ onFirstListenerAdd : ( ) => {
71
+ const provider = new Watcher ( this . logService ) ;
72
+ this . watchers . set ( session , provider ) ;
73
+ const transformer = getUriTransformer ( context . remoteAuthority ) ;
74
+ provider . onDidChangeFile ( ( events ) => {
75
+ emitter . fire ( events . map ( ( event ) => ( {
76
+ ...event ,
77
+ resource : transformer . transformOutgoing ( event . resource ) ,
78
+ } ) ) ) ;
79
+ } ) ;
80
+ provider . onDidErrorOccur ( ( event ) => this . logService . error ( event ) ) ;
81
+ } ,
82
+ onLastListenerRemove : ( ) => {
83
+ this . watchers . get ( session ) ! . dispose ( ) ;
84
+ this . watchers . delete ( session ) ;
85
+ } ,
86
+ } ) ;
87
+
88
+ return emitter . event ;
89
+ }
90
+
91
+ private readFileStream ( resource : UriComponents , opts : FileReadStreamOptions ) : Event < ReadableStreamEventPayload < VSBuffer > > {
92
+ let fileStream : VSBufferReadableStream | undefined ;
93
+ const emitter = new Emitter < ReadableStreamEventPayload < VSBuffer > > ( {
94
+ onFirstListenerAdd : ( ) => {
95
+ if ( ! fileStream ) {
96
+ fileStream = createReadStream ( this . provider , this . transform ( resource ) , {
97
+ ...opts ,
98
+ bufferSize : 64 * 1024 , // From DiskFileSystemProvider
99
+ } ) ;
100
+ fileStream . on ( "data" , ( data ) => emitter . fire ( data ) ) ;
101
+ fileStream . on ( "error" , ( error ) => emitter . fire ( error ) ) ;
102
+ fileStream . on ( "end" , ( ) => emitter . fire ( "end" ) ) ;
103
+ }
104
+ } ,
105
+ onLastListenerRemove : ( ) => fileStream && fileStream . destroy ( ) ,
106
+ } ) ;
107
+
108
+ return emitter . event ;
109
+ }
110
+
91
111
public call ( _ : unknown , command : string , args ?: any ) : Promise < any > {
92
112
switch ( command ) {
93
113
case "stat" : return this . stat ( args [ 0 ] ) ;
94
114
case "open" : return this . open ( args [ 0 ] , args [ 1 ] ) ;
95
115
case "close" : return this . close ( args [ 0 ] ) ;
96
116
case "read" : return this . read ( args [ 0 ] , args [ 1 ] , args [ 2 ] ) ;
117
+ case "readFile" : return this . readFile ( args [ 0 ] ) ;
97
118
case "write" : return this . write ( args [ 0 ] , args [ 1 ] , args [ 2 ] , args [ 3 ] , args [ 4 ] ) ;
119
+ case "writeFile" : return this . writeFile ( args [ 0 ] , args [ 1 ] , args [ 2 ] ) ;
98
120
case "delete" : return this . delete ( args [ 0 ] , args [ 1 ] ) ;
99
121
case "mkdir" : return this . mkdir ( args [ 0 ] ) ;
100
122
case "readdir" : return this . readdir ( args [ 0 ] ) ;
@@ -130,10 +152,18 @@ export class FileProviderChannel implements IServerChannel, IDisposable {
130
152
return [ buffer , bytesRead ] ;
131
153
}
132
154
155
+ private async readFile ( resource : UriComponents ) : Promise < VSBuffer > {
156
+ return VSBuffer . wrap ( await this . provider . readFile ( this . transform ( resource ) ) ) ;
157
+ }
158
+
133
159
private write ( fd : number , pos : number , buffer : VSBuffer , offset : number , length : number ) : Promise < number > {
134
160
return this . provider . write ( fd , pos , buffer . buffer , offset , length ) ;
135
161
}
136
162
163
+ private writeFile ( resource : UriComponents , buffer : VSBuffer , opts : FileWriteOptions ) : Promise < void > {
164
+ return this . provider . writeFile ( this . transform ( resource ) , buffer . buffer , opts ) ;
165
+ }
166
+
137
167
private async delete ( resource : UriComponents , opts : FileDeleteOptions ) : Promise < void > {
138
168
return this . provider . delete ( this . transform ( resource ) , opts ) ;
139
169
}
0 commit comments