Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
4276d2c3
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,发现更多精彩内容 >>
未验证
提交
4276d2c3
编写于
3月 11, 2019
作者:
M
Martin Aeschlimann
提交者:
GitHub
3月 11, 2019
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #70071 from Microsoft/aeschli/recentWithLabel
Store label with recent entry
上级
860f5a99
a8db84c3
变更
21
隐藏空白更改
内联
并排
Showing
21 changed file
with
456 addition
and
352 deletion
+456
-352
src/vs/base/common/labels.ts
src/vs/base/common/labels.ts
+14
-0
src/vs/code/electron-main/windows.ts
src/vs/code/electron-main/windows.ts
+28
-23
src/vs/platform/history/common/history.ts
src/vs/platform/history/common/history.ts
+36
-6
src/vs/platform/history/electron-main/historyMainService.ts
src/vs/platform/history/electron-main/historyMainService.ts
+101
-118
src/vs/platform/history/electron-main/historyStorage.ts
src/vs/platform/history/electron-main/historyStorage.ts
+41
-22
src/vs/platform/history/test/electron-main/historyStorage.test.ts
...latform/history/test/electron-main/historyStorage.test.ts
+72
-29
src/vs/platform/windows/common/windows.ts
src/vs/platform/windows/common/windows.ts
+4
-3
src/vs/platform/windows/electron-browser/windowService.ts
src/vs/platform/windows/electron-browser/windowService.ts
+18
-1
src/vs/platform/windows/electron-main/windowsService.ts
src/vs/platform/windows/electron-main/windowsService.ts
+7
-10
src/vs/platform/windows/node/windowsIpc.ts
src/vs/platform/windows/node/windowsIpc.ts
+19
-19
src/vs/platform/workspaces/common/workspaces.ts
src/vs/platform/workspaces/common/workspaces.ts
+2
-2
src/vs/platform/workspaces/electron-main/workspacesMainService.ts
...latform/workspaces/electron-main/workspacesMainService.ts
+21
-17
src/vs/platform/workspaces/node/workspacesIpc.ts
src/vs/platform/workspaces/node/workspacesIpc.ts
+7
-0
src/vs/workbench/api/node/apiCommands.ts
src/vs/workbench/api/node/apiCommands.ts
+9
-5
src/vs/workbench/browser/dnd.ts
src/vs/workbench/browser/dnd.ts
+4
-3
src/vs/workbench/browser/parts/titlebar/menubarControl.ts
src/vs/workbench/browser/parts/titlebar/menubarControl.ts
+11
-14
src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts
src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts
+18
-39
src/vs/workbench/electron-browser/actions/windowActions.ts
src/vs/workbench/electron-browser/actions/windowActions.ts
+32
-34
src/vs/workbench/services/dialogs/electron-browser/dialogService.ts
...kbench/services/dialogs/electron-browser/dialogService.ts
+1
-1
src/vs/workbench/services/workspace/node/workspaceEditingService.ts
...kbench/services/workspace/node/workspaceEditingService.ts
+9
-4
src/vs/workbench/test/workbenchTestServices.ts
src/vs/workbench/test/workbenchTestServices.ts
+2
-2
未找到文件。
src/vs/base/common/labels.ts
浏览文件 @
4276d2c3
...
...
@@ -9,6 +9,7 @@ import { endsWith, ltrim, startsWithIgnoreCase, rtrim, startsWith } from 'vs/bas
import
{
Schemas
}
from
'
vs/base/common/network
'
;
import
{
isLinux
,
isWindows
,
isMacintosh
}
from
'
vs/base/common/platform
'
;
import
{
isEqual
,
basename
}
from
'
vs/base/common/resources
'
;
import
{
CharCode
}
from
'
vs/base/common/charCode
'
;
export
interface
IWorkspaceFolderProvider
{
getWorkspaceFolder
(
resource
:
URI
):
{
uri
:
URI
,
name
?:
string
}
|
null
;
...
...
@@ -383,3 +384,16 @@ export function mnemonicButtonLabel(label: string): string {
export
function
unmnemonicLabel
(
label
:
string
):
string
{
return
label
.
replace
(
/&/g
,
'
&&
'
);
}
/**
* Splits a path in name and parent path, supporting both '/' and '\'
*/
export
function
splitName
(
fullPath
:
string
):
{
name
:
string
,
parentPath
:
string
}
{
for
(
let
i
=
fullPath
.
length
-
1
;
i
>=
1
;
i
--
)
{
const
code
=
fullPath
.
charCodeAt
(
i
);
if
(
code
===
CharCode
.
Slash
||
code
===
CharCode
.
Backslash
)
{
return
{
parentPath
:
fullPath
.
substr
(
0
,
i
),
name
:
fullPath
.
substr
(
i
+
1
)
};
}
}
return
{
parentPath
:
''
,
name
:
fullPath
};
}
src/vs/code/electron-main/windows.ts
浏览文件 @
4276d2c3
...
...
@@ -24,9 +24,9 @@ import { Event as CommonEvent, Emitter } from 'vs/base/common/event';
import
product
from
'
vs/platform/product/node/product
'
;
import
{
ITelemetryService
}
from
'
vs/platform/telemetry/common/telemetry
'
;
import
{
IWindowsMainService
,
IOpenConfiguration
,
IWindowsCountChangedEvent
,
ICodeWindow
,
IWindowState
as
ISingleWindowState
,
WindowMode
}
from
'
vs/platform/windows/electron-main/windows
'
;
import
{
IHistoryMainService
}
from
'
vs/platform/history/common/history
'
;
import
{
IHistoryMainService
,
IRecent
}
from
'
vs/platform/history/common/history
'
;
import
{
IProcessEnvironment
,
isMacintosh
,
isWindows
}
from
'
vs/base/common/platform
'
;
import
{
IWorkspacesMainService
,
IWorkspaceIdentifier
,
WORKSPACE_FILTER
,
ISingleFolderWorkspaceIdentifier
,
isSingleFolderWorkspaceIdentifier
,
hasWorkspaceFileExtension
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
IWorkspacesMainService
,
IWorkspaceIdentifier
,
WORKSPACE_FILTER
,
isSingleFolderWorkspaceIdentifier
,
hasWorkspaceFileExtension
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
IInstantiationService
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
mnemonicButtonLabel
}
from
'
vs/base/common/labels
'
;
import
{
Schemas
}
from
'
vs/base/common/network
'
;
...
...
@@ -37,6 +37,7 @@ import { exists } from 'vs/base/node/pfs';
import
{
getComparisonKey
,
isEqual
,
normalizePath
,
basename
as
resourcesBasename
,
originalFSPath
,
hasTrailingPathSeparator
,
removeTrailingPathSeparator
}
from
'
vs/base/common/resources
'
;
import
{
getRemoteAuthority
}
from
'
vs/platform/remote/common/remoteHosts
'
;
import
{
restoreWindowsState
,
WindowsStateStorageData
,
getWindowsStateStoreData
}
from
'
vs/code/electron-main/windowsStateStorage
'
;
import
{
getWorkspaceIdentifier
}
from
'
vs/platform/workspaces/electron-main/workspacesMainService
'
;
const
enum
WindowError
{
UNRESPONSIVE
=
1
,
...
...
@@ -114,6 +115,9 @@ interface IPathToOpen extends IPath {
// indicator to create the file path in the Code instance
createFilePath
?:
boolean
;
// optional label for the recent history
label
?:
string
;
}
function
isFolderPathToOpen
(
path
:
IPathToOpen
):
path
is
IFolderPathToOpen
{
...
...
@@ -130,6 +134,9 @@ interface IFolderPathToOpen {
// the remote authority for the Code instance to open. Undefined if not remote.
remoteAuthority
?:
string
;
// optional label for the recent history
label
?:
string
;
}
function
isWorkspacePathToOpen
(
path
:
IPathToOpen
):
path
is
IWorkspacePathToOpen
{
...
...
@@ -146,6 +153,9 @@ interface IWorkspacePathToOpen {
// the remote authority for the Code instance to open. Undefined if not remote.
remoteAuthority
?:
string
;
// optional label for the recent history
label
?:
string
;
}
export
class
WindowsManager
implements
IWindowsMainService
{
...
...
@@ -479,23 +489,18 @@ export class WindowsManager implements IWindowsMainService {
// Remember in recent document list (unless this opens for extension development)
// Also do not add paths when files are opened for diffing, only if opened individually
if
(
!
usedWindows
.
some
(
w
=>
w
.
isExtensionDevelopmentHost
)
&&
!
openConfig
.
diffMode
)
{
const
recentlyOpenedWorkspaces
:
Array
<
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
>
=
[];
const
recentlyOpenedFiles
:
URI
[]
=
[];
pathsToOpen
.
forEach
(
win
=>
{
if
(
win
.
workspace
)
{
recentlyOpenedWorkspaces
.
push
(
win
.
workspace
);
}
else
if
(
win
.
folderUri
)
{
recentlyOpenedWorkspaces
.
push
(
win
.
folderUri
);
}
else
if
(
win
.
fileUri
)
{
recentlyOpenedFiles
.
push
(
win
.
fileUri
);
if
(
!
usedWindows
.
some
(
w
=>
w
.
isExtensionDevelopmentHost
)
&&
!
openConfig
.
diffMode
&&
!
this
.
environmentService
.
skipAddToRecentlyOpened
)
{
const
recents
:
IRecent
[]
=
[];
for
(
let
pathToOpen
of
pathsToOpen
)
{
if
(
pathToOpen
.
workspace
)
{
recents
.
push
({
label
:
pathToOpen
.
label
,
workspace
:
pathToOpen
.
workspace
});
}
else
if
(
pathToOpen
.
folderUri
)
{
recents
.
push
({
label
:
pathToOpen
.
label
,
folderUri
:
pathToOpen
.
folderUri
});
}
else
if
(
pathToOpen
.
fileUri
)
{
recents
.
push
({
label
:
pathToOpen
.
label
,
fileUri
:
pathToOpen
.
fileUri
});
}
});
if
(
!
this
.
environmentService
.
skipAddToRecentlyOpened
)
{
this
.
historyMainService
.
addRecentlyOpened
(
recentlyOpenedWorkspaces
,
recentlyOpenedFiles
);
}
this
.
historyMainService
.
addRecentlyOpened
(
recents
);
}
// If we got started with --wait from the CLI, we need to signal to the outside when the window
...
...
@@ -845,6 +850,7 @@ export class WindowsManager implements IWindowsMainService {
const
path
=
this
.
parseUri
(
pathToOpen
.
uri
,
pathToOpen
.
typeHint
,
parseOptions
);
if
(
path
)
{
path
.
label
=
pathToOpen
.
label
;
pathsToOpen
.
push
(
path
);
}
else
{
...
...
@@ -1032,7 +1038,7 @@ export class WindowsManager implements IWindowsMainService {
}
if
(
hasWorkspaceFileExtension
(
uri
.
path
)
&&
!
options
.
forceOpenWorkspaceAsFile
)
{
return
{
workspace
:
this
.
workspacesMainService
.
getWorkspaceIdentifier
(
uri
),
workspace
:
getWorkspaceIdentifier
(
uri
),
remoteAuthority
};
}
...
...
@@ -1099,9 +1105,8 @@ export class WindowsManager implements IWindowsMainService {
}
}
}
catch
(
error
)
{
this
.
historyMainService
.
removeFromRecentlyOpened
([
candidate
]);
// since file does not seem to exist anymore, remove from recent
const
fileUri
=
URI
.
file
(
candidate
);
this
.
historyMainService
.
removeFromRecentlyOpened
([
fileUri
]);
// since file does not seem to exist anymore, remove from recent
if
(
options
&&
options
.
ignoreFileNotFound
)
{
return
{
fileUri
,
createFilePath
:
true
,
remoteAuthority
};
// assume this is a file that does not yet exist
}
...
...
@@ -1514,7 +1519,7 @@ export class WindowsManager implements IWindowsMainService {
private
doEnterWorkspace
(
win
:
ICodeWindow
,
result
:
IEnterWorkspaceResult
):
IEnterWorkspaceResult
{
// Mark as recently opened
this
.
historyMainService
.
addRecentlyOpened
([
result
.
workspace
],
[
]);
this
.
historyMainService
.
addRecentlyOpened
([
{
workspace
:
result
.
workspace
}
]);
// Trigger Eevent to indicate load of workspace into window
this
.
_onWindowReady
.
fire
(
win
);
...
...
@@ -1969,7 +1974,7 @@ class WorkspacesManager {
if
(
!
isValid
)
{
return
null
;
// return early if the workspace is not valid
}
const
workspaceIdentifier
=
this
.
workspacesMainService
.
getWorkspaceIdentifier
(
path
);
const
workspaceIdentifier
=
getWorkspaceIdentifier
(
path
);
return
this
.
doOpenWorkspace
(
window
,
workspaceIdentifier
);
});
...
...
@@ -1985,7 +1990,7 @@ class WorkspacesManager {
}
// Prevent overwriting a workspace that is currently opened in another window
if
(
findWindowOnWorkspace
(
this
.
windowsMainService
.
getWindows
(),
this
.
workspacesMainService
.
getWorkspaceIdentifier
(
path
)))
{
if
(
findWindowOnWorkspace
(
this
.
windowsMainService
.
getWindows
(),
getWorkspaceIdentifier
(
path
)))
{
const
options
:
Electron
.
MessageBoxOptions
=
{
title
:
product
.
nameLong
,
type
:
'
info
'
,
...
...
src/vs/platform/history/common/history.ts
浏览文件 @
4276d2c3
...
...
@@ -3,27 +3,57 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
{
IPath
}
from
'
vs/platform/windows/common/windows
'
;
import
{
Event
as
CommonEvent
}
from
'
vs/base/common/event
'
;
import
{
createDecorator
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
IWorkspaceIdentifier
,
ISingleFolderWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
IPath
}
from
'
vs/platform/windows/common/windows
'
;
export
const
IHistoryMainService
=
createDecorator
<
IHistoryMainService
>
(
'
historyMainService
'
);
export
interface
IRecentlyOpened
{
workspaces
:
Array
<
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
>
;
files
:
URI
[];
workspaces
:
Array
<
IRecentWorkspace
|
IRecentFolder
>
;
files
:
IRecentFile
[];
}
export
type
IRecent
=
IRecentWorkspace
|
IRecentFolder
|
IRecentFile
;
export
interface
IRecentWorkspace
{
workspace
:
IWorkspaceIdentifier
;
label
?:
string
;
}
export
interface
IRecentFolder
{
folderUri
:
ISingleFolderWorkspaceIdentifier
;
label
?:
string
;
}
export
interface
IRecentFile
{
fileUri
:
URI
;
label
?:
string
;
}
export
function
isRecentWorkspace
(
curr
:
IRecent
):
curr
is
IRecentWorkspace
{
return
!!
curr
[
'
workspace
'
];
}
export
function
isRecentFolder
(
curr
:
IRecent
):
curr
is
IRecentFolder
{
return
!!
curr
[
'
folderUri
'
];
}
export
function
isRecentFile
(
curr
:
IRecent
):
curr
is
IRecentFile
{
return
!!
curr
[
'
fileUri
'
];
}
export
interface
IHistoryMainService
{
_serviceBrand
:
any
;
onRecentlyOpenedChange
:
CommonEvent
<
void
>
;
addRecentlyOpened
(
workspaces
:
undefined
|
Array
<
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
>
,
files
:
URI
[]):
void
;
getRecentlyOpened
(
currentWorkspace
?:
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
,
currentFiles
?:
IPath
[]):
IRecentlyOpened
;
removeFromRecentlyOpened
(
paths
:
Array
<
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
|
URI
|
string
>
):
void
;
addRecentlyOpened
(
recents
:
IRecent
[]):
void
;
getRecentlyOpened
(
currentWorkspace
?:
IWorkspaceIdentifier
,
currentFolder
?:
ISingleFolderWorkspaceIdentifier
,
currentFiles
?:
IPath
[]):
IRecentlyOpened
;
removeFromRecentlyOpened
(
paths
:
URI
[]
):
void
;
clearRecentlyOpened
():
void
;
updateWindowsJumpList
():
void
;
...
...
src/vs/platform/history/electron-main/historyMainService.ts
浏览文件 @
4276d2c3
...
...
@@ -11,12 +11,11 @@ import { ILogService } from 'vs/platform/log/common/log';
import
{
getBaseLabel
,
getPathLabel
}
from
'
vs/base/common/labels
'
;
import
{
IPath
}
from
'
vs/platform/windows/common/windows
'
;
import
{
Event
as
CommonEvent
,
Emitter
}
from
'
vs/base/common/event
'
;
import
{
isWindows
,
isMacintosh
,
isLinux
}
from
'
vs/base/common/platform
'
;
import
{
IWorkspaceIdentifier
,
IWorkspacesMainService
,
ISingleFolderWorkspaceIdentifier
,
isSingleFolderWorkspaceIdentifier
,
isWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
IHistoryMainService
,
IRecentlyOpened
}
from
'
vs/platform/history/common/history
'
;
import
{
isEqual
}
from
'
vs/base/common/extpath
'
;
import
{
isWindows
,
isMacintosh
}
from
'
vs/base/common/platform
'
;
import
{
IWorkspaceIdentifier
,
IWorkspacesMainService
,
ISingleFolderWorkspaceIdentifier
,
isSingleFolderWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
IHistoryMainService
,
IRecentlyOpened
,
isRecentWorkspace
,
isRecentFolder
,
IRecent
,
isRecentFile
,
IRecentFolder
,
IRecentWorkspace
,
IRecentFile
}
from
'
vs/platform/history/common/history
'
;
import
{
RunOnceScheduler
}
from
'
vs/base/common/async
'
;
import
{
getComparisonKey
,
isEqual
as
areResourcesEqual
,
dirname
,
originalFSPath
}
from
'
vs/base/common/resources
'
;
import
{
isEqual
as
areResourcesEqual
,
dirname
,
originalFSPath
}
from
'
vs/base/common/resources
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
Schemas
}
from
'
vs/base/common/network
'
;
import
{
IEnvironmentService
}
from
'
vs/platform/environment/common/environment
'
;
...
...
@@ -47,37 +46,26 @@ export class HistoryMainService implements IHistoryMainService {
this
.
macOSRecentDocumentsUpdater
=
new
RunOnceScheduler
(()
=>
this
.
updateMacOSRecentDocuments
(),
800
);
}
addRecentlyOpened
(
workspaces
:
Array
<
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
>
,
files
:
URI
[]):
void
{
if
((
workspaces
&&
workspaces
.
length
>
0
)
||
(
files
&&
files
.
length
>
0
))
{
const
mru
=
this
.
getRecentlyOpened
();
// Workspaces
if
(
Array
.
isArray
(
workspaces
))
{
workspaces
.
forEach
(
workspace
=>
{
const
isUntitledWorkspace
=
!
isSingleFolderWorkspaceIdentifier
(
workspace
)
&&
this
.
workspacesMainService
.
isUntitledWorkspace
(
workspace
);
if
(
isUntitledWorkspace
)
{
return
;
// only store saved workspaces
}
mru
.
workspaces
.
unshift
(
workspace
);
mru
.
workspaces
=
arrays
.
distinct
(
mru
.
workspaces
,
workspace
=>
this
.
distinctFn
(
workspace
));
// We do not add to recent documents here because on Windows we do this from a custom
// JumpList and on macOS we fill the recent documents in one go from all our data later.
});
}
// Files
if
(
Array
.
isArray
(
files
))
{
files
.
forEach
((
fileUri
)
=>
{
mru
.
files
.
unshift
(
fileUri
);
mru
.
files
=
arrays
.
distinct
(
mru
.
files
,
file
=>
this
.
distinctFn
(
file
));
addRecentlyOpened
(
newlyAdded
:
IRecent
[]):
void
{
const
mru
=
this
.
getRecentlyOpened
();
for
(
let
curr
of
newlyAdded
)
{
if
(
isRecentWorkspace
(
curr
))
{
if
(
!
this
.
workspacesMainService
.
isUntitledWorkspace
(
curr
.
workspace
)
&&
indexOfWorkspace
(
mru
.
workspaces
,
curr
.
workspace
)
===
-
1
)
{
mru
.
workspaces
.
unshift
(
curr
);
}
}
else
if
(
isRecentFolder
(
curr
))
{
if
(
indexOfFolder
(
mru
.
workspaces
,
curr
.
folderUri
)
===
-
1
)
{
mru
.
workspaces
.
unshift
(
curr
);
}
}
else
{
if
(
indexOfFile
(
mru
.
files
,
curr
.
fileUri
)
===
-
1
)
{
mru
.
files
.
unshift
(
curr
);
// Add to recent documents (Windows only, macOS later)
if
(
isWindows
&&
fileUri
.
scheme
===
Schemas
.
file
)
{
app
.
addRecentDocument
(
fileUri
.
fsPath
);
if
(
isWindows
&&
curr
.
fileUri
.
scheme
===
Schemas
.
file
)
{
app
.
addRecentDocument
(
curr
.
fileUri
.
fsPath
);
}
}
);
}
}
// Make sure its bounded
...
...
@@ -94,53 +82,23 @@ export class HistoryMainService implements IHistoryMainService {
}
}
removeFromRecentlyOpened
(
pathsToRemove
:
Array
<
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
|
URI
|
string
>
):
void
{
const
mru
=
this
.
getRecentlyOpened
();
let
update
=
false
;
pathsToRemove
.
forEach
(
pathToRemove
=>
{
// Remove workspace
let
index
=
arrays
.
firstIndex
(
mru
.
workspaces
,
workspace
=>
{
if
(
isWorkspaceIdentifier
(
pathToRemove
))
{
return
isWorkspaceIdentifier
(
workspace
)
&&
areResourcesEqual
(
pathToRemove
.
configPath
,
workspace
.
configPath
,
!
isLinux
/* ignorecase */
);
removeFromRecentlyOpened
(
toRemove
:
URI
[]):
void
{
const
keep
=
(
recent
:
IRecent
)
=>
{
const
uri
=
location
(
recent
);
for
(
const
r
of
toRemove
)
{
if
(
areResourcesEqual
(
r
,
uri
))
{
return
false
;
}
if
(
isSingleFolderWorkspaceIdentifier
(
pathToRemove
))
{
return
isSingleFolderWorkspaceIdentifier
(
workspace
)
&&
areResourcesEqual
(
pathToRemove
,
workspace
);
}
if
(
typeof
pathToRemove
===
'
string
'
)
{
if
(
isSingleFolderWorkspaceIdentifier
(
workspace
))
{
return
workspace
.
scheme
===
Schemas
.
file
&&
isEqual
(
pathToRemove
,
workspace
.
fsPath
,
!
isLinux
/* ignorecase */
);
}
if
(
isWorkspaceIdentifier
(
workspace
))
{
return
workspace
.
configPath
.
scheme
===
Schemas
.
file
&&
isEqual
(
pathToRemove
,
workspace
.
configPath
.
fsPath
,
!
isLinux
/* ignorecase */
);
}
}
return
false
;
});
if
(
index
>=
0
)
{
mru
.
workspaces
.
splice
(
index
,
1
);
update
=
true
;
}
return
true
;
};
// Remove file
index
=
arrays
.
firstIndex
(
mru
.
files
,
file
=>
{
if
(
pathToRemove
instanceof
URI
)
{
return
areResourcesEqual
(
file
,
pathToRemove
);
}
else
if
(
typeof
pathToRemove
===
'
string
'
)
{
return
isEqual
(
file
.
fsPath
,
pathToRemove
,
!
isLinux
/* ignorecase */
);
}
return
false
;
});
if
(
index
>=
0
)
{
mru
.
files
.
splice
(
index
,
1
);
update
=
true
;
}
});
const
mru
=
this
.
getRecentlyOpened
();
const
workspaces
=
mru
.
workspaces
.
filter
(
keep
);
const
files
=
mru
.
files
.
filter
(
keep
);
if
(
update
)
{
this
.
saveRecentlyOpened
(
mru
);
if
(
workspaces
.
length
!==
mru
.
workspaces
.
length
||
files
.
length
!==
mru
.
files
.
length
)
{
this
.
saveRecentlyOpened
(
{
files
,
workspaces
}
);
this
.
_onRecentlyOpenedChange
.
fire
();
// Schedule update to recent documents on macOS dock
...
...
@@ -164,28 +122,19 @@ export class HistoryMainService implements IHistoryMainService {
const
mru
=
this
.
getRecentlyOpened
();
// Fill in workspaces
let
entries
=
0
;
for
(
let
i
=
0
;
i
<
mru
.
workspaces
.
length
&&
entries
<
HistoryMainService
.
MAX_MACOS_DOCK_RECENT_FOLDERS
;
i
++
)
{
const
workspace
=
mru
.
workspaces
[
i
];
if
(
isSingleFolderWorkspaceIdentifier
(
workspace
))
{
if
(
workspace
.
scheme
===
Schemas
.
file
)
{
app
.
addRecentDocument
(
originalFSPath
(
workspace
));
entries
++
;
}
}
else
{
if
(
workspace
.
configPath
.
scheme
===
Schemas
.
file
)
{
app
.
addRecentDocument
(
originalFSPath
(
workspace
.
configPath
));
entries
++
;
}
for
(
let
i
=
0
,
entries
=
0
;
i
<
mru
.
workspaces
.
length
&&
entries
<
HistoryMainService
.
MAX_MACOS_DOCK_RECENT_FOLDERS
;
i
++
)
{
const
loc
=
location
(
mru
.
workspaces
[
i
]);
if
(
loc
.
scheme
===
Schemas
.
file
)
{
app
.
addRecentDocument
(
originalFSPath
(
loc
));
entries
++
;
}
}
// Fill in files
entries
=
0
;
for
(
let
i
=
0
;
i
<
mru
.
files
.
length
&&
entries
<
HistoryMainService
.
MAX_MACOS_DOCK_RECENT_FILES
;
i
++
)
{
const
file
=
mru
.
files
[
i
];
if
(
file
.
scheme
===
Schemas
.
file
)
{
app
.
addRecentDocument
(
originalFSPath
(
file
));
for
(
let
i
=
0
,
entries
=
0
;
i
<
mru
.
files
.
length
&&
entries
<
HistoryMainService
.
MAX_MACOS_DOCK_RECENT_FILES
;
i
++
)
{
const
loc
=
location
(
mru
.
files
[
i
]);
if
(
loc
.
scheme
===
Schemas
.
file
)
{
app
.
addRecentDocument
(
originalFSPath
(
loc
));
entries
++
;
}
}
...
...
@@ -199,37 +148,48 @@ export class HistoryMainService implements IHistoryMainService {
this
.
_onRecentlyOpenedChange
.
fire
();
}
getRecentlyOpened
(
currentWorkspace
?:
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
,
currentFiles
?:
IPath
[]):
IRecentlyOpened
{
getRecentlyOpened
(
currentWorkspace
?:
IWorkspaceIdentifier
,
currentFolder
?:
ISingleFolderWorkspaceIdentifier
,
currentFiles
?:
IPath
[]):
IRecentlyOpened
{
// Get from storage
let
{
workspaces
,
files
}
=
this
.
getRecentlyOpenedFromStorage
()
;
const
workspaces
:
Array
<
IRecentFolder
|
IRecentWorkspace
>
=
[];
const
files
:
IRecentFile
[]
=
[]
;
// Add current workspace to beginning if set
if
(
currentWorkspace
)
{
workspaces
.
unshift
(
currentWorkspace
);
if
(
currentWorkspace
&&
!
this
.
workspacesMainService
.
isUntitledWorkspace
(
currentWorkspace
))
{
workspaces
.
push
({
workspace
:
currentWorkspace
});
}
if
(
currentFolder
)
{
workspaces
.
push
({
folderUri
:
currentFolder
});
}
// Add currently files to open to the beginning if any
if
(
currentFiles
)
{
files
.
unshift
(...
arrays
.
coalesce
(
currentFiles
.
map
(
f
=>
f
.
fileUri
)));
for
(
let
currentFile
of
currentFiles
)
{
const
fileUri
=
currentFile
.
fileUri
;
if
(
fileUri
&&
indexOfFile
(
files
,
fileUri
)
===
-
1
)
{
files
.
push
({
fileUri
});
}
}
}
// Clear those dupes
workspaces
=
arrays
.
distinct
(
workspaces
,
workspace
=>
this
.
distinctFn
(
workspace
));
files
=
arrays
.
distinct
(
files
,
file
=>
this
.
distinctFn
(
file
));
// Hide untitled workspaces
workspaces
=
workspaces
.
filter
(
workspace
=>
isSingleFolderWorkspaceIdentifier
(
workspace
)
||
!
this
.
workspacesMainService
.
isUntitledWorkspace
(
workspace
));
return
{
workspaces
,
files
};
}
private
distinctFn
(
workspaceOrFile
:
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
|
URI
):
string
{
if
(
workspaceOrFile
instanceof
URI
)
{
return
getComparisonKey
(
workspaceOrFile
);
// Get from storage
let
recents
=
this
.
getRecentlyOpenedFromStorage
();
for
(
let
recent
of
recents
.
workspaces
)
{
let
index
=
isRecentFolder
(
recent
)
?
indexOfFolder
(
workspaces
,
recent
.
folderUri
)
:
indexOfWorkspace
(
workspaces
,
recent
.
workspace
);
if
(
index
>=
0
)
{
workspaces
[
index
].
label
=
workspaces
[
index
].
label
||
recent
.
label
;
}
else
{
workspaces
.
push
(
recent
);
}
}
return
workspaceOrFile
.
id
;
for
(
let
recent
of
recents
.
files
)
{
let
index
=
indexOfFile
(
files
,
recent
.
fileUri
);
if
(
index
>=
0
)
{
files
[
index
].
label
=
files
[
index
].
label
||
recent
.
label
;
}
else
{
files
.
push
(
recent
);
}
}
return
{
workspaces
,
files
};
}
private
getRecentlyOpenedFromStorage
():
IRecentlyOpened
{
...
...
@@ -272,7 +232,7 @@ export class HistoryMainService implements IHistoryMainService {
// so we need to update our list of recent paths with the choice of the user to not add them again
// Also: Windows will not show our custom category at all if there is any entry which was removed
// by the user! See https://github.com/Microsoft/vscode/issues/15052
let
toRemove
:
Array
<
ISingleFolderWorkspaceIdentifier
|
IWorkspaceIdentifier
>
=
[];
let
toRemove
:
URI
[]
=
[];
for
(
let
item
of
app
.
getJumpListSettings
().
removedItems
)
{
const
args
=
item
.
args
;
if
(
args
)
{
...
...
@@ -288,8 +248,9 @@ export class HistoryMainService implements IHistoryMainService {
jumpList
.
push
({
type
:
'
custom
'
,
name
:
nls
.
localize
(
'
recentFolders
'
,
"
Recent Workspaces
"
),
items
:
arrays
.
coalesce
(
this
.
getRecentlyOpened
().
workspaces
.
slice
(
0
,
7
/* limit number of entries here */
).
map
(
workspace
=>
{
const
title
=
getSimpleWorkspaceLabel
(
workspace
,
this
.
environmentService
.
untitledWorkspacesHome
);
items
:
arrays
.
coalesce
(
this
.
getRecentlyOpened
().
workspaces
.
slice
(
0
,
7
/* limit number of entries here */
).
map
(
recent
=>
{
const
workspace
=
isRecentWorkspace
(
recent
)
?
recent
.
workspace
:
recent
.
folderUri
;
const
title
=
recent
.
label
||
getSimpleWorkspaceLabel
(
workspace
,
this
.
environmentService
.
untitledWorkspacesHome
);
let
description
;
let
args
;
if
(
isSingleFolderWorkspaceIdentifier
(
workspace
))
{
...
...
@@ -325,3 +286,25 @@ export class HistoryMainService implements IHistoryMainService {
}
}
}
function
location
(
recent
:
IRecent
):
URI
{
if
(
isRecentFolder
(
recent
))
{
return
recent
.
folderUri
;
}
if
(
isRecentFile
(
recent
))
{
return
recent
.
fileUri
;
}
return
recent
.
workspace
.
configPath
;
}
function
indexOfWorkspace
(
arr
:
IRecent
[],
workspace
:
IWorkspaceIdentifier
):
number
{
return
arrays
.
firstIndex
(
arr
,
w
=>
isRecentWorkspace
(
w
)
&&
w
.
workspace
.
id
===
workspace
.
id
);
}
function
indexOfFolder
(
arr
:
IRecent
[],
folderURI
:
ISingleFolderWorkspaceIdentifier
):
number
{
return
arrays
.
firstIndex
(
arr
,
f
=>
isRecentFolder
(
f
)
&&
areResourcesEqual
(
f
.
folderUri
,
folderURI
));
}
function
indexOfFile
(
arr
:
IRecentFile
[],
fileURI
:
URI
):
number
{
return
arrays
.
firstIndex
(
arr
,
f
=>
areResourcesEqual
(
f
.
fileUri
,
fileURI
));
}
\ No newline at end of file
src/vs/platform/history/electron-main/historyStorage.ts
浏览文件 @
4276d2c3
...
...
@@ -3,12 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
{
UriComponents
,
URI
}
from
'
vs/base/common/uri
'
;
import
{
IRecentlyOpened
}
from
'
vs/platform/history/common/history
'
;
import
{
isSingleFolderWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
IRecentlyOpened
,
isRecentFolder
}
from
'
vs/platform/history/common/history
'
;
interface
ISerializedRecentlyOpened
{
workspaces3
:
Array
<
ISerializedWorkspace
|
string
>
;
// workspace or URI.toString()
files2
:
string
[];
// files as URI.toString()
workspaces3
:
Array
<
ISerializedWorkspace
|
string
>
;
// workspace or URI.toString() // added in 1.32
workspaceLabels
?:
Array
<
string
|
null
>
;
// added in 1.33
files2
:
string
[];
// files as URI.toString() // added in 1.32
fileLabels
?:
Array
<
string
|
null
>
;
// added in 1.33
}
interface
ILegacySerializedRecentlyOpened
{
...
...
@@ -27,19 +28,21 @@ export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefine
if
(
data
)
{
const
storedRecents
=
data
as
ISerializedRecentlyOpened
&
ILegacySerializedRecentlyOpened
;
if
(
Array
.
isArray
(
storedRecents
.
workspaces3
))
{
for
(
const
workspace
of
storedRecents
.
workspaces3
)
{
for
(
let
i
=
0
;
i
<
storedRecents
.
workspaces3
.
length
;
i
++
)
{
const
workspace
=
storedRecents
.
workspaces3
[
i
];
const
label
:
string
|
undefined
=
(
Array
.
isArray
(
storedRecents
.
workspaceLabels
)
&&
storedRecents
.
workspaceLabels
[
i
])
||
undefined
;
if
(
typeof
workspace
===
'
object
'
&&
typeof
workspace
.
id
===
'
string
'
&&
typeof
workspace
.
configURIPath
===
'
string
'
)
{
result
.
workspaces
.
push
({
id
:
workspace
.
id
,
configPath
:
URI
.
parse
(
workspace
.
configURIPath
)
});
result
.
workspaces
.
push
({
label
,
workspace
:
{
id
:
workspace
.
id
,
configPath
:
URI
.
parse
(
workspace
.
configURIPath
)
}
});
}
else
if
(
typeof
workspace
===
'
string
'
)
{
result
.
workspaces
.
push
(
URI
.
parse
(
workspace
)
);
result
.
workspaces
.
push
(
{
label
,
folderUri
:
URI
.
parse
(
workspace
)
}
);
}
}
}
else
if
(
Array
.
isArray
(
storedRecents
.
workspaces2
))
{
for
(
const
workspace
of
storedRecents
.
workspaces2
)
{
if
(
typeof
workspace
===
'
object
'
&&
typeof
workspace
.
id
===
'
string
'
&&
typeof
workspace
.
configPath
===
'
string
'
)
{
result
.
workspaces
.
push
({
id
:
workspace
.
id
,
configPath
:
URI
.
file
(
workspace
.
configPath
)
});
result
.
workspaces
.
push
({
workspace
:
{
id
:
workspace
.
id
,
configPath
:
URI
.
file
(
workspace
.
configPath
)
}
});
}
else
if
(
typeof
workspace
===
'
string
'
)
{
result
.
workspaces
.
push
(
URI
.
parse
(
workspace
)
);
result
.
workspaces
.
push
(
{
folderUri
:
URI
.
parse
(
workspace
)
}
);
}
}
}
else
if
(
Array
.
isArray
(
storedRecents
.
workspaces
))
{
...
...
@@ -47,26 +50,28 @@ export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefine
// format of 1.25 and before
for
(
const
workspace
of
storedRecents
.
workspaces
)
{
if
(
typeof
workspace
===
'
string
'
)
{
result
.
workspaces
.
push
(
URI
.
file
(
workspace
)
);
result
.
workspaces
.
push
(
{
folderUri
:
URI
.
file
(
workspace
)
}
);
}
else
if
(
typeof
workspace
===
'
object
'
&&
typeof
workspace
[
'
id
'
]
===
'
string
'
&&
typeof
workspace
[
'
configPath
'
]
===
'
string
'
)
{
result
.
workspaces
.
push
({
id
:
workspace
[
'
id
'
],
configPath
:
URI
.
file
(
workspace
[
'
configPath
'
])
});
result
.
workspaces
.
push
({
workspace
:
{
id
:
workspace
[
'
id
'
],
configPath
:
URI
.
file
(
workspace
[
'
configPath
'
])
}
});
}
else
if
(
workspace
&&
typeof
workspace
[
'
path
'
]
===
'
string
'
&&
typeof
workspace
[
'
scheme
'
]
===
'
string
'
)
{
// added by 1.26-insiders
result
.
workspaces
.
push
(
URI
.
revive
(
workspace
)
);
result
.
workspaces
.
push
(
{
folderUri
:
URI
.
revive
(
workspace
)
}
);
}
}
}
if
(
Array
.
isArray
(
storedRecents
.
files2
))
{
for
(
const
file
of
storedRecents
.
files2
)
{
for
(
let
i
=
0
;
i
<
storedRecents
.
files2
.
length
;
i
++
)
{
const
file
=
storedRecents
.
files2
[
i
];
const
label
:
string
|
undefined
=
(
Array
.
isArray
(
storedRecents
.
fileLabels
)
&&
storedRecents
.
fileLabels
[
i
])
||
undefined
;
if
(
typeof
file
===
'
string
'
)
{
result
.
files
.
push
(
URI
.
parse
(
file
)
);
result
.
files
.
push
(
{
label
,
fileUri
:
URI
.
parse
(
file
)
}
);
}
}
}
else
if
(
Array
.
isArray
(
storedRecents
.
files
))
{
for
(
const
file
of
storedRecents
.
files
)
{
if
(
typeof
file
===
'
string
'
)
{
result
.
files
.
push
(
URI
.
file
(
file
)
);
result
.
files
.
push
(
{
fileUri
:
URI
.
file
(
file
)
}
);
}
}
}
...
...
@@ -75,19 +80,33 @@ export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefine
return
result
;
}
export
function
toStoreData
(
recent
:
IRecentlyOpened
):
RecentlyOpenedStorageData
{
export
function
toStoreData
(
recent
s
:
IRecentlyOpened
):
RecentlyOpenedStorageData
{
const
serialized
:
ISerializedRecentlyOpened
=
{
workspaces3
:
[],
files2
:
[]
};
for
(
const
workspace
of
recent
.
workspaces
)
{
if
(
isSingleFolderWorkspaceIdentifier
(
workspace
))
{
serialized
.
workspaces3
.
push
(
workspace
.
toString
());
let
hasLabel
=
false
;
const
workspaceLabels
:
(
string
|
null
)[]
=
[];
for
(
const
recent
of
recents
.
workspaces
)
{
if
(
isRecentFolder
(
recent
))
{
serialized
.
workspaces3
.
push
(
recent
.
folderUri
.
toString
());
}
else
{
serialized
.
workspaces3
.
push
({
id
:
workspace
.
id
,
configURIPath
:
workspace
.
configPath
.
toString
()
});
serialized
.
workspaces3
.
push
({
id
:
recent
.
workspace
.
id
,
configURIPath
:
recent
.
workspace
.
configPath
.
toString
()
});
}
workspaceLabels
.
push
(
recent
.
label
||
null
);
hasLabel
=
hasLabel
||
!!
recent
.
label
;
}
if
(
hasLabel
)
{
serialized
.
workspaceLabels
=
workspaceLabels
;
}
for
(
const
file
of
recent
.
files
)
{
serialized
.
files2
.
push
(
file
.
toString
());
hasLabel
=
false
;
const
fileLabels
:
(
string
|
null
)[]
=
[];
for
(
const
recent
of
recents
.
files
)
{
serialized
.
files2
.
push
(
recent
.
fileUri
.
toString
());
fileLabels
.
push
(
recent
.
label
||
null
);
hasLabel
=
hasLabel
||
!!
recent
.
label
;
}
if
(
hasLabel
)
{
serialized
.
fileLabels
=
fileLabels
;
}
return
serialized
;
...
...
src/vs/platform/history/test/electron-main/historyStorage.test.ts
浏览文件 @
4276d2c3
...
...
@@ -8,7 +8,7 @@ import * as path from 'vs/base/common/path';
import
{
IWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
IRecentlyOpened
}
from
'
vs/platform/history/common/history
'
;
import
{
IRecentlyOpened
,
isRecentFolder
,
IRecentFolder
,
IRecentWorkspace
}
from
'
vs/platform/history/common/history
'
;
import
{
toStoreData
,
restoreRecentlyOpened
}
from
'
vs/platform/history/electron-main/historyStorage
'
;
function
toWorkspace
(
uri
:
URI
):
IWorkspaceIdentifier
{
...
...
@@ -30,18 +30,22 @@ function assertEqualWorkspace(w1: IWorkspaceIdentifier | undefined, w2: IWorkspa
assertEqualURI
(
w1
.
configPath
,
w2
.
configPath
,
message
);
}
function
assertEqualRecentlyOpened
(
expected
:
IRecentlyOpened
,
actual
:
IRecentlyOpened
,
message
?:
string
)
{
assert
.
equal
(
expected
.
files
.
length
,
actual
.
files
.
length
,
message
);
for
(
let
i
=
0
;
i
<
expected
.
files
.
length
;
i
++
)
{
assertEqualURI
(
expected
.
files
[
i
],
actual
.
files
[
i
],
message
);
function
assertEqualRecentlyOpened
(
actual
:
IRecentlyOpened
,
expected
:
IRecentlyOpened
,
message
?:
string
)
{
assert
.
equal
(
actual
.
files
.
length
,
expected
.
files
.
length
,
message
);
for
(
let
i
=
0
;
i
<
actual
.
files
.
length
;
i
++
)
{
assertEqualURI
(
actual
.
files
[
i
].
fileUri
,
expected
.
files
[
i
].
fileUri
,
message
);
assert
.
equal
(
actual
.
files
[
i
].
label
,
expected
.
files
[
i
].
label
);
}
assert
.
equal
(
expected
.
workspaces
.
length
,
actual
.
workspaces
.
length
,
message
);
for
(
let
i
=
0
;
i
<
expected
.
workspaces
.
length
;
i
++
)
{
if
(
expected
.
workspaces
[
i
]
instanceof
URI
)
{
assertEqualURI
(
<
URI
>
expected
.
workspaces
[
i
],
<
URI
>
actual
.
workspaces
[
i
],
message
);
assert
.
equal
(
actual
.
workspaces
.
length
,
expected
.
workspaces
.
length
,
message
);
for
(
let
i
=
0
;
i
<
actual
.
workspaces
.
length
;
i
++
)
{
let
expectedRecent
=
expected
.
workspaces
[
i
];
let
actualRecent
=
actual
.
workspaces
[
i
];
if
(
isRecentFolder
(
actualRecent
))
{
assertEqualURI
(
actualRecent
.
folderUri
,
(
<
IRecentFolder
>
expectedRecent
).
folderUri
,
message
);
}
else
{
assertEqualWorkspace
(
<
IWorkspaceIdentifier
>
expected
.
workspaces
[
i
],
<
IWorkspaceIdentifier
>
actual
.
workspaces
[
i
]
,
message
);
assertEqualWorkspace
(
actualRecent
.
workspace
,
(
<
IRecentWorkspace
>
expectedRecent
).
workspace
,
message
);
}
assert
.
equal
(
actualRecent
.
label
,
expectedRecent
.
label
);
}
}
...
...
@@ -68,26 +72,31 @@ suite('History Storage', () => {
};
assertRestoring
(
ro
,
'
empty
'
);
ro
=
{
files
:
[
testFileURI
],
files
:
[
{
fileUri
:
testFileURI
}
],
workspaces
:
[]
};
assertRestoring
(
ro
,
'
file
'
);
ro
=
{
files
:
[],
workspaces
:
[
testFolderURI
]
workspaces
:
[
{
folderUri
:
testFolderURI
}
]
};
assertRestoring
(
ro
,
'
folder
'
);
ro
=
{
files
:
[],
workspaces
:
[
toWorkspace
(
testWSPath
),
testFolderURI
]
workspaces
:
[
{
workspace
:
toWorkspace
(
testWSPath
)
},
{
folderUri
:
testFolderURI
}
]
};
assertRestoring
(
ro
,
'
workspaces and folders
'
);
ro
=
{
files
:
[
testRemoteFileURI
],
workspaces
:
[
toWorkspace
(
testRemoteWSURI
),
testRemoteFolderURI
]
files
:
[
{
fileUri
:
testRemoteFileURI
}
],
workspaces
:
[
{
workspace
:
toWorkspace
(
testRemoteWSURI
)
},
{
folderUri
:
testRemoteFolderURI
}
]
};
assertRestoring
(
ro
,
'
remote workspaces and folders
'
);
ro
=
{
files
:
[{
label
:
'
abc
'
,
fileUri
:
testFileURI
}],
workspaces
:
[{
label
:
'
def
'
,
workspace
:
toWorkspace
(
testWSPath
)
},
{
folderUri
:
testRemoteFolderURI
}]
};
assertRestoring
(
ro
,
'
labels
'
);
});
test
(
'
open 1_25
'
,
()
=>
{
...
...
@@ -111,15 +120,15 @@ suite('History Storage', () => {
let
actual
=
restoreRecentlyOpened
(
JSON
.
parse
(
v1_25_win
));
let
expected
:
IRecentlyOpened
=
{
files
:
[
URI
.
file
(
'
C:
\\
workspaces
\\
test.code-workspace
'
),
URI
.
file
(
'
C:
\\
workspaces
\\
testing
\\
test-ext
\\
.gitignore
'
)
],
files
:
[
{
fileUri
:
URI
.
file
(
'
C:
\\
workspaces
\\
test.code-workspace
'
)
},
{
fileUri
:
URI
.
file
(
'
C:
\\
workspaces
\\
testing
\\
test-ext
\\
.gitignore
'
)
}
],
workspaces
:
[
{
id
:
'
2fa677dbdf5f771e775af84dea9feaea
'
,
configPath
:
URI
.
file
(
'
C:
\\
workspaces
\\
testing
\\
test.code-workspace
'
)
},
URI
.
file
(
'
C:
\\
workspaces
\\
testing
\\
test-ext
'
)
,
{
id
:
'
d87a0241f8abc86b95c4e5481ebcbf56
'
,
configPath
:
URI
.
file
(
'
C:
\\
workspaces
\\
test.code-workspace
'
)
}
{
workspace
:
{
id
:
'
2fa677dbdf5f771e775af84dea9feaea
'
,
configPath
:
URI
.
file
(
'
C:
\\
workspaces
\\
testing
\\
test.code-workspace
'
)
}
},
{
folderUri
:
URI
.
file
(
'
C:
\\
workspaces
\\
testing
\\
test-ext
'
)
}
,
{
workspace
:
{
id
:
'
d87a0241f8abc86b95c4e5481ebcbf56
'
,
configPath
:
URI
.
file
(
'
C:
\\
workspaces
\\
test.code-workspace
'
)
}
}
]
};
assertEqualRecentlyOpened
(
expected
,
actual
,
'
v1_31_win
'
);
assertEqualRecentlyOpened
(
actual
,
expected
,
'
v1_31_win
'
);
});
test
(
'
open 1_31
'
,
()
=>
{
...
...
@@ -139,15 +148,15 @@ suite('History Storage', () => {
let
actual
=
restoreRecentlyOpened
(
JSON
.
parse
(
v1_31_win
));
let
expected
:
IRecentlyOpened
=
{
files
:
[
URI
.
parse
(
'
file:///c%3A/workspaces/vscode/.yarnrc
'
)
],
files
:
[
{
fileUri
:
URI
.
parse
(
'
file:///c%3A/workspaces/vscode/.yarnrc
'
)
}
],
workspaces
:
[
URI
.
parse
(
'
file:///c%3A/workspaces/testing/test-ext
'
)
,
URI
.
parse
(
'
file:///c%3A/WINDOWS/system32
'
)
,
{
id
:
'
d87a0241f8abc86b95c4e5481ebcbf56
'
,
configPath
:
URI
.
file
(
'
c:
\\
workspaces
\\
test.code-workspace
'
)
}
{
folderUri
:
URI
.
parse
(
'
file:///c%3A/workspaces/testing/test-ext
'
)
}
,
{
folderUri
:
URI
.
parse
(
'
file:///c%3A/WINDOWS/system32
'
)
}
,
{
workspace
:
{
id
:
'
d87a0241f8abc86b95c4e5481ebcbf56
'
,
configPath
:
URI
.
file
(
'
c:
\\
workspaces
\\
test.code-workspace
'
)
}
}
]
};
assertEqualRecentlyOpened
(
expected
,
actual
,
'
v1_31_win
'
);
assertEqualRecentlyOpened
(
actual
,
expected
,
'
v1_31_win
'
);
});
test
(
'
open 1_32
'
,
()
=>
{
...
...
@@ -166,15 +175,49 @@ suite('History Storage', () => {
let
windowsState
=
restoreRecentlyOpened
(
JSON
.
parse
(
v1_32
));
let
expected
:
IRecentlyOpened
=
{
files
:
[
URI
.
parse
(
'
file:///home/user/.config/code-oss-dev/storage.json
'
)],
files
:
[{
fileUri
:
URI
.
parse
(
'
file:///home/user/.config/code-oss-dev/storage.json
'
)
}],
workspaces
:
[
{
workspace
:
{
id
:
'
53b714b46ef1a2d4346568b4f591028c
'
,
configPath
:
URI
.
parse
(
'
file:///home/user/workspaces/testing/custom.code-workspace
'
)
}
},
{
folderUri
:
URI
.
parse
(
'
file:///home/user/workspaces/testing/folding
'
)
}
]
};
assertEqualRecentlyOpened
(
windowsState
,
expected
,
'
v1_32
'
);
});
test
(
'
open 1_33
'
,
()
=>
{
const
v1_33
=
`{
"workspaces3": [
{
"id": "53b714b46ef1a2d4346568b4f591028c",
"configURIPath": "file:///home/user/workspaces/testing/custom.code-workspace"
},
"file:///home/user/workspaces/testing/folding"
],
"files2": [
"file:///home/user/.config/code-oss-dev/storage.json"
],
"workspaceLabels": [
null,
"abc"
],
"fileLabels": [
"def"
]
}`
;
let
windowsState
=
restoreRecentlyOpened
(
JSON
.
parse
(
v1_33
));
let
expected
:
IRecentlyOpened
=
{
files
:
[{
label
:
'
def
'
,
fileUri
:
URI
.
parse
(
'
file:///home/user/.config/code-oss-dev/storage.json
'
)
}],
workspaces
:
[
{
id
:
'
53b714b46ef1a2d4346568b4f591028c
'
,
configPath
:
URI
.
parse
(
'
file:///home/user/workspaces/testing/custom.code-workspace
'
)
},
URI
.
parse
(
'
file:///home/user/workspaces/testing/folding
'
)
{
workspace
:
{
id
:
'
53b714b46ef1a2d4346568b4f591028c
'
,
configPath
:
URI
.
parse
(
'
file:///home/user/workspaces/testing/custom.code-workspace
'
)
}
},
{
label
:
'
abc
'
,
folderUri
:
URI
.
parse
(
'
file:///home/user/workspaces/testing/folding
'
)
}
]
};
assertEqualRecentlyOpened
(
expected
,
windowsState
,
'
v1_32
'
);
assertEqualRecentlyOpened
(
windowsState
,
expected
,
'
v1_33
'
);
});
});
\ No newline at end of file
src/vs/platform/windows/common/windows.ts
浏览文件 @
4276d2c3
...
...
@@ -9,7 +9,7 @@ import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import
{
IProcessEnvironment
,
isMacintosh
,
isLinux
}
from
'
vs/base/common/platform
'
;
import
{
ParsedArgs
,
IEnvironmentService
}
from
'
vs/platform/environment/common/environment
'
;
import
{
IWorkspaceIdentifier
,
ISingleFolderWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
IRecentlyOpened
}
from
'
vs/platform/history/common/history
'
;
import
{
IRecentlyOpened
,
IRecent
}
from
'
vs/platform/history/common/history
'
;
import
{
ISerializableCommandAction
}
from
'
vs/platform/actions/common/actions
'
;
import
{
ExportData
}
from
'
vs/base/common/performance
'
;
import
{
LogLevel
}
from
'
vs/platform/log/common/log
'
;
...
...
@@ -117,8 +117,8 @@ export interface IWindowsService {
enterWorkspace
(
windowId
:
number
,
path
:
URI
):
Promise
<
IEnterWorkspaceResult
|
undefined
>
;
toggleFullScreen
(
windowId
:
number
):
Promise
<
void
>
;
setRepresentedFilename
(
windowId
:
number
,
fileName
:
string
):
Promise
<
void
>
;
addRecentlyOpened
(
workspaces
:
URI
[],
folders
:
URI
[],
files
:
URI
[]):
Promise
<
void
>
;
removeFromRecentlyOpened
(
paths
:
Array
<
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
|
URI
|
string
>
):
Promise
<
void
>
;
addRecentlyOpened
(
recents
:
IRecent
[]):
Promise
<
void
>
;
removeFromRecentlyOpened
(
paths
:
URI
[]
):
Promise
<
void
>
;
clearRecentlyOpened
():
Promise
<
void
>
;
getRecentlyOpened
(
windowId
:
number
):
Promise
<
IRecentlyOpened
>
;
focusWindow
(
windowId
:
number
):
Promise
<
void
>
;
...
...
@@ -190,6 +190,7 @@ export type URIType = 'file' | 'folder';
export
interface
IURIToOpen
{
uri
:
URI
;
typeHint
?:
URIType
;
label
?:
string
;
}
export
interface
IWindowService
{
...
...
src/vs/platform/windows/electron-browser/windowService.ts
浏览文件 @
4276d2c3
...
...
@@ -10,6 +10,8 @@ import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import
{
ParsedArgs
}
from
'
vs/platform/environment/common/environment
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
Disposable
}
from
'
vs/base/common/lifecycle
'
;
import
{
hasWorkspaceFileExtension
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
ILabelService
}
from
'
vs/platform/label/common/label
'
;
export
class
WindowService
extends
Disposable
implements
IWindowService
{
...
...
@@ -25,7 +27,8 @@ export class WindowService extends Disposable implements IWindowService {
constructor
(
private
configuration
:
IWindowConfiguration
,
@
IWindowsService
private
readonly
windowsService
:
IWindowsService
@
IWindowsService
private
readonly
windowsService
:
IWindowsService
,
@
ILabelService
private
readonly
labelService
:
ILabelService
)
{
super
();
...
...
@@ -96,6 +99,9 @@ export class WindowService extends Disposable implements IWindowService {
}
openWindow
(
uris
:
IURIToOpen
[],
options
?:
IOpenSettings
):
Promise
<
void
>
{
if
(
!!
this
.
configuration
.
remoteAuthority
)
{
uris
.
forEach
(
u
=>
u
.
label
=
u
.
label
||
this
.
getRecentLabel
(
u
,
!!
(
options
&&
options
.
forceOpenWorkspaceAsFile
)));
}
return
this
.
windowsService
.
openWindow
(
this
.
windowId
,
uris
,
options
);
}
...
...
@@ -170,4 +176,15 @@ export class WindowService extends Disposable implements IWindowService {
resolveProxy
(
url
:
string
):
Promise
<
string
|
undefined
>
{
return
this
.
windowsService
.
resolveProxy
(
this
.
windowId
,
url
);
}
private
getRecentLabel
(
u
:
IURIToOpen
,
forceOpenWorkspaceAsFile
:
boolean
):
string
{
if
(
u
.
typeHint
===
'
folder
'
)
{
return
this
.
labelService
.
getWorkspaceLabel
(
u
.
uri
,
{
verbose
:
true
});
}
else
if
(
!
forceOpenWorkspaceAsFile
&&
hasWorkspaceFileExtension
(
u
.
uri
.
path
))
{
return
this
.
labelService
.
getWorkspaceLabel
({
id
:
''
,
configPath
:
u
.
uri
},
{
verbose
:
true
});
}
else
{
return
this
.
labelService
.
getUriLabel
(
u
.
uri
);
}
}
}
src/vs/platform/windows/electron-main/windowsService.ts
浏览文件 @
4276d2c3
...
...
@@ -16,8 +16,8 @@ import { Event } from 'vs/base/common/event';
import
{
IURLService
,
IURLHandler
}
from
'
vs/platform/url/common/url
'
;
import
{
ILifecycleService
}
from
'
vs/platform/lifecycle/electron-main/lifecycleMain
'
;
import
{
IWindowsMainService
,
ISharedProcess
,
ICodeWindow
}
from
'
vs/platform/windows/electron-main/windows
'
;
import
{
IHistoryMainService
,
IRecentlyOpened
}
from
'
vs/platform/history/common/history
'
;
import
{
IWorkspaceIdentifier
,
ISingleFolderWorkspaceIdentifier
,
IWorkspacesMainService
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
IHistoryMainService
,
IRecentlyOpened
,
IRecent
}
from
'
vs/platform/history/common/history
'
;
import
{
IWorkspaceIdentifier
,
ISingleFolderWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
ISerializableCommandAction
}
from
'
vs/platform/actions/common/actions
'
;
import
{
Schemas
}
from
'
vs/base/common/network
'
;
import
{
mnemonicButtonLabel
}
from
'
vs/base/common/labels
'
;
...
...
@@ -50,8 +50,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
@
IURLService
urlService
:
IURLService
,
@
ILifecycleService
private
readonly
lifecycleService
:
ILifecycleService
,
@
IHistoryMainService
private
readonly
historyService
:
IHistoryMainService
,
@
ILogService
private
readonly
logService
:
ILogService
,
@
IWorkspacesMainService
private
readonly
workspacesMainService
:
IWorkspacesMainService
,
@
ILogService
private
readonly
logService
:
ILogService
)
{
urlService
.
registerHandler
(
this
);
...
...
@@ -157,14 +156,12 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
return
this
.
withWindow
(
windowId
,
codeWindow
=>
codeWindow
.
setRepresentedFilename
(
fileName
));
}
async
addRecentlyOpened
(
workspaces
:
URI
[],
folders
:
URI
[],
files
:
URI
[]):
Promise
<
void
>
{
async
addRecentlyOpened
(
recents
:
IRecent
[]):
Promise
<
void
>
{
this
.
logService
.
trace
(
'
windowsService#addRecentlyOpened
'
);
const
workspaceIdentifiers
=
workspaces
.
map
(
w
=>
this
.
workspacesMainService
.
getWorkspaceIdentifier
(
w
));
this
.
historyService
.
addRecentlyOpened
([...
workspaceIdentifiers
,
...
folders
],
files
);
this
.
historyService
.
addRecentlyOpened
(
recents
);
}
async
removeFromRecentlyOpened
(
paths
:
Array
<
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
|
URI
|
string
>
):
Promise
<
void
>
{
async
removeFromRecentlyOpened
(
paths
:
URI
[]
):
Promise
<
void
>
{
this
.
logService
.
trace
(
'
windowsService#removeFromRecentlyOpened
'
);
this
.
historyService
.
removeFromRecentlyOpened
(
paths
);
...
...
@@ -179,7 +176,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
async
getRecentlyOpened
(
windowId
:
number
):
Promise
<
IRecentlyOpened
>
{
this
.
logService
.
trace
(
'
windowsService#getRecentlyOpened
'
,
windowId
);
return
this
.
withWindow
(
windowId
,
codeWindow
=>
this
.
historyService
.
getRecentlyOpened
(
codeWindow
.
config
.
workspace
||
codeWindow
.
config
.
folderUri
,
codeWindow
.
config
.
filesToOpen
),
()
=>
this
.
historyService
.
getRecentlyOpened
())
!
;
return
this
.
withWindow
(
windowId
,
codeWindow
=>
this
.
historyService
.
getRecentlyOpened
(
codeWindow
.
config
.
workspace
,
codeWindow
.
config
.
folderUri
,
codeWindow
.
config
.
filesToOpen
),
()
=>
this
.
historyService
.
getRecentlyOpened
())
!
;
}
async
newWindowTab
():
Promise
<
void
>
{
...
...
src/vs/platform/windows/node/windowsIpc.ts
浏览文件 @
4276d2c3
...
...
@@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event';
import
{
IChannel
,
IServerChannel
}
from
'
vs/base/parts/ipc/node/ipc
'
;
import
{
IWindowsService
,
INativeOpenDialogOptions
,
IEnterWorkspaceResult
,
CrashReporterStartOptions
,
IMessageBoxResult
,
MessageBoxOptions
,
SaveDialogOptions
,
OpenDialogOptions
,
IDevToolsOptions
,
INewWindowOptions
,
IURIToOpen
}
from
'
vs/platform/windows/common/windows
'
;
import
{
IWorkspaceIdentifier
,
ISingleFolderWorkspaceIdentifier
,
reviveWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
IRecentlyOpened
}
from
'
vs/platform/history/common/history
'
;
import
{
IRecentlyOpened
,
isRecentFile
,
isRecentFolder
,
IRecent
,
isRecentWorkspace
}
from
'
vs/platform/history/common/history
'
;
import
{
ISerializableCommandAction
}
from
'
vs/platform/actions/common/actions
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
ParsedArgs
}
from
'
vs/platform/environment/common/environment
'
;
...
...
@@ -59,14 +59,17 @@ export class WindowsChannel implements IServerChannel {
case
'
enterWorkspace
'
:
return
this
.
service
.
enterWorkspace
(
arg
[
0
],
URI
.
revive
(
arg
[
1
]));
case
'
toggleFullScreen
'
:
return
this
.
service
.
toggleFullScreen
(
arg
);
case
'
setRepresentedFilename
'
:
return
this
.
service
.
setRepresentedFilename
(
arg
[
0
],
arg
[
1
]);
case
'
addRecentlyOpened
'
:
return
this
.
service
.
addRecentlyOpened
(
arg
[
0
].
map
(
URI
.
revive
),
arg
[
1
].
map
(
URI
.
revive
),
arg
[
2
].
map
(
URI
.
revive
));
case
'
removeFromRecentlyOpened
'
:
{
let
paths
:
Array
<
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
|
URI
|
string
>
=
arg
;
if
(
Array
.
isArray
(
paths
))
{
paths
=
paths
.
map
(
path
=>
isChanneledWorkspaceIdentifier
(
path
)
?
reviveWorkspaceIdentifier
(
path
)
:
typeof
path
===
'
string
'
?
path
:
URI
.
revive
(
path
));
case
'
addRecentlyOpened
'
:
return
this
.
service
.
addRecentlyOpened
(
arg
.
map
((
recent
:
IRecent
)
=>
{
if
(
isRecentFile
(
recent
))
{
recent
.
fileUri
=
URI
.
revive
(
recent
.
fileUri
);
}
else
if
(
isRecentFolder
(
recent
))
{
recent
.
folderUri
=
URI
.
revive
(
recent
.
folderUri
);
}
else
{
recent
.
workspace
=
reviveWorkspaceIdentifier
(
recent
.
workspace
);
}
return
this
.
service
.
removeFromRecentlyOpened
(
paths
);
}
return
recent
;
}));
case
'
removeFromRecentlyOpened
'
:
return
this
.
service
.
removeFromRecentlyOpened
(
arg
.
map
(
URI
.
revive
));
case
'
clearRecentlyOpened
'
:
return
this
.
service
.
clearRecentlyOpened
();
case
'
newWindowTab
'
:
return
this
.
service
.
newWindowTab
();
case
'
showPreviousWindowTab
'
:
return
this
.
service
.
showPreviousWindowTab
();
...
...
@@ -85,7 +88,7 @@ export class WindowsChannel implements IServerChannel {
case
'
minimizeWindow
'
:
return
this
.
service
.
minimizeWindow
(
arg
);
case
'
onWindowTitleDoubleClick
'
:
return
this
.
service
.
onWindowTitleDoubleClick
(
arg
);
case
'
setDocumentEdited
'
:
return
this
.
service
.
setDocumentEdited
(
arg
[
0
],
arg
[
1
]);
case
'
openWindow
'
:
return
this
.
service
.
openWindow
(
arg
[
0
],
arg
[
1
]
?
(
<
IURIToOpen
[]
>
arg
[
1
]).
map
(
r
=>
({
uri
:
URI
.
revive
(
r
.
uri
),
typeHint
:
r
.
typeHint
})
)
:
arg
[
1
],
arg
[
2
]);
case
'
openWindow
'
:
return
this
.
service
.
openWindow
(
arg
[
0
],
arg
[
1
]
?
(
<
IURIToOpen
[]
>
arg
[
1
]).
map
(
r
=>
{
r
.
uri
=
URI
.
revive
(
r
.
uri
);
return
r
;
}
)
:
arg
[
1
],
arg
[
2
]);
case
'
openNewWindow
'
:
return
this
.
service
.
openNewWindow
(
arg
);
case
'
showWindow
'
:
return
this
.
service
.
showWindow
(
arg
);
case
'
getWindows
'
:
return
this
.
service
.
getWindows
();
...
...
@@ -166,7 +169,8 @@ export class WindowsChannelClient implements IWindowsService {
enterWorkspace
(
windowId
:
number
,
path
:
URI
):
Promise
<
IEnterWorkspaceResult
>
{
return
this
.
channel
.
call
(
'
enterWorkspace
'
,
[
windowId
,
path
]).
then
((
result
:
IEnterWorkspaceResult
)
=>
{
return
{
backupPath
:
result
.
backupPath
,
workspace
:
reviveWorkspaceIdentifier
(
result
.
workspace
)
};
result
.
workspace
=
reviveWorkspaceIdentifier
(
result
.
workspace
);
return
result
;
});
}
...
...
@@ -178,11 +182,11 @@ export class WindowsChannelClient implements IWindowsService {
return
this
.
channel
.
call
(
'
setRepresentedFilename
'
,
[
windowId
,
fileName
]);
}
addRecentlyOpened
(
workspaces
:
URI
[],
folders
:
URI
[],
files
:
URI
[]):
Promise
<
void
>
{
return
this
.
channel
.
call
(
'
addRecentlyOpened
'
,
[
workspaces
,
folders
,
files
]
);
addRecentlyOpened
(
recent
:
IRecent
[]):
Promise
<
void
>
{
return
this
.
channel
.
call
(
'
addRecentlyOpened
'
,
recent
);
}
removeFromRecentlyOpened
(
paths
:
Array
<
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
|
URI
>
):
Promise
<
void
>
{
removeFromRecentlyOpened
(
paths
:
Array
<
URI
>
):
Promise
<
void
>
{
return
this
.
channel
.
call
(
'
removeFromRecentlyOpened
'
,
paths
);
}
...
...
@@ -193,8 +197,8 @@ export class WindowsChannelClient implements IWindowsService {
getRecentlyOpened
(
windowId
:
number
):
Promise
<
IRecentlyOpened
>
{
return
this
.
channel
.
call
(
'
getRecentlyOpened
'
,
windowId
)
.
then
((
recentlyOpened
:
IRecentlyOpened
)
=>
{
recentlyOpened
.
workspaces
=
recentlyOpened
.
workspaces
.
map
(
workspace
=>
isChanneledWorkspaceIdentifier
(
workspace
)
?
reviveWorkspaceIdentifier
(
workspace
)
:
URI
.
revive
(
workspace
));
recentlyOpened
.
files
=
recentlyOpened
.
files
.
map
(
URI
.
revive
);
recentlyOpened
.
workspaces
.
forEach
(
recent
=>
isRecentWorkspace
(
recent
)
?
recent
.
workspace
=
reviveWorkspaceIdentifier
(
recent
.
workspace
)
:
recent
.
folderUri
=
URI
.
revive
(
recent
.
folderUri
));
recentlyOpened
.
files
.
forEach
(
recent
=>
recent
.
fileUri
=
URI
.
revive
(
recent
.
fileUri
)
);
return
recentlyOpened
;
});
}
...
...
@@ -336,8 +340,4 @@ export class WindowsChannelClient implements IWindowsService {
resolveProxy
(
windowId
:
number
,
url
:
string
):
Promise
<
string
|
undefined
>
{
return
Promise
.
resolve
(
this
.
channel
.
call
(
'
resolveProxy
'
,
[
windowId
,
url
]));
}
}
function
isChanneledWorkspaceIdentifier
(
obj
:
any
):
obj
is
IWorkspaceIdentifier
{
return
obj
&&
obj
[
'
configPath
'
];
}
\ No newline at end of file
src/vs/platform/workspaces/common/workspaces.ts
浏览文件 @
4276d2c3
...
...
@@ -106,8 +106,6 @@ export interface IWorkspacesMainService extends IWorkspacesService {
deleteUntitledWorkspaceSync
(
workspace
:
IWorkspaceIdentifier
):
void
;
getUntitledWorkspacesSync
():
IUntitledWorkspaceInfo
[];
getWorkspaceIdentifier
(
workspacePath
:
URI
):
IWorkspaceIdentifier
;
}
export
interface
IWorkspacesService
{
...
...
@@ -116,6 +114,8 @@ export interface IWorkspacesService {
createUntitledWorkspace
(
folders
?:
IWorkspaceFolderCreationData
[],
remoteAuthority
?:
string
):
Promise
<
IWorkspaceIdentifier
>
;
deleteUntitledWorkspace
(
workspace
:
IWorkspaceIdentifier
):
Promise
<
void
>
;
getWorkspaceIdentifier
(
workspacePath
:
URI
):
Promise
<
IWorkspaceIdentifier
>
;
}
export
function
isSingleFolderWorkspaceIdentifier
(
obj
:
any
):
obj
is
ISingleFolderWorkspaceIdentifier
{
...
...
src/vs/platform/workspaces/electron-main/workspacesMainService.ts
浏览文件 @
4276d2c3
...
...
@@ -68,7 +68,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
private
doResolveWorkspace
(
path
:
URI
,
contents
:
string
):
IResolvedWorkspace
|
null
{
try
{
const
workspace
=
this
.
doParseStoredWorkspace
(
path
,
contents
);
const
workspaceIdentifier
=
this
.
getWorkspaceIdentifier
(
path
);
const
workspaceIdentifier
=
getWorkspaceIdentifier
(
path
);
return
{
id
:
workspaceIdentifier
.
id
,
configPath
:
workspaceIdentifier
.
configPath
,
...
...
@@ -143,25 +143,13 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
}
return
{
workspace
:
this
.
getWorkspaceIdentifier
(
untitledWorkspaceConfigPath
),
workspace
:
getWorkspaceIdentifier
(
untitledWorkspaceConfigPath
),
storedWorkspace
:
{
folders
:
storedWorkspaceFolder
,
remoteAuthority
}
};
}
getWorkspaceId
(
configPath
:
URI
):
string
{
let
workspaceConfigPath
=
configPath
.
scheme
===
Schemas
.
file
?
originalFSPath
(
configPath
)
:
configPath
.
toString
();
if
(
!
isLinux
)
{
workspaceConfigPath
=
workspaceConfigPath
.
toLowerCase
();
// sanitize for platform file system
}
return
createHash
(
'
md5
'
).
update
(
workspaceConfigPath
).
digest
(
'
hex
'
);
}
getWorkspaceIdentifier
(
configPath
:
URI
):
IWorkspaceIdentifier
{
return
{
configPath
,
id
:
this
.
getWorkspaceId
(
configPath
)
};
getWorkspaceIdentifier
(
configPath
:
URI
):
Promise
<
IWorkspaceIdentifier
>
{
return
Promise
.
resolve
(
getWorkspaceIdentifier
(
configPath
));
}
isUntitledWorkspace
(
workspace
:
IWorkspaceIdentifier
):
boolean
{
...
...
@@ -206,7 +194,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
try
{
const
untitledWorkspacePaths
=
readdirSync
(
this
.
untitledWorkspacesHome
.
fsPath
).
map
(
folder
=>
joinPath
(
this
.
untitledWorkspacesHome
,
folder
,
UNTITLED_WORKSPACE_NAME
));
for
(
const
untitledWorkspacePath
of
untitledWorkspacePaths
)
{
const
workspace
=
this
.
getWorkspaceIdentifier
(
untitledWorkspacePath
);
const
workspace
=
getWorkspaceIdentifier
(
untitledWorkspacePath
);
const
resolvedWorkspace
=
this
.
resolveLocalWorkspaceSync
(
untitledWorkspacePath
);
if
(
!
resolvedWorkspace
)
{
this
.
doDeleteUntitledWorkspaceSync
(
workspace
);
...
...
@@ -222,3 +210,19 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
return
untitledWorkspaces
;
}
}
function
getWorkspaceId
(
configPath
:
URI
):
string
{
let
workspaceConfigPath
=
configPath
.
scheme
===
Schemas
.
file
?
originalFSPath
(
configPath
)
:
configPath
.
toString
();
if
(
!
isLinux
)
{
workspaceConfigPath
=
workspaceConfigPath
.
toLowerCase
();
// sanitize for platform file system
}
return
createHash
(
'
md5
'
).
update
(
workspaceConfigPath
).
digest
(
'
hex
'
);
}
export
function
getWorkspaceIdentifier
(
configPath
:
URI
):
IWorkspaceIdentifier
{
return
{
configPath
,
id
:
getWorkspaceId
(
configPath
)
};
}
src/vs/platform/workspaces/node/workspacesIpc.ts
浏览文件 @
4276d2c3
...
...
@@ -37,6 +37,9 @@ export class WorkspacesChannel implements IServerChannel {
const
w
:
IWorkspaceIdentifier
=
arg
;
return
this
.
service
.
deleteUntitledWorkspace
({
id
:
w
.
id
,
configPath
:
URI
.
revive
(
w
.
configPath
)
});
}
case
'
getWorkspaceIdentifier
'
:
{
return
this
.
service
.
getWorkspaceIdentifier
(
URI
.
revive
(
arg
));
}
}
throw
new
Error
(
`Call not found:
${
command
}
`
);
...
...
@@ -56,4 +59,8 @@ export class WorkspacesChannelClient implements IWorkspacesService {
deleteUntitledWorkspace
(
workspaceIdentifier
:
IWorkspaceIdentifier
):
Promise
<
void
>
{
return
this
.
channel
.
call
(
'
deleteUntitledWorkspace
'
,
workspaceIdentifier
);
}
getWorkspaceIdentifier
(
configPath
:
URI
):
Promise
<
IWorkspaceIdentifier
>
{
return
this
.
channel
.
call
(
'
getWorkspaceIdentifier
'
,
configPath
);
}
}
src/vs/workbench/api/node/apiCommands.ts
浏览文件 @
4276d2c3
...
...
@@ -11,7 +11,6 @@ import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import
{
EditorViewColumn
}
from
'
vs/workbench/api/shared/editor
'
;
import
{
EditorGroupLayout
}
from
'
vs/workbench/services/editor/common/editorGroupsService
'
;
import
{
ServicesAccessor
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
IWorkspaceIdentifier
,
ISingleFolderWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
IWindowsService
,
IOpenSettings
}
from
'
vs/platform/windows/common/windows
'
;
import
{
IDownloadService
}
from
'
vs/platform/download/common/download
'
;
...
...
@@ -52,6 +51,7 @@ export class OpenFolderAPICommand {
if
(
arg
.
noRecentEntry
)
{
options
.
args
=
{
_
:
[],
'
skip-add-to-recently-opened
'
:
true
};
}
uri
=
URI
.
revive
(
uri
);
return
executor
.
executeCommand
(
'
_files.windowOpen
'
,
[{
uri
}],
options
);
}
}
...
...
@@ -106,15 +106,19 @@ export class OpenAPICommand {
}
CommandsRegistry
.
registerCommand
(
OpenAPICommand
.
ID
,
adjustHandler
(
OpenAPICommand
.
execute
));
CommandsRegistry
.
registerCommand
(
'
_workbench.removeFromRecentlyOpened
'
,
function
(
accessor
:
ServicesAccessor
,
path
:
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
|
URI
|
string
)
{
CommandsRegistry
.
registerCommand
(
'
_workbench.removeFromRecentlyOpened
'
,
function
(
accessor
:
ServicesAccessor
,
uri
:
URI
)
{
const
windowsService
=
accessor
.
get
(
IWindowsService
);
return
windowsService
.
removeFromRecentlyOpened
([
path
]).
then
(()
=>
undefined
);
return
windowsService
.
removeFromRecentlyOpened
([
uri
]).
then
(()
=>
undefined
);
});
export
class
RemoveFromRecentlyOpenedAPICommand
{
public
static
ID
=
'
vscode.removeFromRecentlyOpened
'
;
public
static
execute
(
executor
:
ICommandsExecutor
,
path
:
string
):
Promise
<
any
>
{
public
static
execute
(
executor
:
ICommandsExecutor
,
path
:
string
|
URI
):
Promise
<
any
>
{
if
(
typeof
path
===
'
string
'
)
{
path
=
path
.
match
(
/^
[^
:
/
?#
]
+:
\/\/
/
)
?
URI
.
parse
(
path
)
:
URI
.
file
(
path
);
}
else
{
path
=
URI
.
revive
(
path
);
// called from extension host
}
return
executor
.
executeCommand
(
'
_workbench.removeFromRecentlyOpened
'
,
path
);
}
}
...
...
src/vs/workbench/browser/dnd.ts
浏览文件 @
4276d2c3
...
...
@@ -28,6 +28,7 @@ import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/co
import
{
Disposable
}
from
'
vs/base/common/lifecycle
'
;
import
{
addDisposableListener
,
EventType
}
from
'
vs/base/browser/dom
'
;
import
{
IEditorGroup
}
from
'
vs/workbench/services/editor/common/editorGroupsService
'
;
import
{
IRecentFile
}
from
'
vs/platform/history/common/history
'
;
export
interface
IDraggedResource
{
resource
:
URI
;
...
...
@@ -178,9 +179,9 @@ export class ResourcesDropHandler {
}
// Add external ones to recently open list unless dropped resource is a workspace
const
filesToAddToHistory
=
untitledOrFileResources
.
filter
(
d
=>
d
.
isExternal
&&
d
.
resource
.
scheme
===
Schemas
.
file
).
map
(
d
=>
d
.
resource
);
if
(
filesToAddToHistory
.
length
)
{
this
.
windowsService
.
addRecentlyOpened
(
[],
[],
filesToAddToHistory
);
const
recents
:
IRecentFile
[]
=
untitledOrFileResources
.
filter
(
d
=>
d
.
isExternal
&&
d
.
resource
.
scheme
===
Schemas
.
file
).
map
(
d
=>
({
fileUri
:
d
.
resource
})
);
if
(
recents
.
length
)
{
this
.
windowsService
.
addRecentlyOpened
(
recents
);
}
const
editors
:
IResourceEditor
[]
=
untitledOrFileResources
.
map
(
untitledOrFileResource
=>
({
...
...
src/vs/workbench/browser/parts/titlebar/menubarControl.ts
浏览文件 @
4276d2c3
...
...
@@ -17,8 +17,7 @@ import { isMacintosh, isLinux } from 'vs/base/common/platform';
import
{
IConfigurationService
,
IConfigurationChangeEvent
}
from
'
vs/platform/configuration/common/configuration
'
;
import
{
Event
,
Emitter
}
from
'
vs/base/common/event
'
;
import
{
Disposable
}
from
'
vs/base/common/lifecycle
'
;
import
{
IRecentlyOpened
}
from
'
vs/platform/history/common/history
'
;
import
{
IWorkspaceIdentifier
,
ISingleFolderWorkspaceIdentifier
,
isSingleFolderWorkspaceIdentifier
,
isWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
IRecentlyOpened
,
isRecentFolder
,
IRecent
,
isRecentWorkspace
}
from
'
vs/platform/history/common/history
'
;
import
{
RunOnceScheduler
}
from
'
vs/base/common/async
'
;
import
{
MENUBAR_SELECTION_FOREGROUND
,
MENUBAR_SELECTION_BACKGROUND
,
MENUBAR_SELECTION_BORDER
,
TITLE_BAR_ACTIVE_FOREGROUND
,
TITLE_BAR_INACTIVE_FOREGROUND
}
from
'
vs/workbench/common/theme
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
...
...
@@ -348,33 +347,31 @@ export class MenubarControl extends Disposable {
return
label
;
}
private
createOpenRecentMenuAction
(
workspace
:
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
|
URI
,
isFile
:
boolean
):
IAction
&
{
uri
:
URI
}
{
private
createOpenRecentMenuAction
(
recent
:
IRecent
,
isFile
:
boolean
):
IAction
&
{
uri
:
URI
}
{
let
label
:
string
;
let
uri
:
URI
;
let
commandId
:
string
;
let
typeHint
:
URIType
|
undefined
;
if
(
is
SingleFolderWorkspaceIdentifier
(
workspace
)
&&
!
isFile
)
{
label
=
this
.
labelService
.
getWorkspaceLabel
(
workspace
,
{
verbose
:
true
})
;
uri
=
workspace
;
if
(
is
RecentFolder
(
recent
)
)
{
uri
=
recent
.
folderUri
;
label
=
recent
.
label
||
this
.
labelService
.
getWorkspaceLabel
(
uri
,
{
verbose
:
true
})
;
commandId
=
'
openRecentFolder
'
;
typeHint
=
'
folder
'
;
}
else
if
(
is
WorkspaceIdentifier
(
workspace
))
{
label
=
this
.
labelService
.
getWorkspaceLabel
(
workspace
,
{
verbose
:
true
})
;
uri
=
workspace
.
configPath
;
}
else
if
(
is
RecentWorkspace
(
recent
))
{
uri
=
recent
.
workspace
.
configPath
;
label
=
recent
.
label
||
this
.
labelService
.
getWorkspaceLabel
(
recent
.
workspace
,
{
verbose
:
true
})
;
commandId
=
'
openRecentWorkspace
'
;
typeHint
=
'
file
'
;
}
else
{
uri
=
workspace
;
label
=
this
.
labelService
.
getUriLabel
(
uri
);
uri
=
recent
.
fileUri
;
label
=
recent
.
label
||
this
.
labelService
.
getUriLabel
(
uri
);
commandId
=
'
openRecentFile
'
;
typeHint
=
'
file
'
;
}
label
=
unmnemonicLabel
(
label
);
const
ret
:
IAction
=
new
Action
(
commandId
,
label
,
undefined
,
undefined
,
(
event
)
=>
{
const
ret
:
IAction
=
new
Action
(
commandId
,
unmnemonicLabel
(
label
),
undefined
,
undefined
,
(
event
)
=>
{
const
openInNewWindow
=
event
&&
((
!
isMacintosh
&&
(
event
.
ctrlKey
||
event
.
shiftKey
))
||
(
isMacintosh
&&
(
event
.
metaKey
||
event
.
altKey
)));
return
this
.
windowService
.
openWindow
([{
uri
,
typeHint
}],
{
...
...
src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts
浏览文件 @
4276d2c3
...
...
@@ -6,7 +6,6 @@
import
'
vs/css!./welcomePage
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
*
as
strings
from
'
vs/base/common/strings
'
;
import
*
as
path
from
'
vs/base/common/path
'
;
import
{
ICommandService
}
from
'
vs/platform/commands/common/commands
'
;
import
*
as
arrays
from
'
vs/base/common/arrays
'
;
import
{
WalkThroughInput
}
from
'
vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput
'
;
...
...
@@ -20,7 +19,6 @@ import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configur
import
{
localize
}
from
'
vs/nls
'
;
import
{
Action
}
from
'
vs/base/common/actions
'
;
import
{
ITelemetryService
}
from
'
vs/platform/telemetry/common/telemetry
'
;
import
{
IEnvironmentService
}
from
'
vs/platform/environment/common/environment
'
;
import
{
Schemas
}
from
'
vs/base/common/network
'
;
import
{
IBackupFileService
}
from
'
vs/workbench/services/backup/common/backup
'
;
import
{
getInstalledExtensions
,
IExtensionStatus
,
onExtensionChanged
,
isKeymapExtension
}
from
'
vs/workbench/contrib/extensions/common/extensionsUtils
'
;
...
...
@@ -28,12 +26,11 @@ import { IExtensionEnablementService, IExtensionManagementService, IExtensionGal
import
{
used
}
from
'
vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page
'
;
import
{
ILifecycleService
,
StartupKind
}
from
'
vs/platform/lifecycle/common/lifecycle
'
;
import
{
IDisposable
,
dispose
}
from
'
vs/base/common/lifecycle
'
;
import
{
tildify
,
getBaseLabel
}
from
'
vs/base/common/labels
'
;
import
{
splitName
}
from
'
vs/base/common/labels
'
;
import
{
registerThemingParticipant
}
from
'
vs/platform/theme/common/themeService
'
;
import
{
registerColor
,
focusBorder
,
textLinkForeground
,
textLinkActiveForeground
,
foreground
,
descriptionForeground
,
contrastBorder
,
activeContrastBorder
}
from
'
vs/platform/theme/common/colorRegistry
'
;
import
{
getExtraColor
}
from
'
vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils
'
;
import
{
IExtensionsWorkbenchService
}
from
'
vs/workbench/contrib/extensions/common/extensions
'
;
import
{
IWorkspaceIdentifier
,
ISingleFolderWorkspaceIdentifier
,
isSingleFolderWorkspaceIdentifier
,
isWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
IEditorInputFactory
,
EditorInput
}
from
'
vs/workbench/common/editor
'
;
import
{
INotificationService
,
Severity
}
from
'
vs/platform/notification/common/notification
'
;
import
{
TimeoutTimer
}
from
'
vs/base/common/async
'
;
...
...
@@ -42,6 +39,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
import
{
IFileService
}
from
'
vs/platform/files/common/files
'
;
import
{
ExtensionType
}
from
'
vs/platform/extensions/common/extensions
'
;
import
{
joinPath
}
from
'
vs/base/common/resources
'
;
import
{
IRecentlyOpened
,
isRecentWorkspace
,
IRecentWorkspace
,
IRecentFolder
,
isRecentFolder
}
from
'
vs/platform/history/common/history
'
;
used
();
...
...
@@ -256,7 +254,6 @@ class WelcomePage {
@
IWindowService
private
readonly
windowService
:
IWindowService
,
@
IWorkspaceContextService
private
readonly
contextService
:
IWorkspaceContextService
,
@
IConfigurationService
private
readonly
configurationService
:
IConfigurationService
,
@
IEnvironmentService
private
readonly
environmentService
:
IEnvironmentService
,
@
ILabelService
private
readonly
labelService
:
ILabelService
,
@
INotificationService
private
readonly
notificationService
:
INotificationService
,
@
IExtensionEnablementService
private
readonly
extensionEnablementService
:
IExtensionEnablementService
,
...
...
@@ -289,7 +286,7 @@ class WelcomePage {
return
this
.
editorService
.
openEditor
(
this
.
editorInput
,
{
pinned
:
false
});
}
private
onReady
(
container
:
HTMLElement
,
recentlyOpened
:
Promise
<
{
files
:
URI
[];
workspaces
:
Array
<
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
>
;
}
>
,
installedExtensions
:
Promise
<
IExtensionStatus
[]
>
):
void
{
private
onReady
(
container
:
HTMLElement
,
recentlyOpened
:
Promise
<
IRecentlyOpened
>
,
installedExtensions
:
Promise
<
IExtensionStatus
[]
>
):
void
{
const
enabled
=
isWelcomePageEnabled
(
this
.
configurationService
,
this
.
contextService
);
const
showOnStartup
=
<
HTMLInputElement
>
container
.
querySelector
(
'
#showOnStartup
'
);
if
(
enabled
)
{
...
...
@@ -301,7 +298,7 @@ class WelcomePage {
recentlyOpened
.
then
(({
workspaces
})
=>
{
// Filter out the current workspace
workspaces
=
workspaces
.
filter
(
workspace
=>
!
this
.
contextService
.
isCurrentWorkspace
(
workspace
));
workspaces
=
workspaces
.
filter
(
recent
=>
!
this
.
contextService
.
isCurrentWorkspace
(
isRecentWorkspace
(
recent
)
?
recent
.
workspace
:
recent
.
folderUri
));
if
(
!
workspaces
.
length
)
{
const
recent
=
container
.
querySelector
(
'
.welcomePage
'
)
as
HTMLElement
;
recent
.
classList
.
add
(
'
emptyRecent
'
);
...
...
@@ -339,47 +336,29 @@ class WelcomePage {
}));
}
private
createListEntries
(
workspaces
:
(
URI
|
IWorkspaceIdentifi
er
)[])
{
return
workspaces
.
map
(
workspace
=>
{
let
label
:
string
;
private
createListEntries
(
recents
:
(
IRecentWorkspace
|
IRecentFold
er
)[])
{
return
recents
.
map
(
recent
=>
{
let
fullPath
:
string
;
let
resource
:
URI
;
let
typeHint
:
URIType
|
undefined
;
if
(
is
SingleFolderWorkspaceIdentifier
(
workspace
))
{
resource
=
workspace
;
label
=
this
.
labelService
.
getWorkspaceLabel
(
workspace
);
if
(
is
RecentFolder
(
recent
))
{
resource
=
recent
.
folderUri
;
fullPath
=
recent
.
label
||
this
.
labelService
.
getWorkspaceLabel
(
recent
.
folderUri
,
{
verbose
:
true
}
);
typeHint
=
'
folder
'
;
}
else
if
(
isWorkspaceIdentifier
(
workspace
))
{
label
=
this
.
labelService
.
getWorkspaceLabel
(
workspace
);
resource
=
workspace
.
configPath
;
typeHint
=
'
file
'
;
}
else
{
label
=
getBaseLabel
(
workspace
);
resource
=
URI
.
file
(
workspace
)
;
fullPath
=
recent
.
label
||
this
.
labelService
.
getWorkspaceLabel
(
recent
.
workspace
,
{
verbose
:
true
}
);
resource
=
recent
.
workspace
.
configPath
;
typeHint
=
'
file
'
;
}
const
li
=
document
.
createElement
(
'
li
'
);
const
{
name
,
parentPath
}
=
splitName
(
fullPath
);
const
li
=
document
.
createElement
(
'
li
'
);
const
a
=
document
.
createElement
(
'
a
'
);
let
name
=
label
;
let
parentFolderPath
:
string
|
undefined
;
if
(
resource
.
scheme
===
Schemas
.
file
)
{
let
parentFolder
=
path
.
dirname
(
resource
.
fsPath
);
if
(
!
name
&&
parentFolder
)
{
const
tmp
=
name
;
name
=
parentFolder
;
parentFolder
=
tmp
;
}
parentFolderPath
=
tildify
(
parentFolder
,
this
.
environmentService
.
userHome
);
}
else
{
parentFolderPath
=
this
.
labelService
.
getUriLabel
(
resource
);
}
a
.
innerText
=
name
;
a
.
title
=
label
;
a
.
setAttribute
(
'
aria-label
'
,
localize
(
'
welcomePage.openFolderWithPath
'
,
"
Open folder {0} with path {1}
"
,
name
,
parent
Folder
Path
));
a
.
title
=
fullPath
;
a
.
setAttribute
(
'
aria-label
'
,
localize
(
'
welcomePage.openFolderWithPath
'
,
"
Open folder {0} with path {1}
"
,
name
,
parentPath
));
a
.
href
=
'
javascript:void(0)
'
;
a
.
addEventListener
(
'
click
'
,
e
=>
{
/* __GDPR__
...
...
@@ -401,8 +380,8 @@ class WelcomePage {
const
span
=
document
.
createElement
(
'
span
'
);
span
.
classList
.
add
(
'
path
'
);
span
.
classList
.
add
(
'
detail
'
);
span
.
innerText
=
parent
Folder
Path
;
span
.
title
=
label
;
span
.
innerText
=
parentPath
;
span
.
title
=
fullPath
;
li
.
appendChild
(
span
);
return
li
;
...
...
src/vs/workbench/electron-browser/actions/windowActions.ts
浏览文件 @
4276d2c3
...
...
@@ -14,11 +14,8 @@ import { isMacintosh } from 'vs/base/common/platform';
import
*
as
browser
from
'
vs/base/browser/browser
'
;
import
{
IKeybindingService
}
from
'
vs/platform/keybinding/common/keybinding
'
;
import
{
webFrame
}
from
'
electron
'
;
import
{
getBaseLabel
}
from
'
vs/base/common/labels
'
;
import
{
IWorkspaceIdentifier
,
ISingleFolderWorkspaceIdentifier
,
isSingleFolderWorkspaceIdentifier
,
isWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
FileKind
}
from
'
vs/platform/files/common/files
'
;
import
{
ILabelService
}
from
'
vs/platform/label/common/label
'
;
import
{
dirname
}
from
'
vs/base/common/resources
'
;
import
{
IModelService
}
from
'
vs/editor/common/services/modelService
'
;
import
{
IModeService
}
from
'
vs/editor/common/services/modeService
'
;
import
{
IQuickInputService
,
IQuickPickItem
,
IQuickInputButton
,
IQuickPickSeparator
,
IKeyMods
}
from
'
vs/platform/quickinput/common/quickInput
'
;
...
...
@@ -27,6 +24,8 @@ import product from 'vs/platform/product/node/product';
import
{
ICommandHandler
}
from
'
vs/platform/commands/common/commands
'
;
import
{
ServicesAccessor
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
IConfigurationService
}
from
'
vs/platform/configuration/common/configuration
'
;
import
{
IRecentFolder
,
IRecentFile
,
IRecentWorkspace
,
IRecent
,
isRecentFolder
,
isRecentWorkspace
}
from
'
vs/platform/history/common/history
'
;
import
{
splitName
}
from
'
vs/base/common/labels
'
;
export
class
CloseCurrentWindowAction
extends
Action
{
...
...
@@ -337,32 +336,31 @@ export abstract class BaseOpenRecentAction extends Action {
.
then
(({
workspaces
,
files
})
=>
this
.
openRecent
(
workspaces
,
files
));
}
private
openRecent
(
recentWorkspaces
:
Array
<
I
WorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
>
,
recentFiles
:
URI
[]):
void
{
const
toPick
=
(
workspace
:
IWorkspaceIdentifier
|
ISingleFolderWorkspaceIdentifier
|
URI
,
fileKind
:
FileKind
,
labelService
:
ILabelService
,
buttons
:
IQuickInputButton
[]
|
undefined
)
=>
{
let
resource
:
URI
;
let
label
:
string
;
let
description
:
string
;
if
(
is
SingleFolderWorkspaceIdentifier
(
workspace
)
&&
fileKind
!==
FileKind
.
FILE
)
{
resource
=
workspace
;
label
=
labelService
.
getWorkspaceLabel
(
workspace
);
description
=
labelService
.
getUriLabel
(
dirname
(
resource
)
!
)
;
}
else
if
(
is
WorkspaceIdentifier
(
workspace
))
{
resource
=
workspace
.
configPath
;
label
=
labelService
.
getWorkspaceLabel
(
workspace
);
description
=
labelService
.
getUriLabel
(
dirname
(
resource
)
!
)
;
private
openRecent
(
recentWorkspaces
:
Array
<
I
RecentWorkspace
|
IRecentFolder
>
,
recentFiles
:
IRecentFile
[]):
void
{
const
toPick
=
(
recent
:
IRecent
,
labelService
:
ILabelService
,
buttons
:
IQuickInputButton
[]
|
undefined
)
=>
{
let
resource
:
URI
|
undefined
;
let
fullLabel
:
string
|
undefined
;
let
fileKind
:
FileKind
|
undefined
;
if
(
is
RecentFolder
(
recent
)
)
{
resource
=
recent
.
folderUri
;
fullLabel
=
recent
.
label
||
labelService
.
getWorkspaceLabel
(
recent
.
folderUri
,
{
verbose
:
true
}
);
fileKind
=
FileKind
.
FOLDER
;
}
else
if
(
is
RecentWorkspace
(
recent
))
{
resource
=
recent
.
workspace
.
configPath
;
fullLabel
=
recent
.
label
||
labelService
.
getWorkspaceLabel
(
recent
.
workspace
,
{
verbose
:
true
}
);
fileKind
=
FileKind
.
ROOT_FOLDER
;
}
else
{
resource
=
workspace
;
label
=
getBaseLabel
(
workspace
);
description
=
labelService
.
getUriLabel
(
dirname
(
resource
)
!
)
;
resource
=
recent
.
fileUri
;
fullLabel
=
recent
.
label
||
labelService
.
getUriLabel
(
recent
.
fileUri
);
fileKind
=
FileKind
.
FILE
;
}
const
{
name
,
parentPath
}
=
splitName
(
fullLabel
);
return
{
iconClasses
:
getIconClasses
(
this
.
modelService
,
this
.
modeService
,
resource
,
fileKind
),
label
,
description
,
label
:
name
,
description
:
parentPath
,
buttons
,
workspace
,
resource
,
fileKind
,
};
...
...
@@ -373,11 +371,12 @@ export abstract class BaseOpenRecentAction extends Action {
return
this
.
windowService
.
openWindow
([{
uri
,
typeHint
:
isFile
?
'
file
'
:
'
folder
'
}],
{
forceNewWindow
,
forceOpenWorkspaceAsFile
:
isFile
});
};
const
workspacePicks
=
recentWorkspaces
.
map
(
workspace
=>
toPick
(
workspace
,
isSingleFolderWorkspaceIdentifier
(
workspace
)
?
FileKind
.
FOLDER
:
FileKind
.
ROOT_FOLDER
,
this
.
labelService
,
!
this
.
isQuickNavigate
()
?
[
this
.
removeFromRecentlyOpened
]
:
undefined
));
const
filePicks
=
recentFiles
.
map
(
p
=>
toPick
(
p
,
FileKind
.
FILE
,
this
.
labelService
,
!
this
.
isQuickNavigate
()
?
[
this
.
removeFromRecentlyOpened
]
:
undefined
));
const
workspacePicks
=
recentWorkspaces
.
map
(
workspace
=>
toPick
(
workspace
,
this
.
labelService
,
!
this
.
isQuickNavigate
()
?
[
this
.
removeFromRecentlyOpened
]
:
undefined
));
const
filePicks
=
recentFiles
.
map
(
p
=>
toPick
(
p
,
this
.
labelService
,
!
this
.
isQuickNavigate
()
?
[
this
.
removeFromRecentlyOpened
]
:
undefined
));
// focus second entry if the first recent workspace is the current workspace
let
autoFocusSecondEntry
:
boolean
=
recentWorkspaces
[
0
]
&&
this
.
contextService
.
isCurrentWorkspace
(
recentWorkspaces
[
0
]);
const
firstEntry
=
recentWorkspaces
[
0
];
let
autoFocusSecondEntry
:
boolean
=
firstEntry
&&
this
.
contextService
.
isCurrentWorkspace
(
isRecentWorkspace
(
firstEntry
)
?
firstEntry
.
workspace
:
firstEntry
.
folderUri
);
let
keyMods
:
IKeyMods
;
const
workspaceSeparator
:
IQuickPickSeparator
=
{
type
:
'
separator
'
,
label
:
nls
.
localize
(
'
workspaces
'
,
"
workspaces
"
)
};
...
...
@@ -391,14 +390,13 @@ export abstract class BaseOpenRecentAction extends Action {
onKeyMods
:
mods
=>
keyMods
=
mods
,
quickNavigate
:
this
.
isQuickNavigate
()
?
{
keybindings
:
this
.
keybindingService
.
lookupKeybindings
(
this
.
id
)
}
:
undefined
,
onDidTriggerItemButton
:
context
=>
{
this
.
windowsService
.
removeFromRecentlyOpened
([
context
.
item
.
workspa
ce
]).
then
(()
=>
context
.
removeItem
());
this
.
windowsService
.
removeFromRecentlyOpened
([
context
.
item
.
resour
ce
]).
then
(()
=>
context
.
removeItem
());
}
})
.
then
((
pick
):
Promise
<
void
>
|
void
=>
{
if
(
pick
)
{
return
runPick
(
pick
.
resource
,
pick
.
fileKind
===
FileKind
.
FILE
,
keyMods
);
}
});
}).
then
((
pick
):
Promise
<
void
>
|
void
=>
{
if
(
pick
)
{
return
runPick
(
pick
.
resource
,
pick
.
fileKind
===
FileKind
.
FILE
,
keyMods
);
}
});
}
}
...
...
src/vs/workbench/services/dialogs/electron-browser/dialogService.ts
浏览文件 @
4276d2c3
...
...
@@ -172,7 +172,7 @@ export class FileDialogService implements IFileDialogService {
@
IHistoryService
private
readonly
historyService
:
IHistoryService
,
@
IEnvironmentService
private
readonly
environmentService
:
IEnvironmentService
,
@
IInstantiationService
private
readonly
instantiationService
:
IInstantiationService
,
@
IConfigurationService
private
readonly
configurationService
:
IConfigurationService
,
@
IConfigurationService
private
readonly
configurationService
:
IConfigurationService
)
{
}
defaultFilePath
(
schemeFilter
=
this
.
getSchemeFilterForWindow
()):
URI
|
undefined
{
...
...
src/vs/workbench/services/workspace/node/workspaceEditingService.ts
浏览文件 @
4276d2c3
...
...
@@ -30,6 +30,7 @@ import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/d
import
{
mnemonicButtonLabel
}
from
'
vs/base/common/labels
'
;
import
{
IConfigurationService
}
from
'
vs/platform/configuration/common/configuration
'
;
import
{
registerSingleton
}
from
'
vs/platform/instantiation/common/extensions
'
;
import
{
ILabelService
}
from
'
vs/platform/label/common/label
'
;
export
class
WorkspaceEditingService
implements
IWorkspaceEditingService
{
...
...
@@ -51,7 +52,8 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
@
IEnvironmentService
private
readonly
environmentService
:
IEnvironmentService
,
@
IFileDialogService
private
readonly
fileDialogService
:
IFileDialogService
,
@
IDialogService
private
readonly
dialogService
:
IDialogService
,
@
ILifecycleService
readonly
lifecycleService
:
ILifecycleService
@
ILifecycleService
readonly
lifecycleService
:
ILifecycleService
,
@
ILabelService
readonly
labelService
:
ILabelService
)
{
lifecycleService
.
onBeforeShutdown
(
async
e
=>
{
...
...
@@ -116,9 +118,12 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
return
this
.
pickNewWorkspacePath
().
then
(
newWorkspacePath
=>
{
if
(
newWorkspacePath
)
{
return
this
.
saveWorkspaceAs
(
workspaceIdentifier
,
newWorkspacePath
).
then
(
_
=>
{
this
.
windowsService
.
addRecentlyOpened
([
newWorkspacePath
],
[],
[]);
this
.
workspaceService
.
deleteUntitledWorkspace
(
workspaceIdentifier
);
return
false
;
return
this
.
workspaceService
.
getWorkspaceIdentifier
(
newWorkspacePath
).
then
(
newWorkspaceIdentifier
=>
{
const
label
=
this
.
labelService
.
getWorkspaceLabel
(
newWorkspaceIdentifier
,
{
verbose
:
true
});
this
.
windowsService
.
addRecentlyOpened
([{
label
,
workspace
:
newWorkspaceIdentifier
}]);
this
.
workspaceService
.
deleteUntitledWorkspace
(
workspaceIdentifier
);
return
false
;
});
},
()
=>
false
);
}
return
true
;
// keep veto if no target was provided
...
...
src/vs/workbench/test/workbenchTestServices.ts
浏览文件 @
4276d2c3
...
...
@@ -44,7 +44,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import
{
IThemeService
}
from
'
vs/platform/theme/common/themeService
'
;
import
{
TestThemeService
}
from
'
vs/platform/theme/test/common/testThemeService
'
;
import
{
IWorkspaceIdentifier
,
ISingleFolderWorkspaceIdentifier
,
isSingleFolderWorkspaceIdentifier
}
from
'
vs/platform/workspaces/common/workspaces
'
;
import
{
IRecentlyOpened
}
from
'
vs/platform/history/common/history
'
;
import
{
IRecentlyOpened
,
IRecent
}
from
'
vs/platform/history/common/history
'
;
import
{
ITextResourceConfigurationService
,
ITextResourcePropertiesService
}
from
'
vs/editor/common/services/resourceConfiguration
'
;
import
{
IPosition
,
Position
as
EditorPosition
}
from
'
vs/editor/common/core/position
'
;
import
{
IMenuService
,
MenuId
,
IMenu
,
ISerializableCommandAction
}
from
'
vs/platform/actions/common/actions
'
;
...
...
@@ -1356,7 +1356,7 @@ export class TestWindowsService implements IWindowsService {
return
Promise
.
resolve
();
}
addRecentlyOpened
(
_
workspaces
:
URI
[],
_folders
:
URI
[],
_files
:
URI
[]):
Promise
<
void
>
{
addRecentlyOpened
(
_
recents
:
IRecent
[]):
Promise
<
void
>
{
return
Promise
.
resolve
();
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录