Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
34a17728
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,发现更多精彩内容 >>
提交
34a17728
编写于
4月 01, 2020
作者:
B
Benjamin Pasero
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix #94046
上级
e1e47d3b
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
128 addition
and
98 deletion
+128
-98
src/vs/base/common/fuzzyScorer.ts
src/vs/base/common/fuzzyScorer.ts
+81
-60
src/vs/base/test/common/fuzzyScorer.test.ts
src/vs/base/test/common/fuzzyScorer.test.ts
+46
-37
src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts
src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts
+1
-1
未找到文件。
src/vs/base/common/fuzzyScorer.ts
浏览文件 @
34a17728
...
...
@@ -9,7 +9,6 @@ import { sep } from 'vs/base/common/path';
import
{
isWindows
,
isLinux
}
from
'
vs/base/common/platform
'
;
import
{
stripWildcards
,
equalsIgnoreCase
}
from
'
vs/base/common/strings
'
;
import
{
CharCode
}
from
'
vs/base/common/charCode
'
;
import
{
distinctES6
}
from
'
vs/base/common/arrays
'
;
export
type
Score
=
[
number
/* score */
,
number
[]
/* match positions */
];
export
type
ScorerCache
=
{
[
key
:
string
]:
IItemScore
};
...
...
@@ -20,40 +19,7 @@ const NO_SCORE: Score = [NO_MATCH, []];
// const DEBUG = false;
// const DEBUG_MATRIX = false;
export
function
score
(
target
:
string
,
query
:
IPreparedQuery
,
fuzzy
:
boolean
):
Score
{
if
(
query
.
values
&&
query
.
values
.
length
>
1
)
{
return
scoreMultiple
(
target
,
query
.
values
,
fuzzy
);
}
return
scoreSingle
(
target
,
query
.
normalized
,
query
.
normalizedLowercase
,
fuzzy
);
}
function
scoreMultiple
(
target
:
string
,
query
:
IPreparedQueryPiece
[],
fuzzy
:
boolean
):
Score
{
let
totalScore
=
NO_MATCH
;
const
totalPositions
:
number
[]
=
[];
for
(
const
{
normalized
,
normalizedLowercase
}
of
query
)
{
const
[
scoreValue
,
positions
]
=
scoreSingle
(
target
,
normalized
,
normalizedLowercase
,
fuzzy
);
if
(
scoreValue
===
NO_MATCH
)
{
// if a single query value does not match, return with
// no score entirely, we require all queries to match
return
NO_SCORE
;
}
totalScore
+=
scoreValue
;
totalPositions
.
push
(...
positions
);
}
if
(
totalScore
===
NO_MATCH
)
{
return
NO_SCORE
;
}
// if we have a score, ensure that the positions are
// sorted in ascending order and distinct
return
[
totalScore
,
distinctES6
(
totalPositions
).
sort
((
a
,
b
)
=>
a
-
b
)];
}
function
scoreSingle
(
target
:
string
,
query
:
string
,
queryLower
:
string
,
fuzzy
:
boolean
):
Score
{
export
function
score
(
target
:
string
,
query
:
string
,
queryLower
:
string
,
fuzzy
:
boolean
):
Score
{
if
(
!
target
||
!
query
)
{
return
NO_SCORE
;
// return early if target or query are undefined
}
...
...
@@ -459,56 +425,80 @@ export function scoreItem<T>(item: T, query: IPreparedQuery, fuzzy: boolean, acc
return
itemScore
;
}
function
createMatches
(
offsets
:
undefined
|
number
[]):
IMatch
[]
{
let
ret
:
IMatch
[]
=
[];
if
(
!
offsets
)
{
return
ret
;
function
doScoreItem
(
label
:
string
,
description
:
string
|
undefined
,
path
:
string
|
undefined
,
query
:
IPreparedQuery
,
fuzzy
:
boolean
):
IItemScore
{
const
preferLabelMatches
=
!
path
||
!
query
.
containsPathSeparator
;
// Treat identity matches on full path highest
if
(
path
&&
(
isLinux
?
query
.
pathNormalized
===
path
:
equalsIgnoreCase
(
query
.
pathNormalized
,
path
)))
{
return
{
score
:
PATH_IDENTITY_SCORE
,
labelMatch
:
[{
start
:
0
,
end
:
label
.
length
}],
descriptionMatch
:
description
?
[{
start
:
0
,
end
:
description
.
length
}]
:
undefined
};
}
let
last
:
IMatch
|
undefined
;
for
(
const
pos
of
offsets
)
{
if
(
last
&&
last
.
end
===
pos
)
{
last
.
end
+=
1
;
}
else
{
last
=
{
start
:
pos
,
end
:
pos
+
1
};
ret
.
push
(
last
);
}
// Score: multiple inputs
if
(
query
.
values
&&
query
.
values
.
length
>
1
)
{
return
doScoreMultiple
(
label
,
description
,
path
,
query
.
values
,
preferLabelMatches
,
fuzzy
);
}
return
ret
;
// Score: single input
return
doScoreSingle
(
label
,
description
,
path
,
query
,
preferLabelMatches
,
fuzzy
);
}
function
doScoreItem
(
label
:
string
,
description
:
string
|
undefined
,
path
:
string
|
undefined
,
query
:
IPreparedQuery
,
fuzzy
:
boolean
):
IItemScore
{
function
doScoreMultiple
(
label
:
string
,
description
:
string
|
undefined
,
path
:
string
|
undefined
,
query
:
IPreparedQueryPiece
[],
preferLabelMatches
:
boolean
,
fuzzy
:
boolean
):
IItemScore
{
let
totalScore
=
NO_MATCH
;
const
totalLabelMatches
:
IMatch
[]
=
[];
const
totalDescriptionMatches
:
IMatch
[]
=
[];
// 1.) treat identity matches on full path highest
if
(
path
&&
(
isLinux
?
query
.
pathNormalized
===
path
:
equalsIgnoreCase
(
query
.
pathNormalized
,
path
)))
{
return
{
score
:
PATH_IDENTITY_SCORE
,
labelMatch
:
[{
start
:
0
,
end
:
label
.
length
}],
descriptionMatch
:
description
?
[{
start
:
0
,
end
:
description
.
length
}]
:
undefined
};
for
(
const
queryPiece
of
query
)
{
const
{
score
,
labelMatch
,
descriptionMatch
}
=
doScoreSingle
(
label
,
description
,
path
,
queryPiece
,
preferLabelMatches
,
fuzzy
);
if
(
score
===
NO_MATCH
)
{
// if a single query value does not match, return with
// no score entirely, we require all queries to match
return
NO_ITEM_SCORE
;
}
totalScore
+=
score
;
if
(
labelMatch
)
{
totalLabelMatches
.
push
(...
labelMatch
);
}
if
(
descriptionMatch
)
{
totalDescriptionMatches
.
push
(...
descriptionMatch
);
}
}
// We only consider label matches if the query is not including file path separators
const
preferLabelMatches
=
!
path
||
!
query
.
containsPathSeparator
;
// if we have a score, ensure that the positions are
// sorted in ascending order and distinct
return
{
score
:
totalScore
,
labelMatch
:
normalizeMatches
(
totalLabelMatches
),
descriptionMatch
:
normalizeMatches
(
totalDescriptionMatches
)
};
}
function
doScoreSingle
(
label
:
string
,
description
:
string
|
undefined
,
path
:
string
|
undefined
,
query
:
IPreparedQueryPiece
,
preferLabelMatches
:
boolean
,
fuzzy
:
boolean
):
IItemScore
{
// Prefer label matches if told so
if
(
preferLabelMatches
)
{
//
2.) t
reat prefix matches on the label second highest
//
T
reat prefix matches on the label second highest
const
prefixLabelMatch
=
matchesPrefix
(
query
.
normalized
,
label
);
if
(
prefixLabelMatch
)
{
return
{
score
:
LABEL_PREFIX_SCORE
,
labelMatch
:
prefixLabelMatch
};
}
//
3.) t
reat camelcase matches on the label third highest
//
T
reat camelcase matches on the label third highest
const
camelcaseLabelMatch
=
matchesCamelCase
(
query
.
normalized
,
label
);
if
(
camelcaseLabelMatch
)
{
return
{
score
:
LABEL_CAMELCASE_SCORE
,
labelMatch
:
camelcaseLabelMatch
};
}
//
4.) p
refer scores on the label if any
const
[
labelScore
,
labelPositions
]
=
score
(
label
,
query
,
fuzzy
);
//
P
refer scores on the label if any
const
[
labelScore
,
labelPositions
]
=
score
(
label
,
query
.
normalized
,
query
.
normalizedLowercase
,
fuzzy
);
if
(
labelScore
)
{
return
{
score
:
labelScore
+
LABEL_SCORE_THRESHOLD
,
labelMatch
:
createMatches
(
labelPositions
)
};
}
}
//
5.) f
inally compute description + label scores if we have a description
//
F
inally compute description + label scores if we have a description
if
(
description
)
{
let
descriptionPrefix
=
description
;
if
(
!!
path
)
{
...
...
@@ -518,7 +508,7 @@ function doScoreItem(label: string, description: string | undefined, path: strin
const
descriptionPrefixLength
=
descriptionPrefix
.
length
;
const
descriptionAndLabel
=
`
${
descriptionPrefix
}${
label
}
`
;
const
[
labelDescriptionScore
,
labelDescriptionPositions
]
=
score
(
descriptionAndLabel
,
query
,
fuzzy
);
const
[
labelDescriptionScore
,
labelDescriptionPositions
]
=
score
(
descriptionAndLabel
,
query
.
normalized
,
query
.
normalizedLowercase
,
fuzzy
);
if
(
labelDescriptionScore
)
{
const
labelDescriptionMatches
=
createMatches
(
labelDescriptionPositions
);
const
labelMatch
:
IMatch
[]
=
[];
...
...
@@ -551,6 +541,37 @@ function doScoreItem(label: string, description: string | undefined, path: strin
return
NO_ITEM_SCORE
;
}
function
createMatches
(
offsets
:
undefined
|
number
[]):
IMatch
[]
{
let
ret
:
IMatch
[]
=
[];
if
(
!
offsets
)
{
return
ret
;
}
let
last
:
IMatch
|
undefined
;
for
(
const
pos
of
offsets
)
{
if
(
last
&&
last
.
end
===
pos
)
{
last
.
end
+=
1
;
}
else
{
last
=
{
start
:
pos
,
end
:
pos
+
1
};
ret
.
push
(
last
);
}
}
return
ret
;
}
function
normalizeMatches
(
matches
:
IMatch
[]):
IMatch
[]
{
const
positions
=
new
Set
<
number
>
();
for
(
const
match
of
matches
)
{
for
(
let
i
=
match
.
start
;
i
<
match
.
end
;
i
++
)
{
positions
.
add
(
i
);
}
}
return
createMatches
(
Array
.
from
(
positions
.
values
()).
sort
((
a
,
b
)
=>
a
-
b
));
}
export
function
compareItemsByScore
<
T
>
(
itemA
:
T
,
itemB
:
T
,
query
:
IPreparedQuery
,
fuzzy
:
boolean
,
accessor
:
IItemAccessor
<
T
>
,
cache
:
ScorerCache
):
number
{
const
itemScoreA
=
scoreItem
(
itemA
,
query
,
fuzzy
,
accessor
,
cache
);
const
itemScoreB
=
scoreItem
(
itemB
,
query
,
fuzzy
,
accessor
,
cache
);
...
...
src/vs/base/test/common/fuzzyScorer.test.ts
浏览文件 @
34a17728
...
...
@@ -43,7 +43,9 @@ class NullAccessorClass implements scorer.IItemAccessor<URI> {
}
function
_doScore
(
target
:
string
,
query
:
string
,
fuzzy
:
boolean
):
scorer
.
Score
{
return
scorer
.
score
(
target
,
scorer
.
prepareQuery
(
query
),
fuzzy
);
const
preparedQuery
=
scorer
.
prepareQuery
(
query
);
return
scorer
.
score
(
target
,
preparedQuery
.
normalized
,
preparedQuery
.
normalizedLowercase
,
fuzzy
);
}
function
scoreItem
<
T
>
(
item
:
T
,
query
:
string
,
fuzzy
:
boolean
,
accessor
:
scorer
.
IItemAccessor
<
T
>
,
cache
:
scorer
.
ScorerCache
):
scorer
.
IItemScore
{
...
...
@@ -109,42 +111,6 @@ suite('Fuzzy Scorer', () => {
assert
.
equal
(
_doScore
(
target
,
'
eo
'
,
false
)[
0
],
0
);
});
test
(
'
score (fuzzy, multiple)
'
,
function
()
{
const
target
=
'
HeLlo-World
'
;
const
[
firstSingleScore
,
firstSinglePositions
]
=
_doScore
(
target
,
'
HelLo
'
,
true
);
const
[
secondSingleScore
,
secondSinglePositions
]
=
_doScore
(
target
,
'
World
'
,
true
);
const
firstAndSecondSinglePositions
=
[...
firstSinglePositions
,
...
secondSinglePositions
];
let
[
multiScore
,
multiPositions
]
=
_doScore
(
target
,
'
HelLo World
'
,
true
);
function
assertScore
()
{
assert
.
ok
(
multiScore
>=
firstSingleScore
+
secondSingleScore
);
for
(
let
i
=
0
;
i
<
multiPositions
.
length
;
i
++
)
{
assert
.
equal
(
multiPositions
[
i
],
firstAndSecondSinglePositions
[
i
]);
}
}
function
assertNoScore
()
{
assert
.
equal
(
multiScore
,
0
);
assert
.
equal
(
multiPositions
.
length
,
0
);
}
assertScore
();
[
multiScore
,
multiPositions
]
=
_doScore
(
target
,
'
World HelLo
'
,
true
);
assertScore
();
[
multiScore
,
multiPositions
]
=
_doScore
(
target
,
'
World HelLo World
'
,
true
);
assertScore
();
[
multiScore
,
multiPositions
]
=
_doScore
(
target
,
'
World HelLo Nothing
'
,
true
);
assertNoScore
();
[
multiScore
,
multiPositions
]
=
_doScore
(
target
,
'
More Nothing
'
,
true
);
assertNoScore
();
});
test
(
'
scoreItem - matches are proper
'
,
function
()
{
let
res
=
scoreItem
(
null
,
'
something
'
,
true
,
ResourceAccessor
,
cache
);
assert
.
ok
(
!
res
.
score
);
...
...
@@ -217,6 +183,49 @@ suite('Fuzzy Scorer', () => {
assert
.
ok
(
pathRes
.
score
>
noRes
.
score
);
});
test
(
'
scoreItem - multiple
'
,
function
()
{
const
resource
=
URI
.
file
(
'
/xyz/some/path/someFile123.txt
'
);
let
res1
=
scoreItem
(
resource
,
'
xyz some
'
,
true
,
ResourceAccessor
,
cache
);
assert
.
ok
(
res1
.
score
);
assert
.
equal
(
res1
.
labelMatch
?.
length
,
1
);
assert
.
equal
(
res1
.
labelMatch
!
[
0
].
start
,
0
);
assert
.
equal
(
res1
.
labelMatch
!
[
0
].
end
,
4
);
assert
.
equal
(
res1
.
descriptionMatch
?.
length
,
1
);
assert
.
equal
(
res1
.
descriptionMatch
!
[
0
].
start
,
1
);
assert
.
equal
(
res1
.
descriptionMatch
!
[
0
].
end
,
4
);
let
res2
=
scoreItem
(
resource
,
'
some xyz
'
,
true
,
ResourceAccessor
,
cache
);
assert
.
ok
(
res2
.
score
);
assert
.
equal
(
res1
.
score
,
res2
.
score
);
assert
.
equal
(
res2
.
labelMatch
?.
length
,
1
);
assert
.
equal
(
res2
.
labelMatch
!
[
0
].
start
,
0
);
assert
.
equal
(
res2
.
labelMatch
!
[
0
].
end
,
4
);
assert
.
equal
(
res2
.
descriptionMatch
?.
length
,
1
);
assert
.
equal
(
res2
.
descriptionMatch
!
[
0
].
start
,
1
);
assert
.
equal
(
res2
.
descriptionMatch
!
[
0
].
end
,
4
);
let
res3
=
scoreItem
(
resource
,
'
some xyz file file123
'
,
true
,
ResourceAccessor
,
cache
);
assert
.
ok
(
res3
.
score
);
assert
.
ok
(
res3
.
score
>
res2
.
score
);
assert
.
equal
(
res3
.
labelMatch
?.
length
,
1
);
assert
.
equal
(
res3
.
labelMatch
!
[
0
].
start
,
0
);
assert
.
equal
(
res3
.
labelMatch
!
[
0
].
end
,
11
);
assert
.
equal
(
res3
.
descriptionMatch
?.
length
,
1
);
assert
.
equal
(
res3
.
descriptionMatch
!
[
0
].
start
,
1
);
assert
.
equal
(
res3
.
descriptionMatch
!
[
0
].
end
,
4
);
let
res4
=
scoreItem
(
resource
,
'
path z y
'
,
true
,
ResourceAccessor
,
cache
);
assert
.
ok
(
res4
.
score
);
assert
.
ok
(
res4
.
score
<
res2
.
score
);
assert
.
equal
(
res4
.
labelMatch
?.
length
,
0
);
assert
.
equal
(
res4
.
descriptionMatch
?.
length
,
2
);
assert
.
equal
(
res4
.
descriptionMatch
!
[
0
].
start
,
2
);
assert
.
equal
(
res4
.
descriptionMatch
!
[
0
].
end
,
4
);
assert
.
equal
(
res4
.
descriptionMatch
!
[
1
].
start
,
10
);
assert
.
equal
(
res4
.
descriptionMatch
!
[
1
].
end
,
14
);
});
test
(
'
scoreItem - invalid input
'
,
function
()
{
let
res
=
scoreItem
(
null
,
null
!
,
true
,
ResourceAccessor
,
cache
);
...
...
src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts
浏览文件 @
34a17728
...
...
@@ -241,7 +241,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
// Score by container if specified
if
(
includeSymbol
&&
containerQuery
)
{
if
(
containerLabel
)
{
if
(
containerLabel
&&
containerQuery
.
original
.
length
>
0
)
{
containerScore
=
fuzzyScore
(
containerQuery
.
original
,
containerQuery
.
originalLowercase
,
filterPos
,
containerLabel
,
containerLabel
.
toLowerCase
(),
0
,
true
);
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录