Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
c0571ed0
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,发现更多精彩内容 >>
提交
c0571ed0
编写于
4月 01, 2019
作者:
B
Benjamin Pasero
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
files2 - restore file change event normalization logic
上级
54384a70
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
118 addition
and
11 deletion
+118
-11
src/vs/workbench/services/files2/node/diskFileSystemProvider.ts
.../workbench/services/files2/node/diskFileSystemProvider.ts
+118
-11
未找到文件。
src/vs/workbench/services/files2/node/diskFileSystemProvider.ts
浏览文件 @
c0571ed0
...
...
@@ -6,7 +6,7 @@
import
{
mkdir
,
open
,
close
,
read
,
write
}
from
'
fs
'
;
import
{
promisify
}
from
'
util
'
;
import
{
IDisposable
,
Disposable
,
toDisposable
,
dispose
}
from
'
vs/base/common/lifecycle
'
;
import
{
IFileSystemProvider
,
FileSystemProviderCapabilities
,
IFileChange
,
IWatchOptions
,
IStat
,
FileType
,
FileDeleteOptions
,
FileOverwriteOptions
,
FileWriteOptions
,
FileOpenOptions
,
FileSystemProviderErrorCode
,
createFileSystemProviderError
,
FileSystemProviderError
,
FileChangeType
}
from
'
vs/platform/files/common/files
'
;
import
{
IFileSystemProvider
,
FileSystemProviderCapabilities
,
IFileChange
,
IWatchOptions
,
IStat
,
FileType
,
FileDeleteOptions
,
FileOverwriteOptions
,
FileWriteOptions
,
FileOpenOptions
,
FileSystemProviderErrorCode
,
createFileSystemProviderError
,
FileSystemProviderError
,
FileChangeType
,
isParent
}
from
'
vs/platform/files/common/files
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
Event
,
Emitter
}
from
'
vs/base/common/event
'
;
import
{
isLinux
,
isWindows
}
from
'
vs/base/common/platform
'
;
...
...
@@ -14,9 +14,10 @@ import { statLink, readdir, unlink, move, copy, readFile, writeFile, fileExists,
import
{
normalize
,
basename
,
dirname
}
from
'
vs/base/common/path
'
;
import
{
joinPath
}
from
'
vs/base/common/resources
'
;
import
{
isEqual
}
from
'
vs/base/common/extpath
'
;
import
{
retry
}
from
'
vs/base/common/async
'
;
import
{
ILogService
}
from
'
vs/platform/log/common/log
'
;
import
{
retry
,
ThrottledDelayer
}
from
'
vs/base/common/async
'
;
import
{
ILogService
,
LogLevel
}
from
'
vs/platform/log/common/log
'
;
import
{
localize
}
from
'
vs/nls
'
;
import
{
ResourceMap
}
from
'
vs/base/common/map
'
;
export
class
DiskFileSystemProvider
extends
Disposable
implements
IFileSystemProvider
{
...
...
@@ -306,6 +307,9 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
private
_onDidChangeFile
:
Emitter
<
IFileChange
[]
>
=
this
.
_register
(
new
Emitter
<
IFileChange
[]
>
());
get
onDidChangeFile
():
Event
<
IFileChange
[]
>
{
return
this
.
_onDidChangeFile
.
event
;
}
private
fileChangesDelayer
:
ThrottledDelayer
<
void
>
=
new
ThrottledDelayer
<
void
>
(
50
);
private
fileChangesBuffer
:
IFileChange
[]
=
[];
watch
(
resource
:
URI
,
opts
:
IWatchOptions
):
IDisposable
{
if
(
opts
.
recursive
)
{
return
this
.
watchRecursive
(
resource
,
opts
);
...
...
@@ -328,21 +332,51 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
}
disposable
=
watchNonRecursive
({
path
:
resource
.
fsPath
,
isDirectory
:
fileStat
.
type
===
FileType
.
Directory
},
(
eventType
:
'
change
'
|
'
delete
'
,
path
:
string
)
=>
{
// Logging
this
.
logService
.
trace
(
`[File Watcher (node.js)]
${
eventType
===
'
change
'
?
'
[CHANGED]
'
:
'
[DELETED]
'
}
${
path
}
`
);
// Emit as event
this
.
_onDidChangeFile
.
fire
([{
this
.
onFileChange
({
type
:
eventType
===
'
change
'
?
FileChangeType
.
UPDATED
:
FileChangeType
.
DELETED
,
resource
:
URI
.
file
(
path
)
}
]
);
});
},
error
=>
this
.
logService
.
error
(
error
));
});
return
toDisposable
(()
=>
dispose
(
disposable
));
}
private
onFileChange
(
event
:
IFileChange
):
void
{
// Add to buffer
this
.
fileChangesBuffer
.
push
(
event
);
// Logging
if
(
this
.
logService
.
getLevel
()
===
LogLevel
.
Trace
)
{
this
.
logService
.
trace
(
`[File Watcher (node.js)]
${
event
.
type
===
FileChangeType
.
ADDED
?
'
[ADDED]
'
:
event
.
type
===
FileChangeType
.
DELETED
?
'
[DELETED]
'
:
'
[CHANGED]
'
}
${
event
.
resource
.
fsPath
}
`
);
}
// Handle emit through delayer to accommodate for bulk changes and thus reduce spam
this
.
fileChangesDelayer
.
trigger
(()
=>
{
const
buffer
=
this
.
fileChangesBuffer
;
this
.
fileChangesBuffer
=
[];
// Event normalization
const
normalizer
=
new
FileChangeEventNormalizer
();
for
(
const
event
of
buffer
)
{
normalizer
.
processEvent
(
event
);
}
const
normalizedEvents
=
normalizer
.
normalize
();
if
(
this
.
logService
.
getLevel
()
===
LogLevel
.
Trace
)
{
normalizedEvents
.
forEach
(
event
=>
{
this
.
logService
.
trace
(
`[File Watcher (node.js)] >> normalized
${
event
.
type
===
FileChangeType
.
ADDED
?
'
[ADDED]
'
:
event
.
type
===
FileChangeType
.
DELETED
?
'
[DELETED]
'
:
'
[CHANGED]
'
}
${
event
.
resource
.
fsPath
}
`
);
});
}
// Fire
this
.
_onDidChangeFile
.
fire
(
normalizedEvents
);
return
Promise
.
resolve
();
});
}
//#endregion
//#region Helpers
...
...
@@ -379,4 +413,77 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
}
//#endregion
}
\ No newline at end of file
}
class
FileChangeEventNormalizer
{
private
normalized
:
IFileChange
[]
=
[];
private
mapPathToChange
:
ResourceMap
<
IFileChange
>
=
new
ResourceMap
();
processEvent
(
event
:
IFileChange
):
void
{
const
existingEvent
=
this
.
mapPathToChange
.
get
(
event
.
resource
);
// Event path already exists
if
(
existingEvent
)
{
const
currentChangeType
=
existingEvent
.
type
;
const
newChangeType
=
event
.
type
;
// ignore CREATE followed by DELETE in one go
if
(
currentChangeType
===
FileChangeType
.
ADDED
&&
newChangeType
===
FileChangeType
.
DELETED
)
{
this
.
mapPathToChange
.
delete
(
event
.
resource
);
this
.
normalized
.
splice
(
this
.
normalized
.
indexOf
(
existingEvent
),
1
);
}
// flatten DELETE followed by CREATE into CHANGE
else
if
(
currentChangeType
===
FileChangeType
.
DELETED
&&
newChangeType
===
FileChangeType
.
ADDED
)
{
existingEvent
.
type
=
FileChangeType
.
UPDATED
;
}
// Do nothing. Keep the created event
else
if
(
currentChangeType
===
FileChangeType
.
ADDED
&&
newChangeType
===
FileChangeType
.
UPDATED
)
{
}
// Otherwise apply change type
else
{
existingEvent
.
type
=
newChangeType
;
}
}
// Otherwise store it
else
{
this
.
normalized
.
push
(
event
);
this
.
mapPathToChange
.
set
(
event
.
resource
,
event
);
}
}
normalize
():
IFileChange
[]
{
const
addedChangeEvents
:
IFileChange
[]
=
[];
const
deletedPaths
:
string
[]
=
[];
// This algorithm will remove all DELETE events up to the root folder
// that got deleted if any. This ensures that we are not producing
// DELETE events for each file inside a folder that gets deleted.
//
// 1.) split ADD/CHANGE and DELETED events
// 2.) sort short deleted paths to the top
// 3.) for each DELETE, check if there is a deleted parent and ignore the event in that case
return
this
.
normalized
.
filter
(
e
=>
{
if
(
e
.
type
!==
FileChangeType
.
DELETED
)
{
addedChangeEvents
.
push
(
e
);
return
false
;
// remove ADD / CHANGE
}
return
true
;
// keep DELETE
}).
sort
((
e1
,
e2
)
=>
{
return
e1
.
resource
.
fsPath
.
length
-
e2
.
resource
.
fsPath
.
length
;
// shortest path first
}).
filter
(
e
=>
{
if
(
deletedPaths
.
some
(
d
=>
isParent
(
e
.
resource
.
fsPath
,
d
,
!
isLinux
/* ignorecase */
)))
{
return
false
;
// DELETE is ignored if parent is deleted already
}
// otherwise mark as deleted
deletedPaths
.
push
(
e
.
resource
.
fsPath
);
return
true
;
}).
concat
(
addedChangeEvents
);
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录