Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
掘金者说
vscode
提交
5352ba33
V
vscode
项目概览
掘金者说
/
vscode
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
vscode
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
5352ba33
编写于
8月 16, 2019
作者:
B
Benjamin Pasero
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
web - synchronise global state changes
上级
73f852d2
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
143 addition
and
100 deletion
+143
-100
src/vs/platform/storage/browser/storageService.ts
src/vs/platform/storage/browser/storageService.ts
+138
-21
src/vs/platform/storage/common/storage.ts
src/vs/platform/storage/common/storage.ts
+0
-74
src/vs/platform/storage/test/electron-browser/storage.test.ts
...vs/platform/storage/test/electron-browser/storage.test.ts
+5
-5
未找到文件。
src/vs/platform/storage/browser/storageService.ts
浏览文件 @
5352ba33
...
...
@@ -5,15 +5,17 @@
import
{
Disposable
}
from
'
vs/base/common/lifecycle
'
;
import
{
Event
,
Emitter
}
from
'
vs/base/common/event
'
;
import
{
IWorkspaceStorageChangeEvent
,
IStorageService
,
StorageScope
,
IWillSaveStateEvent
,
WillSaveStateReason
,
logStorage
,
FileStorageDatabase
}
from
'
vs/platform/storage/common/storage
'
;
import
{
IWorkspaceStorageChangeEvent
,
IStorageService
,
StorageScope
,
IWillSaveStateEvent
,
WillSaveStateReason
,
logStorage
}
from
'
vs/platform/storage/common/storage
'
;
import
{
IEnvironmentService
}
from
'
vs/platform/environment/common/environment
'
;
import
{
IWorkspaceInitializationPayload
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
ServiceIdentifier
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
IFileService
}
from
'
vs/platform/files/common/files
'
;
import
{
IStorage
,
Storage
}
from
'
vs/base/parts/storage/common/storage
'
;
import
{
IFileService
,
FileChangeType
}
from
'
vs/platform/files/common/files
'
;
import
{
IStorage
,
Storage
,
IStorageDatabase
,
IStorageItemsChangeEvent
,
IUpdateRequest
}
from
'
vs/base/parts/storage/common/storage
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
joinPath
}
from
'
vs/base/common/resources
'
;
import
{
runWhenIdle
}
from
'
vs/base/common/async
'
;
import
{
runWhenIdle
,
RunOnceScheduler
}
from
'
vs/base/common/async
'
;
import
{
serializableToMap
,
mapToSerializable
}
from
'
vs/base/common/map
'
;
import
{
VSBuffer
}
from
'
vs/base/common/buffer
'
;
export
class
BrowserStorageService
extends
Disposable
implements
IStorageService
{
...
...
@@ -35,6 +37,7 @@ export class BrowserStorageService extends Disposable implements IStorageService
private
workspaceStorageFile
:
URI
;
private
initializePromise
:
Promise
<
void
>
;
private
periodicSaveScheduler
=
this
.
_register
(
new
RunOnceScheduler
(()
=>
this
.
saveState
(),
5000
));
get
hasPendingUpdate
():
boolean
{
return
this
.
globalStorageDatabase
.
hasPendingUpdate
||
this
.
workspaceStorageDatabase
.
hasPendingUpdate
;
...
...
@@ -51,20 +54,23 @@ export class BrowserStorageService extends Disposable implements IStorageService
// long running operation.
// Instead, periodically ask customers to save save. The library will be clever enough
// to only save state that has actually changed.
this
.
saveStatePeriodically
();
this
.
periodicSaveScheduler
.
schedule
();
}
private
saveStatePeriodically
():
void
{
setTimeout
(()
=>
{
runWhenIdle
(()
=>
{
private
saveState
():
void
{
runWhenIdle
(()
=>
{
// this event will potentially cause new state to be stored
// this event will potentially cause new state to be stored
// since new state will only be created while the document
// has focus, one optimization is to not run this when the
// document has no focus, assuming that state has not changed
if
(
document
.
hasFocus
())
{
this
.
_onWillSaveState
.
fire
({
reason
:
WillSaveStateReason
.
NONE
});
}
// repeat
this
.
saveStatePeriodically
();
});
},
5000
);
// repeat
this
.
periodicSaveScheduler
.
schedule
();
});
}
initialize
(
payload
:
IWorkspaceInitializationPayload
):
Promise
<
void
>
{
...
...
@@ -83,14 +89,14 @@ export class BrowserStorageService extends Disposable implements IStorageService
// Workspace Storage
this
.
workspaceStorageFile
=
joinPath
(
stateRoot
,
`
${
payload
.
id
}
.json`
);
this
.
workspaceStorageDatabase
=
this
.
_register
(
new
FileStorageDatabase
(
this
.
workspaceStorageFile
,
this
.
fileService
));
this
.
workspaceStorage
=
new
Storage
(
this
.
workspaceStorageDatabase
);
this
.
workspaceStorageDatabase
=
this
.
_register
(
new
FileStorageDatabase
(
this
.
workspaceStorageFile
,
false
/* do not watch for external changes */
,
this
.
fileService
));
this
.
workspaceStorage
=
this
.
_register
(
new
Storage
(
this
.
workspaceStorageDatabase
)
);
this
.
_register
(
this
.
workspaceStorage
.
onDidChangeStorage
(
key
=>
this
.
_onDidChangeStorage
.
fire
({
key
,
scope
:
StorageScope
.
WORKSPACE
})));
// Global Storage
this
.
globalStorageFile
=
joinPath
(
stateRoot
,
'
global.json
'
);
this
.
globalStorageDatabase
=
this
.
_register
(
new
FileStorageDatabase
(
this
.
globalStorageFile
,
this
.
fileService
));
this
.
globalStorage
=
new
Storage
(
this
.
globalStorageDatabase
);
this
.
globalStorageDatabase
=
this
.
_register
(
new
FileStorageDatabase
(
this
.
globalStorageFile
,
t
rue
/* watch for external changes */
,
t
his
.
fileService
));
this
.
globalStorage
=
this
.
_register
(
new
Storage
(
this
.
globalStorageDatabase
)
);
this
.
_register
(
this
.
globalStorage
.
onDidChangeStorage
(
key
=>
this
.
_onDidChangeStorage
.
fire
({
key
,
scope
:
StorageScope
.
GLOBAL
})));
// Init both
...
...
@@ -140,14 +146,125 @@ export class BrowserStorageService extends Disposable implements IStorageService
}
close
():
void
{
// Signal as event so that clients can still store data
this
.
_onWillSaveState
.
fire
({
reason
:
WillSaveStateReason
.
SHUTDOWN
});
// We explicitly do not close our DBs because writing data onBeforeUnload()
// can result in unexpected results. Namely, it seems that - even though this
// operation is async - sometimes it is being triggered on unload and
// succeeds. Often though, the DBs turn out to be empty because the write
// never had a chance to complete.
//
// Instead we trigger dispose() to ensure that no timeouts or callbacks
// get triggered in this phase.
this
.
dispose
();
}
}
export
class
FileStorageDatabase
extends
Disposable
implements
IStorageDatabase
{
private
readonly
_onDidChangeItemsExternal
:
Emitter
<
IStorageItemsChangeEvent
>
=
this
.
_register
(
new
Emitter
<
IStorageItemsChangeEvent
>
());
readonly
onDidChangeItemsExternal
:
Event
<
IStorageItemsChangeEvent
>
=
this
.
_onDidChangeItemsExternal
.
event
;
private
cache
:
Map
<
string
,
string
>
|
undefined
;
private
pendingUpdate
:
Promise
<
void
>
=
Promise
.
resolve
();
private
_hasPendingUpdate
=
false
;
get
hasPendingUpdate
():
boolean
{
return
this
.
_hasPendingUpdate
;
}
private
isWatching
=
false
;
constructor
(
private
readonly
file
:
URI
,
private
readonly
watchForExternalChanges
:
boolean
,
@
IFileService
private
readonly
fileService
:
IFileService
)
{
super
();
}
private
async
ensureWatching
():
Promise
<
void
>
{
if
(
this
.
isWatching
||
!
this
.
watchForExternalChanges
)
{
return
;
}
const
exists
=
await
this
.
fileService
.
exists
(
this
.
file
);
if
(
this
.
isWatching
||
!
exists
)
{
return
;
// file must exist to be watched
}
this
.
isWatching
=
true
;
this
.
_register
(
this
.
fileService
.
watch
(
this
.
file
));
this
.
_register
(
this
.
fileService
.
onFileChanges
(
e
=>
{
if
(
document
.
hasFocus
())
{
return
;
// ignore changes from ourselves by checking for focus
}
if
(
!
e
.
contains
(
this
.
file
,
FileChangeType
.
UPDATED
))
{
return
;
// not our file
}
this
.
onDidStorageChangeExternal
();
}));
}
private
async
onDidStorageChangeExternal
():
Promise
<
void
>
{
const
items
=
await
this
.
doGetItemsFromFile
();
this
.
cache
=
items
;
this
.
_onDidChangeItemsExternal
.
fire
({
items
});
}
async
getItems
():
Promise
<
Map
<
string
,
string
>>
{
if
(
!
this
.
cache
)
{
try
{
this
.
cache
=
await
this
.
doGetItemsFromFile
();
}
catch
(
error
)
{
this
.
cache
=
new
Map
();
}
}
return
this
.
cache
;
}
private
async
doGetItemsFromFile
():
Promise
<
Map
<
string
,
string
>>
{
await
this
.
pendingUpdate
;
const
itemsRaw
=
await
this
.
fileService
.
readFile
(
this
.
file
);
this
.
ensureWatching
();
// now that the file must exist, ensure we watch it for changes
return
serializableToMap
(
JSON
.
parse
(
itemsRaw
.
value
.
toString
()));
}
async
updateItems
(
request
:
IUpdateRequest
):
Promise
<
void
>
{
const
items
=
await
this
.
getItems
();
if
(
request
.
insert
)
{
request
.
insert
.
forEach
((
value
,
key
)
=>
items
.
set
(
key
,
value
));
}
if
(
request
.
delete
)
{
request
.
delete
.
forEach
(
key
=>
items
.
delete
(
key
));
}
await
this
.
pendingUpdate
;
this
.
_hasPendingUpdate
=
true
;
this
.
pendingUpdate
=
this
.
fileService
.
writeFile
(
this
.
file
,
VSBuffer
.
fromString
(
JSON
.
stringify
(
mapToSerializable
(
items
))))
.
then
(()
=>
{
this
.
ensureWatching
();
// now that the file must exist, ensure we watch it for changes
})
.
finally
(()
=>
{
this
.
_hasPendingUpdate
=
false
;
});
return
this
.
pendingUpdate
;
}
close
():
Promise
<
void
>
{
return
this
.
pendingUpdate
;
}
}
src/vs/platform/storage/common/storage.ts
浏览文件 @
5352ba33
...
...
@@ -7,11 +7,6 @@ import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/co
import
{
Event
,
Emitter
}
from
'
vs/base/common/event
'
;
import
{
Disposable
}
from
'
vs/base/common/lifecycle
'
;
import
{
isUndefinedOrNull
}
from
'
vs/base/common/types
'
;
import
{
IUpdateRequest
,
IStorageDatabase
}
from
'
vs/base/parts/storage/common/storage
'
;
import
{
serializableToMap
,
mapToSerializable
}
from
'
vs/base/common/map
'
;
import
{
VSBuffer
}
from
'
vs/base/common/buffer
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
IFileService
}
from
'
vs/platform/files/common/files
'
;
export
const
IStorageService
=
createDecorator
<
IStorageService
>
(
'
storageService
'
);
...
...
@@ -212,75 +207,6 @@ export class InMemoryStorageService extends Disposable implements IStorageServic
}
}
export
class
FileStorageDatabase
extends
Disposable
implements
IStorageDatabase
{
readonly
onDidChangeItemsExternal
=
Event
.
None
;
// TODO@Ben implement global UI storage events
private
cache
:
Map
<
string
,
string
>
|
undefined
;
private
pendingUpdate
:
Promise
<
void
>
=
Promise
.
resolve
();
private
_hasPendingUpdate
=
false
;
get
hasPendingUpdate
():
boolean
{
return
this
.
_hasPendingUpdate
;
}
constructor
(
private
readonly
file
:
URI
,
private
readonly
fileService
:
IFileService
)
{
super
();
}
async
getItems
():
Promise
<
Map
<
string
,
string
>>
{
if
(
!
this
.
cache
)
{
try
{
this
.
cache
=
await
this
.
doGetItemsFromFile
();
}
catch
(
error
)
{
this
.
cache
=
new
Map
();
}
}
return
this
.
cache
;
}
private
async
doGetItemsFromFile
():
Promise
<
Map
<
string
,
string
>>
{
await
this
.
pendingUpdate
;
const
itemsRaw
=
await
this
.
fileService
.
readFile
(
this
.
file
);
return
serializableToMap
(
JSON
.
parse
(
itemsRaw
.
value
.
toString
()));
}
async
updateItems
(
request
:
IUpdateRequest
):
Promise
<
void
>
{
const
items
=
await
this
.
getItems
();
if
(
request
.
insert
)
{
request
.
insert
.
forEach
((
value
,
key
)
=>
items
.
set
(
key
,
value
));
}
if
(
request
.
delete
)
{
request
.
delete
.
forEach
(
key
=>
items
.
delete
(
key
));
}
await
this
.
pendingUpdate
;
this
.
_hasPendingUpdate
=
true
;
this
.
pendingUpdate
=
this
.
fileService
.
writeFile
(
this
.
file
,
VSBuffer
.
fromString
(
JSON
.
stringify
(
mapToSerializable
(
items
))))
.
then
(()
=>
undefined
)
.
finally
(()
=>
{
this
.
_hasPendingUpdate
=
false
;
});
return
this
.
pendingUpdate
;
}
close
():
Promise
<
void
>
{
return
this
.
pendingUpdate
;
}
}
export
async
function
logStorage
(
global
:
Map
<
string
,
string
>
,
workspace
:
Map
<
string
,
string
>
,
globalPath
:
string
,
workspacePath
:
string
):
Promise
<
void
>
{
const
safeParse
=
(
value
:
string
)
=>
{
try
{
...
...
src/vs/platform/storage/test/
node
/storage.test.ts
→
src/vs/platform/storage/test/
electron-browser
/storage.test.ts
浏览文件 @
5352ba33
...
...
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import
{
equal
}
from
'
assert
'
;
import
{
FileStorageDatabase
}
from
'
vs/platform/storage/
common/storag
e
'
;
import
{
FileStorageDatabase
}
from
'
vs/platform/storage/
browser/storageServic
e
'
;
import
{
generateUuid
}
from
'
vs/base/common/uuid
'
;
import
{
join
}
from
'
vs/base/common/path
'
;
import
{
tmpdir
}
from
'
os
'
;
...
...
@@ -49,7 +49,7 @@ suite('Storage', () => {
});
test
(
'
File Based Storage
'
,
async
()
=>
{
let
storage
=
new
Storage
(
new
FileStorageDatabase
(
URI
.
file
(
join
(
testDir
,
'
storage.json
'
)),
fileService
));
let
storage
=
new
Storage
(
new
FileStorageDatabase
(
URI
.
file
(
join
(
testDir
,
'
storage.json
'
)),
f
alse
,
f
ileService
));
await
storage
.
init
();
...
...
@@ -63,7 +63,7 @@ suite('Storage', () => {
await
storage
.
close
();
storage
=
new
Storage
(
new
FileStorageDatabase
(
URI
.
file
(
join
(
testDir
,
'
storage.json
'
)),
fileService
));
storage
=
new
Storage
(
new
FileStorageDatabase
(
URI
.
file
(
join
(
testDir
,
'
storage.json
'
)),
f
alse
,
f
ileService
));
await
storage
.
init
();
...
...
@@ -81,7 +81,7 @@ suite('Storage', () => {
await
storage
.
close
();
storage
=
new
Storage
(
new
FileStorageDatabase
(
URI
.
file
(
join
(
testDir
,
'
storage.json
'
)),
fileService
));
storage
=
new
Storage
(
new
FileStorageDatabase
(
URI
.
file
(
join
(
testDir
,
'
storage.json
'
)),
f
alse
,
f
ileService
));
await
storage
.
init
();
...
...
@@ -89,4 +89,4 @@ suite('Storage', () => {
equal
(
storage
.
get
(
'
barNumber
'
,
'
undefinedNumber
'
),
'
undefinedNumber
'
);
equal
(
storage
.
get
(
'
barBoolean
'
,
'
undefinedBoolean
'
),
'
undefinedBoolean
'
);
});
});
\ No newline at end of file
});
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录