Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
掘金者说
vscode
提交
d92afa06
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,发现更多精彩内容 >>
提交
d92afa06
编写于
12月 19, 2019
作者:
B
Benjamin Pasero
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
editors - introduce and use EditorsObserver and use for MRU list
上级
c7da14d0
变更
8
展开全部
隐藏空白更改
内联
并排
Showing
8 changed file
with
876 addition
and
727 deletion
+876
-727
src/vs/workbench/browser/parts/editor/editor.ts
src/vs/workbench/browser/parts/editor/editor.ts
+10
-0
src/vs/workbench/browser/parts/editor/editorsObserver.ts
src/vs/workbench/browser/parts/editor/editorsObserver.ts
+296
-0
src/vs/workbench/services/editor/browser/editorService.ts
src/vs/workbench/services/editor/browser/editorService.ts
+12
-1
src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts
...ench/services/editor/test/browser/editorsObserver.test.ts
+538
-0
src/vs/workbench/services/history/browser/history.ts
src/vs/workbench/services/history/browser/history.ts
+12
-306
src/vs/workbench/services/history/common/history.ts
src/vs/workbench/services/history/common/history.ts
+2
-2
src/vs/workbench/services/history/test/history.test.ts
src/vs/workbench/services/history/test/history.test.ts
+3
-417
src/vs/workbench/test/workbenchTestServices.ts
src/vs/workbench/test/workbenchTestServices.ts
+3
-1
未找到文件。
src/vs/workbench/browser/parts/editor/editor.ts
浏览文件 @
d92afa06
...
...
@@ -151,4 +151,14 @@ export interface EditorServiceImpl extends IEditorService {
* Emitted when an editor failed to open.
*/
readonly
onDidOpenEditorFail
:
Event
<
IEditorIdentifier
>
;
/**
* Emitted when the list of most recently active editors change.
*/
readonly
onDidMostRecentlyActiveEditorsChange
:
Event
<
void
>
;
/**
* Access to the list of most recently active editors.
*/
readonly
mostRecentlyActiveEditors
:
ReadonlyArray
<
IEditorIdentifier
>
;
}
src/vs/workbench/browser/parts/editor/editorsObserver.ts
0 → 100644
浏览文件 @
d92afa06
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
{
IEditorInput
,
IEditorInputFactoryRegistry
,
IEditorIdentifier
,
GroupIdentifier
,
Extensions
}
from
'
vs/workbench/common/editor
'
;
import
{
dispose
,
Disposable
,
DisposableStore
}
from
'
vs/base/common/lifecycle
'
;
import
{
IStorageService
,
StorageScope
}
from
'
vs/platform/storage/common/storage
'
;
import
{
Registry
}
from
'
vs/platform/registry/common/platform
'
;
import
{
Event
,
Emitter
}
from
'
vs/base/common/event
'
;
import
{
IEditorGroupsService
,
IEditorGroup
,
EditorsOrder
,
GroupChangeKind
,
GroupsOrder
}
from
'
vs/workbench/services/editor/common/editorGroupsService
'
;
import
{
coalesce
}
from
'
vs/base/common/arrays
'
;
import
{
LinkedMap
,
Touch
}
from
'
vs/base/common/map
'
;
interface
ISerializedEditorsList
{
entries
:
ISerializedEditorIdentifier
[];
}
interface
ISerializedEditorIdentifier
{
groupId
:
GroupIdentifier
;
index
:
number
;
}
/**
* A observer of opened editors across all editor groups by most recently used.
* Rules:
* - the last editor in the list is the one most recently activated
* - the first editor in the list is the one that was activated the longest time ago
* - an editor that opens inactive will be placed behind the currently active editor
*/
export
class
EditorsObserver
extends
Disposable
{
private
static
readonly
STORAGE_KEY
=
'
editorsObserver.state
'
;
private
readonly
keyMap
=
new
Map
<
GroupIdentifier
,
Map
<
IEditorInput
,
IEditorIdentifier
>>
();
private
readonly
mostRecentEditorsMap
=
new
LinkedMap
<
IEditorIdentifier
,
IEditorIdentifier
>
();
private
readonly
_onDidChange
=
this
.
_register
(
new
Emitter
<
void
>
());
readonly
onDidChange
=
this
.
_onDidChange
.
event
;
get
editors
():
IEditorIdentifier
[]
{
return
this
.
mostRecentEditorsMap
.
values
();
}
constructor
(
@
IEditorGroupsService
private
editorGroupsService
:
IEditorGroupsService
,
@
IStorageService
private
readonly
storageService
:
IStorageService
)
{
super
();
this
.
registerListeners
();
}
private
registerListeners
():
void
{
this
.
_register
(
this
.
storageService
.
onWillSaveState
(()
=>
this
.
saveState
()));
this
.
_register
(
this
.
editorGroupsService
.
onDidAddGroup
(
group
=>
this
.
onGroupAdded
(
group
)));
this
.
editorGroupsService
.
whenRestored
.
then
(()
=>
this
.
loadState
());
}
private
onGroupAdded
(
group
:
IEditorGroup
):
void
{
// Make sure to add any already existing editor
// of the new group into our list in LRU order
const
groupEditorsMru
=
group
.
getEditors
(
EditorsOrder
.
MOST_RECENTLY_ACTIVE
);
for
(
let
i
=
groupEditorsMru
.
length
-
1
;
i
>=
0
;
i
--
)
{
this
.
addMostRecentEditor
(
group
,
groupEditorsMru
[
i
],
false
/* is not active */
);
}
// Make sure that active editor is put as first if group is active
if
(
this
.
editorGroupsService
.
activeGroup
===
group
&&
group
.
activeEditor
)
{
this
.
addMostRecentEditor
(
group
,
group
.
activeEditor
,
true
/* is active */
);
}
// Group Listeners
this
.
registerGroupListeners
(
group
);
}
private
registerGroupListeners
(
group
:
IEditorGroup
):
void
{
const
groupDisposables
=
new
DisposableStore
();
groupDisposables
.
add
(
group
.
onDidGroupChange
(
e
=>
{
switch
(
e
.
kind
)
{
// Group gets active: put active editor as most recent
case
GroupChangeKind
.
GROUP_ACTIVE
:
{
if
(
this
.
editorGroupsService
.
activeGroup
===
group
&&
group
.
activeEditor
)
{
this
.
addMostRecentEditor
(
group
,
group
.
activeEditor
,
true
/* is active */
);
}
break
;
}
// Editor gets active: put active editor as most recent
// if group is active, otherwise second most recent
case
GroupChangeKind
.
EDITOR_ACTIVE
:
{
if
(
e
.
editor
)
{
this
.
addMostRecentEditor
(
group
,
e
.
editor
,
this
.
editorGroupsService
.
activeGroup
===
group
);
}
break
;
}
// Editor opens: put it as second most recent
case
GroupChangeKind
.
EDITOR_OPEN
:
{
if
(
e
.
editor
)
{
this
.
addMostRecentEditor
(
group
,
e
.
editor
,
false
/* is not active */
);
}
break
;
}
// Editor closes: remove from recently opened
case
GroupChangeKind
.
EDITOR_CLOSE
:
{
if
(
e
.
editor
)
{
this
.
removeMostRecentEditor
(
group
,
e
.
editor
);
}
break
;
}
}
}));
// Make sure to cleanup on dispose
Event
.
once
(
group
.
onWillDispose
)(()
=>
dispose
(
groupDisposables
));
}
private
addMostRecentEditor
(
group
:
IEditorGroup
,
editor
:
IEditorInput
,
isActive
:
boolean
):
void
{
const
key
=
this
.
ensureKey
(
group
,
editor
);
const
mostRecentEditor
=
this
.
mostRecentEditorsMap
.
first
;
// Active or first entry: add to end of map
if
(
isActive
||
!
mostRecentEditor
)
{
this
.
mostRecentEditorsMap
.
set
(
key
,
key
,
mostRecentEditor
?
Touch
.
AsOld
/* make first */
:
undefined
);
}
// Otherwise: insert before most recent
else
{
// we have most recent editors. as such we
// put this newly opened editor right before
// the current most recent one because it cannot
// be the most recently active one unless
// it becomes active. but it is still more
// active then any other editor in the list.
this
.
mostRecentEditorsMap
.
set
(
key
,
key
,
Touch
.
AsOld
/* make first */
);
this
.
mostRecentEditorsMap
.
set
(
mostRecentEditor
,
mostRecentEditor
,
Touch
.
AsOld
/* make first */
);
}
// Event
this
.
_onDidChange
.
fire
();
}
private
removeMostRecentEditor
(
group
:
IEditorGroup
,
editor
:
IEditorInput
):
void
{
const
key
=
this
.
findKey
(
group
,
editor
);
if
(
key
)
{
// Remove from most recent editors
this
.
mostRecentEditorsMap
.
delete
(
key
);
// Remove from key map
const
map
=
this
.
keyMap
.
get
(
group
.
id
);
if
(
map
&&
map
.
delete
(
key
.
editor
)
&&
map
.
size
===
0
)
{
this
.
keyMap
.
delete
(
group
.
id
);
}
// Event
this
.
_onDidChange
.
fire
();
}
}
private
findKey
(
group
:
IEditorGroup
,
editor
:
IEditorInput
):
IEditorIdentifier
|
undefined
{
const
groupMap
=
this
.
keyMap
.
get
(
group
.
id
);
if
(
!
groupMap
)
{
return
undefined
;
}
return
groupMap
.
get
(
editor
);
}
private
ensureKey
(
group
:
IEditorGroup
,
editor
:
IEditorInput
):
IEditorIdentifier
{
let
groupMap
=
this
.
keyMap
.
get
(
group
.
id
);
if
(
!
groupMap
)
{
groupMap
=
new
Map
();
this
.
keyMap
.
set
(
group
.
id
,
groupMap
);
}
let
key
=
groupMap
.
get
(
editor
);
if
(
!
key
)
{
key
=
{
groupId
:
group
.
id
,
editor
};
groupMap
.
set
(
editor
,
key
);
}
return
key
;
}
private
saveState
():
void
{
if
(
this
.
mostRecentEditorsMap
.
isEmpty
())
{
this
.
storageService
.
remove
(
EditorsObserver
.
STORAGE_KEY
,
StorageScope
.
WORKSPACE
);
}
else
{
this
.
storageService
.
store
(
EditorsObserver
.
STORAGE_KEY
,
JSON
.
stringify
(
this
.
serialize
()),
StorageScope
.
WORKSPACE
);
}
}
private
serialize
():
ISerializedEditorsList
{
const
registry
=
Registry
.
as
<
IEditorInputFactoryRegistry
>
(
Extensions
.
EditorInputFactories
);
const
entries
=
this
.
mostRecentEditorsMap
.
values
();
const
mapGroupToSerializableEditorsOfGroup
=
new
Map
<
IEditorGroup
,
IEditorInput
[]
>
();
return
{
entries
:
coalesce
(
entries
.
map
(({
editor
,
groupId
})
=>
{
// Find group for entry
const
group
=
this
.
editorGroupsService
.
getGroup
(
groupId
);
if
(
!
group
)
{
return
undefined
;
}
// Find serializable editors of group
let
serializableEditorsOfGroup
=
mapGroupToSerializableEditorsOfGroup
.
get
(
group
);
if
(
!
serializableEditorsOfGroup
)
{
serializableEditorsOfGroup
=
group
.
getEditors
(
EditorsOrder
.
SEQUENTIAL
).
filter
(
editor
=>
{
const
factory
=
registry
.
getEditorInputFactory
(
editor
.
getTypeId
());
return
factory
?.
canSerialize
(
editor
);
});
mapGroupToSerializableEditorsOfGroup
.
set
(
group
,
serializableEditorsOfGroup
);
}
// Only store the index of the editor of that group
// which can be undefined if the editor is not serializable
const
index
=
serializableEditorsOfGroup
.
indexOf
(
editor
);
if
(
index
===
-
1
)
{
return
undefined
;
}
return
{
groupId
,
index
};
}))
};
}
private
loadState
():
void
{
const
serialized
=
this
.
storageService
.
get
(
EditorsObserver
.
STORAGE_KEY
,
StorageScope
.
WORKSPACE
);
// Previous state:
if
(
serialized
)
{
// Load editors map from persisted state
this
.
deserialize
(
JSON
.
parse
(
serialized
));
}
// No previous state: best we can do is add each editor
// from oldest to most recently used editor group
else
{
const
groups
=
this
.
editorGroupsService
.
getGroups
(
GroupsOrder
.
MOST_RECENTLY_ACTIVE
);
for
(
let
i
=
groups
.
length
-
1
;
i
>=
0
;
i
--
)
{
const
group
=
groups
[
i
];
const
groupEditorsMru
=
group
.
getEditors
(
EditorsOrder
.
MOST_RECENTLY_ACTIVE
);
for
(
let
i
=
groupEditorsMru
.
length
-
1
;
i
>=
0
;
i
--
)
{
this
.
addMostRecentEditor
(
group
,
groupEditorsMru
[
i
],
true
/* enforce as active to preserve order */
);
}
}
}
// Ensure we listen on group changes for those that exist on startup
for
(
const
group
of
this
.
editorGroupsService
.
groups
)
{
this
.
registerGroupListeners
(
group
);
}
}
private
deserialize
(
serialized
:
ISerializedEditorsList
):
void
{
const
mapValues
:
[
IEditorIdentifier
,
IEditorIdentifier
][]
=
[];
for
(
const
{
groupId
,
index
}
of
serialized
.
entries
)
{
// Find group for entry
const
group
=
this
.
editorGroupsService
.
getGroup
(
groupId
);
if
(
!
group
)
{
continue
;
}
// Find editor for entry
const
editor
=
group
.
getEditorByIndex
(
index
);
if
(
!
editor
)
{
continue
;
}
// Make sure key is registered as well
const
editorIdentifier
=
this
.
ensureKey
(
group
,
editor
);
mapValues
.
push
([
editorIdentifier
,
editorIdentifier
]);
}
// Fill map with deserialized values
this
.
mostRecentEditorsMap
.
fromJSON
(
mapValues
);
}
}
src/vs/workbench/services/editor/browser/editorService.ts
浏览文件 @
d92afa06
...
...
@@ -27,6 +27,7 @@ import { IEditorGroupView, IEditorOpeningEvent, EditorServiceImpl } from 'vs/wor
import
{
ILabelService
}
from
'
vs/platform/label/common/label
'
;
import
{
registerSingleton
}
from
'
vs/platform/instantiation/common/extensions
'
;
import
{
withNullAsUndefined
}
from
'
vs/base/common/types
'
;
import
{
EditorsObserver
}
from
'
vs/workbench/browser/parts/editor/editorsObserver
'
;
type
CachedEditorInput
=
ResourceEditorInput
|
IFileEditorInput
;
type
OpenInEditorGroup
=
IEditorGroup
|
GroupIdentifier
|
SIDE_GROUP_TYPE
|
ACTIVE_GROUP_TYPE
;
...
...
@@ -51,14 +52,19 @@ export class EditorService extends Disposable implements EditorServiceImpl {
private
readonly
_onDidOpenEditorFail
=
this
.
_register
(
new
Emitter
<
IEditorIdentifier
>
());
readonly
onDidOpenEditorFail
=
this
.
_onDidOpenEditorFail
.
event
;
private
readonly
_onDidMostRecentlyActiveEditorsChange
=
this
.
_register
(
new
Emitter
<
void
>
());
readonly
onDidMostRecentlyActiveEditorsChange
=
this
.
_onDidMostRecentlyActiveEditorsChange
.
event
;
//#endregion
private
fileInputFactory
:
IFileInputFactory
;
private
openEditorHandlers
:
IOpenEditorOverrideHandler
[]
=
[];
private
readonly
openEditorHandlers
:
IOpenEditorOverrideHandler
[]
=
[];
private
lastActiveEditor
:
IEditorInput
|
undefined
=
undefined
;
private
lastActiveGroupId
:
GroupIdentifier
|
undefined
=
undefined
;
private
readonly
editorsObserver
=
this
.
_register
(
this
.
instantiationService
.
createInstance
(
EditorsObserver
));
constructor
(
@
IEditorGroupsService
private
readonly
editorGroupService
:
IEditorGroupsService
,
@
IUntitledTextEditorService
private
readonly
untitledTextEditorService
:
IUntitledTextEditorService
,
...
...
@@ -80,6 +86,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
this
.
editorGroupService
.
whenRestored
.
then
(()
=>
this
.
onEditorsRestored
());
this
.
editorGroupService
.
onDidActiveGroupChange
(
group
=>
this
.
handleActiveEditorChange
(
group
));
this
.
editorGroupService
.
onDidAddGroup
(
group
=>
this
.
registerGroupListeners
(
group
as
IEditorGroupView
));
this
.
editorsObserver
.
onDidChange
(()
=>
this
.
_onDidMostRecentlyActiveEditorsChange
.
fire
());
}
private
onEditorsRestored
():
void
{
...
...
@@ -188,6 +195,10 @@ export class EditorService extends Disposable implements EditorServiceImpl {
return
editors
;
}
get
mostRecentlyActiveEditors
():
IEditorIdentifier
[]
{
return
this
.
editorsObserver
.
editors
;
}
get
activeEditor
():
IEditorInput
|
undefined
{
const
activeGroup
=
this
.
editorGroupService
.
activeGroup
;
...
...
src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts
0 → 100644
浏览文件 @
d92afa06
此差异已折叠。
点击以展开。
src/vs/workbench/services/history/browser/history.ts
浏览文件 @
d92afa06
...
...
@@ -7,7 +7,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import
{
URI
,
UriComponents
}
from
'
vs/base/common/uri
'
;
import
{
IEditor
}
from
'
vs/editor/common/editorCommon
'
;
import
{
ITextEditorOptions
,
IResourceInput
,
ITextEditorSelection
}
from
'
vs/platform/editor/common/editor
'
;
import
{
IEditorInput
,
IEditor
as
IBaseEditor
,
Extensions
as
EditorExtensions
,
EditorInput
,
IEditorCloseEvent
,
IEditorInputFactoryRegistry
,
toResource
,
IEditorIdentifier
,
GroupIdentifier
,
Extensions
}
from
'
vs/workbench/common/editor
'
;
import
{
IEditorInput
,
IEditor
as
IBaseEditor
,
Extensions
as
EditorExtensions
,
EditorInput
,
IEditorCloseEvent
,
IEditorInputFactoryRegistry
,
toResource
,
IEditorIdentifier
,
GroupIdentifier
}
from
'
vs/workbench/common/editor
'
;
import
{
IEditorService
}
from
'
vs/workbench/services/editor/common/editorService
'
;
import
{
IHistoryService
}
from
'
vs/workbench/services/history/common/history
'
;
import
{
FileChangesEvent
,
IFileService
,
FileChangeType
,
FILES_EXCLUDE_CONFIG
}
from
'
vs/platform/files/common/files
'
;
...
...
@@ -16,9 +16,9 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import
{
dispose
,
Disposable
,
DisposableStore
}
from
'
vs/base/common/lifecycle
'
;
import
{
IStorageService
,
StorageScope
}
from
'
vs/platform/storage/common/storage
'
;
import
{
Registry
}
from
'
vs/platform/registry/common/platform
'
;
import
{
Event
,
Emitter
}
from
'
vs/base/common/event
'
;
import
{
Event
}
from
'
vs/base/common/event
'
;
import
{
IConfigurationService
,
IConfigurationChangeEvent
}
from
'
vs/platform/configuration/common/configuration
'
;
import
{
IEditorGroupsService
,
IEditorGroup
,
EditorsOrder
,
GroupChangeKind
,
Group
sOrder
}
from
'
vs/workbench/services/editor/common/editorGroupsService
'
;
import
{
IEditorGroupsService
,
Editor
sOrder
}
from
'
vs/workbench/services/editor/common/editorGroupsService
'
;
import
{
getCodeEditor
,
ICodeEditor
}
from
'
vs/editor/browser/editorBrowser
'
;
import
{
getExcludes
,
ISearchConfiguration
}
from
'
vs/workbench/services/search/common/search
'
;
import
{
IExpression
}
from
'
vs/base/common/glob
'
;
...
...
@@ -34,9 +34,6 @@ import { withNullAsUndefined } from 'vs/base/common/types';
import
{
addDisposableListener
,
EventType
,
EventHelper
}
from
'
vs/base/browser/dom
'
;
import
{
IWorkspacesService
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
Schemas
}
from
'
vs/base/common/network
'
;
import
{
LinkedMap
,
Touch
}
from
'
vs/base/common/map
'
;
//#region Text Editor State helper
/**
* Stores the selection & view state of an editor and allows to compare it to other selection states.
...
...
@@ -86,295 +83,6 @@ export class TextEditorState {
}
}
//#endregion
//#region Editors History
interface
ISerializedEditorHistory
{
history
:
ISerializedEditorIdentifier
[];
}
interface
ISerializedEditorIdentifier
{
groupId
:
GroupIdentifier
;
index
:
number
;
}
/**
* A history of opened editors across all editor groups by most recently used.
* Rules:
* - the last editor in the history is the one most recently activated
* - the first editor in the history is the one that was activated the longest time ago
* - an editor that opens inactive will be placed behind the currently active editor
*/
export
class
EditorsHistory
extends
Disposable
{
private
static
readonly
STORAGE_KEY
=
'
history.editors
'
;
private
readonly
keyMap
=
new
Map
<
GroupIdentifier
,
Map
<
IEditorInput
,
IEditorIdentifier
>>
();
private
readonly
mostRecentEditorsMap
=
new
LinkedMap
<
IEditorIdentifier
,
IEditorIdentifier
>
();
private
readonly
_onDidChange
=
this
.
_register
(
new
Emitter
<
void
>
());
readonly
onDidChange
=
this
.
_onDidChange
.
event
;
get
editors
():
IEditorIdentifier
[]
{
return
this
.
mostRecentEditorsMap
.
values
();
}
constructor
(
@
IEditorGroupsService
private
editorGroupsService
:
IEditorGroupsService
,
@
IStorageService
private
readonly
storageService
:
IStorageService
)
{
super
();
this
.
registerListeners
();
}
private
registerListeners
():
void
{
this
.
_register
(
this
.
storageService
.
onWillSaveState
(()
=>
this
.
saveState
()));
this
.
_register
(
this
.
editorGroupsService
.
onDidAddGroup
(
group
=>
this
.
onGroupAdded
(
group
)));
this
.
editorGroupsService
.
whenRestored
.
then
(()
=>
this
.
loadState
());
}
private
onGroupAdded
(
group
:
IEditorGroup
):
void
{
// Make sure to add any already existing editor
// of the new group into our history in LRU order
const
groupEditorsMru
=
group
.
getEditors
(
EditorsOrder
.
MOST_RECENTLY_ACTIVE
);
for
(
let
i
=
groupEditorsMru
.
length
-
1
;
i
>=
0
;
i
--
)
{
this
.
addMostRecentEditor
(
group
,
groupEditorsMru
[
i
],
false
/* is not active */
);
}
// Make sure that active editor is put as first if group is active
if
(
this
.
editorGroupsService
.
activeGroup
===
group
&&
group
.
activeEditor
)
{
this
.
addMostRecentEditor
(
group
,
group
.
activeEditor
,
true
/* is active */
);
}
// Group Listeners
this
.
registerGroupListeners
(
group
);
}
private
registerGroupListeners
(
group
:
IEditorGroup
):
void
{
const
groupDisposables
=
new
DisposableStore
();
groupDisposables
.
add
(
group
.
onDidGroupChange
(
e
=>
{
switch
(
e
.
kind
)
{
// Group gets active: put active editor as most recent
case
GroupChangeKind
.
GROUP_ACTIVE
:
{
if
(
this
.
editorGroupsService
.
activeGroup
===
group
&&
group
.
activeEditor
)
{
this
.
addMostRecentEditor
(
group
,
group
.
activeEditor
,
true
/* is active */
);
}
break
;
}
// Editor gets active: put active editor as most recent
// if group is active, otherwise second most recent
case
GroupChangeKind
.
EDITOR_ACTIVE
:
{
if
(
e
.
editor
)
{
this
.
addMostRecentEditor
(
group
,
e
.
editor
,
this
.
editorGroupsService
.
activeGroup
===
group
);
}
break
;
}
// Editor opens: put it as second most recent
case
GroupChangeKind
.
EDITOR_OPEN
:
{
if
(
e
.
editor
)
{
this
.
addMostRecentEditor
(
group
,
e
.
editor
,
false
/* is not active */
);
}
break
;
}
// Editor closes: remove from recently opened
case
GroupChangeKind
.
EDITOR_CLOSE
:
{
if
(
e
.
editor
)
{
this
.
removeMostRecentEditor
(
group
,
e
.
editor
);
}
break
;
}
}
}));
// Make sure to cleanup on dispose
Event
.
once
(
group
.
onWillDispose
)(()
=>
dispose
(
groupDisposables
));
}
private
addMostRecentEditor
(
group
:
IEditorGroup
,
editor
:
IEditorInput
,
isActive
:
boolean
):
void
{
const
key
=
this
.
ensureKey
(
group
,
editor
);
const
mostRecentEditor
=
this
.
mostRecentEditorsMap
.
first
;
// Active or first entry: add to end of map
if
(
isActive
||
!
mostRecentEditor
)
{
this
.
mostRecentEditorsMap
.
set
(
key
,
key
,
mostRecentEditor
?
Touch
.
AsOld
/* make first */
:
undefined
);
}
// Otherwise: insert before most recent
else
{
// we have most recent editors. as such we
// put this newly opened editor right before
// the current most recent one because it cannot
// be the most recently active one unless
// it becomes active. but it is still more
// active then any other editor in the list.
this
.
mostRecentEditorsMap
.
set
(
key
,
key
,
Touch
.
AsOld
/* make first */
);
this
.
mostRecentEditorsMap
.
set
(
mostRecentEditor
,
mostRecentEditor
,
Touch
.
AsOld
/* make first */
);
}
// Event
this
.
_onDidChange
.
fire
();
}
private
removeMostRecentEditor
(
group
:
IEditorGroup
,
editor
:
IEditorInput
):
void
{
const
key
=
this
.
findKey
(
group
,
editor
);
if
(
key
)
{
// Remove from most recent editors
this
.
mostRecentEditorsMap
.
delete
(
key
);
// Remove from key map
const
map
=
this
.
keyMap
.
get
(
group
.
id
);
if
(
map
&&
map
.
delete
(
key
.
editor
)
&&
map
.
size
===
0
)
{
this
.
keyMap
.
delete
(
group
.
id
);
}
// Event
this
.
_onDidChange
.
fire
();
}
}
private
findKey
(
group
:
IEditorGroup
,
editor
:
IEditorInput
):
IEditorIdentifier
|
undefined
{
const
groupMap
=
this
.
keyMap
.
get
(
group
.
id
);
if
(
!
groupMap
)
{
return
undefined
;
}
return
groupMap
.
get
(
editor
);
}
private
ensureKey
(
group
:
IEditorGroup
,
editor
:
IEditorInput
):
IEditorIdentifier
{
let
groupMap
=
this
.
keyMap
.
get
(
group
.
id
);
if
(
!
groupMap
)
{
groupMap
=
new
Map
();
this
.
keyMap
.
set
(
group
.
id
,
groupMap
);
}
let
key
=
groupMap
.
get
(
editor
);
if
(
!
key
)
{
key
=
{
groupId
:
group
.
id
,
editor
};
groupMap
.
set
(
editor
,
key
);
}
return
key
;
}
private
saveState
():
void
{
if
(
this
.
mostRecentEditorsMap
.
isEmpty
())
{
this
.
storageService
.
remove
(
EditorsHistory
.
STORAGE_KEY
,
StorageScope
.
WORKSPACE
);
}
else
{
this
.
storageService
.
store
(
EditorsHistory
.
STORAGE_KEY
,
JSON
.
stringify
(
this
.
serialize
()),
StorageScope
.
WORKSPACE
);
}
}
private
serialize
():
ISerializedEditorHistory
{
const
registry
=
Registry
.
as
<
IEditorInputFactoryRegistry
>
(
Extensions
.
EditorInputFactories
);
const
history
=
this
.
mostRecentEditorsMap
.
values
();
const
mapGroupToSerializableEditorsOfGroup
=
new
Map
<
IEditorGroup
,
IEditorInput
[]
>
();
return
{
history
:
coalesce
(
history
.
map
(({
editor
,
groupId
})
=>
{
// Find group for entry
const
group
=
this
.
editorGroupsService
.
getGroup
(
groupId
);
if
(
!
group
)
{
return
undefined
;
}
// Find serializable editors of group
let
serializableEditorsOfGroup
=
mapGroupToSerializableEditorsOfGroup
.
get
(
group
);
if
(
!
serializableEditorsOfGroup
)
{
serializableEditorsOfGroup
=
group
.
getEditors
(
EditorsOrder
.
SEQUENTIAL
).
filter
(
editor
=>
{
const
factory
=
registry
.
getEditorInputFactory
(
editor
.
getTypeId
());
return
factory
?.
canSerialize
(
editor
);
});
mapGroupToSerializableEditorsOfGroup
.
set
(
group
,
serializableEditorsOfGroup
);
}
// Only store the index of the editor of that group
// which can be undefined if the editor is not serializable
const
index
=
serializableEditorsOfGroup
.
indexOf
(
editor
);
if
(
index
===
-
1
)
{
return
undefined
;
}
return
{
groupId
,
index
};
}))
};
}
private
loadState
():
void
{
const
serialized
=
this
.
storageService
.
get
(
EditorsHistory
.
STORAGE_KEY
,
StorageScope
.
WORKSPACE
);
// Previous state:
if
(
serialized
)
{
// Load history map from persisted state
this
.
deserialize
(
JSON
.
parse
(
serialized
));
}
// No previous state: best we can do is add each editor
// from oldest to most recently used editor group
else
{
const
groups
=
this
.
editorGroupsService
.
getGroups
(
GroupsOrder
.
MOST_RECENTLY_ACTIVE
);
for
(
let
i
=
groups
.
length
-
1
;
i
>=
0
;
i
--
)
{
const
group
=
groups
[
i
];
const
groupEditorsMru
=
group
.
getEditors
(
EditorsOrder
.
MOST_RECENTLY_ACTIVE
);
for
(
let
i
=
groupEditorsMru
.
length
-
1
;
i
>=
0
;
i
--
)
{
this
.
addMostRecentEditor
(
group
,
groupEditorsMru
[
i
],
true
/* enforce as active to preserve order */
);
}
}
}
// Ensure we listen on group changes for those that exist on startup
for
(
const
group
of
this
.
editorGroupsService
.
groups
)
{
this
.
registerGroupListeners
(
group
);
}
}
deserialize
(
serialized
:
ISerializedEditorHistory
):
void
{
const
mapValues
:
[
IEditorIdentifier
,
IEditorIdentifier
][]
=
[];
for
(
const
{
groupId
,
index
}
of
serialized
.
history
)
{
// Find group for entry
const
group
=
this
.
editorGroupsService
.
getGroup
(
groupId
);
if
(
!
group
)
{
continue
;
}
// Find editor for entry
const
editor
=
group
.
getEditorByIndex
(
index
);
if
(
!
editor
)
{
continue
;
}
// Make sure key is registered as well
const
editorIdentifier
=
this
.
ensureKey
(
group
,
editor
);
mapValues
.
push
([
editorIdentifier
,
editorIdentifier
]);
}
// Fill map with deserialized values
this
.
mostRecentEditorsMap
.
fromJSON
(
mapValues
);
}
}
//#endregion
interface
ISerializedEditorHistoryEntry
{
resourceJSON
?:
object
;
editorInputJSON
?:
{
typeId
:
string
;
deserialized
:
string
;
};
...
...
@@ -424,7 +132,7 @@ export class HistoryService extends Disposable implements IHistoryService {
this
.
_register
(
this
.
storageService
.
onWillSaveState
(()
=>
this
.
saveState
()));
this
.
_register
(
this
.
fileService
.
onFileChanges
(
event
=>
this
.
onFileChanges
(
event
)));
this
.
_register
(
this
.
resourceFilter
.
onExpressionChange
(()
=>
this
.
removeExcludedFromHistory
()));
this
.
_register
(
this
.
mostRecentlyUsedOpenEditors
.
onDid
Change
(()
=>
this
.
handleEditorEventInRecentEditorsStack
()));
this
.
_register
(
this
.
editorService
.
onDidMostRecentlyActiveEditors
Change
(()
=>
this
.
handleEditorEventInRecentEditorsStack
()));
// if the service is created late enough that an editor is already opened
// make sure to trigger the onActiveEditorChanged() to track the editor
...
...
@@ -1102,7 +810,7 @@ export class HistoryService extends Disposable implements IHistoryService {
this
.
editorHistoryListeners
.
clear
();
}
getHistory
():
Array
<
IEditorInput
|
IResourceInput
>
{
getHistory
():
Readonly
Array
<
IEditorInput
|
IResourceInput
>
{
this
.
ensureHistoryLoaded
();
return
this
.
history
.
slice
(
0
);
...
...
@@ -1266,12 +974,10 @@ export class HistoryService extends Disposable implements IHistoryService {
//#region Editor Most Recently Used History
private
readonly
mostRecentlyUsedOpenEditors
=
this
.
_register
(
this
.
instantiationService
.
createInstance
(
EditorsHistory
));
private
recentlyUsedEditorsStack
:
IEditorIdentifier
[]
|
undefined
=
undefined
;
private
recentlyUsedEditorsStack
:
ReadonlyArray
<
IEditorIdentifier
>
|
undefined
=
undefined
;
private
recentlyUsedEditorsStackIndex
=
0
;
private
recentlyUsedEditorsInGroupStack
:
IEditorIdentifier
[]
|
undefined
=
undefined
;
private
recentlyUsedEditorsInGroupStack
:
ReadonlyArray
<
IEditorIdentifier
>
|
undefined
=
undefined
;
private
recentlyUsedEditorsInGroupStackIndex
=
0
;
private
navigatingInRecentlyUsedEditorsStack
=
false
;
...
...
@@ -1309,15 +1015,15 @@ export class HistoryService extends Disposable implements IHistoryService {
}
}
private
ensureRecentlyUsedStack
(
indexModifier
:
(
index
:
number
)
=>
number
,
groupId
?:
GroupIdentifier
):
[
IEditorIdentifier
[]
,
number
]
{
let
editors
:
IEditorIdentifier
[]
;
private
ensureRecentlyUsedStack
(
indexModifier
:
(
index
:
number
)
=>
number
,
groupId
?:
GroupIdentifier
):
[
ReadonlyArray
<
IEditorIdentifier
>
,
number
]
{
let
editors
:
ReadonlyArray
<
IEditorIdentifier
>
;
let
index
:
number
;
const
group
=
typeof
groupId
===
'
number
'
?
this
.
editorGroupService
.
getGroup
(
groupId
)
:
undefined
;
// Across groups
if
(
!
group
)
{
editors
=
this
.
recentlyUsedEditorsStack
||
this
.
mostRecentlyUsedOpenEditors
.
e
ditors
;
editors
=
this
.
recentlyUsedEditorsStack
||
this
.
editorService
.
mostRecentlyActiveE
ditors
;
index
=
this
.
recentlyUsedEditorsStackIndex
;
}
...
...
@@ -1362,8 +1068,8 @@ export class HistoryService extends Disposable implements IHistoryService {
}
}
getMostRecentlyUsedOpenEditors
():
Array
<
IEditorIdentifier
>
{
return
this
.
mostRecentlyUsedOpenEditors
.
e
ditors
;
getMostRecentlyUsedOpenEditors
():
Readonly
Array
<
IEditorIdentifier
>
{
return
this
.
editorService
.
mostRecentlyActiveE
ditors
;
}
//#endregion
...
...
src/vs/workbench/services/history/common/history.ts
浏览文件 @
d92afa06
...
...
@@ -57,7 +57,7 @@ export interface IHistoryService {
/**
* Get the entire history of editors that were opened.
*/
getHistory
():
Array
<
IEditorInput
|
IResourceInput
>
;
getHistory
():
Readonly
Array
<
IEditorInput
|
IResourceInput
>
;
/**
* Looking at the editor history, returns the workspace root of the last file that was
...
...
@@ -91,5 +91,5 @@ export interface IHistoryService {
/**
* Get a list of most recently used editors that are open.
*/
getMostRecentlyUsedOpenEditors
():
Array
<
IEditorIdentifier
>
;
getMostRecentlyUsedOpenEditors
():
Readonly
Array
<
IEditorIdentifier
>
;
}
src/vs/workbench/services/history/test/history.test.ts
浏览文件 @
d92afa06
...
...
@@ -11,18 +11,16 @@ import { Registry } from 'vs/platform/registry/common/platform';
import
{
EditorPart
}
from
'
vs/workbench/browser/parts/editor/editorPart
'
;
import
{
IEditorRegistry
,
EditorDescriptor
,
Extensions
}
from
'
vs/workbench/browser/editor
'
;
import
{
SyncDescriptor
}
from
'
vs/platform/instantiation/common/descriptors
'
;
import
{
GroupDirection
,
IEditorGroupsService
}
from
'
vs/workbench/services/editor/common/editorGroupsService
'
;
import
{
EditorActivation
,
IEditorModel
}
from
'
vs/platform/editor/common/editor
'
;
import
{
IEditorGroupsService
}
from
'
vs/workbench/services/editor/common/editorGroupsService
'
;
import
{
IEditorModel
}
from
'
vs/platform/editor/common/editor
'
;
import
{
BaseEditor
}
from
'
vs/workbench/browser/parts/editor/baseEditor
'
;
import
{
NullTelemetryService
}
from
'
vs/platform/telemetry/common/telemetryUtils
'
;
import
{
TestThemeService
}
from
'
vs/platform/theme/test/common/testThemeService
'
;
import
{
CancellationToken
}
from
'
vs/base/common/cancellation
'
;
import
{
EditorsHistory
,
HistoryService
}
from
'
vs/workbench/services/history/browser/history
'
;
import
{
WillSaveStateReason
}
from
'
vs/platform/storage/common/storage
'
;
import
{
HistoryService
}
from
'
vs/workbench/services/history/browser/history
'
;
import
{
IInstantiationService
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
IEditorService
}
from
'
vs/workbench/services/editor/common/editorService
'
;
import
{
EditorService
}
from
'
vs/workbench/services/editor/browser/editorService
'
;
import
{
timeout
}
from
'
vs/base/common/async
'
;
import
{
IDisposable
,
dispose
}
from
'
vs/base/common/lifecycle
'
;
import
{
IHistoryService
}
from
'
vs/workbench/services/history/common/history
'
;
...
...
@@ -180,418 +178,6 @@ suite('HistoryService', function () {
part
.
dispose
();
});
suite
(
'
EditorHistory
'
,
function
()
{
test
(
'
basics (single group)
'
,
async
()
=>
{
const
instantiationService
=
workbenchInstantiationService
();
const
part
=
instantiationService
.
createInstance
(
EditorPart
);
part
.
create
(
document
.
createElement
(
'
div
'
));
part
.
layout
(
400
,
300
);
await
part
.
whenRestored
;
const
history
=
new
EditorsHistory
(
part
,
new
TestStorageService
());
let
historyChangeListenerCalled
=
false
;
const
listener
=
history
.
onDidChange
(()
=>
{
historyChangeListenerCalled
=
true
;
});
let
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
0
);
assert
.
equal
(
historyChangeListenerCalled
,
false
);
const
input1
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar1
'
));
await
part
.
activeGroup
.
openEditor
(
input1
,
EditorOptions
.
create
({
pinned
:
true
}));
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
1
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
part
.
activeGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input1
);
assert
.
equal
(
historyChangeListenerCalled
,
true
);
const
input2
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar2
'
));
const
input3
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar3
'
));
await
part
.
activeGroup
.
openEditor
(
input2
,
EditorOptions
.
create
({
pinned
:
true
}));
await
part
.
activeGroup
.
openEditor
(
input3
,
EditorOptions
.
create
({
pinned
:
true
}));
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
3
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
part
.
activeGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input3
);
assert
.
equal
(
currentHistory
[
1
].
groupId
,
part
.
activeGroup
.
id
);
assert
.
equal
(
currentHistory
[
1
].
editor
,
input2
);
assert
.
equal
(
currentHistory
[
2
].
groupId
,
part
.
activeGroup
.
id
);
assert
.
equal
(
currentHistory
[
2
].
editor
,
input1
);
await
part
.
activeGroup
.
openEditor
(
input2
,
EditorOptions
.
create
({
pinned
:
true
}));
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
3
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
part
.
activeGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input2
);
assert
.
equal
(
currentHistory
[
1
].
groupId
,
part
.
activeGroup
.
id
);
assert
.
equal
(
currentHistory
[
1
].
editor
,
input3
);
assert
.
equal
(
currentHistory
[
2
].
groupId
,
part
.
activeGroup
.
id
);
assert
.
equal
(
currentHistory
[
2
].
editor
,
input1
);
historyChangeListenerCalled
=
false
;
await
part
.
activeGroup
.
closeEditor
(
input1
);
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
2
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
part
.
activeGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input2
);
assert
.
equal
(
currentHistory
[
1
].
groupId
,
part
.
activeGroup
.
id
);
assert
.
equal
(
currentHistory
[
1
].
editor
,
input3
);
assert
.
equal
(
historyChangeListenerCalled
,
true
);
await
part
.
activeGroup
.
closeAllEditors
();
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
0
);
part
.
dispose
();
listener
.
dispose
();
});
test
(
'
basics (multi group)
'
,
async
()
=>
{
const
instantiationService
=
workbenchInstantiationService
();
const
part
=
instantiationService
.
createInstance
(
EditorPart
);
part
.
create
(
document
.
createElement
(
'
div
'
));
part
.
layout
(
400
,
300
);
await
part
.
whenRestored
;
const
rootGroup
=
part
.
activeGroup
;
const
history
=
new
EditorsHistory
(
part
,
new
TestStorageService
());
let
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
0
);
const
sideGroup
=
part
.
addGroup
(
rootGroup
,
GroupDirection
.
RIGHT
);
const
input1
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar1
'
));
await
rootGroup
.
openEditor
(
input1
,
EditorOptions
.
create
({
pinned
:
true
,
activation
:
EditorActivation
.
ACTIVATE
}));
await
sideGroup
.
openEditor
(
input1
,
EditorOptions
.
create
({
pinned
:
true
,
activation
:
EditorActivation
.
ACTIVATE
}));
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
2
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
sideGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input1
);
assert
.
equal
(
currentHistory
[
1
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
1
].
editor
,
input1
);
await
rootGroup
.
openEditor
(
input1
,
EditorOptions
.
create
({
pinned
:
true
,
activation
:
EditorActivation
.
ACTIVATE
}));
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
2
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input1
);
assert
.
equal
(
currentHistory
[
1
].
groupId
,
sideGroup
.
id
);
assert
.
equal
(
currentHistory
[
1
].
editor
,
input1
);
// Opening an editor inactive should not change
// the most recent editor, but rather put it behind
const
input2
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar2
'
));
await
rootGroup
.
openEditor
(
input2
,
EditorOptions
.
create
({
inactive
:
true
}));
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
3
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input1
);
assert
.
equal
(
currentHistory
[
1
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
1
].
editor
,
input2
);
assert
.
equal
(
currentHistory
[
2
].
groupId
,
sideGroup
.
id
);
assert
.
equal
(
currentHistory
[
2
].
editor
,
input1
);
await
rootGroup
.
closeAllEditors
();
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
1
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
sideGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input1
);
await
sideGroup
.
closeAllEditors
();
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
0
);
part
.
dispose
();
});
test
(
'
copy group
'
,
async
()
=>
{
const
instantiationService
=
workbenchInstantiationService
();
const
part
=
instantiationService
.
createInstance
(
EditorPart
);
part
.
create
(
document
.
createElement
(
'
div
'
));
part
.
layout
(
400
,
300
);
await
part
.
whenRestored
;
const
history
=
new
EditorsHistory
(
part
,
new
TestStorageService
());
const
input1
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar1
'
));
const
input2
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar2
'
));
const
input3
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar3
'
));
const
rootGroup
=
part
.
activeGroup
;
await
rootGroup
.
openEditor
(
input1
,
EditorOptions
.
create
({
pinned
:
true
}));
await
rootGroup
.
openEditor
(
input2
,
EditorOptions
.
create
({
pinned
:
true
}));
await
rootGroup
.
openEditor
(
input3
,
EditorOptions
.
create
({
pinned
:
true
}));
let
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
3
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input3
);
assert
.
equal
(
currentHistory
[
1
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
1
].
editor
,
input2
);
assert
.
equal
(
currentHistory
[
2
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
2
].
editor
,
input1
);
const
copiedGroup
=
part
.
copyGroup
(
rootGroup
,
rootGroup
,
GroupDirection
.
RIGHT
);
copiedGroup
.
setActive
(
true
);
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
6
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
copiedGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input3
);
assert
.
equal
(
currentHistory
[
1
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
1
].
editor
,
input3
);
assert
.
equal
(
currentHistory
[
2
].
groupId
,
copiedGroup
.
id
);
assert
.
equal
(
currentHistory
[
2
].
editor
,
input2
);
assert
.
equal
(
currentHistory
[
3
].
groupId
,
copiedGroup
.
id
);
assert
.
equal
(
currentHistory
[
3
].
editor
,
input1
);
assert
.
equal
(
currentHistory
[
4
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
4
].
editor
,
input2
);
assert
.
equal
(
currentHistory
[
5
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
5
].
editor
,
input1
);
part
.
dispose
();
});
test
(
'
initial editors are part of history and state is persisted & restored (single group)
'
,
async
()
=>
{
const
instantiationService
=
workbenchInstantiationService
();
instantiationService
.
invokeFunction
(
accessor
=>
Registry
.
as
<
IEditorInputFactoryRegistry
>
(
EditorExtensions
.
EditorInputFactories
).
start
(
accessor
));
const
part
=
instantiationService
.
createInstance
(
EditorPart
);
part
.
create
(
document
.
createElement
(
'
div
'
));
part
.
layout
(
400
,
300
);
await
part
.
whenRestored
;
const
rootGroup
=
part
.
activeGroup
;
const
input1
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar1
'
));
const
input2
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar2
'
));
const
input3
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar3
'
));
await
rootGroup
.
openEditor
(
input1
,
EditorOptions
.
create
({
pinned
:
true
}));
await
rootGroup
.
openEditor
(
input2
,
EditorOptions
.
create
({
pinned
:
true
}));
await
rootGroup
.
openEditor
(
input3
,
EditorOptions
.
create
({
pinned
:
true
}));
const
storage
=
new
TestStorageService
();
const
history
=
new
EditorsHistory
(
part
,
storage
);
await
part
.
whenRestored
;
let
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
3
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input3
);
assert
.
equal
(
currentHistory
[
1
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
1
].
editor
,
input2
);
assert
.
equal
(
currentHistory
[
2
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
2
].
editor
,
input1
);
storage
.
_onWillSaveState
.
fire
({
reason
:
WillSaveStateReason
.
SHUTDOWN
});
const
restoredHistory
=
new
EditorsHistory
(
part
,
storage
);
await
part
.
whenRestored
;
currentHistory
=
restoredHistory
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
3
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input3
);
assert
.
equal
(
currentHistory
[
1
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
1
].
editor
,
input2
);
assert
.
equal
(
currentHistory
[
2
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
2
].
editor
,
input1
);
part
.
dispose
();
});
test
(
'
initial editors are part of history (multi group)
'
,
async
()
=>
{
const
instantiationService
=
workbenchInstantiationService
();
const
part
=
instantiationService
.
createInstance
(
EditorPart
);
part
.
create
(
document
.
createElement
(
'
div
'
));
part
.
layout
(
400
,
300
);
await
part
.
whenRestored
;
const
rootGroup
=
part
.
activeGroup
;
const
input1
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar1
'
));
const
input2
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar2
'
));
const
input3
=
new
HistoryTestEditorInput
(
URI
.
parse
(
'
foo://bar3
'
));
await
rootGroup
.
openEditor
(
input1
,
EditorOptions
.
create
({
pinned
:
true
}));
await
rootGroup
.
openEditor
(
input2
,
EditorOptions
.
create
({
pinned
:
true
}));
const
sideGroup
=
part
.
addGroup
(
rootGroup
,
GroupDirection
.
RIGHT
);
await
sideGroup
.
openEditor
(
input3
,
EditorOptions
.
create
({
pinned
:
true
}));
const
storage
=
new
TestStorageService
();
const
history
=
new
EditorsHistory
(
part
,
storage
);
await
part
.
whenRestored
;
let
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
3
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
sideGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input3
);
assert
.
equal
(
currentHistory
[
1
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
1
].
editor
,
input2
);
assert
.
equal
(
currentHistory
[
2
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
2
].
editor
,
input1
);
storage
.
_onWillSaveState
.
fire
({
reason
:
WillSaveStateReason
.
SHUTDOWN
});
const
restoredHistory
=
new
EditorsHistory
(
part
,
storage
);
await
part
.
whenRestored
;
currentHistory
=
restoredHistory
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
3
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
sideGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input3
);
assert
.
equal
(
currentHistory
[
1
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
1
].
editor
,
input2
);
assert
.
equal
(
currentHistory
[
2
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
2
].
editor
,
input1
);
part
.
dispose
();
});
test
(
'
history does not restore editors that cannot be serialized
'
,
async
()
=>
{
const
instantiationService
=
workbenchInstantiationService
();
instantiationService
.
invokeFunction
(
accessor
=>
Registry
.
as
<
IEditorInputFactoryRegistry
>
(
EditorExtensions
.
EditorInputFactories
).
start
(
accessor
));
const
part
=
instantiationService
.
createInstance
(
EditorPart
);
part
.
create
(
document
.
createElement
(
'
div
'
));
part
.
layout
(
400
,
300
);
await
part
.
whenRestored
;
const
rootGroup
=
part
.
activeGroup
;
const
input1
=
new
TestEditorInput
(
URI
.
parse
(
'
foo://bar1
'
));
await
rootGroup
.
openEditor
(
input1
,
EditorOptions
.
create
({
pinned
:
true
}));
const
storage
=
new
TestStorageService
();
const
history
=
new
EditorsHistory
(
part
,
storage
);
await
part
.
whenRestored
;
let
currentHistory
=
history
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
1
);
assert
.
equal
(
currentHistory
[
0
].
groupId
,
rootGroup
.
id
);
assert
.
equal
(
currentHistory
[
0
].
editor
,
input1
);
storage
.
_onWillSaveState
.
fire
({
reason
:
WillSaveStateReason
.
SHUTDOWN
});
const
restoredHistory
=
new
EditorsHistory
(
part
,
storage
);
await
part
.
whenRestored
;
currentHistory
=
restoredHistory
.
editors
;
assert
.
equal
(
currentHistory
.
length
,
0
);
part
.
dispose
();
});
test
(
'
open next/previous recently used editor (single group)
'
,
async
()
=>
{
const
[
part
,
historyService
]
=
await
createServices
();
const
input1
=
new
TestEditorInput
(
URI
.
parse
(
'
foo://bar1
'
));
const
input2
=
new
TestEditorInput
(
URI
.
parse
(
'
foo://bar2
'
));
await
part
.
activeGroup
.
openEditor
(
input1
,
EditorOptions
.
create
({
pinned
:
true
}));
assert
.
equal
(
part
.
activeGroup
.
activeEditor
,
input1
);
await
part
.
activeGroup
.
openEditor
(
input2
,
EditorOptions
.
create
({
pinned
:
true
}));
assert
.
equal
(
part
.
activeGroup
.
activeEditor
,
input2
);
historyService
.
openPreviouslyUsedEditor
();
assert
.
equal
(
part
.
activeGroup
.
activeEditor
,
input1
);
historyService
.
openNextRecentlyUsedEditor
();
assert
.
equal
(
part
.
activeGroup
.
activeEditor
,
input2
);
historyService
.
openPreviouslyUsedEditor
(
part
.
activeGroup
.
id
);
assert
.
equal
(
part
.
activeGroup
.
activeEditor
,
input1
);
historyService
.
openNextRecentlyUsedEditor
(
part
.
activeGroup
.
id
);
assert
.
equal
(
part
.
activeGroup
.
activeEditor
,
input2
);
part
.
dispose
();
});
test
(
'
open next/previous recently used editor (multi group)
'
,
async
()
=>
{
const
[
part
,
historyService
]
=
await
createServices
();
const
rootGroup
=
part
.
activeGroup
;
const
input1
=
new
TestEditorInput
(
URI
.
parse
(
'
foo://bar1
'
));
const
input2
=
new
TestEditorInput
(
URI
.
parse
(
'
foo://bar2
'
));
const
sideGroup
=
part
.
addGroup
(
rootGroup
,
GroupDirection
.
RIGHT
);
await
rootGroup
.
openEditor
(
input1
,
EditorOptions
.
create
({
pinned
:
true
}));
await
sideGroup
.
openEditor
(
input2
,
EditorOptions
.
create
({
pinned
:
true
}));
historyService
.
openPreviouslyUsedEditor
();
assert
.
equal
(
part
.
activeGroup
,
rootGroup
);
assert
.
equal
(
rootGroup
.
activeEditor
,
input1
);
historyService
.
openNextRecentlyUsedEditor
();
assert
.
equal
(
part
.
activeGroup
,
sideGroup
);
assert
.
equal
(
sideGroup
.
activeEditor
,
input2
);
part
.
dispose
();
});
test
(
'
open next/previous recently is reset when other input opens
'
,
async
()
=>
{
const
[
part
,
historyService
]
=
await
createServices
();
const
input1
=
new
TestEditorInput
(
URI
.
parse
(
'
foo://bar1
'
));
const
input2
=
new
TestEditorInput
(
URI
.
parse
(
'
foo://bar2
'
));
const
input3
=
new
TestEditorInput
(
URI
.
parse
(
'
foo://bar3
'
));
const
input4
=
new
TestEditorInput
(
URI
.
parse
(
'
foo://bar4
'
));
await
part
.
activeGroup
.
openEditor
(
input1
,
EditorOptions
.
create
({
pinned
:
true
}));
await
part
.
activeGroup
.
openEditor
(
input2
,
EditorOptions
.
create
({
pinned
:
true
}));
await
part
.
activeGroup
.
openEditor
(
input3
,
EditorOptions
.
create
({
pinned
:
true
}));
historyService
.
openPreviouslyUsedEditor
();
assert
.
equal
(
part
.
activeGroup
.
activeEditor
,
input2
);
await
timeout
(
0
);
await
part
.
activeGroup
.
openEditor
(
input4
,
EditorOptions
.
create
({
pinned
:
true
}));
historyService
.
openPreviouslyUsedEditor
();
assert
.
equal
(
part
.
activeGroup
.
activeEditor
,
input2
);
historyService
.
openNextRecentlyUsedEditor
();
assert
.
equal
(
part
.
activeGroup
.
activeEditor
,
input4
);
part
.
dispose
();
});
});
});
src/vs/workbench/test/workbenchTestServices.ts
浏览文件 @
d92afa06
...
...
@@ -374,7 +374,7 @@ export class TestHistoryService implements IHistoryService {
remove
(
_input
:
IEditorInput
|
IResourceInput
):
void
{
}
clear
():
void
{
}
clearRecentlyOpened
():
void
{
}
getHistory
():
Array
<
IEditorInput
|
IResourceInput
>
{
return
[];
}
getHistory
():
Readonly
Array
<
IEditorInput
|
IResourceInput
>
{
return
[];
}
openNextRecentlyUsedEditor
(
group
?:
GroupIdentifier
):
void
{
}
openPreviouslyUsedEditor
(
group
?:
GroupIdentifier
):
void
{
}
getMostRecentlyUsedOpenEditors
():
Array
<
IEditorIdentifier
>
{
return
[];
}
...
...
@@ -902,11 +902,13 @@ export class TestEditorService implements EditorServiceImpl {
onDidVisibleEditorsChange
:
Event
<
void
>
=
Event
.
None
;
onDidCloseEditor
:
Event
<
IEditorCloseEvent
>
=
Event
.
None
;
onDidOpenEditorFail
:
Event
<
IEditorIdentifier
>
=
Event
.
None
;
onDidMostRecentlyActiveEditorsChange
:
Event
<
void
>
=
Event
.
None
;
activeControl
!
:
IVisibleEditor
;
activeTextEditorWidget
:
any
;
activeEditor
!
:
IEditorInput
;
editors
:
ReadonlyArray
<
IEditorInput
>
=
[];
mostRecentlyActiveEditors
:
ReadonlyArray
<
IEditorIdentifier
>
=
[];
visibleControls
:
ReadonlyArray
<
IVisibleEditor
>
=
[];
visibleTextEditorWidgets
=
[];
visibleEditors
:
ReadonlyArray
<
IEditorInput
>
=
[];
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录