Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
57a072f8
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,发现更多精彩内容 >>
未验证
提交
57a072f8
编写于
1月 24, 2018
作者:
J
Johannes Rieken
提交者:
GitHub
1月 24, 2018
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #41552 from Microsoft/rename-workspaceedit-proto
Allow renaming/creating/deleting files in a workspaced edit
上级
bddc5d69
1ccfbe40
变更
24
隐藏空白更改
内联
并排
Showing
24 changed file
with
609 addition
and
372 deletion
+609
-372
extensions/vscode-api-tests/src/workspace.test.ts
extensions/vscode-api-tests/src/workspace.test.ts
+41
-0
src/vs/editor/browser/services/bulkEdit.ts
src/vs/editor/browser/services/bulkEdit.ts
+182
-183
src/vs/editor/common/modes.ts
src/vs/editor/common/modes.ts
+27
-5
src/vs/editor/contrib/quickFix/quickFixCommands.ts
src/vs/editor/contrib/quickFix/quickFixCommands.ts
+2
-4
src/vs/editor/contrib/quickFix/test/quickFix.test.ts
src/vs/editor/contrib/quickFix/test/quickFix.test.ts
+2
-2
src/vs/editor/contrib/rename/rename.ts
src/vs/editor/contrib/rename/rename.ts
+3
-5
src/vs/monaco.d.ts
src/vs/monaco.d.ts
+9
-4
src/vs/platform/progress/common/progress.ts
src/vs/platform/progress/common/progress.ts
+6
-0
src/vs/vscode.d.ts
src/vs/vscode.d.ts
+36
-0
src/vs/workbench/api/electron-browser/mainThreadEditors.ts
src/vs/workbench/api/electron-browser/mainThreadEditors.ts
+13
-31
src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts
...kbench/api/electron-browser/mainThreadLanguageFeatures.ts
+3
-15
src/vs/workbench/api/node/extHost.protocol.ts
src/vs/workbench/api/node/extHost.protocol.ts
+29
-20
src/vs/workbench/api/node/extHostApiCommands.ts
src/vs/workbench/api/node/extHostApiCommands.ts
+1
-5
src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts
src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts
+7
-7
src/vs/workbench/api/node/extHostLanguageFeatures.ts
src/vs/workbench/api/node/extHostLanguageFeatures.ts
+6
-4
src/vs/workbench/api/node/extHostTextEditors.ts
src/vs/workbench/api/node/extHostTextEditors.ts
+13
-26
src/vs/workbench/api/node/extHostTypeConverters.ts
src/vs/workbench/api/node/extHostTypeConverters.ts
+24
-11
src/vs/workbench/api/node/extHostTypes.ts
src/vs/workbench/api/node/extHostTypes.ts
+58
-15
src/vs/workbench/parts/search/browser/replaceService.ts
src/vs/workbench/parts/search/browser/replaceService.ts
+10
-8
src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts
...ectron-browser/api/extHostDocumentSaveParticipant.test.ts
+18
-12
src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts
...test/electron-browser/api/extHostLanguageFeatures.test.ts
+3
-2
src/vs/workbench/test/electron-browser/api/extHostTextEditors.test.ts
...ench/test/electron-browser/api/extHostTextEditors.test.ts
+8
-7
src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts
.../workbench/test/electron-browser/api/extHostTypes.test.ts
+47
-0
src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts
...bench/test/electron-browser/api/mainThreadEditors.test.ts
+61
-6
未找到文件。
extensions/vscode-api-tests/src/workspace.test.ts
浏览文件 @
57a072f8
...
...
@@ -10,6 +10,7 @@ import * as vscode from 'vscode';
import
{
createRandomFile
,
deleteFile
,
closeAllEditors
,
pathEquals
}
from
'
./utils
'
;
import
{
join
,
basename
}
from
'
path
'
;
import
*
as
fs
from
'
fs
'
;
import
{
Uri
}
from
'
vscode
'
;
suite
(
'
workspace-namespace
'
,
()
=>
{
...
...
@@ -505,4 +506,44 @@ suite('workspace-namespace', () => {
return
vscode
.
workspace
.
applyEdit
(
edit
);
});
});
test
(
'
applyEdit should fail when editing deleted resource
'
,
async
()
=>
{
const
resource
=
await
createRandomFile
();
const
edit
=
new
vscode
.
WorkspaceEdit
();
edit
.
deleteResource
(
resource
);
try
{
edit
.
insert
(
resource
,
new
vscode
.
Position
(
0
,
0
),
''
);
assert
.
fail
(
false
,
'
Should disallow edit of deleted resource
'
);
}
catch
{
// noop
}
});
test
(
'
applyEdit should fail when renaming deleted resource
'
,
async
()
=>
{
const
resource
=
await
createRandomFile
();
const
edit
=
new
vscode
.
WorkspaceEdit
();
edit
.
deleteResource
(
resource
);
try
{
edit
.
renameResource
(
resource
,
resource
);
assert
.
fail
(
false
,
'
Should disallow rename of deleted resource
'
);
}
catch
{
// noop
}
});
test
(
'
applyEdit should fail when editing renamed from resource
'
,
async
()
=>
{
const
resource
=
await
createRandomFile
();
const
newResource
=
Uri
.
parse
(
resource
.
fsPath
+
'
.1
'
);
const
edit
=
new
vscode
.
WorkspaceEdit
();
edit
.
renameResource
(
resource
,
newResource
);
try
{
edit
.
insert
(
resource
,
new
vscode
.
Position
(
0
,
0
),
''
);
assert
.
fail
(
false
,
'
Should disallow editing renamed file
'
);
}
catch
{
// noop
}
});
});
src/vs/editor/browser/services/bulkEdit.ts
浏览文件 @
57a072f8
...
...
@@ -5,68 +5,48 @@
'
use strict
'
;
import
*
as
nls
from
'
vs/nls
'
;
import
{
flatten
}
from
'
vs/base/common/arrays
'
;
import
{
IStringDictionary
,
forEach
,
values
,
groupBy
,
size
}
from
'
vs/base/common/collections
'
;
import
{
IDisposable
,
dispose
,
IReference
}
from
'
vs/base/common/lifecycle
'
;
import
URI
from
'
vs/base/common/uri
'
;
import
{
TPromise
}
from
'
vs/base/common/winjs.base
'
;
import
{
ITextModelService
,
ITextEditorModel
}
from
'
vs/editor/common/services/resolverService
'
;
import
{
IFileService
,
IFileChang
e
}
from
'
vs/platform/files/common/files
'
;
import
{
IFileService
,
FileChangeTyp
e
}
from
'
vs/platform/files/common/files
'
;
import
{
EditOperation
}
from
'
vs/editor/common/core/editOperation
'
;
import
{
Range
,
IRange
}
from
'
vs/editor/common/core/range
'
;
import
{
Selection
,
ISelection
}
from
'
vs/editor/common/core/selection
'
;
import
{
Range
}
from
'
vs/editor/common/core/range
'
;
import
{
Selection
}
from
'
vs/editor/common/core/selection
'
;
import
{
IIdentifiedSingleEditOperation
,
ITextModel
,
EndOfLineSequence
}
from
'
vs/editor/common/model
'
;
import
{
IProgressRunner
}
from
'
vs/platform/progress/common/progress
'
;
import
{
IProgressRunner
,
emptyProgressRunner
,
IProgress
}
from
'
vs/platform/progress/common/progress
'
;
import
{
ICodeEditor
}
from
'
vs/editor/browser/editorBrowser
'
;
import
{
optional
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
ResourceTextEdit
,
ResourceFileEdit
,
isResourceFileEdit
,
isResourceTextEdit
}
from
'
vs/editor/common/modes
'
;
import
{
getPathLabel
}
from
'
vs/base/common/labels
'
;
export
interface
IResourceEdit
{
resource
:
URI
;
range
?:
IRange
;
newText
:
string
;
newEol
?:
EndOfLineSequence
;
}
interface
IRecording
{
stop
():
void
;
hasChanged
(
resource
:
URI
):
boolean
;
allChanges
():
IFileChange
[];
}
class
ChangeRecorder
{
private
_fileService
:
IFileService
;
constructor
(
fileService
?:
IFileService
)
{
this
.
_fileService
=
fileService
;
}
abstract
class
IRecording
{
public
start
():
IRecording
{
const
changes
:
IStringDictionary
<
IFileChange
[]
>
=
Object
.
create
(
null
);
static
start
(
fileService
:
IFileService
):
IRecording
{
const
_changes
=
new
Set
<
string
>
();
let
stop
:
IDisposable
;
if
(
this
.
_fileService
)
{
stop
=
this
.
_fileService
.
onFileChanges
((
event
)
=>
{
event
.
changes
.
forEach
(
change
=>
{
const
key
=
String
(
change
.
resource
);
let
array
=
changes
[
key
];
if
(
!
array
)
{
changes
[
key
]
=
array
=
[];
if
(
fileService
)
{
// watch only when there is a fileservice available
stop
=
fileService
.
onFileChanges
(
event
=>
{
for
(
const
change
of
event
.
changes
)
{
if
(
change
.
type
===
FileChangeType
.
UPDATED
)
{
_changes
.
add
(
change
.
resource
.
toString
());
}
array
.
push
(
change
);
});
}
});
}
return
{
stop
:
()
=>
{
return
stop
&&
stop
.
dispose
();
},
hasChanged
:
(
resource
:
URI
)
=>
!!
changes
[
resource
.
toString
()],
allChanges
:
()
=>
flatten
(
values
(
changes
))
stop
()
{
return
dispose
(
stop
);
},
hasChanged
(
resource
)
{
return
_changes
.
has
(
resource
.
toString
());
}
};
}
abstract
stop
():
void
;
abstract
hasChanged
(
resource
:
URI
):
boolean
;
}
class
EditTask
implements
IDisposable
{
...
...
@@ -84,26 +64,34 @@ class EditTask implements IDisposable {
this
.
_edits
=
[];
}
public
addEdit
(
edit
:
IResourceEdit
):
void
{
if
(
typeof
edit
.
newEol
===
'
number
'
)
{
// honor eol-change
this
.
_newEol
=
edit
.
newEol
;
dispose
()
{
if
(
this
.
_model
)
{
this
.
_modelReference
.
dispose
();
this
.
_modelReference
=
null
;
}
}
if
(
edit
.
range
||
edit
.
newText
)
{
// create edit operation
let
range
:
Range
;
if
(
!
edit
.
range
)
{
range
=
this
.
_model
.
getFullModelRange
();
}
else
{
range
=
Range
.
lift
(
edit
.
range
);
addEdit
(
resourceEdit
:
ResourceTextEdit
):
void
{
for
(
const
edit
of
resourceEdit
.
edits
)
{
if
(
typeof
edit
.
eol
===
'
number
'
)
{
// honor eol-change
this
.
_newEol
=
edit
.
eol
;
}
if
(
edit
.
range
||
edit
.
text
)
{
// create edit operation
let
range
:
Range
;
if
(
!
edit
.
range
)
{
range
=
this
.
_model
.
getFullModelRange
();
}
else
{
range
=
Range
.
lift
(
edit
.
range
);
}
this
.
_edits
.
push
(
EditOperation
.
replaceMove
(
range
,
edit
.
text
));
}
this
.
_edits
.
push
(
EditOperation
.
replaceMove
(
range
,
edit
.
newText
));
}
}
public
apply
():
void
{
apply
():
void
{
if
(
this
.
_edits
.
length
>
0
)
{
this
.
_edits
=
this
.
_edits
.
map
((
value
,
index
)
=>
({
value
,
index
})).
sort
((
a
,
b
)
=>
{
...
...
@@ -160,16 +148,10 @@ class EditTask implements IDisposable {
return
[
this
.
_endCursorSelection
];
}
public
getEndCursorSelection
():
Selection
{
getEndCursorSelection
():
Selection
{
return
this
.
_endCursorSelection
;
}
dispose
()
{
if
(
this
.
_model
)
{
this
.
_modelReference
.
dispose
();
this
.
_modelReference
=
null
;
}
}
}
class
SourceModelEditTask
extends
EditTask
{
...
...
@@ -189,34 +171,42 @@ class SourceModelEditTask extends EditTask {
class
BulkEditModel
implements
IDisposable
{
private
_textModelResolverService
:
ITextModelService
;
private
_numberOfResourcesToModify
:
number
=
0
;
private
_edits
:
IStringDictionary
<
IResourceEdit
[]
>
=
Object
.
create
(
null
);
private
_edits
=
new
Map
<
string
,
ResourceTextEdit
[]
>
();
private
_tasks
:
EditTask
[];
private
_sourceModel
:
URI
;
private
_sourceSelections
:
Selection
[];
private
_sourceModelTask
:
SourceModelEditTask
;
constructor
(
textModelResolverService
:
ITextModelService
,
sourceModel
:
URI
,
sourceSelections
:
Selection
[],
edits
:
IResourceEdit
[],
private
progress
:
IProgressRunner
=
null
)
{
private
_progress
:
IProgress
<
void
>
;
constructor
(
textModelResolverService
:
ITextModelService
,
editor
:
ICodeEditor
,
edits
:
ResourceTextEdit
[],
progress
:
IProgress
<
void
>
)
{
this
.
_textModelResolverService
=
textModelResolverService
;
this
.
_sourceModel
=
sourceModel
;
this
.
_sourceSelections
=
sourceSelections
;
this
.
_sourceModelTask
=
null
;
this
.
_sourceModel
=
editor
?
editor
.
getModel
().
uri
:
undefined
;
this
.
_sourceSelections
=
editor
?
editor
.
getSelections
()
:
undefined
;
this
.
_sourceModelTask
=
undefined
;
this
.
_progress
=
progress
;
for
(
let
edit
of
edits
)
{
this
.
_addEdit
(
edit
);
}
edits
.
forEach
(
this
.
addEdit
,
this
);
}
private
_addEdit
(
edit
:
IResourceEdit
):
void
{
let
array
=
this
.
_edits
[
edit
.
resource
.
toString
()];
dispose
():
void
{
this
.
_tasks
=
dispose
(
this
.
_tasks
);
}
addEdit
(
edit
:
ResourceTextEdit
):
void
{
let
array
=
this
.
_edits
.
get
(
edit
.
resource
.
toString
());
if
(
!
array
)
{
this
.
_edits
[
edit
.
resource
.
toString
()]
=
array
=
[];
this
.
_
numberOfResourcesToModify
+=
1
;
array
=
[];
this
.
_
edits
.
set
(
edit
.
resource
.
toString
(),
array
)
;
}
array
.
push
(
edit
);
}
publi
c
prepare
():
TPromise
<
BulkEditModel
>
{
asyn
c
prepare
():
TPromise
<
BulkEditModel
>
{
if
(
this
.
_tasks
)
{
throw
new
Error
(
'
illegal state - already prepared
'
);
...
...
@@ -225,158 +215,167 @@ class BulkEditModel implements IDisposable {
this
.
_tasks
=
[];
const
promises
:
TPromise
<
any
>
[]
=
[];
if
(
this
.
progress
)
{
this
.
progress
.
total
(
this
.
_numberOfResourcesToModify
*
2
);
}
forEach
(
this
.
_edits
,
entry
=>
{
const
promise
=
this
.
_textModelResolverService
.
createModelReference
(
URI
.
parse
(
entry
.
key
)).
then
(
ref
=>
{
this
.
_edits
.
forEach
((
value
,
key
)
=>
{
const
promise
=
this
.
_textModelResolverService
.
createModelReference
(
URI
.
parse
(
key
)).
then
(
ref
=>
{
const
model
=
ref
.
object
;
if
(
!
model
||
!
model
.
textEditorModel
)
{
throw
new
Error
(
`Cannot load file
${
entry
.
key
}
`
);
throw
new
Error
(
`Cannot load file
${
key
}
`
);
}
const
textEditorModel
=
model
.
textEditorModel
;
let
task
:
EditTask
;
if
(
this
.
_sourceModel
&&
textEditorModel
.
uri
.
toString
()
===
this
.
_sourceModel
.
toString
())
{
if
(
this
.
_sourceModel
&&
model
.
textEditorModel
.
uri
.
toString
()
===
this
.
_sourceModel
.
toString
())
{
this
.
_sourceModelTask
=
new
SourceModelEditTask
(
ref
,
this
.
_sourceSelections
);
task
=
this
.
_sourceModelTask
;
}
else
{
task
=
new
EditTask
(
ref
);
}
entry
.
value
.
forEach
(
edit
=>
task
.
addEdit
(
edit
));
value
.
forEach
(
edit
=>
task
.
addEdit
(
edit
));
this
.
_tasks
.
push
(
task
);
if
(
this
.
progress
)
{
this
.
progress
.
worked
(
1
);
}
this
.
_progress
.
report
(
undefined
);
});
promises
.
push
(
promise
);
});
await
TPromise
.
join
(
promises
);
return
TPromise
.
join
(
promises
).
then
(
_
=>
this
)
;
return
this
;
}
public
apply
():
Selection
{
this
.
_tasks
.
forEach
(
task
=>
this
.
applyTask
(
task
));
let
r
:
Selection
=
null
;
if
(
this
.
_sourceModelTask
)
{
r
=
this
.
_sourceModelTask
.
getEndCursorSelection
();
apply
():
Selection
{
for
(
const
task
of
this
.
_tasks
)
{
task
.
apply
();
this
.
_progress
.
report
(
undefined
);
}
return
r
;
}
private
applyTask
(
task
:
EditTask
):
void
{
task
.
apply
();
if
(
this
.
progress
)
{
this
.
progress
.
worked
(
1
);
}
}
dispose
():
void
{
this
.
_tasks
=
dispose
(
this
.
_tasks
);
return
this
.
_sourceModelTask
?
this
.
_sourceModelTask
.
getEndCursorSelection
()
:
undefined
;
}
}
export
interface
BulkEdit
{
progress
(
progress
:
IProgressRunner
):
void
;
add
(
edit
:
IResourceEdit
[]):
void
;
finish
():
TPromise
<
ISelection
>
;
ariaMessage
():
string
;
}
export
function
bulkEdit
(
textModelResolverService
:
ITextModelService
,
editor
:
ICodeEditor
,
edits
:
IResourceEdit
[],
fileService
?:
IFileService
,
progress
:
IProgressRunner
=
null
):
TPromise
<
any
>
{
let
bulk
=
createBulkEdit
(
textModelResolverService
,
editor
,
fileService
);
bulk
.
add
(
edits
);
bulk
.
progress
(
progress
);
return
bulk
.
finish
();
}
export
type
Edit
=
ResourceFileEdit
|
ResourceTextEdit
;
export
function
createBulkEdit
(
textModelResolverService
:
ITextModelService
,
editor
?:
ICodeEditor
,
fileService
?:
IFileService
):
BulkEdit
{
export
class
BulkEdit
{
let
all
:
IResourceEdit
[]
=
[];
let
recording
=
new
ChangeRecorder
(
fileService
).
start
();
let
progressRunner
:
IProgressRunner
;
function
progress
(
progress
:
IProgressRunner
)
{
progressRunner
=
progress
;
static
perform
(
edits
:
Edit
[],
textModelService
:
ITextModelService
,
fileService
:
IFileService
,
editor
:
ICodeEditor
):
TPromise
<
any
>
{
const
edit
=
new
BulkEdit
(
editor
,
null
,
textModelService
,
fileService
);
edit
.
add
(
edits
);
return
edit
.
perform
();
}
function
add
(
edits
:
IResourceEdit
[]):
void
{
all
.
push
(...
edits
);
private
_edits
:
Edit
[]
=
[];
private
_editor
:
ICodeEditor
;
private
_progress
:
IProgressRunner
;
constructor
(
editor
:
ICodeEditor
,
progress
:
IProgressRunner
,
@
ITextModelService
private
_textModelService
:
ITextModelService
,
@
optional
(
IFileService
)
private
_fileService
:
IFileService
)
{
this
.
_editor
=
editor
;
this
.
_progress
=
progress
||
emptyProgressRunner
;
}
function
getConcurrentEdits
()
{
let
names
:
string
[];
for
(
let
edit
of
all
)
{
if
(
recording
.
hasChanged
(
edit
.
resource
))
{
if
(
!
names
)
{
names
=
[];
}
names
.
push
(
edit
.
resource
.
fsPath
);
}
add
(
edits
:
Edit
[]
|
Edit
):
void
{
if
(
Array
.
isArray
(
edits
))
{
this
.
_edits
.
push
(...
edits
);
}
else
{
this
.
_edits
.
push
(
edits
);
}
if
(
names
)
{
return
nls
.
localize
(
'
conflict
'
,
"
These files have changed in the meantime: {0}
"
,
names
.
join
(
'
,
'
));
}
ariaMessage
():
string
{
const
editCount
=
this
.
_edits
.
reduce
((
prev
,
cur
)
=>
isResourceFileEdit
(
cur
)
?
prev
:
prev
+
cur
.
edits
.
length
,
0
);
const
resourceCount
=
this
.
_edits
.
length
;
if
(
editCount
===
0
)
{
return
nls
.
localize
(
'
summary.0
'
,
"
Made no edits
"
);
}
else
if
(
editCount
>
1
&&
resourceCount
>
1
)
{
return
nls
.
localize
(
'
summary.nm
'
,
"
Made {0} text edits in {1} files
"
,
editCount
,
resourceCount
);
}
else
{
return
nls
.
localize
(
'
summary.n0
'
,
"
Made {0} text edits in one file
"
,
editCount
,
resourceCount
);
}
return
undefined
;
}
function
finish
():
TPromise
<
ISelection
>
{
async
perform
():
TPromise
<
Selection
>
{
let
seen
=
new
Set
<
string
>
();
let
total
=
0
;
const
groups
:
Edit
[][]
=
[];
let
group
:
Edit
[];
for
(
const
edit
of
this
.
_edits
)
{
if
(
!
group
||
(
isResourceFileEdit
(
group
[
0
])
&&
!
isResourceFileEdit
(
edit
))
||
(
isResourceTextEdit
(
group
[
0
])
&&
!
isResourceTextEdit
(
edit
))
)
{
group
=
[];
groups
.
push
(
group
);
}
group
.
push
(
edit
);
if
(
all
.
length
===
0
)
{
return
TPromise
.
as
(
undefined
);
if
(
isResourceFileEdit
(
edit
))
{
total
+=
1
;
}
else
if
(
!
seen
.
has
(
edit
.
resource
.
toString
()))
{
seen
.
add
(
edit
.
resource
.
toString
());
total
+=
2
;
}
}
let
concurrentEdits
=
getConcurrentEdits
();
if
(
concurrentEdits
)
{
return
TPromise
.
wrapError
<
ISelection
>
(
new
Error
(
concurrentEdits
));
// define total work and progress callback
// for child operations
this
.
_progress
.
total
(
total
);
let
progress
:
IProgress
<
void
>
=
{
report
:
_
=>
this
.
_progress
.
worked
(
1
)
};
// do it. return the last selection computed
// by a text change (can be undefined then)
let
res
:
Selection
=
undefined
;
for
(
const
group
of
groups
)
{
if
(
isResourceFileEdit
(
group
[
0
]))
{
await
this
.
_performFileEdits
(
<
ResourceFileEdit
[]
>
group
,
progress
);
}
else
{
res
=
await
this
.
_performTextEdits
(
<
ResourceTextEdit
[]
>
group
,
progress
)
||
res
;
}
}
return
res
;
}
private
async
_performFileEdits
(
edits
:
ResourceFileEdit
[],
progress
:
IProgress
<
void
>
)
{
for
(
const
edit
of
edits
)
{
let
uri
:
URI
;
let
selections
:
Selection
[];
progress
.
report
(
undefined
);
if
(
editor
&&
editor
.
getModel
())
{
uri
=
editor
.
getModel
().
uri
;
selections
=
editor
.
getSelections
();
if
(
edit
.
newUri
&&
edit
.
oldUri
)
{
await
this
.
_fileService
.
moveFile
(
edit
.
oldUri
,
edit
.
newUri
,
false
);
}
else
if
(
!
edit
.
newUri
&&
edit
.
oldUri
)
{
await
this
.
_fileService
.
del
(
edit
.
oldUri
,
true
);
}
else
if
(
edit
.
newUri
&&
!
edit
.
oldUri
)
{
await
this
.
_fileService
.
createFile
(
edit
.
newUri
,
undefined
,
{
overwrite
:
false
});
}
}
}
const
model
=
new
BulkEditModel
(
textModelResolverService
,
uri
,
selections
,
all
,
progressRunner
);
private
async
_performTextEdits
(
edits
:
ResourceTextEdit
[],
progress
:
IProgress
<
void
>
):
TPromise
<
Selection
>
{
return
model
.
prepare
().
then
(
_
=>
{
const
recording
=
IRecording
.
start
(
this
.
_fileService
);
const
model
=
new
BulkEditModel
(
this
.
_textModelService
,
this
.
_editor
,
edits
,
progress
);
let
concurrentEdits
=
getConcurrentEdits
();
if
(
concurrentEdits
)
{
throw
new
Error
(
concurrentEdits
);
}
await
model
.
prepare
();
recording
.
stop
();
const
conflicts
=
edits
.
filter
(
edit
=>
recording
.
hasChanged
(
edit
.
resource
))
.
map
(
edit
=>
getPathLabel
(
edit
.
resource
));
const
result
=
model
.
apply
();
model
.
dispose
();
return
result
;
});
}
recording
.
stop
();
function
ariaMessage
():
string
{
let
editCount
=
all
.
length
;
let
resourceCount
=
size
(
groupBy
(
all
,
edit
=>
edit
.
resource
.
toString
()));
if
(
editCount
===
0
)
{
return
nls
.
localize
(
'
summary.0
'
,
"
Made no edits
"
);
}
else
if
(
editCount
>
1
&&
resourceCount
>
1
)
{
return
nls
.
localize
(
'
summary.nm
'
,
"
Made {0} text edits in {1} files
"
,
editCount
,
resourceCount
);
}
else
{
return
nls
.
localize
(
'
summary.n0
'
,
"
Made {0} text edits in one file
"
,
editCount
,
resourceCount
);
if
(
conflicts
.
length
>
0
)
{
model
.
dispose
();
throw
new
Error
(
nls
.
localize
(
'
conflict
'
,
"
These files have changed in the meantime: {0}
"
,
conflicts
.
join
(
'
,
'
)));
}
}
return
{
progress
,
add
,
finish
,
ariaMessage
};
const
selection
=
await
model
.
apply
();
model
.
dispose
();
return
selection
;
}
}
src/vs/editor/common/modes.ts
浏览文件 @
57a072f8
...
...
@@ -17,6 +17,7 @@ import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationReg
import
{
Color
}
from
'
vs/base/common/color
'
;
import
{
IMarkerData
}
from
'
vs/platform/markers/common/markers
'
;
import
*
as
model
from
'
vs/editor/common/model
'
;
import
{
isObject
}
from
'
vs/base/common/types
'
;
/**
* Open ended enum at runtime
...
...
@@ -825,15 +826,36 @@ export interface DocumentColorProvider {
provideColorPresentations
(
model
:
model
.
ITextModel
,
colorInfo
:
IColorInformation
,
token
:
CancellationToken
):
IColorPresentation
[]
|
Thenable
<
IColorPresentation
[]
>
;
}
export
interface
IResourceEdit
{
/**
* @internal
*/
export
function
isResourceFileEdit
(
thing
:
any
):
thing
is
ResourceFileEdit
{
return
isObject
(
thing
)
&&
(
Boolean
((
<
ResourceFileEdit
>
thing
).
newUri
)
||
Boolean
((
<
ResourceFileEdit
>
thing
).
oldUri
));
}
/**
* @internal
*/
export
function
isResourceTextEdit
(
thing
:
any
):
thing
is
ResourceTextEdit
{
return
isObject
(
thing
)
&&
(
<
ResourceTextEdit
>
thing
).
resource
&&
Array
.
isArray
((
<
ResourceTextEdit
>
thing
).
edits
);
}
export
interface
ResourceFileEdit
{
oldUri
:
URI
;
newUri
:
URI
;
}
export
interface
ResourceTextEdit
{
resource
:
URI
;
range
:
IRange
;
newText
:
string
;
modelVersionId
?:
number
;
edits
:
TextEdit
[]
;
}
export
interface
WorkspaceEdit
{
edits
:
IResourceEdit
[]
;
rejectReason
?:
string
;
edits
:
Array
<
ResourceTextEdit
|
ResourceFileEdit
>
;
rejectReason
?:
string
;
// TODO@joh, move to rename
}
export
interface
RenameProvider
{
provideRenameEdits
(
model
:
model
.
ITextModel
,
position
:
Position
,
newName
:
string
,
token
:
CancellationToken
):
WorkspaceEdit
|
Thenable
<
WorkspaceEdit
>
;
}
...
...
src/vs/editor/contrib/quickFix/quickFixCommands.ts
浏览文件 @
57a072f8
...
...
@@ -23,7 +23,7 @@ import { QuickFixModel, QuickFixComputeEvent } from './quickFixModel';
import
{
CodeActionKind
,
CodeActionAutoApply
}
from
'
./codeActionTrigger
'
;
import
{
TPromise
}
from
'
vs/base/common/winjs.base
'
;
import
{
CodeAction
}
from
'
vs/editor/common/modes
'
;
import
{
create
BulkEdit
}
from
'
vs/editor/browser/services/bulkEdit
'
;
import
{
BulkEdit
}
from
'
vs/editor/browser/services/bulkEdit
'
;
import
{
IFileService
}
from
'
vs/platform/files/common/files
'
;
import
{
ITextModelService
}
from
'
vs/editor/common/services/resolverService
'
;
...
...
@@ -129,9 +129,7 @@ export class QuickFixController implements IEditorContribution {
private
async
_onApplyCodeAction
(
action
:
CodeAction
):
TPromise
<
void
>
{
if
(
action
.
edit
)
{
const
edit
=
createBulkEdit
(
this
.
_textModelService
,
this
.
_editor
,
this
.
_fileService
);
edit
.
add
(
action
.
edit
.
edits
);
await
edit
.
finish
();
await
BulkEdit
.
perform
(
action
.
edit
.
edits
,
this
.
_textModelService
,
this
.
_fileService
,
this
.
_editor
);
}
if
(
action
.
command
)
{
...
...
src/vs/editor/contrib/quickFix/test/quickFix.test.ts
浏览文件 @
57a072f8
...
...
@@ -8,7 +8,7 @@ import * as assert from 'assert';
import
URI
from
'
vs/base/common/uri
'
;
import
Severity
from
'
vs/base/common/severity
'
;
import
{
TextModel
}
from
'
vs/editor/common/model/textModel
'
;
import
{
CodeActionProviderRegistry
,
LanguageIdentifier
,
CodeActionProvider
,
Command
,
WorkspaceEdit
,
IResource
Edit
,
CodeAction
,
CodeActionContext
}
from
'
vs/editor/common/modes
'
;
import
{
CodeActionProviderRegistry
,
LanguageIdentifier
,
CodeActionProvider
,
Command
,
WorkspaceEdit
,
ResourceText
Edit
,
CodeAction
,
CodeActionContext
}
from
'
vs/editor/common/modes
'
;
import
{
IDisposable
,
dispose
}
from
'
vs/base/common/lifecycle
'
;
import
{
Range
}
from
'
vs/editor/common/core/range
'
;
import
{
getCodeActions
}
from
'
vs/editor/contrib/quickFix/quickFix
'
;
...
...
@@ -58,7 +58,7 @@ suite('QuickFix', () => {
bcd
:
{
diagnostics
:
[],
edit
:
new
class
implements
WorkspaceEdit
{
edits
:
IResource
Edit
[];
edits
:
ResourceText
Edit
[];
},
title
:
'
abc
'
}
...
...
src/vs/editor/contrib/rename/rename.ts
浏览文件 @
57a072f8
...
...
@@ -18,7 +18,7 @@ import { registerEditorAction, registerEditorContribution, ServicesAccessor, Edi
import
{
IEditorContribution
}
from
'
vs/editor/common/editorCommon
'
;
import
{
ITextModel
}
from
'
vs/editor/common/model
'
;
import
{
EditorContextKeys
}
from
'
vs/editor/common/editorContextKeys
'
;
import
{
create
BulkEdit
}
from
'
vs/editor/browser/services/bulkEdit
'
;
import
{
BulkEdit
}
from
'
vs/editor/browser/services/bulkEdit
'
;
import
{
ICodeEditor
}
from
'
vs/editor/browser/editorBrowser
'
;
import
RenameInputField
from
'
./renameInputField
'
;
import
{
ITextModelService
}
from
'
vs/editor/common/services/resolverService
'
;
...
...
@@ -147,9 +147,7 @@ class RenameController implements IEditorContribution {
this
.
_renameInputVisible
.
reset
();
this
.
editor
.
focus
();
// start recording of file changes so that we can figure out if a file that
// is to be renamed conflicts with another (concurrent) modification
const
edit
=
createBulkEdit
(
this
.
_textModelResolverService
,
<
ICodeEditor
>
this
.
editor
,
this
.
_fileService
);
const
edit
=
new
BulkEdit
(
this
.
editor
,
null
,
this
.
_textModelResolverService
,
this
.
_fileService
);
const
state
=
new
EditorState
(
this
.
editor
,
CodeEditorStateFlag
.
Position
|
CodeEditorStateFlag
.
Value
|
CodeEditorStateFlag
.
Selection
|
CodeEditorStateFlag
.
Scroll
);
const
renameOperation
=
rename
(
this
.
editor
.
getModel
(),
this
.
editor
.
getPosition
(),
newName
).
then
(
result
=>
{
...
...
@@ -163,7 +161,7 @@ class RenameController implements IEditorContribution {
}
edit
.
add
(
result
.
edits
);
return
edit
.
finish
().
then
(
selection
=>
{
return
edit
.
perform
().
then
(
selection
=>
{
if
(
selection
)
{
this
.
editor
.
setSelection
(
selection
);
}
...
...
src/vs/monaco.d.ts
浏览文件 @
57a072f8
...
...
@@ -4913,14 +4913,19 @@ declare module monaco.languages {
provideColorPresentations
(
model
:
editor
.
ITextModel
,
colorInfo
:
IColorInformation
,
token
:
CancellationToken
):
IColorPresentation
[]
|
Thenable
<
IColorPresentation
[]
>
;
}
export
interface
IResourceEdit
{
export
interface
ResourceFileEdit
{
oldUri
:
Uri
;
newUri
:
Uri
;
}
export
interface
ResourceTextEdit
{
resource
:
Uri
;
range
:
IRange
;
newText
:
string
;
modelVersionId
?:
number
;
edits
:
TextEdit
[]
;
}
export
interface
WorkspaceEdit
{
edits
:
IResourceEdit
[]
;
edits
:
Array
<
ResourceTextEdit
|
ResourceFileEdit
>
;
rejectReason
?:
string
;
}
...
...
src/vs/platform/progress/common/progress.ts
浏览文件 @
57a072f8
...
...
@@ -31,6 +31,12 @@ export interface IProgressRunner {
done
():
void
;
}
export
const
emptyProgressRunner
:
IProgressRunner
=
Object
.
freeze
({
total
()
{
},
worked
()
{
},
done
()
{
}
});
export
interface
IProgress
<
T
>
{
report
(
item
:
T
):
void
;
}
...
...
src/vs/vscode.d.ts
浏览文件 @
57a072f8
...
...
@@ -2559,6 +2559,42 @@ declare module 'vscode' {
* @return An array of `[Uri, TextEdit[]]`-tuples.
*/
entries
():
[
Uri
,
TextEdit
[]][];
/**
* Renames a given resource in the workspace.
*
* @param from Uri of current resource.
* @param to Uri of renamed resource.
*/
renameResource
(
from
:
Uri
,
to
:
Uri
):
void
;
/**
* Create a new resource in the workspace.
*
* @param uri Uri of resource to create.
*/
createResource
(
uri
:
Uri
):
void
;
/**
* Delete a given resource in the workspace.
*
* @param uri Uri of resource to delete.
*/
deleteResource
(
uri
:
Uri
):
void
;
/**
* Get the resource edits for this workspace edit.
*
* @returns A array of uri-tuples in which a rename-edit
* is represented as `[from, to]`, a delete-operation as `[from, null]`,
* and a create-operation as `[null, to]`;
*/
resourceEdits
():
[
Uri
,
Uri
][];
/**
*
*/
allEntries
():
([
Uri
,
TextEdit
[]]
|
[
Uri
,
Uri
])[];
}
/**
...
...
src/vs/workbench/api/electron-browser/mainThreadEditors.ts
浏览文件 @
57a072f8
...
...
@@ -15,17 +15,18 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
import
{
IEditorGroupService
}
from
'
vs/workbench/services/group/common/groupService
'
;
import
{
Position
as
EditorPosition
,
ITextEditorOptions
}
from
'
vs/platform/editor/common/editor
'
;
import
{
MainThreadTextEditor
}
from
'
./mainThreadEditor
'
;
import
{
ITextEditorConfigurationUpdate
,
TextEditorRevealType
,
IApplyEditsOptions
,
IUndoStopOptions
}
from
'
vs/workbench/api/node/extHost.protocol
'
;
import
{
ITextEditorConfigurationUpdate
,
TextEditorRevealType
,
IApplyEditsOptions
,
IUndoStopOptions
,
WorkspaceEditDto
,
reviveWorkspaceEditDto
}
from
'
vs/workbench/api/node/extHost.protocol
'
;
import
{
MainThreadDocumentsAndEditors
}
from
'
./mainThreadDocumentsAndEditors
'
;
import
{
equals
as
objectEquals
}
from
'
vs/base/common/objects
'
;
import
{
ExtHostContext
,
MainThreadEditorsShape
,
ExtHostEditorsShape
,
ITextDocumentShowOptions
,
ITextEditorPositionData
,
IExtHostContext
,
IWorkspaceResourceEdit
}
from
'
../node/extHost.protocol
'
;
import
{
ExtHostContext
,
MainThreadEditorsShape
,
ExtHostEditorsShape
,
ITextDocumentShowOptions
,
ITextEditorPositionData
,
IExtHostContext
}
from
'
../node/extHost.protocol
'
;
import
{
IRange
}
from
'
vs/editor/common/core/range
'
;
import
{
ISelection
}
from
'
vs/editor/common/core/selection
'
;
import
{
ITextModelService
}
from
'
vs/editor/common/services/resolverService
'
;
import
{
IFileService
}
from
'
vs/platform/files/common/files
'
;
import
{
bulkEdit
,
IResource
Edit
}
from
'
vs/editor/browser/services/bulkEdit
'
;
import
{
Bulk
Edit
}
from
'
vs/editor/browser/services/bulkEdit
'
;
import
{
IModelService
}
from
'
vs/editor/common/services/modelService
'
;
import
{
isCodeEditor
,
ICodeEditor
}
from
'
vs/editor/browser/editorBrowser
'
;
import
{
isResourceFileEdit
}
from
'
vs/editor/common/modes
'
;
export
class
MainThreadEditors
implements
MainThreadEditorsShape
{
...
...
@@ -210,40 +211,22 @@ export class MainThreadEditors implements MainThreadEditorsShape {
return
TPromise
.
as
(
this
.
_documentsAndEditors
.
getEditor
(
id
).
applyEdits
(
modelVersionId
,
edits
,
opts
));
}
$tryApplyWorkspaceEdit
(
workspaceResourceEdits
:
IWorkspaceResourceEdit
[]):
TPromise
<
boolean
>
{
$tryApplyWorkspaceEdit
(
dto
:
WorkspaceEditDto
):
TPromise
<
boolean
>
{
const
{
edits
}
=
reviveWorkspaceEditDto
(
dto
);
// First check if loaded models were not changed in the meantime
for
(
let
i
=
0
,
len
=
workspaceResourceEdits
.
length
;
i
<
len
;
i
++
)
{
const
workspaceResourceEdit
=
workspaceResourceEdits
[
i
];
if
(
workspaceResourceEdit
.
modelVersionId
)
{
const
uri
=
URI
.
revive
(
workspaceResourceEdit
.
resource
);
let
model
=
this
.
_modelService
.
getModel
(
uri
);
if
(
model
&&
model
.
getVersionId
()
!==
workspaceResourceEdit
.
modelVersionId
)
{
for
(
let
i
=
0
,
len
=
edits
.
length
;
i
<
len
;
i
++
)
{
const
edit
=
edits
[
i
];
if
(
!
isResourceFileEdit
(
edit
)
&&
edit
.
modelVersionId
)
{
let
model
=
this
.
_modelService
.
getModel
(
edit
.
resource
);
if
(
model
&&
model
.
getVersionId
()
!==
edit
.
modelVersionId
)
{
// model changed in the meantime
return
TPromise
.
as
(
false
);
}
}
}
// Convert to shape expected by bulkEdit below
let
resourceEdits
:
IResourceEdit
[]
=
[];
for
(
let
i
=
0
,
len
=
workspaceResourceEdits
.
length
;
i
<
len
;
i
++
)
{
const
workspaceResourceEdit
=
workspaceResourceEdits
[
i
];
const
uri
=
URI
.
revive
(
workspaceResourceEdit
.
resource
);
const
edits
=
workspaceResourceEdit
.
edits
;
for
(
let
j
=
0
,
lenJ
=
edits
.
length
;
j
<
lenJ
;
j
++
)
{
const
edit
=
edits
[
j
];
resourceEdits
.
push
({
resource
:
uri
,
newText
:
edit
.
newText
,
newEol
:
edit
.
newEol
,
range
:
edit
.
range
});
}
}
let
codeEditor
:
ICodeEditor
;
let
editor
=
this
.
_workbenchEditorService
.
getActiveEditor
();
if
(
editor
)
{
...
...
@@ -253,8 +236,7 @@ export class MainThreadEditors implements MainThreadEditorsShape {
}
}
return
bulkEdit
(
this
.
_textModelResolverService
,
codeEditor
,
resourceEdits
,
this
.
_fileService
)
.
then
(()
=>
true
);
return
BulkEdit
.
perform
(
edits
,
this
.
_textModelResolverService
,
this
.
_fileService
,
codeEditor
).
then
(()
=>
true
);
}
$tryInsertSnippet
(
id
:
string
,
template
:
string
,
ranges
:
IRange
[],
opts
:
IUndoStopOptions
):
TPromise
<
boolean
>
{
...
...
src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts
浏览文件 @
57a072f8
...
...
@@ -15,7 +15,7 @@ import { wireCancellationToken } from 'vs/base/common/async';
import
{
CancellationToken
}
from
'
vs/base/common/cancellation
'
;
import
{
Position
as
EditorPosition
}
from
'
vs/editor/common/core/position
'
;
import
{
Range
as
EditorRange
}
from
'
vs/editor/common/core/range
'
;
import
{
ExtHostContext
,
MainThreadLanguageFeaturesShape
,
ExtHostLanguageFeaturesShape
,
MainContext
,
IExtHostContext
,
ISerializedLanguageConfiguration
,
ISerializedRegExp
,
ISerializedIndentationRule
,
ISerializedOnEnterRule
,
LocationDto
,
SymbolInformationDto
,
WorkspaceEditDto
,
ResourceEditDto
,
CodeAction
Dto
}
from
'
../node/extHost.protocol
'
;
import
{
ExtHostContext
,
MainThreadLanguageFeaturesShape
,
ExtHostLanguageFeaturesShape
,
MainContext
,
IExtHostContext
,
ISerializedLanguageConfiguration
,
ISerializedRegExp
,
ISerializedIndentationRule
,
ISerializedOnEnterRule
,
LocationDto
,
SymbolInformationDto
,
CodeActionDto
,
reviveWorkspaceEdit
Dto
}
from
'
../node/extHost.protocol
'
;
import
{
LanguageConfigurationRegistry
}
from
'
vs/editor/common/modes/languageConfigurationRegistry
'
;
import
{
LanguageConfiguration
,
IndentationRule
,
OnEnterRule
}
from
'
vs/editor/common/modes/languageConfiguration
'
;
import
{
IHeapService
}
from
'
./mainThreadHeapService
'
;
...
...
@@ -86,21 +86,9 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}
private
static
_reviveResourceEditDto
(
data
:
ResourceEditDto
):
modes
.
IResourceEdit
{
data
.
resource
=
URI
.
revive
(
data
.
resource
);
return
<
modes
.
IResourceEdit
>
data
;
}
private
static
_reviveWorkspaceEditDto
(
data
:
WorkspaceEditDto
):
modes
.
WorkspaceEdit
{
if
(
data
&&
data
.
edits
)
{
data
.
edits
.
forEach
(
MainThreadLanguageFeatures
.
_reviveResourceEditDto
);
}
return
<
modes
.
WorkspaceEdit
>
data
;
}
private
static
_reviveCodeActionDto
(
data
:
CodeActionDto
[]):
modes
.
CodeAction
[]
{
if
(
data
)
{
data
.
forEach
(
code
=>
MainThreadLanguageFeatures
.
_
reviveWorkspaceEditDto
(
code
.
edit
));
data
.
forEach
(
code
=>
reviveWorkspaceEditDto
(
code
.
edit
));
}
return
<
modes
.
CodeAction
[]
>
data
;
}
...
...
@@ -266,7 +254,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerRenameSupport
(
handle
:
number
,
selector
:
vscode
.
DocumentSelector
):
void
{
this
.
_registrations
[
handle
]
=
modes
.
RenameProviderRegistry
.
register
(
toLanguageSelector
(
selector
),
<
modes
.
RenameProvider
>
{
provideRenameEdits
:
(
model
:
ITextModel
,
position
:
EditorPosition
,
newName
:
string
,
token
:
CancellationToken
):
Thenable
<
modes
.
WorkspaceEdit
>
=>
{
return
wireCancellationToken
(
token
,
this
.
_proxy
.
$provideRenameEdits
(
handle
,
model
.
uri
,
position
,
newName
)).
then
(
MainThreadLanguageFeatures
.
_
reviveWorkspaceEditDto
);
return
wireCancellationToken
(
token
,
this
.
_proxy
.
$provideRenameEdits
(
handle
,
model
.
uri
,
position
,
newName
)).
then
(
reviveWorkspaceEditDto
);
}
});
}
...
...
src/vs/workbench/api/node/extHost.protocol.ts
浏览文件 @
57a072f8
...
...
@@ -14,7 +14,7 @@ import {
import
*
as
vscode
from
'
vscode
'
;
import
{
UriComponents
}
from
'
vs/base/common/uri
'
;
import
URI
,
{
UriComponents
}
from
'
vs/base/common/uri
'
;
import
Severity
from
'
vs/base/common/severity
'
;
import
{
TPromise
}
from
'
vs/base/common/winjs.base
'
;
...
...
@@ -52,7 +52,7 @@ import { IStat, FileChangeType } from 'vs/platform/files/common/files';
import
{
ConfigurationScope
}
from
'
vs/platform/configuration/common/configurationRegistry
'
;
import
{
ParsedArgs
}
from
'
vs/platform/environment/common/environment
'
;
import
{
CommentRule
,
CharacterPair
,
EnterAction
}
from
'
vs/editor/common/modes/languageConfiguration
'
;
import
{
EndOfLineSequence
,
ISingleEditOperation
}
from
'
vs/editor/common/model
'
;
import
{
ISingleEditOperation
}
from
'
vs/editor/common/model
'
;
import
{
ILineMatch
,
IPatternInfo
}
from
'
vs/platform/search/common/search
'
;
import
{
LogLevel
}
from
'
vs/platform/log/common/log
'
;
...
...
@@ -194,8 +194,6 @@ export interface IApplyEditsOptions extends IUndoStopOptions {
setEndOfLine
:
EndOfLine
;
}
export
interface
ITextDocumentShowOptions
{
position
?:
EditorPosition
;
preserveFocus
?:
boolean
;
...
...
@@ -203,16 +201,6 @@ export interface ITextDocumentShowOptions {
selection
?:
IRange
;
}
export
interface
IWorkspaceResourceEdit
{
resource
:
UriComponents
;
modelVersionId
?:
number
;
edits
:
{
range
?:
IRange
;
newText
:
string
;
newEol
?:
EndOfLineSequence
;
}[];
}
export
interface
MainThreadEditorsShape
extends
IDisposable
{
$tryShowTextDocument
(
resource
:
UriComponents
,
options
:
ITextDocumentShowOptions
):
TPromise
<
string
>
;
$registerTextEditorDecorationType
(
key
:
string
,
options
:
editorCommon
.
IDecorationRenderOptions
):
void
;
...
...
@@ -225,7 +213,7 @@ export interface MainThreadEditorsShape extends IDisposable {
$tryRevealRange
(
id
:
string
,
range
:
IRange
,
revealType
:
TextEditorRevealType
):
TPromise
<
void
>
;
$trySetSelections
(
id
:
string
,
selections
:
ISelection
[]):
TPromise
<
void
>
;
$tryApplyEdits
(
id
:
string
,
modelVersionId
:
number
,
edits
:
ISingleEditOperation
[],
opts
:
IApplyEditsOptions
):
TPromise
<
boolean
>
;
$tryApplyWorkspaceEdit
(
workspace
ResourceEdits
:
IWorkspaceResourceEdit
[]
):
TPromise
<
boolean
>
;
$tryApplyWorkspaceEdit
(
workspace
EditDto
:
WorkspaceEditDto
):
TPromise
<
boolean
>
;
$tryInsertSnippet
(
id
:
string
,
template
:
string
,
selections
:
IRange
[],
opts
:
IUndoStopOptions
):
TPromise
<
boolean
>
;
$getDiffInformation
(
id
:
string
):
TPromise
<
editorCommon
.
ILineChange
[]
>
;
}
...
...
@@ -625,23 +613,44 @@ export interface WorkspaceSymbolsDto extends IdObject {
symbols
:
SymbolInformationDto
[];
}
export
interface
ResourceEditDto
{
export
interface
ResourceFileEditDto
{
oldUri
:
UriComponents
;
newUri
:
UriComponents
;
}
export
interface
ResourceTextEditDto
{
resource
:
UriComponents
;
range
:
IRange
;
newText
:
string
;
modelVersionId
?:
number
;
edits
:
modes
.
TextEdit
[]
;
}
export
interface
WorkspaceEditDto
{
edits
:
ResourceEditDto
[];
edits
:
(
ResourceFileEditDto
|
ResourceTextEditDto
)[];
// todo@joh reject should go into rename
rejectReason
?:
string
;
}
export
function
reviveWorkspaceEditDto
(
data
:
WorkspaceEditDto
):
modes
.
WorkspaceEdit
{
if
(
data
&&
data
.
edits
)
{
for
(
const
edit
of
data
.
edits
)
{
if
(
typeof
(
<
ResourceTextEditDto
>
edit
).
resource
===
'
object
'
)
{
(
<
ResourceTextEditDto
>
edit
).
resource
=
URI
.
revive
((
<
ResourceTextEditDto
>
edit
).
resource
);
}
else
{
(
<
ResourceFileEditDto
>
edit
).
newUri
=
URI
.
revive
((
<
ResourceFileEditDto
>
edit
).
newUri
);
(
<
ResourceFileEditDto
>
edit
).
oldUri
=
URI
.
revive
((
<
ResourceFileEditDto
>
edit
).
oldUri
);
}
}
}
return
<
modes
.
WorkspaceEdit
>
data
;
}
export
interface
CodeActionDto
{
title
:
string
;
edit
?:
WorkspaceEditDto
;
diagnostics
?:
IMarkerData
[];
command
?:
modes
.
Command
;
scope
?:
string
;
kind
?:
string
;
}
export
interface
ExtHostLanguageFeaturesShape
{
...
...
src/vs/workbench/api/node/extHostApiCommands.ts
浏览文件 @
57a072f8
...
...
@@ -353,11 +353,7 @@ export class ExtHostApiCommands {
if
(
value
.
rejectReason
)
{
return
TPromise
.
wrapError
<
types
.
WorkspaceEdit
>
(
new
Error
(
value
.
rejectReason
));
}
let
workspaceEdit
=
new
types
.
WorkspaceEdit
();
for
(
let
edit
of
value
.
edits
)
{
workspaceEdit
.
replace
(
edit
.
resource
,
typeConverters
.
toRange
(
edit
.
range
),
edit
.
newText
);
}
return
workspaceEdit
;
return
typeConverters
.
WorkspaceEdit
.
to
(
value
);
});
}
...
...
src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts
浏览文件 @
57a072f8
...
...
@@ -8,7 +8,7 @@ import Event from 'vs/base/common/event';
import
URI
,
{
UriComponents
}
from
'
vs/base/common/uri
'
;
import
{
sequence
,
always
}
from
'
vs/base/common/async
'
;
import
{
illegalState
}
from
'
vs/base/common/errors
'
;
import
{
ExtHostDocumentSaveParticipantShape
,
MainThreadEditorsShape
,
IWorkspaceResourceEdit
}
from
'
vs/workbench/api/node/extHost.protocol
'
;
import
{
ExtHostDocumentSaveParticipantShape
,
MainThreadEditorsShape
,
ResourceTextEditDto
}
from
'
vs/workbench/api/node/extHost.protocol
'
;
import
{
TextEdit
}
from
'
vs/workbench/api/node/extHostTypes
'
;
import
{
fromRange
,
TextDocumentSaveReason
,
EndOfLine
}
from
'
vs/workbench/api/node/extHostTypeConverters
'
;
import
{
ExtHostDocuments
}
from
'
vs/workbench/api/node/extHostDocuments
'
;
...
...
@@ -142,7 +142,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
}).
then
(
values
=>
{
let
workspaceResourceEdit
:
IWorkspaceResourceEdit
=
{
const
resourceEdit
:
ResourceTextEditDto
=
{
resource
:
document
.
uri
,
edits
:
[]
};
...
...
@@ -150,10 +150,10 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
for
(
const
value
of
values
)
{
if
(
Array
.
isArray
(
value
)
&&
(
<
vscode
.
TextEdit
[]
>
value
).
every
(
e
=>
e
instanceof
TextEdit
))
{
for
(
const
{
newText
,
newEol
,
range
}
of
value
)
{
workspaceR
esourceEdit
.
edits
.
push
({
r
esourceEdit
.
edits
.
push
({
range
:
range
&&
fromRange
(
range
),
newText
,
newE
ol
:
EndOfLine
.
from
(
newEol
)
text
:
newText
,
e
ol
:
EndOfLine
.
from
(
newEol
)
});
}
}
...
...
@@ -161,12 +161,12 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
// apply edits if any and if document
// didn't change somehow in the meantime
if
(
workspaceR
esourceEdit
.
edits
.
length
===
0
)
{
if
(
r
esourceEdit
.
edits
.
length
===
0
)
{
return
undefined
;
}
if
(
version
===
document
.
version
)
{
return
this
.
_mainThreadEditors
.
$tryApplyWorkspaceEdit
(
[
workspaceResourceEdit
]
);
return
this
.
_mainThreadEditors
.
$tryApplyWorkspaceEdit
(
{
edits
:
[
resourceEdit
]
}
);
}
// TODO@joh bubble this to listener?
...
...
src/vs/workbench/api/node/extHostLanguageFeatures.ts
浏览文件 @
57a072f8
...
...
@@ -17,7 +17,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import
{
ExtHostCommands
,
CommandsConverter
}
from
'
vs/workbench/api/node/extHostCommands
'
;
import
{
ExtHostDiagnostics
,
DiagnosticCollection
}
from
'
vs/workbench/api/node/extHostDiagnostics
'
;
import
{
asWinJsPromise
}
from
'
vs/base/common/async
'
;
import
{
MainContext
,
MainThreadLanguageFeaturesShape
,
ExtHostLanguageFeaturesShape
,
ObjectIdentifier
,
IRawColorInfo
,
IMainContext
,
IdObject
,
ISerializedRegExp
,
ISerializedIndentationRule
,
ISerializedOnEnterRule
,
ISerializedLanguageConfiguration
,
SymbolInformationDto
,
SuggestResultDto
,
WorkspaceSymbolsDto
,
SuggestionDto
}
from
'
./extHost.protocol
'
;
import
{
MainContext
,
MainThreadLanguageFeaturesShape
,
ExtHostLanguageFeaturesShape
,
ObjectIdentifier
,
IRawColorInfo
,
IMainContext
,
IdObject
,
ISerializedRegExp
,
ISerializedIndentationRule
,
ISerializedOnEnterRule
,
ISerializedLanguageConfiguration
,
SymbolInformationDto
,
SuggestResultDto
,
WorkspaceSymbolsDto
,
SuggestionDto
,
CodeActionDto
}
from
'
./extHost.protocol
'
;
import
{
regExpLeadsToEndlessLoop
}
from
'
vs/base/common/strings
'
;
import
{
IPosition
}
from
'
vs/editor/common/core/position
'
;
import
{
IRange
}
from
'
vs/editor/common/core/range
'
;
...
...
@@ -255,7 +255,7 @@ class ReferenceAdapter {
}
}
export
interface
CustomCodeAction
extends
modes
.
CodeAction
{
export
interface
CustomCodeAction
extends
CodeActionDto
{
_isSynthetic
?:
boolean
;
}
...
...
@@ -273,7 +273,8 @@ class CodeActionAdapter {
this
.
_provider
=
provider
;
}
provideCodeActions
(
resource
:
URI
,
range
:
IRange
,
context
:
modes
.
CodeActionContext
):
TPromise
<
modes
.
CodeAction
[]
>
{
provideCodeActions
(
resource
:
URI
,
range
:
IRange
,
context
:
modes
.
CodeActionContext
):
TPromise
<
CodeActionDto
[]
>
{
const
doc
=
this
.
_documents
.
getDocumentData
(
resource
).
document
;
const
ran
=
<
vscode
.
Range
>
TypeConverters
.
toRange
(
range
);
...
...
@@ -948,7 +949,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return
this
.
_createDisposable
(
handle
);
}
$provideCodeActions
(
handle
:
number
,
resource
:
UriComponents
,
range
:
IRange
,
context
:
modes
.
CodeActionContext
):
TPromise
<
modes
.
CodeAction
[]
>
{
$provideCodeActions
(
handle
:
number
,
resource
:
UriComponents
,
range
:
IRange
,
context
:
modes
.
CodeActionContext
):
TPromise
<
CodeActionDto
[]
>
{
return
this
.
_withAdapter
(
handle
,
CodeActionAdapter
,
adapter
=>
adapter
.
provideCodeActions
(
URI
.
revive
(
resource
),
range
,
context
));
}
...
...
src/vs/workbench/api/node/extHostTextEditors.ts
浏览文件 @
57a072f8
...
...
@@ -12,7 +12,7 @@ import * as TypeConverters from './extHostTypeConverters';
import
{
TextEditorDecorationType
,
ExtHostTextEditor
}
from
'
./extHostTextEditor
'
;
import
{
ExtHostDocumentsAndEditors
}
from
'
./extHostDocumentsAndEditors
'
;
import
{
Position
as
EditorPosition
}
from
'
vs/platform/editor/common/editor
'
;
import
{
MainContext
,
MainThreadEditorsShape
,
ExtHostEditorsShape
,
ITextDocumentShowOptions
,
ITextEditorPositionData
,
IResolvedTextEditorConfiguration
,
ISelectionChangeEvent
,
IMainContext
,
IWorkspaceResourceEdit
}
from
'
./extHost.protocol
'
;
import
{
MainContext
,
MainThreadEditorsShape
,
ExtHostEditorsShape
,
ITextDocumentShowOptions
,
ITextEditorPositionData
,
IResolvedTextEditorConfiguration
,
ISelectionChangeEvent
,
IMainContext
,
WorkspaceEditDto
}
from
'
./extHost.protocol
'
;
import
*
as
vscode
from
'
vscode
'
;
export
class
ExtHostEditors
implements
ExtHostEditorsShape
{
...
...
@@ -92,36 +92,23 @@ export class ExtHostEditors implements ExtHostEditorsShape {
applyWorkspaceEdit
(
edit
:
vscode
.
WorkspaceEdit
):
TPromise
<
boolean
>
{
let
workspaceResourceEdits
:
IWorkspaceResourceEdit
[]
=
[]
;
const
dto
:
WorkspaceEditDto
=
{
edits
:
[]
}
;
let
entries
=
edit
.
entries
();
for
(
let
entry
of
entries
)
{
let
[
uri
,
edits
]
=
entry
;
let
doc
=
this
.
_extHostDocumentsAndEditors
.
getDocument
(
uri
.
toString
());
let
docVersion
:
number
=
undefined
;
if
(
doc
)
{
docVersion
=
doc
.
version
;
}
let
workspaceResourceEdit
:
IWorkspaceResourceEdit
=
{
resource
:
uri
,
modelVersionId
:
docVersion
,
edits
:
[]
};
for
(
let
edit
of
edits
)
{
workspaceResourceEdit
.
edits
.
push
({
newText
:
edit
.
newText
,
newEol
:
TypeConverters
.
EndOfLine
.
from
(
edit
.
newEol
),
range
:
edit
.
range
&&
TypeConverters
.
fromRange
(
edit
.
range
)
for
(
let
entry
of
edit
.
allEntries
())
{
let
[
uri
,
uriOrEdits
]
=
entry
;
if
(
Array
.
isArray
(
uriOrEdits
))
{
let
doc
=
this
.
_extHostDocumentsAndEditors
.
getDocument
(
uri
.
toString
());
dto
.
edits
.
push
({
resource
:
uri
,
modelVersionId
:
doc
&&
doc
.
version
,
edits
:
uriOrEdits
.
map
(
TypeConverters
.
TextEdit
.
from
)
});
}
else
{
dto
.
edits
.
push
({
oldUri
:
uri
,
newUri
:
uriOrEdits
});
}
workspaceResourceEdits
.
push
(
workspaceResourceEdit
);
}
return
this
.
_proxy
.
$tryApplyWorkspaceEdit
(
workspaceResourceEdits
);
return
this
.
_proxy
.
$tryApplyWorkspaceEdit
(
dto
);
}
// --- called from main thread
...
...
src/vs/workbench/api/node/extHostTypeConverters.ts
浏览文件 @
57a072f8
...
...
@@ -20,6 +20,7 @@ import { ISelection } from 'vs/editor/common/core/selection';
import
*
as
htmlContent
from
'
vs/base/common/htmlContent
'
;
import
{
IRelativePattern
}
from
'
vs/base/common/glob
'
;
import
{
LanguageSelector
,
LanguageFilter
}
from
'
vs/editor/common/modes/languageSelector
'
;
import
{
WorkspaceEditDto
,
ResourceTextEditDto
,
ResourceFileEditDto
}
from
'
vs/workbench/api/node/extHost.protocol
'
;
export
interface
PositionLike
{
line
:
number
;
...
...
@@ -228,24 +229,36 @@ export const TextEdit = {
export
namespace
WorkspaceEdit
{
export
function
from
(
value
:
vscode
.
WorkspaceEdit
):
modes
.
WorkspaceEdit
{
const
result
:
modes
.
WorkspaceEdit
=
{
edits
:
[]
};
for
(
let
entry
of
value
.
entries
())
{
let
[
uri
,
textEdits
]
=
entry
;
for
(
let
textEdit
of
textEdits
)
{
result
.
edits
.
push
({
resource
:
uri
,
newText
:
textEdit
.
newText
,
range
:
fromRange
(
textEdit
.
range
)
});
const
result
:
modes
.
WorkspaceEdit
=
{
edits
:
[]
};
for
(
const
entry
of
value
.
allEntries
())
{
const
[
uri
,
uriOrEdits
]
=
entry
;
if
(
Array
.
isArray
(
uriOrEdits
))
{
// text edits
result
.
edits
.
push
({
resource
:
uri
,
edits
:
uriOrEdits
.
map
(
TextEdit
.
from
)
});
}
else
{
// resource edits
result
.
edits
.
push
({
oldUri
:
uri
,
newUri
:
uriOrEdits
});
}
}
return
result
;
}
export
function
to
(
value
:
modes
.
WorkspaceEdit
)
{
export
function
to
(
value
:
WorkspaceEditDto
)
{
const
result
=
new
types
.
WorkspaceEdit
();
for
(
const
edit
of
value
.
edits
)
{
result
.
replace
(
edit
.
resource
,
toRange
(
edit
.
range
),
edit
.
newText
);
if
(
Array
.
isArray
((
<
ResourceTextEditDto
>
edit
).
edits
))
{
result
.
set
(
URI
.
revive
((
<
ResourceTextEditDto
>
edit
).
resource
),
<
types
.
TextEdit
[]
>
(
<
ResourceTextEditDto
>
edit
).
edits
.
map
(
TextEdit
.
to
)
);
}
else
{
result
.
renameResource
(
URI
.
revive
((
<
ResourceFileEditDto
>
edit
).
oldUri
),
URI
.
revive
((
<
ResourceFileEditDto
>
edit
).
newUri
)
);
}
}
return
result
;
}
...
...
src/vs/workbench/api/node/extHostTypes.ts
浏览文件 @
57a072f8
...
...
@@ -492,10 +492,28 @@ export class TextEdit {
}
}
export
class
WorkspaceEdit
{
export
class
WorkspaceEdit
implements
vscode
.
WorkspaceEdit
{
private
_values
:
[
URI
,
TextEdit
[]][]
=
[];
private
_index
=
new
Map
<
string
,
number
>
();
private
_seqPool
:
number
=
0
;
private
_resourceEdits
:
{
seq
:
number
,
from
:
URI
,
to
:
URI
}[]
=
[];
private
_textEdits
=
new
Map
<
string
,
{
seq
:
number
,
uri
:
URI
,
edits
:
TextEdit
[]
}
>
();
createResource
(
uri
:
vscode
.
Uri
):
void
{
this
.
renameResource
(
undefined
,
uri
);
}
deleteResource
(
uri
:
vscode
.
Uri
):
void
{
this
.
renameResource
(
uri
,
undefined
);
}
renameResource
(
from
:
vscode
.
Uri
,
to
:
vscode
.
Uri
):
void
{
this
.
_resourceEdits
.
push
({
seq
:
this
.
_seqPool
++
,
from
,
to
});
}
resourceEdits
():
[
vscode
.
Uri
,
vscode
.
Uri
][]
{
return
this
.
_resourceEdits
.
map
(({
from
,
to
})
=>
(
<
[
vscode
.
Uri
,
vscode
.
Uri
]
>
[
from
,
to
]));
}
replace
(
uri
:
URI
,
range
:
Range
,
newText
:
string
):
void
{
let
edit
=
new
TextEdit
(
range
,
newText
);
...
...
@@ -503,8 +521,9 @@ export class WorkspaceEdit {
if
(
array
)
{
array
.
push
(
edit
);
}
else
{
this
.
set
(
uri
,
[
edit
])
;
array
=
[
edit
]
;
}
this
.
set
(
uri
,
array
);
}
insert
(
resource
:
URI
,
position
:
Position
,
newText
:
string
):
void
{
...
...
@@ -516,34 +535,58 @@ export class WorkspaceEdit {
}
has
(
uri
:
URI
):
boolean
{
return
this
.
_
index
.
has
(
uri
.
toString
());
return
this
.
_
textEdits
.
has
(
uri
.
toString
());
}
set
(
uri
:
URI
,
edits
:
TextEdit
[]):
void
{
const
idx
=
this
.
_index
.
get
(
uri
.
toString
());
if
(
typeof
idx
===
'
undefined
'
)
{
let
newLen
=
this
.
_values
.
push
([
uri
,
edits
]);
this
.
_index
.
set
(
uri
.
toString
(),
newLen
-
1
);
let
data
=
this
.
_textEdits
.
get
(
uri
.
toString
());
if
(
!
data
)
{
data
=
{
seq
:
this
.
_seqPool
++
,
uri
,
edits
:
[]
};
this
.
_textEdits
.
set
(
uri
.
toString
(),
data
);
}
if
(
!
edits
)
{
data
.
edits
=
undefined
;
}
else
{
this
.
_values
[
idx
][
1
]
=
edits
;
data
.
edits
=
edits
.
slice
(
0
)
;
}
}
get
(
uri
:
URI
):
TextEdit
[]
{
let
idx
=
this
.
_index
.
get
(
uri
.
toString
());
return
typeof
idx
!==
'
undefined
'
&&
this
.
_values
[
idx
][
1
];
if
(
!
this
.
_textEdits
.
has
(
uri
.
toString
()))
{
return
undefined
;
}
const
{
edits
}
=
this
.
_textEdits
.
get
(
uri
.
toString
());
return
edits
?
edits
.
slice
()
:
undefined
;
}
entries
():
[
URI
,
TextEdit
[]][]
{
return
this
.
_values
;
const
res
:
[
URI
,
TextEdit
[]][]
=
[];
this
.
_textEdits
.
forEach
(
value
=>
res
.
push
([
value
.
uri
,
value
.
edits
]));
return
res
.
slice
();
}
allEntries
():
([
URI
,
TextEdit
[]]
|
[
URI
,
URI
])[]
{
// use the 'seq' the we have assigned when inserting
// the operation and use that order in the resulting
// array
const
res
:
([
URI
,
TextEdit
[]]
|
[
URI
,
URI
])[]
=
[];
this
.
_textEdits
.
forEach
(
value
=>
{
const
{
seq
,
uri
,
edits
}
=
value
;
res
[
seq
]
=
[
uri
,
edits
];
});
this
.
_resourceEdits
.
forEach
(
value
=>
{
const
{
seq
,
from
,
to
}
=
value
;
res
[
seq
]
=
[
from
,
to
];
});
return
res
;
}
get
size
():
number
{
return
this
.
_
value
s
.
length
;
return
this
.
_
textEdits
.
size
+
this
.
_resourceEdit
s
.
length
;
}
toJSON
():
any
{
return
this
.
_values
;
return
this
.
entries
()
;
}
}
...
...
src/vs/workbench/parts/search/browser/replaceService.ts
浏览文件 @
57a072f8
...
...
@@ -15,7 +15,7 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
import
{
IModelService
}
from
'
vs/editor/common/services/modelService
'
;
import
{
IModeService
}
from
'
vs/editor/common/services/modeService
'
;
import
{
Match
,
FileMatch
,
FileMatchOrMatch
,
ISearchWorkbenchService
}
from
'
vs/workbench/parts/search/common/searchModel
'
;
import
{
BulkEdit
,
IResourceEdit
,
createBulkEdit
}
from
'
vs/editor/browser/services/bulkEdit
'
;
import
{
BulkEdit
}
from
'
vs/editor/browser/services/bulkEdit
'
;
import
{
IProgressRunner
}
from
'
vs/platform/progress/common/progress
'
;
import
{
IDiffEditor
}
from
'
vs/editor/browser/editorBrowser
'
;
import
{
ITextModelService
,
ITextModelContentProvider
}
from
'
vs/editor/common/services/resolverService
'
;
...
...
@@ -24,6 +24,7 @@ import { ScrollType } from 'vs/editor/common/editorCommon';
import
{
ITextModel
}
from
'
vs/editor/common/model
'
;
import
{
IInstantiationService
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
IFileService
}
from
'
vs/platform/files/common/files
'
;
import
{
ResourceTextEdit
}
from
'
vs/editor/common/modes
'
;
import
{
createTextBufferFactoryFromSnapshot
}
from
'
vs/editor/common/model/textModel
'
;
const
REPLACE_PREVIEW
=
'
replacePreview
'
;
...
...
@@ -104,8 +105,7 @@ export class ReplaceService implements IReplaceService {
public
replace
(
match
:
FileMatchOrMatch
,
progress
?:
IProgressRunner
,
resource
?:
URI
):
TPromise
<
any
>
;
public
replace
(
arg
:
any
,
progress
:
IProgressRunner
=
null
,
resource
:
URI
=
null
):
TPromise
<
any
>
{
let
bulkEdit
:
BulkEdit
=
createBulkEdit
(
this
.
textModelResolverService
,
null
,
this
.
fileService
);
bulkEdit
.
progress
(
progress
);
let
bulkEdit
=
new
BulkEdit
(
null
,
progress
,
this
.
textModelResolverService
,
this
.
fileService
);
if
(
arg
instanceof
Match
)
{
let
match
=
<
Match
>
arg
;
...
...
@@ -127,7 +127,7 @@ export class ReplaceService implements IReplaceService {
});
}
return
bulkEdit
.
finish
();
return
bulkEdit
.
perform
();
}
public
openReplacePreview
(
element
:
FileMatchOrMatch
,
preserveFocus
?:
boolean
,
sideBySide
?:
boolean
,
pinned
?:
boolean
):
TPromise
<
any
>
{
...
...
@@ -175,12 +175,14 @@ export class ReplaceService implements IReplaceService {
});
}
private
createEdit
(
match
:
Match
,
text
:
string
,
resource
:
URI
=
null
):
IResource
Edit
{
private
createEdit
(
match
:
Match
,
text
:
string
,
resource
:
URI
=
null
):
ResourceText
Edit
{
let
fileMatch
:
FileMatch
=
match
.
parent
();
let
resourceEdit
:
IResource
Edit
=
{
let
resourceEdit
:
ResourceText
Edit
=
{
resource
:
resource
!==
null
?
resource
:
fileMatch
.
resource
(),
range
:
match
.
range
(),
newText
:
text
edits
:
[{
range
:
match
.
range
(),
text
:
text
}]
};
return
resourceEdit
;
}
...
...
src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts
浏览文件 @
57a072f8
...
...
@@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import
{
ExtHostDocuments
}
from
'
vs/workbench/api/node/extHostDocuments
'
;
import
{
ExtHostDocumentsAndEditors
}
from
'
vs/workbench/api/node/extHostDocumentsAndEditors
'
;
import
{
TextDocumentSaveReason
,
TextEdit
,
Position
,
EndOfLine
}
from
'
vs/workbench/api/node/extHostTypes
'
;
import
{
MainThreadEditorsShape
,
IWorkspaceResourceEdit
}
from
'
vs/workbench/api/node/extHost.protocol
'
;
import
{
MainThreadEditorsShape
,
WorkspaceEditDto
}
from
'
vs/workbench/api/node/extHost.protocol
'
;
import
{
ExtHostDocumentSaveParticipant
}
from
'
vs/workbench/api/node/extHostDocumentSaveParticipant
'
;
import
{
SingleProxyRPCProtocol
}
from
'
./testRPCProtocol
'
;
import
{
SaveReason
}
from
'
vs/workbench/services/textfile/common/textfiles
'
;
...
...
@@ -18,6 +18,7 @@ import * as vscode from 'vscode';
import
{
mock
}
from
'
vs/workbench/test/electron-browser/api/mock
'
;
import
{
IExtensionDescription
}
from
'
vs/platform/extensions/common/extensions
'
;
import
{
NullLogService
}
from
'
vs/platform/log/common/log
'
;
import
{
isResourceTextEdit
,
ResourceTextEdit
}
from
'
vs/editor/common/modes
'
;
suite
(
'
ExtHostDocumentSaveParticipant
'
,
()
=>
{
...
...
@@ -262,10 +263,10 @@ suite('ExtHostDocumentSaveParticipant', () => {
test
(
'
event delivery, pushEdits sync
'
,
()
=>
{
let
edits
:
IWorkspaceResourceEdit
[]
;
let
dto
:
WorkspaceEditDto
;
const
participant
=
new
ExtHostDocumentSaveParticipant
(
nullLogService
,
documents
,
new
class
extends
mock
<
MainThreadEditorsShape
>
()
{
$tryApplyWorkspaceEdit
(
_edits
:
IWorkspaceResourceEdit
[]
)
{
edits
=
_edits
;
$tryApplyWorkspaceEdit
(
_edits
:
WorkspaceEditDto
)
{
dto
=
_edits
;
return
TPromise
.
as
(
true
);
}
});
...
...
@@ -278,16 +279,17 @@ suite('ExtHostDocumentSaveParticipant', () => {
return
participant
.
$participateInSave
(
resource
,
SaveReason
.
EXPLICIT
).
then
(()
=>
{
sub
.
dispose
();
assert
.
equal
(
edits
.
length
,
1
);
assert
.
equal
(
edits
[
0
].
edits
.
length
,
2
);
assert
.
equal
(
dto
.
edits
.
length
,
1
);
assert
.
ok
(
isResourceTextEdit
(
dto
.
edits
[
0
]));
assert
.
equal
((
<
ResourceTextEdit
>
dto
.
edits
[
0
]).
edits
.
length
,
2
);
});
});
test
(
'
event delivery, concurrent change
'
,
()
=>
{
let
edits
:
IWorkspaceResourceEdit
[]
;
let
edits
:
WorkspaceEditDto
;
const
participant
=
new
ExtHostDocumentSaveParticipant
(
nullLogService
,
documents
,
new
class
extends
mock
<
MainThreadEditorsShape
>
()
{
$tryApplyWorkspaceEdit
(
_edits
:
IWorkspaceResourceEdit
[]
)
{
$tryApplyWorkspaceEdit
(
_edits
:
WorkspaceEditDto
)
{
edits
=
_edits
;
return
TPromise
.
as
(
true
);
}
...
...
@@ -321,16 +323,20 @@ suite('ExtHostDocumentSaveParticipant', () => {
test
(
'
event delivery, two listeners -> two document states
'
,
()
=>
{
const
participant
=
new
ExtHostDocumentSaveParticipant
(
nullLogService
,
documents
,
new
class
extends
mock
<
MainThreadEditorsShape
>
()
{
$tryApplyWorkspaceEdit
(
_edits
:
IWorkspaceResourceEdit
[]
)
{
$tryApplyWorkspaceEdit
(
dto
:
WorkspaceEditDto
)
{
for
(
const
{
resource
,
edits
}
of
_edits
)
{
for
(
const
edit
of
dto
.
edits
)
{
if
(
!
isResourceTextEdit
(
edit
))
{
continue
;
}
const
{
resource
,
edits
}
=
edit
;
const
uri
=
URI
.
revive
(
resource
);
for
(
const
{
newT
ext
,
range
}
of
edits
)
{
for
(
const
{
t
ext
,
range
}
of
edits
)
{
documents
.
$acceptModelChanged
(
uri
.
toString
(),
{
changes
:
[{
range
,
text
,
rangeLength
:
undefined
,
text
:
newText
}],
eol
:
undefined
,
versionId
:
documents
.
getDocumentData
(
uri
).
version
+
1
...
...
src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts
浏览文件 @
57a072f8
...
...
@@ -24,7 +24,7 @@ import { IHeapService } from 'vs/workbench/api/electron-browser/mainThreadHeapSe
import
{
ExtHostDocuments
}
from
'
vs/workbench/api/node/extHostDocuments
'
;
import
{
ExtHostDocumentsAndEditors
}
from
'
vs/workbench/api/node/extHostDocumentsAndEditors
'
;
import
{
getDocumentSymbols
}
from
'
vs/editor/contrib/quickOpen/quickOpen
'
;
import
{
DocumentSymbolProviderRegistry
,
DocumentHighlightKind
,
Hover
}
from
'
vs/editor/common/modes
'
;
import
{
DocumentSymbolProviderRegistry
,
DocumentHighlightKind
,
Hover
,
ResourceTextEdit
}
from
'
vs/editor/common/modes
'
;
import
{
getCodeLensData
}
from
'
vs/editor/contrib/codelens/codelens
'
;
import
{
getDefinitionsAtPosition
,
getImplementationsAtPosition
,
getTypeDefinitionsAtPosition
}
from
'
vs/editor/contrib/goToDeclaration/goToDeclaration
'
;
import
{
getHover
}
from
'
vs/editor/contrib/hover/getHover
'
;
...
...
@@ -840,7 +840,8 @@ suite('ExtHostLanguageFeatures', function () {
return
rpcProtocol
.
sync
().
then
(()
=>
{
return
rename
(
model
,
new
EditorPosition
(
1
,
1
),
'
newName
'
).
then
(
value
=>
{
assert
.
equal
(
value
.
edits
.
length
,
2
);
// least relevant renamer
assert
.
equal
(
value
.
edits
.
length
,
1
);
// least relevant renamer
assert
.
equal
((
<
ResourceTextEdit
[]
>
value
.
edits
)[
0
].
edits
.
length
,
2
);
// least relevant renamer
});
});
});
...
...
src/vs/workbench/test/electron-browser/api/extHostTextEditors.test.ts
浏览文件 @
57a072f8
...
...
@@ -7,25 +7,26 @@
import
*
as
assert
from
'
assert
'
;
import
{
TPromise
}
from
'
vs/base/common/winjs.base
'
;
import
*
as
extHostTypes
from
'
vs/workbench/api/node/extHostTypes
'
;
import
{
MainContext
,
MainThreadEditorsShape
,
IWorkspaceResourceEdit
}
from
'
vs/workbench/api/node/extHost.protocol
'
;
import
{
MainContext
,
MainThreadEditorsShape
,
WorkspaceEditDto
}
from
'
vs/workbench/api/node/extHost.protocol
'
;
import
URI
from
'
vs/base/common/uri
'
;
import
{
mock
}
from
'
vs/workbench/test/electron-browser/api/mock
'
;
import
{
ExtHostDocumentsAndEditors
}
from
'
vs/workbench/api/node/extHostDocumentsAndEditors
'
;
import
{
SingleProxyRPCProtocol
,
TestRPCProtocol
}
from
'
vs/workbench/test/electron-browser/api/testRPCProtocol
'
;
import
{
ExtHostEditors
}
from
'
vs/workbench/api/node/extHostTextEditors
'
;
import
{
ResourceTextEdit
}
from
'
vs/editor/common/modes
'
;
suite
(
'
ExtHostTextEditors.applyWorkspaceEdit
'
,
()
=>
{
const
resource
=
URI
.
parse
(
'
foo:bar
'
);
let
editors
:
ExtHostEditors
;
let
workspaceResourceEdits
:
IWorkspaceResourceEdit
[]
;
let
workspaceResourceEdits
:
WorkspaceEditDto
;
setup
(()
=>
{
workspaceResourceEdits
=
null
;
let
rpcProtocol
=
new
TestRPCProtocol
();
rpcProtocol
.
set
(
MainContext
.
MainThreadEditors
,
new
class
extends
mock
<
MainThreadEditorsShape
>
()
{
$tryApplyWorkspaceEdit
(
_workspaceResourceEdits
:
IWorkspaceResourceEdit
[]
):
TPromise
<
boolean
>
{
$tryApplyWorkspaceEdit
(
_workspaceResourceEdits
:
WorkspaceEditDto
):
TPromise
<
boolean
>
{
workspaceResourceEdits
=
_workspaceResourceEdits
;
return
TPromise
.
as
(
true
);
}
...
...
@@ -48,8 +49,8 @@ suite('ExtHostTextEditors.applyWorkspaceEdit', () => {
let
edit
=
new
extHostTypes
.
WorkspaceEdit
();
edit
.
replace
(
resource
,
new
extHostTypes
.
Range
(
0
,
0
,
0
,
0
),
'
hello
'
);
return
editors
.
applyWorkspaceEdit
(
edit
).
then
((
result
)
=>
{
assert
.
equal
(
workspaceResourceEdits
.
length
,
1
);
assert
.
equal
(
workspaceResourceEdits
[
0
]
.
modelVersionId
,
1337
);
assert
.
equal
(
workspaceResourceEdits
.
edits
.
length
,
1
);
assert
.
equal
(
(
<
ResourceTextEdit
>
workspaceResourceEdits
.
edits
[
0
])
.
modelVersionId
,
1337
);
});
});
...
...
@@ -57,8 +58,8 @@ suite('ExtHostTextEditors.applyWorkspaceEdit', () => {
let
edit
=
new
extHostTypes
.
WorkspaceEdit
();
edit
.
replace
(
URI
.
parse
(
'
foo:bar2
'
),
new
extHostTypes
.
Range
(
0
,
0
,
0
,
0
),
'
hello
'
);
return
editors
.
applyWorkspaceEdit
(
edit
).
then
((
result
)
=>
{
assert
.
equal
(
workspaceResourceEdits
.
length
,
1
);
assert
.
ok
(
typeof
workspaceResourceEdits
[
0
]
.
modelVersionId
===
'
undefined
'
);
assert
.
equal
(
workspaceResourceEdits
.
edits
.
length
,
1
);
assert
.
ok
(
typeof
(
<
ResourceTextEdit
>
workspaceResourceEdits
.
edits
[
0
])
.
modelVersionId
===
'
undefined
'
);
});
});
...
...
src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts
浏览文件 @
57a072f8
...
...
@@ -362,6 +362,53 @@ suite('ExtHostTypes', function () {
});
// test('WorkspaceEdit should fail when editing deleted resource', () => {
// const resource = URI.parse('file:///a.ts');
// const edit = new types.WorkspaceEdit();
// edit.deleteResource(resource);
// try {
// edit.insert(resource, new types.Position(0, 0), '');
// assert.fail(false, 'Should disallow edit of deleted resource');
// } catch {
// // expected
// }
// });
test
(
'
WorkspaceEdit - keep order of text and file changes
'
,
function
()
{
const
edit
=
new
types
.
WorkspaceEdit
();
edit
.
replace
(
URI
.
parse
(
'
foo:a
'
),
new
types
.
Range
(
1
,
1
,
1
,
1
),
'
foo
'
);
edit
.
renameResource
(
URI
.
parse
(
'
foo:a
'
),
URI
.
parse
(
'
foo:b
'
));
edit
.
replace
(
URI
.
parse
(
'
foo:a
'
),
new
types
.
Range
(
2
,
1
,
2
,
1
),
'
bar
'
);
edit
.
replace
(
URI
.
parse
(
'
foo:b
'
),
new
types
.
Range
(
3
,
1
,
3
,
1
),
'
bazz
'
);
const
all
=
edit
.
allEntries
();
assert
.
equal
(
all
.
length
,
3
);
function
isFileChange
(
thing
:
[
URI
,
types
.
TextEdit
[]]
|
[
URI
,
URI
]):
thing
is
[
URI
,
URI
]
{
const
[
f
,
s
]
=
thing
;
return
URI
.
isUri
(
f
)
&&
URI
.
isUri
(
s
);
}
function
isTextChange
(
thing
:
[
URI
,
types
.
TextEdit
[]]
|
[
URI
,
URI
]):
thing
is
[
URI
,
types
.
TextEdit
[]]
{
const
[
f
,
s
]
=
thing
;
return
URI
.
isUri
(
f
)
&&
Array
.
isArray
(
s
);
}
const
[
first
,
second
,
third
]
=
all
;
assert
.
equal
(
first
[
0
].
toString
(),
'
foo:a
'
);
assert
.
ok
(
!
isFileChange
(
first
));
assert
.
ok
(
isTextChange
(
first
)
&&
first
[
1
].
length
===
2
);
assert
.
equal
(
second
[
0
].
toString
(),
'
foo:a
'
);
assert
.
ok
(
isFileChange
(
second
));
assert
.
equal
(
third
[
0
].
toString
(),
'
foo:b
'
);
assert
.
ok
(
!
isFileChange
(
third
));
assert
.
ok
(
isTextChange
(
third
)
&&
third
[
1
].
length
===
1
);
});
test
(
'
DocumentLink
'
,
function
()
{
assert
.
throws
(()
=>
new
types
.
DocumentLink
(
null
,
null
));
assert
.
throws
(()
=>
new
types
.
DocumentLink
(
new
types
.
Range
(
1
,
1
,
1
,
1
),
null
));
...
...
src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts
浏览文件 @
57a072f8
...
...
@@ -13,7 +13,7 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import
{
TestCodeEditorService
}
from
'
vs/editor/test/browser/testCodeEditorService
'
;
import
{
ITextFileService
}
from
'
vs/workbench/services/textfile/common/textfiles
'
;
import
{
IWorkbenchEditorService
}
from
'
vs/workbench/services/editor/common/editorService
'
;
import
{
ExtHostDocumentsAndEditorsShape
,
IWorkspaceResourceEdit
,
ExtHostContext
,
ExtHostDocumentsShape
}
from
'
vs/workbench/api/node/extHost.protocol
'
;
import
{
ExtHostDocumentsAndEditorsShape
,
ExtHostContext
,
ExtHostDocumentsShape
}
from
'
vs/workbench/api/node/extHost.protocol
'
;
import
{
mock
}
from
'
vs/workbench/test/electron-browser/api/mock
'
;
import
{
IEditorGroupService
}
from
'
vs/workbench/services/group/common/groupService
'
;
import
Event
from
'
vs/base/common/event
'
;
...
...
@@ -23,6 +23,10 @@ import { Range } from 'vs/editor/common/core/range';
import
{
Position
}
from
'
vs/editor/common/core/position
'
;
import
{
IModelService
}
from
'
vs/editor/common/services/modelService
'
;
import
{
EditOperation
}
from
'
vs/editor/common/core/editOperation
'
;
import
{
TestFileService
}
from
'
vs/workbench/test/workbenchTestServices
'
;
import
{
TPromise
}
from
'
vs/base/common/winjs.base
'
;
import
{
IFileStat
}
from
'
vs/platform/files/common/files
'
;
import
{
ResourceTextEdit
}
from
'
vs/editor/common/modes
'
;
suite
(
'
MainThreadEditors
'
,
()
=>
{
...
...
@@ -31,10 +35,34 @@ suite('MainThreadEditors', () => {
let
modelService
:
IModelService
;
let
editors
:
MainThreadEditors
;
const
movedResources
=
new
Map
<
URI
,
URI
>
();
const
createdResources
=
new
Set
<
URI
>
();
const
deletedResources
=
new
Set
<
URI
>
();
setup
(()
=>
{
const
configService
=
new
TestConfigurationService
();
modelService
=
new
ModelServiceImpl
(
null
,
configService
);
const
codeEditorService
=
new
TestCodeEditorService
();
movedResources
.
clear
();
createdResources
.
clear
();
deletedResources
.
clear
();
const
fileService
=
new
class
extends
TestFileService
{
async
moveFile
(
from
,
target
):
TPromise
<
IFileStat
>
{
movedResources
.
set
(
from
,
target
);
return
createMockFileStat
(
target
);
}
async
createFile
(
uri
):
TPromise
<
IFileStat
>
{
createdResources
.
add
(
uri
);
return
createMockFileStat
(
uri
);
}
async
del
(
uri
):
TPromise
<
any
>
{
deletedResources
.
add
(
uri
);
}
};
const
textFileService
=
new
class
extends
mock
<
ITextFileService
>
()
{
isDirty
()
{
return
false
;
}
models
=
<
any
>
{
...
...
@@ -69,7 +97,7 @@ suite('MainThreadEditors', () => {
workbenchEditorService
,
codeEditorService
,
null
,
null
,
fileService
,
null
,
null
,
editorGroupService
,
...
...
@@ -82,7 +110,7 @@ suite('MainThreadEditors', () => {
workbenchEditorService
,
editorGroupService
,
null
,
null
,
fileService
,
modelService
);
});
...
...
@@ -91,11 +119,11 @@ suite('MainThreadEditors', () => {
let
model
=
modelService
.
createModel
(
'
something
'
,
null
,
resource
);
let
workspaceResourceEdit
:
IWorkspaceResource
Edit
=
{
let
workspaceResourceEdit
:
ResourceText
Edit
=
{
resource
:
resource
,
modelVersionId
:
model
.
getVersionId
(),
edits
:
[{
newT
ext
:
'
asdfg
'
,
t
ext
:
'
asdfg
'
,
range
:
new
Range
(
1
,
1
,
1
,
1
)
}]
};
...
...
@@ -103,8 +131,35 @@ suite('MainThreadEditors', () => {
// Act as if the user edited the model
model
.
applyEdits
([
EditOperation
.
insert
(
new
Position
(
0
,
0
),
'
something
'
)]);
return
editors
.
$tryApplyWorkspaceEdit
(
[
workspaceResourceEdit
]
).
then
((
result
)
=>
{
return
editors
.
$tryApplyWorkspaceEdit
(
{
edits
:
[
workspaceResourceEdit
]
}
).
then
((
result
)
=>
{
assert
.
equal
(
result
,
false
);
});
});
test
(
`applyWorkspaceEdit with only resource edit`
,
()
=>
{
return
editors
.
$tryApplyWorkspaceEdit
({
edits
:
[
{
oldUri
:
resource
,
newUri
:
resource
},
{
oldUri
:
undefined
,
newUri
:
resource
},
{
oldUri
:
resource
,
newUri
:
undefined
}
]
}).
then
((
result
)
=>
{
assert
.
equal
(
result
,
true
);
assert
.
equal
(
movedResources
.
get
(
resource
),
resource
);
assert
.
equal
(
createdResources
.
has
(
resource
),
true
);
assert
.
equal
(
deletedResources
.
has
(
resource
),
true
);
});
});
});
function
createMockFileStat
(
target
:
URI
):
IFileStat
{
return
{
etag
:
''
,
isDirectory
:
false
,
name
:
target
.
path
,
mtime
:
0
,
resource
:
target
};
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录