Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
d32bbc9f
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,发现更多精彩内容 >>
提交
d32bbc9f
编写于
1月 09, 2017
作者:
B
Benjamin Pasero
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Keep dirty contents when renaming a dirty file or its parent (fixes #11997)
上级
998bf16d
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
99 addition
and
77 deletion
+99
-77
src/vs/workbench/parts/files/browser/fileActions.ts
src/vs/workbench/parts/files/browser/fileActions.ts
+36
-28
src/vs/workbench/parts/files/browser/views/explorerViewer.ts
src/vs/workbench/parts/files/browser/views/explorerViewer.ts
+63
-49
未找到文件。
src/vs/workbench/parts/files/browser/fileActions.ts
浏览文件 @
d32bbc9f
...
...
@@ -46,6 +46,8 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import
{
Keybinding
,
KeyMod
,
KeyCode
}
from
'
vs/base/common/keyCodes
'
;
import
{
getCodeEditor
}
from
'
vs/editor/common/services/codeEditorService
'
;
import
{
IEditorViewState
}
from
'
vs/editor/common/editorCommon
'
;
import
{
IBackupFileService
}
from
'
vs/workbench/services/backup/common/backup
'
;
import
{
ITextModelResolverService
}
from
'
vs/editor/common/services/resolverService
'
;
export
interface
IEditableData
{
action
:
IAction
;
...
...
@@ -271,7 +273,9 @@ class RenameFileAction extends BaseRenameAction {
element
:
FileStat
,
@
IFileService
fileService
:
IFileService
,
@
IMessageService
messageService
:
IMessageService
,
@
ITextFileService
textFileService
:
ITextFileService
@
ITextFileService
textFileService
:
ITextFileService
,
@
ITextModelResolverService
private
textModelResolverService
:
ITextModelResolverService
,
@
IBackupFileService
private
backupFileService
:
IBackupFileService
)
{
super
(
RenameFileAction
.
ID
,
nls
.
localize
(
'
rename
'
,
"
Rename
"
),
element
,
fileService
,
messageService
,
textFileService
);
...
...
@@ -280,40 +284,44 @@ class RenameFileAction extends BaseRenameAction {
public
runAction
(
newName
:
string
):
TPromise
<
any
>
{
// Handle dirty
let
revertPromise
:
TPromise
<
any
>
=
TPromise
.
as
(
null
);
// 1. check for dirty files that are being moved and backup to new target
const
dirty
=
this
.
textFileService
.
getDirty
().
filter
(
d
=>
paths
.
isEqualOrParent
(
d
.
fsPath
,
this
.
element
.
resource
.
fsPath
));
if
(
dirty
.
length
)
{
let
message
:
string
;
if
(
this
.
element
.
isDirectory
)
{
if
(
dirty
.
length
===
1
)
{
message
=
nls
.
localize
(
'
dirtyMessageFolderOne
'
,
"
You are renaming a folder with unsaved changes in 1 file. Do you want to continue?
"
);
}
else
{
message
=
nls
.
localize
(
'
dirtyMessageFolder
'
,
"
You are renaming a folder with unsaved changes in {0} files. Do you want to continue?
"
,
dirty
.
length
);
}
}
else
{
message
=
nls
.
localize
(
'
dirtyMessageFile
'
,
"
You are renaming a file with unsaved changes. Do you want to continue?
"
);
const
dirtyRenamed
:
URI
[]
=
[];
return
TPromise
.
join
(
dirty
.
map
(
d
=>
{
const
targetPath
=
paths
.
join
(
this
.
element
.
parent
.
resource
.
fsPath
,
newName
);
let
renamed
:
URI
;
// If the dirty file itself got moved, just reparent it to the target folder
if
(
this
.
element
.
resource
.
fsPath
===
d
.
fsPath
)
{
renamed
=
URI
.
file
(
targetPath
);
}
const
res
=
this
.
messageService
.
confirm
({
message
,
type
:
'
warning
'
,
detail
:
nls
.
localize
(
'
dirtyWarning
'
,
"
Your changes will be lost if you don't save them.
"
),
primaryButton
:
nls
.
localize
({
key
:
'
renameLabel
'
,
comment
:
[
'
&& denotes a mnemonic
'
]
},
"
&&Rename
"
)
});
if
(
!
res
)
{
return
TPromise
.
as
(
null
);
// Otherwise, a parent of the dirty resource got moved, so we have to reparent more complicated. Example:
else
{
renamed
=
URI
.
file
(
paths
.
join
(
targetPath
,
d
.
fsPath
.
substr
(
this
.
element
.
resource
.
fsPath
.
length
+
1
)));
}
revertPromise
=
this
.
textFileService
.
revertAll
(
dirty
);
}
dirtyRenamed
.
push
(
renamed
);
return
revertPromise
.
then
(()
=>
{
return
this
.
fileService
.
rename
(
this
.
element
.
resource
,
newName
).
then
(
null
,
(
error
:
Error
)
=>
{
this
.
onErrorWithRetry
(
error
,
()
=>
this
.
runAction
(
newName
));
const
model
=
this
.
textFileService
.
models
.
get
(
d
);
return
this
.
backupFileService
.
backupResource
(
renamed
,
model
.
getValue
(),
model
.
getVersionId
());
}))
// 2. soft revert all dirty since we have backed up their contents
.
then
(()
=>
this
.
textFileService
.
revertAll
(
dirty
,
{
soft
:
true
/* do not attempt to load content from disk */
}))
// 3.) run the rename operation
.
then
(()
=>
this
.
fileService
.
rename
(
this
.
element
.
resource
,
newName
).
then
(
null
,
(
error
:
Error
)
=>
{
return
TPromise
.
join
(
dirtyRenamed
.
map
(
d
=>
this
.
backupFileService
.
discardResourceBackup
(
d
))).
then
(()
=>
{
this
.
onErrorWithRetry
(
error
,
()
=>
this
.
runAction
(
newName
));
});
}))
// 4.) resolve those that were dirty to load their previous dirty contents from disk
.
then
(()
=>
{
return
TPromise
.
join
(
dirtyRenamed
.
map
(
t
=>
this
.
textModelResolverService
.
createModelReference
(
t
)));
});
});
}
}
...
...
src/vs/workbench/parts/files/browser/views/explorerViewer.ts
浏览文件 @
d32bbc9f
...
...
@@ -49,6 +49,8 @@ import { Keybinding, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import
{
IKeyboardEvent
}
from
'
vs/base/browser/keyboardEvent
'
;
import
{
IMenuService
,
IMenu
,
MenuId
}
from
'
vs/platform/actions/common/actions
'
;
import
{
fillInActions
}
from
'
vs/platform/actions/browser/menuItemActionItem
'
;
import
{
IBackupFileService
}
from
'
vs/workbench/services/backup/common/backup
'
;
import
{
ITextModelResolverService
}
from
'
vs/editor/common/services/resolverService
'
;
export
class
FileDataSource
implements
IDataSource
{
constructor
(
...
...
@@ -723,7 +725,9 @@ export class FileDragAndDrop implements IDragAndDrop {
@
IFileService
private
fileService
:
IFileService
,
@
IConfigurationService
private
configurationService
:
IConfigurationService
,
@
IInstantiationService
private
instantiationService
:
IInstantiationService
,
@
ITextFileService
private
textFileService
:
ITextFileService
@
ITextFileService
private
textFileService
:
ITextFileService
,
@
ITextModelResolverService
private
textModelResolverService
:
ITextModelResolverService
,
@
IBackupFileService
private
backupFileService
:
IBackupFileService
)
{
this
.
toDispose
=
[];
...
...
@@ -868,70 +872,80 @@ export class FileDragAndDrop implements IDragAndDrop {
promise
=
tree
.
expand
(
target
).
then
(()
=>
{
// Reuse action if user copies
// Reuse
duplicate
action if user copies
if
(
isCopy
)
{
const
copyAction
=
this
.
instantiationService
.
createInstance
(
DuplicateFileAction
,
tree
,
source
,
target
);
return
copyAction
.
run
();
return
this
.
instantiationService
.
createInstance
(
DuplicateFileAction
,
tree
,
source
,
target
).
run
();
}
// Handle dirty (in file or inside the folder if any)
let
revertPromise
:
TPromise
<
any
>
=
TPromise
.
as
(
null
);
const
dirty
=
this
.
textFileService
.
getDirty
().
filter
(
d
=>
paths
.
isEqualOrParent
(
d
.
fsPath
,
source
.
resource
.
fsPath
));
if
(
dirty
.
length
)
{
let
message
:
string
;
if
(
source
.
isDirectory
)
{
if
(
dirty
.
length
===
1
)
{
message
=
nls
.
localize
(
'
dirtyMessageFolderOne
'
,
"
You are moving a folder with unsaved changes in 1 file. Do you want to continue?
"
);
}
else
{
message
=
nls
.
localize
(
'
dirtyMessageFolder
'
,
"
You are moving a folder with unsaved changes in {0} files. Do you want to continue?
"
,
dirty
.
length
);
}
}
else
{
message
=
nls
.
localize
(
'
dirtyMessageFile
'
,
"
You are moving a file with unsaved changes. Do you want to continue?
"
);
const
dirtyMoved
:
URI
[]
=
[];
// Success: load all files that are dirty again to restore their dirty contents
// Error: discard any backups created during the process
const
onSuccess
=
()
=>
TPromise
.
join
(
dirtyMoved
.
map
(
t
=>
this
.
textModelResolverService
.
createModelReference
(
t
)));
const
onError
=
(
error
?:
Error
,
showError
?:
boolean
)
=>
{
if
(
showError
)
{
this
.
messageService
.
show
(
Severity
.
Error
,
error
);
}
const
res
=
this
.
messageService
.
confirm
({
message
,
type
:
'
warning
'
,
detail
:
nls
.
localize
(
'
dirtyWarning
'
,
"
Your changes will be lost if you don't save them.
"
),
primaryButton
:
nls
.
localize
({
key
:
'
moveLabel
'
,
comment
:
[
'
&& denotes a mnemonic
'
]
},
"
&&Move
"
)
});
return
TPromise
.
join
(
dirtyMoved
.
map
(
d
=>
this
.
backupFileService
.
discardResourceBackup
(
d
)));
};
// 1. check for dirty files that are being moved and backup to new target
const
dirty
=
this
.
textFileService
.
getDirty
().
filter
(
d
=>
paths
.
isEqualOrParent
(
d
.
fsPath
,
source
.
resource
.
fsPath
));
return
TPromise
.
join
(
dirty
.
map
(
d
=>
{
let
moved
:
URI
;
if
(
!
res
)
{
return
TPromise
.
as
(
null
);
// If the dirty file itself got moved, just reparent it to the target folder
if
(
source
.
resource
.
fsPath
===
d
.
fsPath
)
{
moved
=
URI
.
file
(
paths
.
join
(
target
.
resource
.
fsPath
,
source
.
name
));
}
revertPromise
=
this
.
textFileService
.
revertAll
(
dirty
);
}
// Otherwise, a parent of the dirty resource got moved, so we have to reparent more complicated. Example:
else
{
moved
=
URI
.
file
(
paths
.
join
(
target
.
resource
.
fsPath
,
d
.
fsPath
.
substr
(
source
.
parent
.
resource
.
fsPath
.
length
+
1
)));
}
return
revertPromise
.
then
(()
=>
{
const
targetResource
=
URI
.
file
(
paths
.
join
(
target
.
resource
.
fsPath
,
source
.
name
));
let
didHandleConflict
=
false
;
dirtyMoved
.
push
(
moved
);
// Move File/Folder
return
this
.
fileService
.
moveFile
(
source
.
resource
,
targetResource
).
then
(
null
,
error
=>
{
const
model
=
this
.
textFileService
.
models
.
get
(
d
);
// Conflict
if
((
<
IFileOperationResult
>
error
).
fileOperationResult
===
FileOperationResult
.
FILE_MOVE_CONFLICT
)
{
didHandleConflict
=
true
;
return
this
.
backupFileService
.
backupResource
(
moved
,
model
.
getValue
(),
model
.
getVersionId
());
}))
const
confirm
:
IConfirmation
=
{
message
:
nls
.
localize
(
'
confirmOverwriteMessage
'
,
"
'{0}' already exists in the destination folder. Do you want to replace it?
"
,
source
.
name
),
detail
:
nls
.
localize
(
'
irreversible
'
,
"
This action is irreversible!
"
),
primaryButton
:
nls
.
localize
({
key
:
'
replaceButtonLabel
'
,
comment
:
[
'
&& denotes a mnemonic
'
]
},
"
&&Replace
"
)
};
// 2. soft revert all dirty since we have backed up their contents
.
then
(()
=>
this
.
textFileService
.
revertAll
(
dirty
,
{
soft
:
true
/* do not attempt to load content from disk */
}))
if
(
this
.
messageService
.
confirm
(
confirm
))
{
return
this
.
fileService
.
moveFile
(
source
.
resource
,
targetResource
,
true
).
then
(
null
,
(
error
)
=>
{
this
.
messageService
.
show
(
Severity
.
Error
,
error
);
});
// 3.) run the move operation
.
then
(()
=>
{
const
targetResource
=
URI
.
file
(
paths
.
join
(
target
.
resource
.
fsPath
,
source
.
name
));
let
didHandleConflict
=
false
;
return
this
.
fileService
.
moveFile
(
source
.
resource
,
targetResource
).
then
(
null
,
error
=>
{
// Conflict
if
((
<
IFileOperationResult
>
error
).
fileOperationResult
===
FileOperationResult
.
FILE_MOVE_CONFLICT
)
{
didHandleConflict
=
true
;
const
confirm
:
IConfirmation
=
{
message
:
nls
.
localize
(
'
confirmOverwriteMessage
'
,
"
'{0}' already exists in the destination folder. Do you want to replace it?
"
,
source
.
name
),
detail
:
nls
.
localize
(
'
irreversible
'
,
"
This action is irreversible!
"
),
primaryButton
:
nls
.
localize
({
key
:
'
replaceButtonLabel
'
,
comment
:
[
'
&& denotes a mnemonic
'
]
},
"
&&Replace
"
)
};
// Move with overwrite if the user confirms
if
(
this
.
messageService
.
confirm
(
confirm
))
{
return
this
.
fileService
.
moveFile
(
source
.
resource
,
targetResource
,
true
).
then
(
onSuccess
,
error
=>
onError
(
error
,
true
));
}
return
onError
();
}
return
;
}
return
onError
(
error
,
true
);
});
})
this
.
messageService
.
show
(
Severity
.
Error
,
error
);
});
});
// 4.) resolve those that were dirty to load their previous dirty contents from disk
.
then
(
onSuccess
,
onError
);
},
errors
.
onUnexpectedError
);
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录