Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
52cc722d
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,发现更多精彩内容 >>
提交
52cc722d
编写于
5月 26, 2017
作者:
J
Johannes Rieken
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
basic support for nested snippets, #24855
上级
4ad0fd04
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
142 addition
and
95 deletion
+142
-95
src/vs/editor/contrib/snippet/browser/snippetController2.ts
src/vs/editor/contrib/snippet/browser/snippetController2.ts
+88
-36
src/vs/editor/contrib/snippet/browser/snippetSession.ts
src/vs/editor/contrib/snippet/browser/snippetSession.ts
+32
-42
src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts
...r/contrib/snippet/test/browser/snippetController2.test.ts
+22
-2
src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts
...ditor/contrib/snippet/test/browser/snippetSession.test.ts
+0
-15
未找到文件。
src/vs/editor/contrib/snippet/browser/snippetController2.ts
浏览文件 @
52cc722d
...
...
@@ -13,6 +13,62 @@ import { SnippetSession } from './snippetSession';
import
{
EditorContextKeys
}
from
'
vs/editor/common/editorContextKeys
'
;
import
{
KeyCode
,
KeyMod
}
from
'
vs/base/common/keyCodes
'
;
class
SnippetSessions
{
private
_stack
:
SnippetSession
[]
=
[];
add
(
session
:
SnippetSession
):
number
{
return
this
.
_stack
.
push
(
session
);
}
clear
():
void
{
dispose
(
this
.
_stack
);
this
.
_stack
.
length
=
0
;
}
get
empty
():
boolean
{
return
this
.
_stack
.
length
===
0
;
}
get
hasPlaceholder
():
boolean
{
return
this
.
_stack
.
some
(
s
=>
s
.
hasPlaceholder
);
}
get
isAtFirstPlaceholder
():
boolean
{
// return !this.empty && this._stack[0].isAtFirstPlaceholder;
return
this
.
_stack
.
every
(
s
=>
s
.
isAtFirstPlaceholder
);
}
get
isAtFinalPlaceholder
():
boolean
{
// return !this.empty && this._stack[0].isAtFinalPlaceholder;
return
this
.
_stack
.
every
(
s
=>
s
.
isAtFinalPlaceholder
);
}
get
isSelectionWithinPlaceholders
():
boolean
{
return
this
.
_stack
.
some
(
s
=>
s
.
isSelectionWithinPlaceholders
());
}
prev
():
void
{
for
(
let
i
=
this
.
_stack
.
length
-
1
;
i
>=
0
;
i
--
)
{
const
snippet
=
this
.
_stack
[
i
];
if
(
!
snippet
.
isAtFirstPlaceholder
)
{
snippet
.
prev
();
break
;
}
}
}
next
():
void
{
for
(
let
i
=
this
.
_stack
.
length
-
1
;
i
>=
0
;
i
--
)
{
const
snippet
=
this
.
_stack
[
i
];
if
(
!
snippet
.
isAtFinalPlaceholder
)
{
snippet
.
next
();
break
;
}
}
}
}
@
commonEditorContribution
export
class
SnippetController2
{
...
...
@@ -28,7 +84,8 @@ export class SnippetController2 {
private
readonly
_hasNextTabstop
:
IContextKey
<
boolean
>
;
private
readonly
_hasPrevTabstop
:
IContextKey
<
boolean
>
;
private
_snippet
:
SnippetSession
;
// private _snippet: SnippetSession;
private
_sessions
=
new
SnippetSessions
();
private
_snippetListener
:
IDisposable
[]
=
[];
constructor
(
...
...
@@ -44,7 +101,7 @@ export class SnippetController2 {
this
.
_inSnippet
.
reset
();
this
.
_hasPrevTabstop
.
reset
();
this
.
_hasNextTabstop
.
reset
();
dispose
(
this
.
_snippet
);
this
.
_sessions
.
clear
(
);
}
getId
():
string
{
...
...
@@ -56,47 +113,49 @@ export class SnippetController2 {
overwriteBefore
:
number
=
0
,
overwriteAfter
:
number
=
0
,
undoStopBefore
:
boolean
=
true
,
undoStopAfter
:
boolean
=
true
):
void
{
// don't listen while inserting the snippet
// as that is the inflight state causing cancelation
this
.
_snippetListener
=
dispose
(
this
.
_snippetListener
);
if
(
undoStopBefore
)
{
this
.
_editor
.
getModel
().
pushStackElement
();
}
if
(
!
this
.
_snippet
)
{
// create a new session
this
.
_snippet
=
new
SnippetSession
(
this
.
_editor
);
this
.
_snippet
.
insert
(
template
,
overwriteBefore
,
overwriteAfter
);
this
.
_snippetListener
=
[
this
.
_editor
.
onDidChangeModel
(()
=>
this
.
cancel
()),
this
.
_editor
.
onDidChangeCursorSelection
(()
=>
this
.
_updateState
())
];
}
else
{
// only insert the snippet when a session
// is already active
this
.
_snippet
.
insert
(
template
,
overwriteBefore
,
overwriteAfter
);
}
const
snippet
=
new
SnippetSession
(
this
.
_editor
);
snippet
.
insert
(
template
,
overwriteBefore
,
overwriteAfter
);
this
.
_sessions
.
add
(
snippet
);
if
(
undoStopAfter
)
{
this
.
_editor
.
getModel
().
pushStackElement
();
}
this
.
_snippetListener
=
[
this
.
_editor
.
onDidChangeModel
(()
=>
this
.
cancel
()),
this
.
_editor
.
onDidChangeCursorSelection
(()
=>
this
.
_updateState
())
];
this
.
_updateState
();
}
private
_updateState
():
void
{
if
(
!
this
.
_snippet
)
{
if
(
this
.
_sessions
.
empty
)
{
// canceled in the meanwhile
return
;
}
if
(
!
this
.
_s
nippet
.
hasPlaceholder
)
{
if
(
!
this
.
_s
essions
.
hasPlaceholder
)
{
// don't listen for selection changes and don't
// update context keys when the snippet is plain text
return
this
.
cancel
();
}
if
(
this
.
_s
nippet
.
isAtFinalPlaceholder
||
!
this
.
_snippet
.
isSelectionWithinPlaceholders
()
)
{
if
(
this
.
_s
essions
.
isAtFinalPlaceholder
||
!
this
.
_sessions
.
isSelectionWithinPlaceholders
)
{
return
this
.
cancel
();
}
this
.
_inSnippet
.
set
(
true
);
this
.
_hasPrevTabstop
.
set
(
!
this
.
_s
nippet
.
isAtFirstPlaceholder
);
this
.
_hasNextTabstop
.
set
(
!
this
.
_s
nippet
.
isAtFinalPlaceholder
);
this
.
_hasPrevTabstop
.
set
(
!
this
.
_s
essions
.
isAtFirstPlaceholder
);
this
.
_hasNextTabstop
.
set
(
!
this
.
_s
essions
.
isAtFinalPlaceholder
);
}
finish
():
void
{
...
...
@@ -106,28 +165,21 @@ export class SnippetController2 {
}
cancel
():
void
{
if
(
this
.
_snippet
)
{
this
.
_inSnippet
.
reset
();
this
.
_hasPrevTabstop
.
reset
();
this
.
_hasNextTabstop
.
reset
();
dispose
(
this
.
_snippetListener
);
dispose
(
this
.
_snippet
);
this
.
_snippet
=
undefined
;
}
this
.
_inSnippet
.
reset
();
this
.
_hasPrevTabstop
.
reset
();
this
.
_hasNextTabstop
.
reset
();
this
.
_sessions
.
clear
();
dispose
(
this
.
_snippetListener
);
}
prev
():
void
{
if
(
this
.
_snippet
)
{
this
.
_snippet
.
prev
();
this
.
_updateState
();
}
this
.
_sessions
.
prev
();
this
.
_updateState
();
}
next
():
void
{
if
(
this
.
_snippet
)
{
this
.
_snippet
.
next
();
this
.
_updateState
();
}
this
.
_sessions
.
next
();
this
.
_updateState
();
}
}
...
...
src/vs/editor/contrib/snippet/browser/snippetSession.ts
浏览文件 @
52cc722d
...
...
@@ -130,11 +130,13 @@ export class OneSnippet {
}
get
isAtFirstPlaceholder
()
{
return
this
.
_placeholderGroupsIdx
===
0
;
return
this
.
_placeholderGroupsIdx
===
0
||
this
.
_placeholderGroups
.
length
===
0
;
}
get
isAtFinalPlaceholder
()
{
if
(
this
.
_placeholderGroupsIdx
<
0
)
{
if
(
this
.
_placeholderGroups
.
length
===
0
)
{
return
true
;
}
else
if
(
this
.
_placeholderGroupsIdx
<
0
)
{
return
false
;
}
else
{
return
this
.
_placeholderGroups
[
this
.
_placeholderGroupsIdx
][
0
].
isFinalTabstop
;
...
...
@@ -196,24 +198,35 @@ export class SnippetSession {
return
selection
;
}
static
makeInsertEditsAndSnippets
(
editor
:
ICommonCodeEditor
,
template
:
string
,
overwriteBefore
:
number
=
0
,
overwriteAfter
:
number
=
0
):
{
edits
:
IIdentifiedSingleEditOperation
[],
snippets
:
OneSnippet
[]
}
{
private
readonly
_editor
:
ICommonCodeEditor
;
private
_snippets
:
OneSnippet
[]
=
[];
constructor
(
editor
:
ICommonCodeEditor
)
{
this
.
_editor
=
editor
;
}
dispose
():
void
{
dispose
(
this
.
_snippets
);
}
insert
(
template
:
string
,
overwriteBefore
:
number
=
0
,
overwriteAfter
:
number
=
0
):
void
{
const
model
=
this
.
_editor
.
getModel
();
const
edits
:
IIdentifiedSingleEditOperation
[]
=
[];
let
delta
=
0
;
let
edits
:
IIdentifiedSingleEditOperation
[]
=
[];
let
snippets
:
OneSnippet
[]
=
[];
let
model
=
editor
.
getModel
();
// know what text the overwrite[Before|After] extensions
// of the primary curser have selected because only when
// secondary selections extend to the same text we can grow them
let
firstBeforeText
=
model
.
getValueInRange
(
SnippetSession
.
adjustSelection
(
model
,
editor
.
getSelection
(),
overwriteBefore
,
0
));
let
firstAfterText
=
model
.
getValueInRange
(
SnippetSession
.
adjustSelection
(
model
,
editor
.
getSelection
(),
0
,
overwriteAfter
));
let
firstBeforeText
=
model
.
getValueInRange
(
SnippetSession
.
adjustSelection
(
model
,
this
.
_
editor
.
getSelection
(),
overwriteBefore
,
0
));
let
firstAfterText
=
model
.
getValueInRange
(
SnippetSession
.
adjustSelection
(
model
,
this
.
_
editor
.
getSelection
(),
0
,
overwriteAfter
));
// sort selections by their start position but remeber
// the original index. that allows you to create correct
// offset-based selection logic without changing the
// primary selection
const
indexedSelection
=
editor
.
getSelections
()
const
indexedSelection
=
this
.
_
editor
.
getSelections
()
.
map
((
selection
,
idx
)
=>
({
selection
,
idx
}))
.
sort
((
a
,
b
)
=>
Range
.
compareRangesUsingStarts
(
a
.
selection
,
b
.
selection
));
...
...
@@ -248,46 +261,18 @@ export class SnippetSession {
// that ensures the primiary cursor stays primary despite not being
// the one with lowest start position
edits
[
idx
]
=
EditOperation
.
replaceMove
(
snippetSelection
,
snippet
.
text
);
snippets
[
idx
]
=
new
OneSnippet
(
editor
,
snippet
,
offset
);
}
return
{
edits
,
snippets
};
}
private
readonly
_editor
:
ICommonCodeEditor
;
private
_snippets
:
OneSnippet
[];
constructor
(
editor
:
ICommonCodeEditor
)
{
this
.
_editor
=
editor
;
}
dispose
():
void
{
dispose
(
this
.
_snippets
);
}
insert
(
template
:
string
,
overwriteBefore
:
number
=
0
,
overwriteAfter
:
number
=
0
):
void
{
const
model
=
this
.
_editor
.
getModel
();
const
{
edits
,
snippets
}
=
SnippetSession
.
makeInsertEditsAndSnippets
(
this
.
_editor
,
template
,
overwriteBefore
,
overwriteAfter
);
let
isNestedInsert
=
true
;
if
(
!
this
.
_snippets
)
{
// keep snippets around
this
.
_snippets
=
snippets
;
isNestedInsert
=
false
;
this
.
_snippets
[
idx
]
=
new
OneSnippet
(
this
.
_editor
,
snippet
,
offset
);
}
// make insert edit and start with first selections
const
newSelections
=
model
.
pushEditOperations
(
this
.
_editor
.
getSelections
(),
edits
,
undoEdits
=>
{
if
(
!
isNestedInsert
&&
this
.
_snippets
[
0
].
hasPlaceholder
)
{
this
.
_editor
.
setSelections
(
model
.
pushEditOperations
(
this
.
_editor
.
getSelections
(),
edits
,
undoEdits
=>
{
if
(
this
.
_snippets
[
0
].
hasPlaceholder
)
{
return
this
.
_move
(
true
);
}
else
{
return
undoEdits
.
map
(
edit
=>
Selection
.
fromPositions
(
edit
.
range
.
getEndPosition
()));
}
});
this
.
_editor
.
setSelections
(
newSelections
);
}));
}
next
():
void
{
...
...
@@ -322,6 +307,11 @@ export class SnippetSession {
}
isSelectionWithinPlaceholders
():
boolean
{
if
(
!
this
.
hasPlaceholder
)
{
return
false
;
}
const
selections
=
this
.
_editor
.
getSelections
();
if
(
selections
.
length
<
this
.
_snippets
.
length
)
{
// this means we started snippet mode with N
...
...
src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts
浏览文件 @
52cc722d
...
...
@@ -148,17 +148,37 @@ suite('SnippetController2', function () {
// assertContextKeys(contextKeys, false, false, false);
});
test
(
'
insert,
insert nested
'
,
function
()
{
test
(
'
insert,
nested snippet
'
,
function
()
{
const
ctrl
=
new
SnippetController2
(
editor
,
contextKeys
);
ctrl
.
insert
(
'
${1:foobar}$0
'
);
assertContextKeys
(
contextKeys
,
true
,
false
,
true
);
assertSelections
(
editor
,
new
Selection
(
1
,
1
,
1
,
7
),
new
Selection
(
2
,
5
,
2
,
11
));
ctrl
.
insert
(
'
farboo
'
);
ctrl
.
insert
(
'
farboo$1$0
'
);
assertSelections
(
editor
,
new
Selection
(
1
,
7
,
1
,
7
),
new
Selection
(
2
,
11
,
2
,
11
));
assertContextKeys
(
contextKeys
,
true
,
false
,
true
);
ctrl
.
next
();
assertSelections
(
editor
,
new
Selection
(
1
,
7
,
1
,
7
),
new
Selection
(
2
,
11
,
2
,
11
));
assertContextKeys
(
contextKeys
,
true
,
true
,
true
);
ctrl
.
next
();
assertSelections
(
editor
,
new
Selection
(
1
,
7
,
1
,
7
),
new
Selection
(
2
,
11
,
2
,
11
));
assertContextKeys
(
contextKeys
,
false
,
false
,
false
);
});
test
(
'
insert, nested plain text
'
,
function
()
{
const
ctrl
=
new
SnippetController2
(
editor
,
contextKeys
);
ctrl
.
insert
(
'
${1:foobar}$0
'
);
assertContextKeys
(
contextKeys
,
true
,
false
,
true
);
assertSelections
(
editor
,
new
Selection
(
1
,
1
,
1
,
7
),
new
Selection
(
2
,
5
,
2
,
11
));
ctrl
.
insert
(
'
farboo
'
);
assertSelections
(
editor
,
new
Selection
(
1
,
7
,
1
,
7
),
new
Selection
(
2
,
11
,
2
,
11
));
assertContextKeys
(
contextKeys
,
true
,
false
,
true
);
ctrl
.
next
();
assertSelections
(
editor
,
new
Selection
(
1
,
7
,
1
,
7
),
new
Selection
(
2
,
11
,
2
,
11
));
assertContextKeys
(
contextKeys
,
false
,
false
,
false
);
});
});
src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts
浏览文件 @
52cc722d
...
...
@@ -403,20 +403,5 @@ suite('SnippetSession', function () {
assert
.
equal
(
model
.
getValue
(),
'
@line=1function foo() {
\n
@line=2console.log(a);
\n
}
'
);
assertSelections
(
editor
,
new
Selection
(
1
,
8
,
1
,
8
),
new
Selection
(
2
,
12
,
2
,
12
));
});
test
(
'
snippet, insert-nested
'
,
function
()
{
const
session
=
new
SnippetSession
(
editor
);
session
.
insert
(
'
foo$1foo$0
'
);
assertSelections
(
editor
,
new
Selection
(
1
,
4
,
1
,
4
),
new
Selection
(
2
,
8
,
2
,
8
));
session
.
insert
(
'
bar
'
);
assert
.
ok
(
session
.
isSelectionWithinPlaceholders
());
assertSelections
(
editor
,
new
Selection
(
1
,
7
,
1
,
7
),
new
Selection
(
2
,
11
,
2
,
11
));
session
.
next
();
assertSelections
(
editor
,
new
Selection
(
1
,
10
,
1
,
10
),
new
Selection
(
2
,
14
,
2
,
14
));
assert
.
ok
(
session
.
isAtFinalPlaceholder
);
});
});
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录