Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
掘金者说
vscode
提交
a19857db
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,发现更多精彩内容 >>
提交
a19857db
编写于
3月 31, 2020
作者:
B
Benjamin Pasero
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
scorer - introduce normalized path
上级
d7d1147d
变更
6
显示空白变更内容
内联
并排
Showing
6 changed file
with
96 addition
and
53 deletion
+96
-53
src/vs/base/common/fuzzyScorer.ts
src/vs/base/common/fuzzyScorer.ts
+53
-27
src/vs/base/test/common/fuzzyScorer.test.ts
src/vs/base/test/common/fuzzyScorer.test.ts
+32
-15
src/vs/workbench/browser/parts/editor/editorQuickAccess.ts
src/vs/workbench/browser/parts/editor/editorQuickAccess.ts
+2
-2
src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts
...s/workbench/contrib/search/browser/anythingQuickAccess.ts
+7
-7
src/vs/workbench/services/search/node/fileSearch.ts
src/vs/workbench/services/search/node/fileSearch.ts
+1
-1
src/vs/workbench/services/search/node/rawSearchService.ts
src/vs/workbench/services/search/node/rawSearchService.ts
+1
-1
未找到文件。
src/vs/base/common/fuzzyScorer.ts
浏览文件 @
a19857db
...
...
@@ -25,15 +25,15 @@ export function score(target: string, query: IPreparedQuery, fuzzy: boolean): Sc
return
scoreMultiple
(
target
,
query
.
values
,
fuzzy
);
}
return
scoreSingle
(
target
,
query
.
value
,
query
.
value
Lowercase
,
fuzzy
);
return
scoreSingle
(
target
,
query
.
normalized
,
query
.
normalized
Lowercase
,
fuzzy
);
}
function
scoreMultiple
(
target
:
string
,
query
:
IPreparedQueryPiece
[],
fuzzy
:
boolean
):
Score
{
let
totalScore
=
NO_MATCH
;
const
totalPositions
:
number
[]
=
[];
for
(
const
{
value
,
value
Lowercase
}
of
query
)
{
const
[
scoreValue
,
positions
]
=
scoreSingle
(
target
,
value
,
value
Lowercase
,
fuzzy
);
for
(
const
{
normalized
,
normalized
Lowercase
}
of
query
)
{
const
[
scoreValue
,
positions
]
=
scoreSingle
(
target
,
normalized
,
normalized
Lowercase
,
fuzzy
);
if
(
scoreValue
===
NO_MATCH
)
{
// if a single query value does not match, return with
// no score entirely, we require all queries to match
...
...
@@ -338,11 +338,26 @@ const LABEL_CAMELCASE_SCORE = 1 << 16;
const
LABEL_SCORE_THRESHOLD
=
1
<<
15
;
export
interface
IPreparedQueryPiece
{
/**
* The original query as provided as input.
*/
original
:
string
;
originalLowercase
:
string
;
value
:
string
;
valueLowercase
:
string
;
/**
* Original normalized to platform separators:
* - Windows: \
* - Posix: /
*/
pathNormalized
:
string
;
/**
* In addition to the normalized path, will have
* whitespace and wildcards removed.
*/
normalized
:
string
;
normalizedLowercase
:
string
;
}
export
interface
IPreparedQuery
extends
IPreparedQueryPiece
{
...
...
@@ -364,17 +379,21 @@ export function prepareQuery(original: string): IPreparedQuery {
}
const
originalLowercase
=
original
.
toLowerCase
();
const
value
=
prepareQueryValue
(
original
);
const
valueLowercase
=
value
.
toLowerCase
();
const
containsPathSeparator
=
value
.
indexOf
(
sep
)
>=
0
;
const
{
pathNormalized
,
normalized
,
normalizedLowercase
}
=
normalizeQuery
(
original
);
const
containsPathSeparator
=
pathNormalized
.
indexOf
(
sep
)
>=
0
;
let
values
:
IPreparedQueryPiece
[]
|
undefined
=
undefined
;
const
originalSplit
=
original
.
split
(
MULTIPL_QUERY_VALUES_SEPARATOR
);
if
(
originalSplit
.
length
>
1
)
{
for
(
const
originalPiece
of
originalSplit
)
{
const
valuePiece
=
prepareQueryValue
(
originalPiece
);
if
(
valuePiece
)
{
const
{
pathNormalized
:
pathNormalizedPiece
,
normalized
:
normalizedPiece
,
normalizedLowercase
:
normalizedLowercasePiece
}
=
normalizeQuery
(
originalPiece
);
if
(
normalizedPiece
)
{
if
(
!
values
)
{
values
=
[];
}
...
...
@@ -382,29 +401,36 @@ export function prepareQuery(original: string): IPreparedQuery {
values
.
push
({
original
:
originalPiece
,
originalLowercase
:
originalPiece
.
toLowerCase
(),
value
:
valuePiece
,
valueLowercase
:
valuePiece
.
toLowerCase
()
pathNormalized
:
pathNormalizedPiece
,
normalized
:
normalizedPiece
,
normalizedLowercase
:
normalizedLowercasePiece
});
}
}
}
return
{
original
,
originalLowercase
,
value
,
value
Lowercase
,
values
,
containsPathSeparator
};
return
{
original
,
originalLowercase
,
pathNormalized
,
normalized
,
normalized
Lowercase
,
values
,
containsPathSeparator
};
}
function
prepareQueryValue
(
original
:
string
):
string
{
let
value
=
stripWildcards
(
original
).
replace
(
/
\s
/g
,
''
);
// get rid of all wildcards and whitespace
function
normalizeQuery
(
original
:
string
):
{
pathNormalized
:
string
,
normalized
:
string
,
normalizedLowercase
:
string
}
{
let
pathNormalized
:
string
;
if
(
isWindows
)
{
value
=
value
.
replace
(
/
\/
/g
,
sep
);
// Help Windows users to search for paths when using slash
pathNormalized
=
original
.
replace
(
/
\/
/g
,
sep
);
// Help Windows users to search for paths when using slash
}
else
{
value
=
value
.
replace
(
/
\\
/g
,
sep
);
// Help macOS/Linux users to search for paths when using backslash
pathNormalized
=
original
.
replace
(
/
\\
/g
,
sep
);
// Help macOS/Linux users to search for paths when using backslash
}
return
value
;
const
normalized
=
stripWildcards
(
pathNormalized
).
replace
(
/
\s
/g
,
''
);
return
{
pathNormalized
,
normalized
,
normalizedLowercase
:
normalized
.
toLowerCase
()
};
}
export
function
scoreItem
<
T
>
(
item
:
T
,
query
:
IPreparedQuery
,
fuzzy
:
boolean
,
accessor
:
IItemAccessor
<
T
>
,
cache
:
ScorerCache
):
IItemScore
{
if
(
!
item
||
!
query
.
value
)
{
if
(
!
item
||
!
query
.
normalized
)
{
return
NO_ITEM_SCORE
;
// we need an item and query to score on at least
}
...
...
@@ -417,9 +443,9 @@ export function scoreItem<T>(item: T, query: IPreparedQuery, fuzzy: boolean, acc
let
cacheHash
:
string
;
if
(
description
)
{
cacheHash
=
`
${
label
}${
description
}${
query
.
value
}${
fuzzy
}
`
;
cacheHash
=
`
${
label
}${
description
}${
query
.
normalized
}${
fuzzy
}
`
;
}
else
{
cacheHash
=
`
${
label
}${
query
.
value
}${
fuzzy
}
`
;
cacheHash
=
`
${
label
}${
query
.
normalized
}${
fuzzy
}
`
;
}
const
cached
=
cache
[
cacheHash
];
...
...
@@ -455,7 +481,7 @@ function createMatches(offsets: undefined | number[]): IMatch[] {
function
doScoreItem
(
label
:
string
,
description
:
string
|
undefined
,
path
:
string
|
undefined
,
query
:
IPreparedQuery
,
fuzzy
:
boolean
):
IItemScore
{
// 1.) treat identity matches on full path highest
if
(
path
&&
(
isLinux
?
query
.
original
===
path
:
equalsIgnoreCase
(
query
.
original
,
path
)))
{
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
};
}
...
...
@@ -464,13 +490,13 @@ function doScoreItem(label: string, description: string | undefined, path: strin
if
(
preferLabelMatches
)
{
// 2.) treat prefix matches on the label second highest
const
prefixLabelMatch
=
matchesPrefix
(
query
.
value
,
label
);
const
prefixLabelMatch
=
matchesPrefix
(
query
.
normalized
,
label
);
if
(
prefixLabelMatch
)
{
return
{
score
:
LABEL_PREFIX_SCORE
,
labelMatch
:
prefixLabelMatch
};
}
// 3.) treat camelcase matches on the label third highest
const
camelcaseLabelMatch
=
matchesCamelCase
(
query
.
value
,
label
);
const
camelcaseLabelMatch
=
matchesCamelCase
(
query
.
normalized
,
label
);
if
(
camelcaseLabelMatch
)
{
return
{
score
:
LABEL_CAMELCASE_SCORE
,
labelMatch
:
camelcaseLabelMatch
};
}
...
...
@@ -702,17 +728,17 @@ function fallbackCompare<T>(itemA: T, itemB: T, query: IPreparedQuery, accessor:
// compare by label
if
(
labelA
!==
labelB
)
{
return
compareAnything
(
labelA
,
labelB
,
query
.
value
);
return
compareAnything
(
labelA
,
labelB
,
query
.
normalized
);
}
// compare by description
if
(
descriptionA
&&
descriptionB
&&
descriptionA
!==
descriptionB
)
{
return
compareAnything
(
descriptionA
,
descriptionB
,
query
.
value
);
return
compareAnything
(
descriptionA
,
descriptionB
,
query
.
normalized
);
}
// compare by path
if
(
pathA
&&
pathB
&&
pathA
!==
pathB
)
{
return
compareAnything
(
pathA
,
pathB
,
query
.
value
);
return
compareAnything
(
pathA
,
pathB
,
query
.
normalized
);
}
// equal
...
...
src/vs/base/test/common/fuzzyScorer.test.ts
浏览文件 @
a19857db
...
...
@@ -857,41 +857,58 @@ suite('Fuzzy Scorer', () => {
});
test
(
'
prepareQuery
'
,
()
=>
{
assert
.
equal
(
scorer
.
prepareQuery
(
'
f*a
'
).
value
,
'
fa
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
f*a
'
).
normalized
,
'
fa
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
model Tester.ts
'
).
original
,
'
model Tester.ts
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
model Tester.ts
'
).
originalLowercase
,
'
model Tester.ts
'
.
toLowerCase
());
assert
.
equal
(
scorer
.
prepareQuery
(
'
model Tester.ts
'
).
value
,
'
modelTester.ts
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
Model Tester.ts
'
).
value
Lowercase
,
'
modeltester.ts
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
model Tester.ts
'
).
normalized
,
'
modelTester.ts
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
Model Tester.ts
'
).
normalized
Lowercase
,
'
modeltester.ts
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
ModelTester.ts
'
).
containsPathSeparator
,
false
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
Model
'
+
sep
+
'
Tester.ts
'
).
containsPathSeparator
,
true
);
// with spaces
let
query
=
scorer
.
prepareQuery
(
'
He*llo World
'
);
assert
.
equal
(
query
.
original
,
'
He*llo World
'
);
assert
.
equal
(
query
.
value
,
'
HelloWorld
'
);
assert
.
equal
(
query
.
value
Lowercase
,
'
HelloWorld
'
.
toLowerCase
());
assert
.
equal
(
query
.
normalized
,
'
HelloWorld
'
);
assert
.
equal
(
query
.
normalized
Lowercase
,
'
HelloWorld
'
.
toLowerCase
());
assert
.
equal
(
query
.
values
?.
length
,
2
);
assert
.
equal
(
query
.
values
?.[
0
].
original
,
'
He*llo
'
);
assert
.
equal
(
query
.
values
?.[
0
].
value
,
'
Hello
'
);
assert
.
equal
(
query
.
values
?.[
0
].
value
Lowercase
,
'
Hello
'
.
toLowerCase
());
assert
.
equal
(
query
.
values
?.[
0
].
normalized
,
'
Hello
'
);
assert
.
equal
(
query
.
values
?.[
0
].
normalized
Lowercase
,
'
Hello
'
.
toLowerCase
());
assert
.
equal
(
query
.
values
?.[
1
].
original
,
'
World
'
);
assert
.
equal
(
query
.
values
?.[
1
].
value
,
'
World
'
);
assert
.
equal
(
query
.
values
?.[
1
].
value
Lowercase
,
'
World
'
.
toLowerCase
());
assert
.
equal
(
query
.
values
?.[
1
].
normalized
,
'
World
'
);
assert
.
equal
(
query
.
values
?.[
1
].
normalized
Lowercase
,
'
World
'
.
toLowerCase
());
// with spaces that are empty
query
=
scorer
.
prepareQuery
(
'
Hello World
'
);
assert
.
equal
(
query
.
original
,
'
Hello World
'
);
assert
.
equal
(
query
.
originalLowercase
,
'
Hello World
'
.
toLowerCase
());
assert
.
equal
(
query
.
value
,
'
HelloWorld
'
);
assert
.
equal
(
query
.
value
Lowercase
,
'
HelloWorld
'
.
toLowerCase
());
assert
.
equal
(
query
.
normalized
,
'
HelloWorld
'
);
assert
.
equal
(
query
.
normalized
Lowercase
,
'
HelloWorld
'
.
toLowerCase
());
assert
.
equal
(
query
.
values
?.
length
,
2
);
assert
.
equal
(
query
.
values
?.[
0
].
original
,
'
Hello
'
);
assert
.
equal
(
query
.
values
?.[
0
].
originalLowercase
,
'
Hello
'
.
toLowerCase
());
assert
.
equal
(
query
.
values
?.[
0
].
value
,
'
Hello
'
);
assert
.
equal
(
query
.
values
?.[
0
].
value
Lowercase
,
'
Hello
'
.
toLowerCase
());
assert
.
equal
(
query
.
values
?.[
0
].
normalized
,
'
Hello
'
);
assert
.
equal
(
query
.
values
?.[
0
].
normalized
Lowercase
,
'
Hello
'
.
toLowerCase
());
assert
.
equal
(
query
.
values
?.[
1
].
original
,
'
World
'
);
assert
.
equal
(
query
.
values
?.[
1
].
originalLowercase
,
'
World
'
.
toLowerCase
());
assert
.
equal
(
query
.
values
?.[
1
].
value
,
'
World
'
);
assert
.
equal
(
query
.
values
?.[
1
].
valueLowercase
,
'
World
'
.
toLowerCase
());
assert
.
equal
(
query
.
values
?.[
1
].
normalized
,
'
World
'
);
assert
.
equal
(
query
.
values
?.[
1
].
normalizedLowercase
,
'
World
'
.
toLowerCase
());
// Path related
if
(
isWindows
)
{
assert
.
equal
(
scorer
.
prepareQuery
(
'
C:
\\
some
\\
path
'
).
pathNormalized
,
'
C:
\\
some
\\
path
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
C:
\\
some
\\
path
'
).
normalized
,
'
C:
\\
some
\\
path
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
C:
\\
some
\\
path
'
).
containsPathSeparator
,
true
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
C:/some/path
'
).
pathNormalized
,
'
C:
\\
some
\\
path
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
C:/some/path
'
).
normalized
,
'
C:
\\
some
\\
path
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
C:/some/path
'
).
containsPathSeparator
,
true
);
}
else
{
assert
.
equal
(
scorer
.
prepareQuery
(
'
/some/path
'
).
pathNormalized
,
'
/some/path
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
/some/path
'
).
normalized
,
'
/some/path
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
/some/path
'
).
containsPathSeparator
,
true
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
\\
some
\\
path
'
).
pathNormalized
,
'
/some/path
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
\\
some
\\
path
'
).
normalized
,
'
/some/path
'
);
assert
.
equal
(
scorer
.
prepareQuery
(
'
\\
some
\\
path
'
).
containsPathSeparator
,
true
);
}
});
});
src/vs/workbench/browser/parts/editor/editorQuickAccess.ts
浏览文件 @
a19857db
...
...
@@ -62,7 +62,7 @@ export abstract class BaseEditorQuickAccessProvider extends PickerQuickAccessPro
// Filtering
const
filteredEditorEntries
=
this
.
doGetEditorPickItems
().
filter
(
entry
=>
{
if
(
!
query
.
value
)
{
if
(
!
query
.
normalized
)
{
return
true
;
}
...
...
@@ -79,7 +79,7 @@ export abstract class BaseEditorQuickAccessProvider extends PickerQuickAccessPro
});
// Sorting
if
(
query
.
value
)
{
if
(
query
.
normalized
)
{
const
groups
=
this
.
editorGroupService
.
getGroups
(
GroupsOrder
.
GRID_APPEARANCE
).
map
(
group
=>
group
.
id
);
filteredEditorEntries
.
sort
((
entryA
,
entryB
)
=>
{
if
(
entryA
.
groupId
!==
entryB
.
groupId
)
{
...
...
src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts
浏览文件 @
a19857db
...
...
@@ -402,7 +402,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
const
configuration
=
this
.
configuration
;
// Just return all history entries if not searching
if
(
!
query
.
value
)
{
if
(
!
query
.
normalized
)
{
return
this
.
historyService
.
getHistory
().
map
(
editor
=>
this
.
createAnythingPick
(
editor
,
configuration
));
}
...
...
@@ -448,9 +448,9 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
//#region File Search
private
fileQueryDelayer
=
this
.
_register
(
new
ThrottledDelayer
<
URI
[]
>
(
AnythingQuickAccessProvider
.
TYPING_SEARCH_DELAY
));
private
readonly
fileQueryDelayer
=
this
.
_register
(
new
ThrottledDelayer
<
URI
[]
>
(
AnythingQuickAccessProvider
.
TYPING_SEARCH_DELAY
));
private
fileQueryBuilder
=
this
.
instantiationService
.
createInstance
(
QueryBuilder
);
private
readonly
fileQueryBuilder
=
this
.
instantiationService
.
createInstance
(
QueryBuilder
);
private
createFileQueryCache
():
FileQueryCacheState
{
return
new
FileQueryCacheState
(
...
...
@@ -462,7 +462,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
}
private
async
getFilePicks
(
query
:
IPreparedQuery
,
excludes
:
ResourceMap
<
boolean
>
,
token
:
CancellationToken
):
Promise
<
Array
<
IAnythingQuickPickItem
>>
{
if
(
!
query
.
value
)
{
if
(
!
query
.
normalized
)
{
return
[];
}
...
...
@@ -692,7 +692,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
private
async
getWorkspaceSymbolPicks
(
query
:
IPreparedQuery
,
token
:
CancellationToken
):
Promise
<
Array
<
IAnythingQuickPickItem
>>
{
const
configuration
=
this
.
configuration
;
if
(
!
query
.
value
||
// we need a value for search for
!
query
.
normalized
||
// we need a value for search for
!
configuration
.
includeSymbols
||
// we need to enable symbols in search
this
.
pickState
.
lastRange
// a range is an indicator for just searching for files
)
{
...
...
src/vs/workbench/services/search/node/fileSearch.ts
浏览文件 @
a19857db
...
...
@@ -77,7 +77,7 @@ export class FileWalker {
this
.
errors
=
[];
if
(
this
.
filePattern
)
{
this
.
normalizedFilePatternLowercase
=
prepareQuery
(
this
.
filePattern
).
value
Lowercase
;
this
.
normalizedFilePatternLowercase
=
prepareQuery
(
this
.
filePattern
).
normalized
Lowercase
;
}
this
.
globalExcludePattern
=
config
.
excludePattern
&&
glob
.
parse
(
config
.
excludePattern
);
...
...
src/vs/workbench/services/search/node/rawSearchService.ts
浏览文件 @
a19857db
...
...
@@ -312,7 +312,7 @@ export class SearchService implements IRawSearchService {
// Pattern match on results
const
results
:
IRawFileMatch
[]
=
[];
const
normalizedSearchValueLowercase
=
prepareQuery
(
searchValue
).
value
Lowercase
;
const
normalizedSearchValueLowercase
=
prepareQuery
(
searchValue
).
normalized
Lowercase
;
for
(
const
entry
of
cachedEntries
)
{
// Check if this entry is a match for the search value
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录