Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
bc0f81bd
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,发现更多精彩内容 >>
提交
bc0f81bd
编写于
1月 13, 2020
作者:
B
Benjamin Pasero
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
debt - cleanup file model a bit
上级
dd54a143
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
206 addition
and
183 deletion
+206
-183
src/vs/workbench/services/textfile/common/saveSequenzializer.ts
.../workbench/services/textfile/common/saveSequenzializer.ts
+95
-0
src/vs/workbench/services/textfile/common/textFileEditorModel.ts
...workbench/services/textfile/common/textFileEditorModel.ts
+8
-102
src/vs/workbench/services/textfile/test/saveSequenzializer.test.ts
...rkbench/services/textfile/test/saveSequenzializer.test.ts
+90
-0
src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts
...kbench/services/textfile/test/textFileEditorModel.test.ts
+1
-80
src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts
...services/textfile/test/textFileEditorModelManager.test.ts
+11
-0
src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService.ts
...esourceProperties/common/textResourcePropertiesService.ts
+0
-0
src/vs/workbench/workbench.common.main.ts
src/vs/workbench/workbench.common.main.ts
+1
-1
未找到文件。
src/vs/workbench/services/textfile/common/saveSequenzializer.ts
0 → 100644
浏览文件 @
bc0f81bd
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
interface
IPendingSave
{
versionId
:
number
;
promise
:
Promise
<
void
>
;
}
interface
ISaveOperation
{
promise
:
Promise
<
void
>
;
promiseResolve
:
()
=>
void
;
promiseReject
:
(
error
:
Error
)
=>
void
;
run
:
()
=>
Promise
<
void
>
;
}
export
class
SaveSequentializer
{
private
_pendingSave
?:
IPendingSave
;
private
_nextSave
?:
ISaveOperation
;
hasPendingSave
(
versionId
?:
number
):
boolean
{
if
(
!
this
.
_pendingSave
)
{
return
false
;
}
if
(
typeof
versionId
===
'
number
'
)
{
return
this
.
_pendingSave
.
versionId
===
versionId
;
}
return
!!
this
.
_pendingSave
;
}
get
pendingSave
():
Promise
<
void
>
|
undefined
{
return
this
.
_pendingSave
?
this
.
_pendingSave
.
promise
:
undefined
;
}
setPending
(
versionId
:
number
,
promise
:
Promise
<
void
>
):
Promise
<
void
>
{
this
.
_pendingSave
=
{
versionId
,
promise
};
promise
.
then
(()
=>
this
.
donePending
(
versionId
),
()
=>
this
.
donePending
(
versionId
));
return
promise
;
}
private
donePending
(
versionId
:
number
):
void
{
if
(
this
.
_pendingSave
&&
versionId
===
this
.
_pendingSave
.
versionId
)
{
// only set pending to done if the promise finished that is associated with that versionId
this
.
_pendingSave
=
undefined
;
// schedule the next save now that we are free if we have any
this
.
triggerNextSave
();
}
}
private
triggerNextSave
():
void
{
if
(
this
.
_nextSave
)
{
const
saveOperation
=
this
.
_nextSave
;
this
.
_nextSave
=
undefined
;
// Run next save and complete on the associated promise
saveOperation
.
run
().
then
(
saveOperation
.
promiseResolve
,
saveOperation
.
promiseReject
);
}
}
setNext
(
run
:
()
=>
Promise
<
void
>
):
Promise
<
void
>
{
// this is our first next save, so we create associated promise with it
// so that we can return a promise that completes when the save operation
// has completed.
if
(
!
this
.
_nextSave
)
{
let
promiseResolve
:
()
=>
void
;
let
promiseReject
:
(
error
:
Error
)
=>
void
;
const
promise
=
new
Promise
<
void
>
((
resolve
,
reject
)
=>
{
promiseResolve
=
resolve
;
promiseReject
=
reject
;
});
this
.
_nextSave
=
{
run
,
promise
,
promiseResolve
:
promiseResolve
!
,
promiseReject
:
promiseReject
!
};
}
// we have a previous next save, just overwrite it
else
{
this
.
_nextSave
.
run
=
run
;
}
return
this
.
_nextSave
.
promise
;
}
}
src/vs/workbench/services/textfile/common/textFileEditorModel.ts
浏览文件 @
bc0f81bd
...
...
@@ -13,7 +13,6 @@ import { EncodingMode, IRevertOptions, SaveReason } from 'vs/workbench/common/ed
import
{
BaseTextEditorModel
}
from
'
vs/workbench/common/editor/textEditorModel
'
;
import
{
IBackupFileService
}
from
'
vs/workbench/services/backup/common/backup
'
;
import
{
IFileService
,
FileOperationError
,
FileOperationResult
,
FileChangesEvent
,
FileChangeType
,
IFileStatWithMetadata
,
ETAG_DISABLED
,
FileSystemProviderCapabilities
}
from
'
vs/platform/files/common/files
'
;
import
{
IInstantiationService
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
IModeService
}
from
'
vs/editor/common/services/modeService
'
;
import
{
IModelService
}
from
'
vs/editor/common/services/modelService
'
;
import
{
timeout
}
from
'
vs/base/common/async
'
;
...
...
@@ -24,8 +23,9 @@ import { isEqual, basename } from 'vs/base/common/resources';
import
{
onUnexpectedError
}
from
'
vs/base/common/errors
'
;
import
{
IWorkingCopyService
}
from
'
vs/workbench/services/workingCopy/common/workingCopyService
'
;
import
{
IFilesConfigurationService
}
from
'
vs/workbench/services/filesConfiguration/common/filesConfigurationService
'
;
import
{
SaveSequentializer
}
from
'
vs/workbench/services/textfile/common/saveSequenzializer
'
;
export
interface
IBackupMetaData
{
interface
IBackupMetaData
{
mtime
:
number
;
ctime
:
number
;
size
:
number
;
...
...
@@ -80,7 +80,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
@
IModeService
modeService
:
IModeService
,
@
IModelService
modelService
:
IModelService
,
@
IFileService
private
readonly
fileService
:
IFileService
,
@
IInstantiationService
private
readonly
instantiationService
:
IInstantiationService
,
@
ITextFileService
private
readonly
textFileService
:
ITextFileService
,
@
IBackupFileService
private
readonly
backupFileService
:
IBackupFileService
,
@
ILogService
private
readonly
logService
:
ILogService
,
...
...
@@ -755,7 +754,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Prepare handler
if
(
!
TextFileEditorModel
.
saveErrorHandler
)
{
TextFileEditorModel
.
setSaveErrorHandler
(
this
.
instantiationService
.
createInstance
(
DefaultSaveErrorHandler
));
const
notificationService
=
this
.
notificationService
;
TextFileEditorModel
.
setSaveErrorHandler
({
onSaveError
(
error
:
Error
,
model
:
TextFileEditorModel
):
void
{
notificationService
.
error
(
nls
.
localize
(
'
genericSaveError
'
,
"
Failed to save '{0}': {1}
"
,
basename
(
model
.
resource
),
toErrorMessage
(
error
,
false
)));
}
});
}
// Handle
...
...
@@ -887,102 +891,4 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
}
interface
IPendingSave
{
versionId
:
number
;
promise
:
Promise
<
void
>
;
}
interface
ISaveOperation
{
promise
:
Promise
<
void
>
;
promiseResolve
:
()
=>
void
;
promiseReject
:
(
error
:
Error
)
=>
void
;
run
:
()
=>
Promise
<
void
>
;
}
export
class
SaveSequentializer
{
private
_pendingSave
?:
IPendingSave
;
private
_nextSave
?:
ISaveOperation
;
hasPendingSave
(
versionId
?:
number
):
boolean
{
if
(
!
this
.
_pendingSave
)
{
return
false
;
}
if
(
typeof
versionId
===
'
number
'
)
{
return
this
.
_pendingSave
.
versionId
===
versionId
;
}
return
!!
this
.
_pendingSave
;
}
get
pendingSave
():
Promise
<
void
>
|
undefined
{
return
this
.
_pendingSave
?
this
.
_pendingSave
.
promise
:
undefined
;
}
setPending
(
versionId
:
number
,
promise
:
Promise
<
void
>
):
Promise
<
void
>
{
this
.
_pendingSave
=
{
versionId
,
promise
};
promise
.
then
(()
=>
this
.
donePending
(
versionId
),
()
=>
this
.
donePending
(
versionId
));
return
promise
;
}
private
donePending
(
versionId
:
number
):
void
{
if
(
this
.
_pendingSave
&&
versionId
===
this
.
_pendingSave
.
versionId
)
{
// only set pending to done if the promise finished that is associated with that versionId
this
.
_pendingSave
=
undefined
;
// schedule the next save now that we are free if we have any
this
.
triggerNextSave
();
}
}
private
triggerNextSave
():
void
{
if
(
this
.
_nextSave
)
{
const
saveOperation
=
this
.
_nextSave
;
this
.
_nextSave
=
undefined
;
// Run next save and complete on the associated promise
saveOperation
.
run
().
then
(
saveOperation
.
promiseResolve
,
saveOperation
.
promiseReject
);
}
}
setNext
(
run
:
()
=>
Promise
<
void
>
):
Promise
<
void
>
{
// this is our first next save, so we create associated promise with it
// so that we can return a promise that completes when the save operation
// has completed.
if
(
!
this
.
_nextSave
)
{
let
promiseResolve
:
()
=>
void
;
let
promiseReject
:
(
error
:
Error
)
=>
void
;
const
promise
=
new
Promise
<
void
>
((
resolve
,
reject
)
=>
{
promiseResolve
=
resolve
;
promiseReject
=
reject
;
});
this
.
_nextSave
=
{
run
,
promise
,
promiseResolve
:
promiseResolve
!
,
promiseReject
:
promiseReject
!
};
}
// we have a previous next save, just overwrite it
else
{
this
.
_nextSave
.
run
=
run
;
}
return
this
.
_nextSave
.
promise
;
}
}
class
DefaultSaveErrorHandler
implements
ISaveErrorHandler
{
constructor
(@
INotificationService
private
readonly
notificationService
:
INotificationService
)
{
}
onSaveError
(
error
:
Error
,
model
:
TextFileEditorModel
):
void
{
this
.
notificationService
.
error
(
nls
.
localize
(
'
genericSaveError
'
,
"
Failed to save '{0}': {1}
"
,
basename
(
model
.
resource
),
toErrorMessage
(
error
,
false
)));
}
}
src/vs/workbench/services/textfile/test/saveSequenzializer.test.ts
0 → 100644
浏览文件 @
bc0f81bd
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
*
as
assert
from
'
assert
'
;
import
{
timeout
}
from
'
vs/base/common/async
'
;
import
{
SaveSequentializer
}
from
'
vs/workbench/services/textfile/common/saveSequenzializer
'
;
suite
(
'
Files - SaveSequentializer
'
,
()
=>
{
test
(
'
SaveSequentializer - pending basics
'
,
async
function
()
{
const
sequentializer
=
new
SaveSequentializer
();
assert
.
ok
(
!
sequentializer
.
hasPendingSave
());
assert
.
ok
(
!
sequentializer
.
hasPendingSave
(
2323
));
assert
.
ok
(
!
sequentializer
.
pendingSave
);
// pending removes itself after done
await
sequentializer
.
setPending
(
1
,
Promise
.
resolve
());
assert
.
ok
(
!
sequentializer
.
hasPendingSave
());
assert
.
ok
(
!
sequentializer
.
hasPendingSave
(
1
));
assert
.
ok
(
!
sequentializer
.
pendingSave
);
// pending removes itself after done (use timeout)
sequentializer
.
setPending
(
2
,
timeout
(
1
));
assert
.
ok
(
sequentializer
.
hasPendingSave
());
assert
.
ok
(
sequentializer
.
hasPendingSave
(
2
));
assert
.
ok
(
!
sequentializer
.
hasPendingSave
(
1
));
assert
.
ok
(
sequentializer
.
pendingSave
);
await
timeout
(
2
);
assert
.
ok
(
!
sequentializer
.
hasPendingSave
());
assert
.
ok
(
!
sequentializer
.
hasPendingSave
(
2
));
assert
.
ok
(
!
sequentializer
.
pendingSave
);
});
test
(
'
SaveSequentializer - pending and next (finishes instantly)
'
,
async
function
()
{
const
sequentializer
=
new
SaveSequentializer
();
let
pendingDone
=
false
;
sequentializer
.
setPending
(
1
,
timeout
(
1
).
then
(()
=>
{
pendingDone
=
true
;
return
;
}));
// next finishes instantly
let
nextDone
=
false
;
const
res
=
sequentializer
.
setNext
(()
=>
Promise
.
resolve
(
null
).
then
(()
=>
{
nextDone
=
true
;
return
;
}));
await
res
;
assert
.
ok
(
pendingDone
);
assert
.
ok
(
nextDone
);
});
test
(
'
SaveSequentializer - pending and next (finishes after timeout)
'
,
async
function
()
{
const
sequentializer
=
new
SaveSequentializer
();
let
pendingDone
=
false
;
sequentializer
.
setPending
(
1
,
timeout
(
1
).
then
(()
=>
{
pendingDone
=
true
;
return
;
}));
// next finishes after timeout
let
nextDone
=
false
;
const
res
=
sequentializer
.
setNext
(()
=>
timeout
(
1
).
then
(()
=>
{
nextDone
=
true
;
return
;
}));
await
res
;
assert
.
ok
(
pendingDone
);
assert
.
ok
(
nextDone
);
});
test
(
'
SaveSequentializer - pending and multiple next (last one wins)
'
,
async
function
()
{
const
sequentializer
=
new
SaveSequentializer
();
let
pendingDone
=
false
;
sequentializer
.
setPending
(
1
,
timeout
(
1
).
then
(()
=>
{
pendingDone
=
true
;
return
;
}));
// next finishes after timeout
let
firstDone
=
false
;
let
firstRes
=
sequentializer
.
setNext
(()
=>
timeout
(
2
).
then
(()
=>
{
firstDone
=
true
;
return
;
}));
let
secondDone
=
false
;
let
secondRes
=
sequentializer
.
setNext
(()
=>
timeout
(
3
).
then
(()
=>
{
secondDone
=
true
;
return
;
}));
let
thirdDone
=
false
;
let
thirdRes
=
sequentializer
.
setNext
(()
=>
timeout
(
4
).
then
(()
=>
{
thirdDone
=
true
;
return
;
}));
await
Promise
.
all
([
firstRes
,
secondRes
,
thirdRes
]);
assert
.
ok
(
pendingDone
);
assert
.
ok
(
!
firstDone
);
assert
.
ok
(
!
secondDone
);
assert
.
ok
(
thirdDone
);
});
});
src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts
浏览文件 @
bc0f81bd
...
...
@@ -6,7 +6,7 @@
import
*
as
assert
from
'
assert
'
;
import
{
IInstantiationService
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
EncodingMode
}
from
'
vs/workbench/common/editor
'
;
import
{
TextFileEditorModel
,
SaveSequentializer
}
from
'
vs/workbench/services/textfile/common/textFileEditorModel
'
;
import
{
TextFileEditorModel
}
from
'
vs/workbench/services/textfile/common/textFileEditorModel
'
;
import
{
ITextFileService
,
ModelState
,
StateChange
,
snapshotToString
}
from
'
vs/workbench/services/textfile/common/textfiles
'
;
import
{
workbenchInstantiationService
,
TestTextFileService
,
createFileInput
,
TestFileService
}
from
'
vs/workbench/test/workbenchTestServices
'
;
import
{
toResource
}
from
'
vs/base/test/common/utils
'
;
...
...
@@ -475,83 +475,4 @@ suite('Files - TextFileEditorModel', () => {
await
model
.
save
();
model
.
dispose
();
});
test
(
'
SaveSequentializer - pending basics
'
,
async
function
()
{
const
sequentializer
=
new
SaveSequentializer
();
assert
.
ok
(
!
sequentializer
.
hasPendingSave
());
assert
.
ok
(
!
sequentializer
.
hasPendingSave
(
2323
));
assert
.
ok
(
!
sequentializer
.
pendingSave
);
// pending removes itself after done
await
sequentializer
.
setPending
(
1
,
Promise
.
resolve
());
assert
.
ok
(
!
sequentializer
.
hasPendingSave
());
assert
.
ok
(
!
sequentializer
.
hasPendingSave
(
1
));
assert
.
ok
(
!
sequentializer
.
pendingSave
);
// pending removes itself after done (use timeout)
sequentializer
.
setPending
(
2
,
timeout
(
1
));
assert
.
ok
(
sequentializer
.
hasPendingSave
());
assert
.
ok
(
sequentializer
.
hasPendingSave
(
2
));
assert
.
ok
(
!
sequentializer
.
hasPendingSave
(
1
));
assert
.
ok
(
sequentializer
.
pendingSave
);
await
timeout
(
2
);
assert
.
ok
(
!
sequentializer
.
hasPendingSave
());
assert
.
ok
(
!
sequentializer
.
hasPendingSave
(
2
));
assert
.
ok
(
!
sequentializer
.
pendingSave
);
});
test
(
'
SaveSequentializer - pending and next (finishes instantly)
'
,
async
function
()
{
const
sequentializer
=
new
SaveSequentializer
();
let
pendingDone
=
false
;
sequentializer
.
setPending
(
1
,
timeout
(
1
).
then
(()
=>
{
pendingDone
=
true
;
return
;
}));
// next finishes instantly
let
nextDone
=
false
;
const
res
=
sequentializer
.
setNext
(()
=>
Promise
.
resolve
(
null
).
then
(()
=>
{
nextDone
=
true
;
return
;
}));
await
res
;
assert
.
ok
(
pendingDone
);
assert
.
ok
(
nextDone
);
});
test
(
'
SaveSequentializer - pending and next (finishes after timeout)
'
,
async
function
()
{
const
sequentializer
=
new
SaveSequentializer
();
let
pendingDone
=
false
;
sequentializer
.
setPending
(
1
,
timeout
(
1
).
then
(()
=>
{
pendingDone
=
true
;
return
;
}));
// next finishes after timeout
let
nextDone
=
false
;
const
res
=
sequentializer
.
setNext
(()
=>
timeout
(
1
).
then
(()
=>
{
nextDone
=
true
;
return
;
}));
await
res
;
assert
.
ok
(
pendingDone
);
assert
.
ok
(
nextDone
);
});
test
(
'
SaveSequentializer - pending and multiple next (last one wins)
'
,
async
function
()
{
const
sequentializer
=
new
SaveSequentializer
();
let
pendingDone
=
false
;
sequentializer
.
setPending
(
1
,
timeout
(
1
).
then
(()
=>
{
pendingDone
=
true
;
return
;
}));
// next finishes after timeout
let
firstDone
=
false
;
let
firstRes
=
sequentializer
.
setNext
(()
=>
timeout
(
2
).
then
(()
=>
{
firstDone
=
true
;
return
;
}));
let
secondDone
=
false
;
let
secondRes
=
sequentializer
.
setNext
(()
=>
timeout
(
3
).
then
(()
=>
{
secondDone
=
true
;
return
;
}));
let
thirdDone
=
false
;
let
thirdRes
=
sequentializer
.
setNext
(()
=>
timeout
(
4
).
then
(()
=>
{
thirdDone
=
true
;
return
;
}));
await
Promise
.
all
([
firstRes
,
secondRes
,
thirdRes
]);
assert
.
ok
(
pendingDone
);
assert
.
ok
(
!
firstDone
);
assert
.
ok
(
!
secondDone
);
assert
.
ok
(
thirdDone
);
});
});
src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts
浏览文件 @
bc0f81bd
...
...
@@ -141,11 +141,18 @@ suite('Files - TextFileEditorModelManager', () => {
const
resource1
=
toResource
.
call
(
this
,
'
/path/index.txt
'
);
const
resource2
=
toResource
.
call
(
this
,
'
/path/other.txt
'
);
let
loadedCounter
=
0
;
let
dirtyCounter
=
0
;
let
revertedCounter
=
0
;
let
savedCounter
=
0
;
let
encodingCounter
=
0
;
manager
.
onModelLoaded
(
e
=>
{
if
(
e
.
resource
.
toString
()
===
resource1
.
toString
())
{
loadedCounter
++
;
}
});
manager
.
onModelDirty
(
e
=>
{
if
(
e
.
resource
.
toString
()
===
resource1
.
toString
())
{
dirtyCounter
++
;
...
...
@@ -171,10 +178,14 @@ suite('Files - TextFileEditorModelManager', () => {
});
const
model1
=
await
manager
.
loadOrCreate
(
resource1
,
{
encoding
:
'
utf8
'
});
assert
.
equal
(
loadedCounter
,
1
);
accessor
.
fileService
.
fireFileChanges
(
new
FileChangesEvent
([{
resource
:
resource1
,
type
:
FileChangeType
.
DELETED
}]));
accessor
.
fileService
.
fireFileChanges
(
new
FileChangesEvent
([{
resource
:
resource1
,
type
:
FileChangeType
.
ADDED
}]));
const
model2
=
await
manager
.
loadOrCreate
(
resource2
,
{
encoding
:
'
utf8
'
});
assert
.
equal
(
loadedCounter
,
2
);
model1
.
textEditorModel
!
.
setValue
(
'
changed
'
);
model1
.
updatePreferredEncoding
(
'
utf16
'
);
...
...
src/vs/workbench/services/text
file
/common/textResourcePropertiesService.ts
→
src/vs/workbench/services/text
resourceProperties
/common/textResourcePropertiesService.ts
浏览文件 @
bc0f81bd
文件已移动
src/vs/workbench/workbench.common.main.ts
浏览文件 @
bc0f81bd
...
...
@@ -71,7 +71,7 @@ import 'vs/workbench/services/history/browser/history';
import
'
vs/workbench/services/activity/browser/activityService
'
;
import
'
vs/workbench/services/keybinding/browser/keybindingService
'
;
import
'
vs/workbench/services/untitled/common/untitledTextEditorService
'
;
import
'
vs/workbench/services/text
file
/common/textResourcePropertiesService
'
;
import
'
vs/workbench/services/text
resourceProperties
/common/textResourcePropertiesService
'
;
import
'
vs/workbench/services/mode/common/workbenchModeService
'
;
import
'
vs/workbench/services/commands/common/commandService
'
;
import
'
vs/workbench/services/themes/browser/workbenchThemeService
'
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录