Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
掘金者说
vscode
提交
b83ec1a0
V
vscode
项目概览
掘金者说
/
vscode
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
vscode
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
b83ec1a0
编写于
6月 07, 2017
作者:
J
Johannes Rieken
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix #26275
上级
1f372174
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
205 addition
and
53 deletion
+205
-53
src/vs/base/common/strings.ts
src/vs/base/common/strings.ts
+37
-0
src/vs/base/test/common/strings.test.ts
src/vs/base/test/common/strings.test.ts
+10
-0
src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts
...kbench/parts/snippets/electron-browser/snippetsService.ts
+70
-53
src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts
...ts/snippets/test/electron-browser/snippetsService.test.ts
+88
-0
未找到文件。
src/vs/base/common/strings.ts
浏览文件 @
b83ec1a0
...
...
@@ -468,6 +468,43 @@ export function commonSuffixLength(a: string, b: string): number {
return
len
;
}
function
substrEquals
(
a
:
string
,
aStart
:
number
,
aEnd
:
number
,
b
:
string
,
bStart
:
number
,
bEnd
:
number
):
boolean
{
while
(
aStart
<
aEnd
&&
bStart
<
bEnd
)
{
if
(
a
[
aStart
]
!==
b
[
bStart
])
{
return
false
;
}
aStart
+=
1
;
bStart
+=
1
;
}
return
true
;
}
/**
* Return the overlap between the suffix of `a` and the prefix of `b`.
* For instance `overlap("foobar", "arr, I'm a pirate") === 2`.
*/
export
function
overlap
(
a
:
string
,
b
:
string
):
number
{
let
aEnd
=
a
.
length
;
let
bEnd
=
b
.
length
;
let
aStart
=
aEnd
-
bEnd
;
if
(
aStart
===
0
)
{
return
a
===
b
?
aEnd
:
0
;
}
else
if
(
aStart
<
0
)
{
bEnd
+=
aStart
;
aStart
=
0
;
}
while
(
aStart
<
aEnd
&&
bEnd
>
0
)
{
if
(
substrEquals
(
a
,
aStart
,
aEnd
,
b
,
0
,
bEnd
))
{
return
bEnd
;
}
bEnd
-=
1
;
aStart
+=
1
;
}
return
0
;
}
// --- unicode
// http://en.wikipedia.org/wiki/Surrogate_pair
// Returns the code point starting at a specified index in a string
...
...
src/vs/base/test/common/strings.test.ts
浏览文件 @
b83ec1a0
...
...
@@ -93,6 +93,16 @@ suite('Strings', () => {
assert
.
strictEqual
(
strings
.
format
(
'
Foo {0} Bar. {1}
'
,
'
(foo)
'
,
'
.test
'
),
'
Foo (foo) Bar. .test
'
);
});
test
(
'
overlap
'
,
function
()
{
assert
.
equal
(
strings
.
overlap
(
'
foobar
'
,
'
arr, I am a priate
'
),
2
);
assert
.
equal
(
strings
.
overlap
(
'
no
'
,
'
overlap
'
),
1
);
assert
.
equal
(
strings
.
overlap
(
'
no
'
,
'
0verlap
'
),
0
);
assert
.
equal
(
strings
.
overlap
(
'
nothing
'
,
''
),
0
);
assert
.
equal
(
strings
.
overlap
(
''
,
'
nothing
'
),
0
);
assert
.
equal
(
strings
.
overlap
(
'
full
'
,
'
full
'
),
4
);
assert
.
equal
(
strings
.
overlap
(
'
full
'
,
'
fulloverlap
'
),
4
);
});
test
(
'
computeLineStarts
'
,
function
()
{
function
assertLineStart
(
text
:
string
,
...
offsets
:
number
[]):
void
{
const
actual
=
strings
.
computeLineStarts
(
text
);
...
...
src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts
浏览文件 @
b83ec1a0
...
...
@@ -5,7 +5,6 @@
'
use strict
'
;
import
{
localize
}
from
'
vs/nls
'
;
import
*
as
strings
from
'
vs/base/common/strings
'
;
import
{
IModel
}
from
'
vs/editor/common/editorCommon
'
;
import
{
ISuggestSupport
,
ISuggestResult
,
ISuggestion
,
LanguageId
}
from
'
vs/editor/common/modes
'
;
import
{
createDecorator
}
from
'
vs/platform/instantiation/common/instantiation
'
;
...
...
@@ -13,6 +12,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import
{
setSnippetSuggestSupport
}
from
'
vs/editor/contrib/suggest/browser/suggest
'
;
import
{
IModeService
}
from
'
vs/editor/common/services/modeService
'
;
import
{
Position
}
from
'
vs/editor/common/core/position
'
;
import
{
overlap
,
compare
,
startsWith
}
from
'
vs/base/common/strings
'
;
export
const
ISnippetsService
=
createDecorator
<
ISnippetsService
>
(
'
snippetService
'
);
...
...
@@ -23,6 +23,8 @@ export interface ISnippetsService {
registerSnippets
(
languageId
:
LanguageId
,
snippets
:
ISnippet
[],
owner
:
string
):
void
;
visitSnippets
(
languageId
:
LanguageId
,
accept
:
(
snippet
:
ISnippet
)
=>
void
):
void
;
getSnippets
(
languageId
:
LanguageId
):
ISnippet
[];
}
export
interface
ISnippet
{
...
...
@@ -33,11 +35,7 @@ export interface ISnippet {
extensionName
?:
string
;
}
interface
ISnippetSuggestion
extends
ISuggestion
{
disambiguateLabel
:
string
;
}
class
SnippetsService
implements
ISnippetsService
{
export
class
SnippetsService
implements
ISnippetsService
{
_serviceBrand
:
any
;
...
...
@@ -49,14 +47,14 @@ class SnippetsService implements ISnippetsService {
setSnippetSuggestSupport
(
new
SnippetSuggestProvider
(
modeService
,
this
));
}
public
registerSnippets
(
languageId
:
LanguageId
,
snippets
:
ISnippet
[],
fileName
:
string
):
void
{
registerSnippets
(
languageId
:
LanguageId
,
snippets
:
ISnippet
[],
fileName
:
string
):
void
{
if
(
!
this
.
_snippets
.
has
(
languageId
))
{
this
.
_snippets
.
set
(
languageId
,
new
Map
<
string
,
ISnippet
[]
>
());
}
this
.
_snippets
.
get
(
languageId
).
set
(
fileName
,
snippets
);
}
public
visitSnippets
(
languageId
:
LanguageId
,
accept
:
(
snippet
:
ISnippet
)
=>
boolean
):
void
{
visitSnippets
(
languageId
:
LanguageId
,
accept
:
(
snippet
:
ISnippet
)
=>
boolean
):
void
{
const
modeSnippets
=
this
.
_snippets
.
get
(
languageId
);
if
(
modeSnippets
)
{
modeSnippets
.
forEach
(
snippets
=>
{
...
...
@@ -67,6 +65,17 @@ class SnippetsService implements ISnippetsService {
});
}
}
getSnippets
(
languageId
:
LanguageId
):
ISnippet
[]
{
const
modeSnippets
=
this
.
_snippets
.
get
(
languageId
);
const
ret
:
ISnippet
[]
=
[];
if
(
modeSnippets
)
{
modeSnippets
.
forEach
(
snippets
=>
{
ret
.
push
(...
snippets
);
});
}
return
ret
;
}
}
registerSingleton
(
ISnippetsService
,
SnippetsService
);
...
...
@@ -75,7 +84,13 @@ export interface ISimpleModel {
getLineContent
(
lineNumber
:
number
):
string
;
}
class
SnippetSuggestProvider
implements
ISuggestSupport
{
interface
ISnippetSuggestion
{
suggestion
:
ISuggestion
;
snippet
:
ISnippet
;
}
export
class
SnippetSuggestProvider
implements
ISuggestSupport
{
constructor
(
@
IModeService
private
_modeService
:
IModeService
,
...
...
@@ -87,55 +102,57 @@ class SnippetSuggestProvider implements ISuggestSupport {
provideCompletionItems
(
model
:
IModel
,
position
:
Position
):
ISuggestResult
{
const
languageId
=
this
.
_getLanguageIdAtPosition
(
model
,
position
);
const
suggestions
:
ISnippetSuggestion
[]
=
[];
const
word
=
model
.
getWordAtPosition
(
position
);
const
currentWord
=
word
?
word
.
word
.
substring
(
0
,
position
.
column
-
word
.
startColumn
).
toLowerCase
()
:
''
;
const
currentFullWord
=
getNonWhitespacePrefix
(
model
,
position
).
toLowerCase
();
this
.
_snippets
.
visitSnippets
(
languageId
,
s
=>
{
const
prefixLower
=
s
.
prefix
.
toLowerCase
();
let
overwriteBefore
=
0
;
if
(
currentWord
.
length
>
0
)
{
// there is a word -> the prefix should match that
if
(
strings
.
startsWith
(
prefixLower
,
currentWord
))
{
overwriteBefore
=
currentWord
.
length
;
}
else
{
return
true
;
}
const
snippets
=
this
.
_snippets
.
getSnippets
(
languageId
);
const
items
:
ISnippetSuggestion
[]
=
[];
}
else
if
(
currentFullWord
.
length
>
currentWord
.
length
)
{
// there is something -> fine if it matches
overwriteBefore
=
strings
.
commonPrefixLength
(
prefixLower
,
currentFullWord
);
}
const
lowWordUntil
=
model
.
getWordUntilPosition
(
position
).
word
.
toLowerCase
();
const
lowLineUntil
=
model
.
getLineContent
(
position
.
lineNumber
).
substr
(
Math
.
max
(
0
,
position
.
column
-
100
),
position
.
column
-
1
).
toLowerCase
();
// store in result
suggestions
.
push
({
type
:
'
snippet
'
,
label
:
s
.
prefix
,
get
disambiguateLabel
()
{
return
localize
(
'
snippetSuggest.longLabel
'
,
"
{0}, {1}
"
,
s
.
prefix
,
s
.
name
);
},
detail
:
s
.
extensionName
||
localize
(
'
detail.userSnippet
'
,
"
User Snippet
"
),
documentation
:
s
.
description
,
insertText
:
s
.
codeSnippet
,
sortText
:
`
${
s
.
prefix
}
-
${
s
.
extensionName
||
''
}
`
,
noAutoAccept
:
true
,
snippetType
:
'
textmate
'
,
overwriteBefore
});
for
(
const
snippet
of
snippets
)
{
const
lowPrefix
=
snippet
.
prefix
.
toLowerCase
();
let
overwriteBefore
:
number
;
return
true
;
});
if
(
lowWordUntil
.
length
>
0
&&
startsWith
(
lowPrefix
,
lowWordUntil
))
{
// cheap match on the (none-empty) current word
overwriteBefore
=
lowWordUntil
.
length
;
}
else
if
(
lowLineUntil
.
length
>
0
)
{
// compute overlap between snippet and line on text
overwriteBefore
=
overlap
(
lowLineUntil
,
snippet
.
prefix
.
toLowerCase
());
}
if
(
overwriteBefore
!==
0
)
{
items
.
push
({
snippet
,
suggestion
:
{
type
:
'
snippet
'
,
label
:
snippet
.
prefix
,
detail
:
snippet
.
extensionName
||
localize
(
'
detail.userSnippet
'
,
"
User Snippet
"
),
documentation
:
snippet
.
description
,
insertText
:
snippet
.
codeSnippet
,
sortText
:
`
${
snippet
.
prefix
}
-
${
snippet
.
extensionName
||
''
}
`
,
noAutoAccept
:
true
,
snippetType
:
'
textmate
'
,
overwriteBefore
}
});
}
}
// dismbiguate suggestions with same labels
let
lastSuggestion
:
ISnippetSuggestion
;
for
(
const
suggestion
of
suggestions
.
sort
(
SnippetSuggestProvider
.
_compareSuggestionsByLabel
))
{
if
(
lastSuggestion
&&
lastSuggestion
.
label
===
suggestion
.
label
)
{
const
suggestions
:
ISuggestion
[]
=
[];
let
lastItem
:
ISnippetSuggestion
;
for
(
const
item
of
items
.
sort
(
SnippetSuggestProvider
.
_compareSuggestionsByLabel
))
{
if
(
lastItem
&&
lastItem
.
suggestion
.
label
===
item
.
suggestion
.
label
)
{
// use the disambiguateLabel instead of the actual label
last
Suggestion
.
label
=
lastSuggestion
.
disambiguateLabel
;
suggestion
.
label
=
suggestion
.
disambiguateLabel
;
last
Item
.
suggestion
.
label
=
localize
(
'
snippetSuggest.longLabel
'
,
"
{0}, {1}
"
,
lastItem
.
suggestion
.
label
,
lastItem
.
snippet
.
name
)
;
item
.
suggestion
.
label
=
localize
(
'
snippetSuggest.longLabel
'
,
"
{0}, {1}
"
,
item
.
suggestion
.
label
,
item
.
snippet
.
name
)
;
}
lastSuggestion
=
suggestion
;
lastItem
=
item
;
suggestions
.
push
(
item
.
suggestion
);
}
return
{
suggestions
};
...
...
@@ -154,8 +171,8 @@ class SnippetSuggestProvider implements ISuggestSupport {
return
languageId
;
}
private
static
_compareSuggestionsByLabel
(
a
:
IS
uggestion
,
b
:
I
Suggestion
):
number
{
return
strings
.
compare
(
a
.
label
,
b
.
label
);
private
static
_compareSuggestionsByLabel
(
a
:
IS
nippetSuggestion
,
b
:
ISnippet
Suggestion
):
number
{
return
compare
(
a
.
suggestion
.
label
,
b
.
suggestion
.
label
);
}
}
...
...
src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts
0 → 100644
浏览文件 @
b83ec1a0
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
*
as
assert
from
'
assert
'
;
import
{
SnippetsService
,
ISnippet
,
SnippetSuggestProvider
}
from
'
vs/workbench/parts/snippets/electron-browser/snippetsService
'
;
import
{
Position
}
from
'
vs/editor/common/core/position
'
;
import
{
ModesRegistry
}
from
'
vs/editor/common/modes/modesRegistry
'
;
import
{
ModeServiceImpl
}
from
'
vs/editor/common/services/modeServiceImpl
'
;
import
{
Model
}
from
'
vs/editor/common/model/model
'
;
suite
(
'
SnippetsService
'
,
function
()
{
suiteSetup
(
function
()
{
ModesRegistry
.
registerLanguage
({
id
:
'
fooLang
'
,
extensions
:
[
'
.fooLang
'
,]
});
});
let
modeService
:
ModeServiceImpl
;
let
snippetService
:
SnippetsService
;
setup
(
function
()
{
modeService
=
new
ModeServiceImpl
();
snippetService
=
new
SnippetsService
(
modeService
);
snippetService
.
registerSnippets
(
modeService
.
getLanguageIdentifier
(
'
fooLang
'
).
id
,
<
ISnippet
[]
>
[{
prefix
:
'
bar
'
,
codeSnippet
:
'
barCodeSnippet
'
,
name
:
'
barTest
'
,
description
:
''
},
{
prefix
:
'
bazz
'
,
codeSnippet
:
'
bazzCodeSnippet
'
,
name
:
'
bazzTest
'
,
description
:
''
}],
'
fooFile.json
'
);
});
test
(
'
snippet completions - simple
'
,
function
()
{
const
provider
=
new
SnippetSuggestProvider
(
modeService
,
snippetService
);
const
model
=
Model
.
createFromString
(
''
,
undefined
,
modeService
.
getLanguageIdentifier
(
'
fooLang
'
));
const
result
=
provider
.
provideCompletionItems
(
model
,
new
Position
(
1
,
1
));
assert
.
equal
(
result
.
incomplete
,
undefined
);
assert
.
equal
(
result
.
suggestions
.
length
,
2
);
});
test
(
'
snippet completions - with prefix
'
,
function
()
{
const
provider
=
new
SnippetSuggestProvider
(
modeService
,
snippetService
);
const
model
=
Model
.
createFromString
(
'
bar
'
,
undefined
,
modeService
.
getLanguageIdentifier
(
'
fooLang
'
));
const
result
=
provider
.
provideCompletionItems
(
model
,
new
Position
(
1
,
4
));
assert
.
equal
(
result
.
incomplete
,
undefined
);
assert
.
equal
(
result
.
suggestions
.
length
,
1
);
assert
.
equal
(
result
.
suggestions
[
0
].
label
,
'
bar
'
);
assert
.
equal
(
result
.
suggestions
[
0
].
insertText
,
'
barCodeSnippet
'
);
});
test
(
'
Cannot use "<?php" as user snippet prefix anymore, #26275
'
,
function
()
{
snippetService
.
registerSnippets
(
modeService
.
getLanguageIdentifier
(
'
fooLang
'
).
id
,
<
ISnippet
[]
>
[{
prefix
:
'
<?php
'
,
codeSnippet
:
'
insert me
'
,
name
:
''
,
description
:
''
}],
'
barFile.json
'
);
const
provider
=
new
SnippetSuggestProvider
(
modeService
,
snippetService
);
let
model
=
Model
.
createFromString
(
'
\t
<?php
'
,
undefined
,
modeService
.
getLanguageIdentifier
(
'
fooLang
'
));
let
result
=
provider
.
provideCompletionItems
(
model
,
new
Position
(
1
,
7
));
assert
.
equal
(
result
.
suggestions
.
length
,
1
);
model
.
dispose
();
model
=
Model
.
createFromString
(
'
\t
<?
'
,
undefined
,
modeService
.
getLanguageIdentifier
(
'
fooLang
'
));
result
=
provider
.
provideCompletionItems
(
model
,
new
Position
(
1
,
4
));
assert
.
equal
(
result
.
suggestions
.
length
,
1
);
model
.
dispose
();
});
});
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录