Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
6e780093
V
vscode
项目概览
xxadev
/
vscode
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
vscode
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
6e780093
编写于
6月 21, 2017
作者:
D
Daniel Imms
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Initial working version of files.watcherExcludes
上级
07c5fe39
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
143 addition
and
61 deletion
+143
-61
src/typings/nsfw.d.ts
src/typings/nsfw.d.ts
+2
-2
src/vs/workbench/services/files/node/fileService.ts
src/vs/workbench/services/files/node/fileService.ts
+1
-1
src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts
...ch/services/files/node/watcher/nsfw/nsfwWatcherService.ts
+90
-38
src/vs/workbench/services/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts
...s/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts
+8
-1
src/vs/workbench/services/files/node/watcher/nsfw/watcher.ts
src/vs/workbench/services/files/node/watcher/nsfw/watcher.ts
+3
-2
src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts
.../workbench/services/files/node/watcher/nsfw/watcherIpc.ts
+8
-1
src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts
...kbench/services/files/node/watcher/nsfw/watcherService.ts
+31
-16
未找到文件。
src/typings/nsfw.d.ts
浏览文件 @
6e780093
...
...
@@ -5,8 +5,8 @@
declare
module
'
nsfw
'
{
interface
NsfwWatcher
{
start
():
void
;
stop
():
void
;
start
():
any
;
stop
():
any
;
}
interface
NsfwWatchingPromise
{
...
...
src/vs/workbench/services/files/node/fileService.ts
浏览文件 @
6e780093
...
...
@@ -153,7 +153,7 @@ export class FileService implements IFileService {
}
private
setupNsfwWorkspceWatching
():
void
{
this
.
toDispose
.
push
(
toDisposable
(
new
NsfwWatcherService
(
this
.
contextService
,
this
.
options
.
watcherIgnoredPatterns
,
e
=>
this
.
_onFileChanges
.
fire
(
e
),
this
.
options
.
errorLogger
,
this
.
options
.
verboseLogging
).
startWatching
()));
this
.
toDispose
.
push
(
toDisposable
(
new
NsfwWatcherService
(
this
.
contextService
,
this
.
configurationService
,
e
=>
this
.
_onFileChanges
.
fire
(
e
),
this
.
options
.
errorLogger
,
this
.
options
.
verboseLogging
).
startWatching
()));
}
public
resolveFile
(
resource
:
uri
,
options
?:
IResolveFileOptions
):
TPromise
<
IFileStat
>
{
...
...
src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts
浏览文件 @
6e780093
...
...
@@ -8,7 +8,7 @@ import * as path from 'path';
import
*
as
watcher
from
'
vs/workbench/services/files/node/watcher/common
'
;
import
*
as
nsfw
from
'
nsfw
'
;
import
{
IWatcherService
,
IWatcherRequest
}
from
'
vs/workbench/services/files/node/watcher/nsfw/watcher
'
;
import
{
TPromise
}
from
'
vs/base/common/winjs.base
'
;
import
{
TPromise
,
ProgressCallback
,
TValueCallback
}
from
'
vs/base/common/winjs.base
'
;
import
{
ThrottledDelayer
}
from
'
vs/base/common/async
'
;
import
{
FileChangeType
}
from
'
vs/platform/files/common/files
'
;
...
...
@@ -17,21 +17,36 @@ nsfwActionToRawChangeType[nsfw.actions.CREATED] = FileChangeType.ADDED;
nsfwActionToRawChangeType
[
nsfw
.
actions
.
MODIFIED
]
=
FileChangeType
.
UPDATED
;
nsfwActionToRawChangeType
[
nsfw
.
actions
.
DELETED
]
=
FileChangeType
.
DELETED
;
interface
IWatcherObjet
{
start
():
void
;
stop
():
void
;
}
interface
IPathWatcher
{
watcher
?:
{
start
():
void
;
stop
():
void
;
};
ready
:
TPromise
<
IWatcherObjet
>
;
watcher
?:
IWatcherObjet
;
ignored
:
string
[];
}
export
class
NsfwWatcherService
implements
IWatcherService
{
private
static
FS_EVENT_DELAY
=
50
;
// aggregate and only emit events when changes have stopped for this duration (in ms)
private
_pathWatchers
:
{
[
watchPath
:
string
]:
IPathWatcher
}
=
{};
private
_watcherPromise
:
TPromise
<
void
>
;
private
_progressCallback
:
ProgressCallback
;
private
_verboseLogging
:
boolean
;
public
initialize
(
verboseLogging
:
boolean
):
TPromise
<
void
>
{
this
.
_verboseLogging
=
verboseLogging
;
this
.
_watcherPromise
=
new
TPromise
<
void
>
((
c
,
e
,
p
)
=>
{
this
.
_progressCallback
=
p
;
});
return
this
.
_watcherPromise
;
}
public
watch
(
request
:
IWatcherRequest
):
TPromise
<
void
>
{
if
(
request
.
verboseLogging
)
{
if
(
this
.
_
verboseLogging
)
{
console
.
log
(
'
request
'
,
request
);
}
...
...
@@ -39,15 +54,21 @@ export class NsfwWatcherService implements IWatcherService {
const
fileEventDelayer
=
new
ThrottledDelayer
(
NsfwWatcherService
.
FS_EVENT_DELAY
);
console
.
log
(
'
starting to watch
'
+
request
.
basePath
);
this
.
_pathWatchers
[
request
.
basePath
]
=
{};
let
readyPromiseCallback
:
TValueCallback
<
IWatcherObjet
>
;
this
.
_pathWatchers
[
request
.
basePath
]
=
{
ready
:
new
TPromise
<
IWatcherObjet
>
(
c
=>
readyPromiseCallback
=
c
),
ignored
:
request
.
ignored
};
const
promise
=
new
TPromise
<
void
>
((
c
,
e
,
p
)
=>
{
nsfw
(
request
.
basePath
,
events
=>
{
console
.
log
(
'
received events for path:
'
+
request
.
basePath
);
for
(
let
i
=
0
;
i
<
events
.
length
;
i
++
)
{
const
e
=
events
[
i
];
// Logging
if
(
request
.
verboseLogging
)
{
if
(
this
.
_
verboseLogging
)
{
const
logPath
=
e
.
action
===
nsfw
.
actions
.
RENAMED
?
path
.
join
(
e
.
directory
,
e
.
oldFile
)
+
'
->
'
+
e
.
newFile
:
path
.
join
(
e
.
directory
,
e
.
file
);
console
.
log
(
e
.
action
===
nsfw
.
actions
.
CREATED
?
'
[CREATED]
'
:
e
.
action
===
nsfw
.
actions
.
DELETED
?
'
[DELETED]
'
:
e
.
action
===
nsfw
.
actions
.
MODIFIED
?
'
[CHANGED]
'
:
'
[RENAMED]
'
,
logPath
);
}
...
...
@@ -57,16 +78,17 @@ export class NsfwWatcherService implements IWatcherService {
if
(
e
.
action
===
nsfw
.
actions
.
RENAMED
)
{
// Rename fires when a file's name changes within a single directory
absolutePath
=
path
.
join
(
e
.
directory
,
e
.
oldFile
);
if
(
!
this
.
_isPathIgnored
(
absolutePath
,
request
.
ignored
))
{
if
(
!
this
.
_isPathIgnored
(
absolutePath
,
this
.
_pathWatchers
[
request
.
basePath
]
.
ignored
))
{
undeliveredFileEvents
.
push
({
type
:
FileChangeType
.
DELETED
,
path
:
absolutePath
});
}
absolutePath
=
path
.
join
(
e
.
directory
,
e
.
newFile
);
if
(
!
this
.
_isPathIgnored
(
absolutePath
,
request
.
ignored
))
{
if
(
!
this
.
_isPathIgnored
(
absolutePath
,
this
.
_pathWatchers
[
request
.
basePath
]
.
ignored
))
{
undeliveredFileEvents
.
push
({
type
:
FileChangeType
.
ADDED
,
path
:
absolutePath
});
}
}
else
{
absolutePath
=
path
.
join
(
e
.
directory
,
e
.
file
);
if
(
!
this
.
_isPathIgnored
(
absolutePath
,
request
.
ignored
))
{
if
(
!
this
.
_isPathIgnored
(
absolutePath
,
this
.
_pathWatchers
[
request
.
basePath
].
ignored
))
{
console
.
log
(
'
adding event for path
'
,
absolutePath
);
undeliveredFileEvents
.
push
({
type
:
nsfwActionToRawChangeType
[
e
.
action
],
path
:
absolutePath
...
...
@@ -78,6 +100,7 @@ export class NsfwWatcherService implements IWatcherService {
// Delay and send buffer
fileEventDelayer
.
trigger
(()
=>
{
const
events
=
undeliveredFileEvents
;
console
.
log
(
'
sending events!
'
,
events
);
undeliveredFileEvents
=
[];
// Broadcast to clients normalized
...
...
@@ -85,7 +108,7 @@ export class NsfwWatcherService implements IWatcherService {
p
(
res
);
// Logging
if
(
request
.
verboseLogging
)
{
if
(
this
.
_
verboseLogging
)
{
res
.
forEach
(
r
=>
{
console
.
log
(
'
>> normalized
'
,
r
.
type
===
FileChangeType
.
ADDED
?
'
[ADDED]
'
:
r
.
type
===
FileChangeType
.
DELETED
?
'
[DELETED]
'
:
'
[CHANGED]
'
,
r
.
path
);
});
...
...
@@ -96,43 +119,70 @@ export class NsfwWatcherService implements IWatcherService {
}).
then
(
watcher
=>
{
console
.
log
(
'
watcher ready
'
+
request
.
basePath
);
this
.
_pathWatchers
[
request
.
basePath
].
watcher
=
watcher
;
return
watcher
.
start
();
const
startPromise
=
watcher
.
start
();
startPromise
.
then
(()
=>
readyPromiseCallback
(
watcher
));
return
startPromise
;
});
});
return
promise
;
}
public
setRoots
(
roots
:
string
[]):
TPromise
<
void
>
{
// TODO: This should probably be the only way to watch a folder
public
setRoots
(
roots
:
IWatcherRequest
[]):
TPromise
<
void
>
{
const
normalizedRoots
=
this
.
_normalizeRoots
(
roots
);
const
rootsToStartWatching
=
normalizedRoots
.
filter
(
r
=>
!
(
r
in
this
.
_pathWatchers
));
const
rootsToStopWatching
=
Object
.
keys
(
this
.
_pathWatchers
).
filter
(
r
=>
normalizedRoots
.
indexOf
(
r
)
===
-
1
);
// TODO: Don't watch inner folders
// TODO: Move verboseLogging to constructor
// Start watching roots that are not currently being watched
const
rootsToStartWatching
=
normalizedRoots
.
filter
(
r
=>
{
return
!
(
r
.
basePath
in
this
.
_pathWatchers
);
});
// Stop watching roots that don't exist in the new roots
const
rootsToStopWatching
=
Object
.
keys
(
this
.
_pathWatchers
).
filter
(
r
=>
{
return
normalizedRoots
.
every
(
normalizedRoot
=>
normalizedRoot
.
basePath
!==
r
);
});
// TODO: Support updating roots when only the ignored files change
// This should be just a matter of updating the ignored part
const
rootsWithChangedOptions
=
Object
.
keys
(
this
.
_pathWatchers
).
filter
(
r
=>
{
return
normalizedRoots
.
some
(
normalizedRoot
=>
{
if
(
normalizedRoot
.
basePath
!==
r
)
{
return
false
;
}
const
ignored
=
this
.
_pathWatchers
[
r
].
ignored
;
// TODO: Improve comments, refactor
if
(
normalizedRoot
.
ignored
.
length
!==
ignored
.
length
)
{
console
.
log
(
'
ignored changed on root:
'
+
r
);
this
.
_pathWatchers
[
r
].
ignored
=
normalizedRoot
.
ignored
;
return
true
;
}
// Check deep equality
for
(
let
i
=
0
;
i
<
ignored
.
length
;
i
++
)
{
if
(
normalizedRoot
.
ignored
[
i
]
!==
ignored
[
i
])
{
console
.
log
(
'
ignored changed on root:
'
+
r
);
this
.
_pathWatchers
[
r
].
ignored
=
normalizedRoot
.
ignored
;
return
true
;
}
}
return
false
;
});
});
// Logging
if
(
t
rue
)
{
console
.
log
(
`Set watch roots: start: [
${
rootsToStartWatching
.
join
(
'
,
'
)}
], stop: [
${
rootsToStopWatching
.
join
(
'
,
'
)}
]`
);
if
(
t
his
.
_verboseLogging
)
{
console
.
log
(
`Set watch roots: start: [
${
rootsToStartWatching
.
map
(
r
=>
r
.
basePath
).
join
(
'
,
'
)}
], stop: [
${
rootsToStopWatching
.
join
(
'
,
'
)}
], changed: [
${
rootsWithChangedOptions
.
join
(
'
,
'
)}
]`
);
}
const
promises
:
TPromise
<
void
>
[]
=
[];
if
(
rootsToStartWatching
.
length
)
{
rootsToStartWatching
.
forEach
(
root
=>
{
promises
.
push
(
this
.
watch
({
basePath
:
root
,
ignored
:
[],
// TODO: Inherit from initial request
verboseLogging
:
true
}));
});
}
if
(
rootsToStopWatching
.
length
)
{
rootsToStopWatching
.
forEach
(
root
=>
{
this
.
_pathWatchers
[
root
].
watcher
.
stop
();
delete
this
.
_pathWatchers
[
root
];
});
}
// Stop watching some roots
rootsToStopWatching
.
forEach
(
root
=>
{
this
.
_pathWatchers
[
root
].
ready
.
then
(
watcher
=>
watcher
.
stop
());
delete
this
.
_pathWatchers
[
root
];
});
// Start watching some roots
rootsToStartWatching
.
forEach
(
root
=>
promises
.
push
(
this
.
watch
(
root
)));
// TODO: Don't watch sub-folders of folders
return
TPromise
.
join
(
promises
).
then
(()
=>
void
0
);
...
...
@@ -142,13 +192,15 @@ export class NsfwWatcherService implements IWatcherService {
* Normalizes a set of root paths by removing any folders that are
* sub-folders of other roots.
*/
protected
_normalizeRoots
(
roots
:
string
[]):
string
[]
{
protected
_normalizeRoots
(
roots
:
IWatcherRequest
[]):
IWatcherRequest
[]
{
return
roots
.
filter
(
r
=>
roots
.
every
(
other
=>
{
return
!
(
r
.
length
>
other
.
length
&&
r
.
indexOf
(
other
)
===
0
);
return
!
(
r
.
basePath
.
length
>
other
.
basePath
.
length
&&
r
.
basePath
.
indexOf
(
other
.
basePath
)
===
0
);
}));
}
private
_isPathIgnored
(
absolutePath
:
string
,
ignored
:
string
[]):
boolean
{
console
.
log
(
'
is "
'
+
absolutePath
+
'
" ignored?
'
+
(
ignored
&&
ignored
.
some
(
ignore
=>
glob
.
match
(
ignore
,
absolutePath
))));
console
.
log
(
'
ignored:
'
,
ignored
);
return
ignored
&&
ignored
.
some
(
ignore
=>
glob
.
match
(
ignore
,
absolutePath
));
}
}
src/vs/workbench/services/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts
浏览文件 @
6e780093
...
...
@@ -7,9 +7,16 @@
import
assert
=
require
(
'
assert
'
);
import
{
NsfwWatcherService
}
from
'
vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService
'
;
import
{
IWatcherRequest
}
from
"
vs/workbench/services/files/node/watcher/nsfw/watcher
"
;
class
TestNsfwWatcherService
extends
NsfwWatcherService
{
public
normalizeRoots
(
roots
:
string
[]):
string
[]
{
return
this
.
_normalizeRoots
(
roots
);
}
public
normalizeRoots
(
roots
:
string
[]):
string
[]
{
// Work with strings as paths to simplify testing
const
requests
:
IWatcherRequest
[]
=
roots
.
map
(
r
=>
{
return
{
basePath
:
r
,
ignored
:
[]
};
});
return
this
.
_normalizeRoots
(
requests
).
map
(
r
=>
r
.
basePath
);
}
}
suite
(
'
NSFW Watcher Service
'
,
()
=>
{
...
...
src/vs/workbench/services/files/node/watcher/nsfw/watcher.ts
浏览文件 @
6e780093
...
...
@@ -10,11 +10,12 @@ import { TPromise } from 'vs/base/common/winjs.base';
export
interface
IWatcherRequest
{
basePath
:
string
;
ignored
:
string
[];
verboseLogging
:
boolean
;
//
verboseLogging: boolean;
}
export
interface
IWatcherService
{
setRoots
(
roots
:
string
[]):
TPromise
<
void
>
;
initialize
(
verboseLogging
:
boolean
):
TPromise
<
void
>
;
setRoots
(
roots
:
IWatcherRequest
[]):
TPromise
<
void
>
;
watch
(
request
:
IWatcherRequest
):
TPromise
<
void
>
;
}
...
...
src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts
浏览文件 @
6e780093
...
...
@@ -10,6 +10,8 @@ import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import
{
IWatcherRequest
,
IWatcherService
}
from
'
./watcher
'
;
export
interface
IWatcherChannel
extends
IChannel
{
call
(
command
:
'
initialize
'
,
verboseLogging
:
boolean
):
TPromise
<
void
>
;
call
(
command
:
'
setRoots
'
,
request
:
IWatcherRequest
[]):
TPromise
<
void
>
;
call
(
command
:
'
watch
'
,
request
:
IWatcherRequest
):
TPromise
<
void
>
;
call
(
command
:
string
,
arg
:
any
):
TPromise
<
any
>
;
}
...
...
@@ -20,6 +22,7 @@ export class WatcherChannel implements IWatcherChannel {
call
(
command
:
string
,
arg
:
any
):
TPromise
<
any
>
{
switch
(
command
)
{
case
'
initialize
'
:
return
this
.
service
.
initialize
(
arg
);
case
'
setRoots
'
:
return
this
.
service
.
setRoots
(
arg
);
case
'
watch
'
:
return
this
.
service
.
watch
(
arg
);
}
...
...
@@ -31,7 +34,11 @@ export class WatcherChannelClient implements IWatcherService {
constructor
(
private
channel
:
IWatcherChannel
)
{
}
setRoots
(
roots
:
string
[]):
TPromise
<
void
>
{
initialize
(
verboseLogging
:
boolean
):
TPromise
<
void
>
{
return
this
.
channel
.
call
(
'
initialize
'
,
verboseLogging
);
}
setRoots
(
roots
:
IWatcherRequest
[]):
TPromise
<
void
>
{
return
this
.
channel
.
call
(
'
setRoots
'
,
roots
);
}
...
...
src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts
浏览文件 @
6e780093
...
...
@@ -11,19 +11,20 @@ import { Client } from 'vs/base/parts/ipc/node/ipc.cp';
import
uri
from
'
vs/base/common/uri
'
;
import
{
toFileChangesEvent
,
IRawFileChange
}
from
'
vs/workbench/services/files/node/watcher/common
'
;
import
{
IWatcherChannel
,
WatcherChannelClient
}
from
'
vs/workbench/services/files/node/watcher/nsfw/watcherIpc
'
;
import
{
FileChangesEvent
}
from
'
vs/platform/files/common/files
'
;
import
{
FileChangesEvent
,
IFilesConfiguration
}
from
'
vs/platform/files/common/files
'
;
import
{
IWorkspaceContextService
}
from
'
vs/platform/workspace/common/workspace
'
;
import
{
normalize
}
from
"
path
"
;
import
{
IConfigurationService
}
from
"
vs/platform/configuration/common/configuration
"
;
export
class
FileWatcher
{
private
static
MAX_RESTARTS
=
5
;
private
service
:
WatcherChannelClient
;
private
isDisposed
:
boolean
;
private
restartCounter
:
number
;
constructor
(
private
contextService
:
IWorkspaceContextService
,
private
ignored
:
string
[]
,
private
configurationService
:
IConfigurationService
,
private
onFileChanges
:
(
changes
:
FileChangesEvent
)
=>
void
,
private
errorLogger
:
(
msg
:
string
)
=>
void
,
private
verboseLogging
:
boolean
,
...
...
@@ -48,13 +49,10 @@ export class FileWatcher {
}
);
// Initialize watcher
const
channel
=
getNextTickChannel
(
client
.
getChannel
<
IWatcherChannel
>
(
'
watcher
'
));
const
service
=
new
WatcherChannelClient
(
channel
);
// Start watching
const
activeRoots
=
this
.
contextService
.
getWorkspace2
().
roots
;
const
basePath
:
string
=
normalize
(
activeRoots
[
0
].
fsPath
);
service
.
watch
({
basePath
,
ignored
:
this
.
ignored
,
verboseLogging
:
this
.
verboseLogging
}).
then
(
null
,
(
err
)
=>
{
this
.
service
=
new
WatcherChannelClient
(
channel
);
this
.
service
.
initialize
(
this
.
verboseLogging
).
then
(
null
,
(
err
)
=>
{
if
(
!
(
err
instanceof
Error
&&
err
.
name
===
'
Canceled
'
&&
err
.
message
===
'
Canceled
'
))
{
return
TPromise
.
wrapError
(
err
);
// the service lib uses the promise cancel error to indicate the process died, we do not want to bubble this up
}
...
...
@@ -73,14 +71,11 @@ export class FileWatcher {
}
}
},
this
.
errorLogger
);
if
(
activeRoots
.
length
>
1
)
{
service
.
setRoots
(
activeRoots
.
map
(
r
=>
r
.
fsPath
));
}
this
.
contextService
.
onDidChangeWorkspaceRoots
(()
=>
{
const
roots
=
this
.
contextService
.
getWorkspace2
().
roots
;
service
.
setRoots
(
roots
.
map
(
r
=>
r
.
fsPath
));
}
);
// Start watching
this
.
updateRoots
()
;
this
.
contextService
.
onDidChangeWorkspaceRoots
(()
=>
this
.
updateRoots
(
));
this
.
configurationService
.
onDidUpdateConfiguration
(()
=>
this
.
updateRoots
()
);
return
()
=>
{
client
.
dispose
();
...
...
@@ -88,6 +83,26 @@ export class FileWatcher {
};
}
private
updateRoots
()
{
const
roots
=
this
.
contextService
.
getWorkspace2
().
roots
;
console
.
log
(
'
updateRoots
'
);
this
.
service
.
setRoots
(
roots
.
map
(
root
=>
{
const
configuration
=
this
.
configurationService
.
getConfiguration
<
IFilesConfiguration
>
(
undefined
,
{
resource
:
root
});
let
ignored
:
string
[]
=
[];
console
.
log
(
'
root:
'
+
root
);
if
(
configuration
.
files
&&
configuration
.
files
.
watcherExclude
)
{
console
.
log
(
'
config:
'
,
configuration
.
files
.
watcherExclude
);
ignored
=
Object
.
keys
(
configuration
.
files
.
watcherExclude
).
filter
(
k
=>
!!
configuration
.
files
.
watcherExclude
[
k
]);
}
return
{
basePath
:
root
.
fsPath
,
ignored
};
}));
}
private
onRawFileEvents
(
events
:
IRawFileChange
[]):
void
{
// Emit through broadcast service
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录