Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
362e4922
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,发现更多精彩内容 >>
提交
362e4922
编写于
8月 20, 2020
作者:
M
Martin Aeschlimann
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
renameOnType: debounce update request, trigger update on changes. Fixes #86845. Fixes #86845
上级
73d583f7
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
128 addition
and
90 deletion
+128
-90
src/vs/editor/contrib/rename/onTypeRename.ts
src/vs/editor/contrib/rename/onTypeRename.ts
+107
-66
src/vs/editor/contrib/rename/test/onTypeRename.test.ts
src/vs/editor/contrib/rename/test/onTypeRename.test.ts
+21
-24
未找到文件。
src/vs/editor/contrib/rename/onTypeRename.ts
浏览文件 @
362e4922
...
...
@@ -16,7 +16,7 @@ import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness, IIdentifiedS
import
{
CancellationToken
}
from
'
vs/base/common/cancellation
'
;
import
{
IRange
,
Range
}
from
'
vs/editor/common/core/range
'
;
import
{
OnTypeRenameProviderRegistry
}
from
'
vs/editor/common/modes
'
;
import
{
first
,
createCancelablePromise
,
CancelablePromise
,
RunOnceSchedul
er
}
from
'
vs/base/common/async
'
;
import
{
first
,
createCancelablePromise
,
CancelablePromise
,
Delay
er
}
from
'
vs/base/common/async
'
;
import
{
ModelDecorationOptions
}
from
'
vs/editor/common/model/textModel
'
;
import
{
ContextKeyExpr
,
RawContextKey
,
IContextKeyService
,
IContextKey
}
from
'
vs/platform/contextkey/common/contextkey
'
;
import
{
EditorContextKeys
}
from
'
vs/editor/common/editorContextKeys
'
;
...
...
@@ -50,13 +50,16 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
private
readonly
_visibleContextKey
:
IContextKey
<
boolean
>
;
private
_rangeUpdateTriggerPromise
:
Promise
<
any
>
|
null
;
private
_rangeSyncTriggerPromise
:
Promise
<
any
>
|
null
;
private
_currentRequest
:
CancelablePromise
<
any
>
|
null
;
private
_currentRequestPosition
:
Position
|
null
;
private
_currentRequestModelVersion
:
number
|
null
;
private
_currentDecorations
:
string
[];
// The one at index 0 is the reference one
private
_stopPattern
:
RegExp
;
private
_ignoreChangeEvent
:
boolean
;
private
readonly
_updateMirrors
:
RunOnceScheduler
;
private
readonly
_localToDispose
=
this
.
_register
(
new
DisposableStore
());
...
...
@@ -68,13 +71,18 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
this
.
_editor
=
editor
;
this
.
_enabled
=
false
;
this
.
_visibleContextKey
=
CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE
.
bindTo
(
contextKeyService
);
this
.
_currentRequest
=
null
;
this
.
_currentRequestPosition
=
null
;
this
.
_currentDecorations
=
[];
this
.
_stopPattern
=
/
\s
/
;
this
.
_ignoreChangeEvent
=
false
;
this
.
_localToDispose
=
this
.
_register
(
new
DisposableStore
());
this
.
_updateMirrors
=
this
.
_register
(
new
RunOnceScheduler
(()
=>
this
.
_doUpdateMirrors
(),
0
));
this
.
_rangeUpdateTriggerPromise
=
null
;
this
.
_rangeSyncTriggerPromise
=
null
;
this
.
_currentRequest
=
null
;
this
.
_currentRequestPosition
=
null
;
this
.
_currentRequestModelVersion
=
null
;
this
.
_register
(
this
.
_editor
.
onDidChangeModel
(()
=>
this
.
reinitialize
()));
...
...
@@ -98,81 +106,67 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
this
.
_enabled
=
isEnabled
;
this
.
clear
LinkedUI
();
this
.
clear
Ranges
();
this
.
_localToDispose
.
clear
();
if
(
!
isEnabled
||
model
===
null
)
{
return
;
}
const
rangeUpdateScheduler
=
new
Delayer
(
200
);
const
triggerRangeUpdate
=
()
=>
{
this
.
_rangeUpdateTriggerPromise
=
rangeUpdateScheduler
.
trigger
(()
=>
this
.
updateRanges
());
};
const
rangeSyncScheduler
=
new
Delayer
(
0
);
const
triggerRangeSync
=
(
decorations
:
string
[])
=>
{
this
.
_rangeSyncTriggerPromise
=
rangeSyncScheduler
.
trigger
(()
=>
this
.
_syncRanges
(
decorations
));
};
this
.
_localToDispose
.
add
(
this
.
_editor
.
onDidChangeCursorPosition
((
e
)
=>
{
if
(
e
.
secondaryPositions
.
length
>
0
)
{
this
.
clearLinkedUI
();
// multi-cursor, don't run
return
;
}
// no regions, run
if
(
this
.
_currentDecorations
.
length
===
0
)
{
this
.
updateLinkedUI
(
e
.
position
);
return
;
}
// has cached regions
const
primaryRange
=
model
.
getDecorationRange
(
this
.
_currentDecorations
[
0
]);
// just moving cursor around, don't run again
if
(
primaryRange
&&
Range
.
containsPosition
(
primaryRange
,
e
.
position
))
{
return
;
}
this
.
_updateMirrors
.
cancel
();
// moving cursor out of primary region, run
this
.
updateLinkedUI
(
e
.
position
);
triggerRangeUpdate
();
}));
this
.
_localToDispose
.
add
(
this
.
_editor
.
onDidChangeModelContent
((
e
)
=>
{
if
(
this
.
_ignoreChangeEvent
)
{
return
;
}
if
(
this
.
_currentDecorations
.
length
===
0
)
{
// nothing to do
return
;
}
if
(
e
.
isUndoing
||
e
.
isRedoing
)
{
return
;
}
for
(
const
change
of
e
.
changes
)
{
if
(
this
.
_stopPattern
.
test
(
change
.
text
))
{
this
.
clearLinkedUI
();
return
;
if
(
!
this
.
_ignoreChangeEvent
)
{
if
(
this
.
_currentDecorations
.
length
>
0
)
{
const
referenceRange
=
model
.
getDecorationRange
(
this
.
_currentDecorations
[
0
]);
if
(
referenceRange
&&
e
.
changes
.
every
(
c
=>
referenceRange
.
intersectRanges
(
c
.
range
)))
{
triggerRangeSync
(
this
.
_currentDecorations
);
return
;
}
}
}
t
his
.
_updateMirrors
.
schedul
e
();
t
riggerRangeUpdat
e
();
}));
this
.
updateLinkedUI
();
this
.
_localToDispose
.
add
({
dispose
:
()
=>
{
rangeUpdateScheduler
.
cancel
();
rangeSyncScheduler
.
cancel
();
}
});
this
.
updateRanges
();
}
private
_
doUpdateMirrors
(
):
void
{
private
_
syncRanges
(
decorations
:
string
[]
):
void
{
// dalayed invocation, make sure we're still on
if
(
!
this
.
_editor
.
hasModel
()
||
this
.
_currentD
ecorations
.
length
===
0
)
{
if
(
!
this
.
_editor
.
hasModel
()
||
decorations
!==
this
.
_currentDecorations
||
d
ecorations
.
length
===
0
)
{
// nothing to do
return
;
}
const
model
=
this
.
_editor
.
getModel
();
const
referenceRange
=
model
.
getDecorationRange
(
this
.
_currentD
ecorations
[
0
]);
const
referenceRange
=
model
.
getDecorationRange
(
d
ecorations
[
0
]);
if
(
!
referenceRange
||
referenceRange
.
startLineNumber
!==
referenceRange
.
endLineNumber
)
{
return
this
.
clear
LinkedUI
();
return
this
.
clear
Ranges
();
}
const
referenceValue
=
model
.
getValueInRange
(
referenceRange
);
if
(
this
.
_stopPattern
.
test
(
referenceValue
))
{
return
this
.
clear
LinkedUI
();
return
this
.
clear
Ranges
();
}
let
edits
:
IIdentifiedSingleEditOperation
[]
=
[];
for
(
let
i
=
1
,
len
=
this
.
_currentD
ecorations
.
length
;
i
<
len
;
i
++
)
{
const
mirrorRange
=
model
.
getDecorationRange
(
this
.
_currentD
ecorations
[
i
]);
for
(
let
i
=
1
,
len
=
d
ecorations
.
length
;
i
<
len
;
i
++
)
{
const
mirrorRange
=
model
.
getDecorationRange
(
d
ecorations
[
i
]);
if
(
!
mirrorRange
)
{
continue
;
}
...
...
@@ -221,11 +215,11 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
}
public
dispose
():
void
{
this
.
clear
LinkedUI
();
this
.
clear
Ranges
();
super
.
dispose
();
}
public
clear
LinkedUI
():
void
{
public
clear
Ranges
():
void
{
this
.
_visibleContextKey
.
set
(
false
);
this
.
_currentDecorations
=
this
.
_editor
.
deltaDecorations
(
this
.
_currentDecorations
,
[]);
if
(
this
.
_currentRequest
)
{
...
...
@@ -233,23 +227,45 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
this
.
_currentRequest
=
null
;
this
.
_currentRequestPosition
=
null
;
}
this
.
_updateMirrors
.
cancel
();
}
public
get
current
Request
():
Promise
<
any
>
{
return
this
.
_
currentRequest
||
Promise
.
resolve
();
public
get
current
UpdateTriggerPromise
():
Promise
<
any
>
{
return
this
.
_
rangeUpdateTriggerPromise
||
Promise
.
resolve
();
}
public
async
updateLinkedUI
(
position
:
Position
|
null
=
this
.
_editor
.
getPosition
(),
force
=
false
):
Promise
<
void
>
{
const
model
=
this
.
_editor
.
getModel
();
if
(
!
this
.
_enabled
&&
!
force
||
!
model
||
!
position
)
{
this
.
clearLinkedUI
();
public
get
currentSyncTriggerPromise
():
Promise
<
any
>
{
return
this
.
_rangeSyncTriggerPromise
||
Promise
.
resolve
();
}
public
async
updateRanges
(
force
=
false
):
Promise
<
void
>
{
if
(
!
this
.
_editor
.
hasModel
())
{
this
.
clearRanges
();
return
;
}
if
(
this
.
_currentRequestPosition
&&
(
this
.
_currentRequestPosition
.
equals
(
position
)))
{
const
position
=
this
.
_editor
.
getPosition
();
if
(
!
this
.
_enabled
&&
!
force
||
this
.
_editor
.
getSelections
().
length
>
1
)
{
// disabled or multicursor
this
.
clearRanges
();
return
;
}
const
model
=
this
.
_editor
.
getModel
();
const
modelVersionId
=
model
.
getVersionId
();
if
(
this
.
_currentRequestPosition
&&
this
.
_currentRequestModelVersion
===
modelVersionId
)
{
if
(
position
.
equals
(
this
.
_currentRequestPosition
))
{
return
;
// same position
}
if
(
this
.
_currentDecorations
&&
this
.
_currentDecorations
.
length
>
0
)
{
const
range
=
model
.
getDecorationRange
(
this
.
_currentDecorations
[
0
]);
if
(
range
&&
range
.
containsPosition
(
position
))
{
return
;
// just moving inside the existing primary range
}
}
}
this
.
_currentRequestPosition
=
position
;
this
.
_currentRequestModelVersion
=
modelVersionId
;
const
request
=
createCancelablePromise
(
async
token
=>
{
try
{
const
response
=
await
getOnTypeRenameRanges
(
model
,
position
,
token
);
...
...
@@ -257,6 +273,9 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
return
;
}
this
.
_currentRequest
=
null
;
if
(
modelVersionId
!==
model
.
getVersionId
())
{
return
;
}
let
ranges
:
IRange
[]
=
[];
if
(
response
?.
ranges
)
{
...
...
@@ -281,7 +300,7 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
if
(
!
foundReferenceRange
)
{
// Cannot do on type rename if the ranges are not where the cursor is...
this
.
clear
LinkedUI
();
this
.
clear
Ranges
();
return
;
}
...
...
@@ -294,12 +313,34 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
}
if
(
this
.
_currentRequest
===
request
||
!
this
.
_currentRequest
)
{
// stop if we are still the latest request
this
.
clear
LinkedUI
();
this
.
clear
Ranges
();
}
}
});
this
.
_currentRequest
=
request
;
return
request
;
}
// private printDecorators(model: ITextModel) {
// return this._currentDecorations.map(d => {
// const range = model.getDecorationRange(d);
// if (range) {
// return this.printRange(range);
// }
// return 'invalid';
// }).join(',');
// }
// private printChanges(changes: IModelContentChange[]) {
// return changes.map(c => {
// return `${this.printRange(c.range)} - ${c.text}`;
// }
// ).join(',');
// }
// private printRange(range: IRange) {
// return `${range.startLineNumber},${range.startColumn}/${range.endLineNumber},${range.endColumn}`;
// }
}
export
class
OnTypeRenameAction
extends
EditorAction
{
...
...
@@ -337,10 +378,10 @@ export class OnTypeRenameAction extends EditorAction {
return
super
.
runCommand
(
accessor
,
args
);
}
run
(
accessor
:
ServicesAccessor
,
editor
:
ICodeEditor
):
Promise
<
void
>
{
run
(
_
accessor
:
ServicesAccessor
,
editor
:
ICodeEditor
):
Promise
<
void
>
{
const
controller
=
OnTypeRenameContribution
.
get
(
editor
);
if
(
controller
)
{
return
Promise
.
resolve
(
controller
.
update
LinkedUI
(
editor
.
getPosition
(),
true
));
return
Promise
.
resolve
(
controller
.
update
Ranges
(
true
));
}
return
Promise
.
resolve
();
}
...
...
@@ -350,7 +391,7 @@ const OnTypeRenameCommand = EditorCommand.bindToContribution<OnTypeRenameContrib
registerEditorCommand
(
new
OnTypeRenameCommand
({
id
:
'
cancelOnTypeRenameInput
'
,
precondition
:
CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE
,
handler
:
x
=>
x
.
clear
LinkedUI
(),
handler
:
x
=>
x
.
clear
Ranges
(),
kbOpts
:
{
kbExpr
:
EditorContextKeys
.
editorTextFocus
,
weight
:
KeybindingWeight
.
EditorContrib
+
99
,
...
...
src/vs/editor/contrib/rename/test/onTypeRename.test.ts
浏览文件 @
362e4922
...
...
@@ -6,7 +6,7 @@
import
*
as
assert
from
'
assert
'
;
import
{
DisposableStore
}
from
'
vs/base/common/lifecycle
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
Position
}
from
'
vs/editor/common/core/position
'
;
import
{
IPosition
,
Position
}
from
'
vs/editor/common/core/position
'
;
import
{
IRange
,
Range
}
from
'
vs/editor/common/core/range
'
;
import
{
Handler
}
from
'
vs/editor/common/editorCommon
'
;
import
*
as
modes
from
'
vs/editor/common/modes
'
;
...
...
@@ -14,6 +14,8 @@ import { OnTypeRenameContribution } from 'vs/editor/contrib/rename/onTypeRename'
import
{
createTestCodeEditor
,
ITestCodeEditor
}
from
'
vs/editor/test/browser/testCodeEditor
'
;
import
{
createTextModel
}
from
'
vs/editor/test/common/editorTestUtils
'
;
import
{
CoreEditingCommands
}
from
'
vs/editor/browser/controller/coreCommands
'
;
import
{
ITextModel
}
from
'
vs/editor/common/model
'
;
import
{
USUAL_WORD_SEPARATORS
}
from
'
vs/editor/common/model/wordHelper
'
;
const
mockFile
=
URI
.
parse
(
'
test:somefile.ttt
'
);
const
mockFileSelector
=
{
scheme
:
'
test
'
};
...
...
@@ -53,16 +55,22 @@ suite('On type rename', () => {
function
testCase
(
name
:
string
,
initialState
:
{
text
:
string
|
string
[],
ranges
:
Range
[],
stopPattern
?:
RegExp
},
initialState
:
{
text
:
string
|
string
[],
stopPattern
?:
RegExp
},
operations
:
(
editor
:
TestEditor
)
=>
Promise
<
void
>
,
expectedEndText
:
string
|
string
[]
)
{
test
(
name
,
async
()
=>
{
disposables
.
add
(
modes
.
OnTypeRenameProviderRegistry
.
register
(
mockFileSelector
,
{
stopPattern
:
initialState
.
stopPattern
||
/^
\s
/
,
provideOnTypeRenameRanges
()
{
return
initialState
.
ranges
;
stopPattern
:
initialState
.
stopPattern
||
/
\s
/
,
provideOnTypeRenameRanges
(
model
:
ITextModel
,
pos
:
IPosition
)
{
const
wordAtPos
=
model
.
getWordAtPosition
(
pos
);
if
(
wordAtPos
)
{
const
matches
=
model
.
findMatches
(
wordAtPos
.
word
,
false
,
false
,
true
,
USUAL_WORD_SEPARATORS
,
false
);
assert
.
ok
(
matches
.
length
>
0
);
return
matches
.
map
(
m
=>
m
.
range
);
}
return
[];
}
}));
...
...
@@ -76,19 +84,15 @@ suite('On type rename', () => {
const
testEditor
:
TestEditor
=
{
setPosition
(
pos
:
Position
)
{
editor
.
setPosition
(
pos
);
return
ontypeRenameContribution
.
current
Request
;
return
ontypeRenameContribution
.
current
UpdateTriggerPromise
;
},
setSelection
(
sel
:
IRange
)
{
editor
.
setSelection
(
sel
);
return
ontypeRenameContribution
.
current
Request
;
return
ontypeRenameContribution
.
current
UpdateTriggerPromise
;
},
trigger
(
source
:
string
|
null
|
undefined
,
handlerId
:
string
,
payload
:
any
)
{
editor
.
trigger
(
source
,
handlerId
,
payload
);
return
new
Promise
((
s
,
e
)
=>
{
setTimeout
(()
=>
{
s
();
},
0
);
});
return
ontypeRenameContribution
.
currentSyncTriggerPromise
;
},
undo
()
{
CoreEditingCommands
.
Undo
.
runEditorCommand
(
null
,
editor
,
null
);
...
...
@@ -114,11 +118,7 @@ suite('On type rename', () => {
}
const
state
=
{
text
:
'
<ooo></ooo>
'
,
ranges
:
[
new
Range
(
1
,
2
,
1
,
5
),
new
Range
(
1
,
8
,
1
,
11
),
]
text
:
'
<ooo></ooo>
'
};
/**
...
...
@@ -299,7 +299,7 @@ suite('On type rename', () => {
const
state3
=
{
...
state
,
stopPattern
:
/
^
s/
stopPattern
:
/s/
};
testCase
(
'
Breakout with stop pattern - insert
'
,
state3
,
async
(
editor
)
=>
{
...
...
@@ -357,7 +357,8 @@ suite('On type rename', () => {
testCase
(
'
Delete - left word then undo
'
,
state
,
async
(
editor
)
=>
{
const
pos
=
new
Position
(
1
,
5
);
await
editor
.
setPosition
(
pos
);
editor
.
trigger
(
'
keyboard
'
,
'
deleteWordLeft
'
,
{});
await
editor
.
trigger
(
'
keyboard
'
,
'
deleteWordLeft
'
,
{});
editor
.
undo
();
editor
.
undo
();
},
'
<ooo></ooo>
'
);
...
...
@@ -430,10 +431,6 @@ suite('On type rename', () => {
text
:
[
'
<ooo>
'
,
'
</ooo>
'
],
ranges
:
[
new
Range
(
1
,
2
,
1
,
5
),
new
Range
(
2
,
3
,
2
,
6
),
]
};
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录