Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
徽霖
Vscode
提交
0f776fe4
V
Vscode
项目概览
徽霖
/
Vscode
通知
9
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,发现更多精彩内容 >>
提交
0f776fe4
编写于
8月 04, 2016
作者:
C
Christof Marti
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
#55 Sort and limit file results in search process
上级
31f8362b
变更
16
显示空白变更内容
内联
并排
Showing
16 changed file
with
700 addition
and
202 deletion
+700
-202
src/vs/base/common/comparers.ts
src/vs/base/common/comparers.ts
+62
-1
src/vs/base/parts/quickopen/browser/quickOpenModel.ts
src/vs/base/parts/quickopen/browser/quickOpenModel.ts
+14
-54
src/vs/platform/search/common/search.ts
src/vs/platform/search/common/search.ts
+17
-1
src/vs/workbench/parts/search/browser/openAnythingHandler.ts
src/vs/workbench/parts/search/browser/openAnythingHandler.ts
+80
-70
src/vs/workbench/parts/search/browser/openFileHandler.ts
src/vs/workbench/parts/search/browser/openFileHandler.ts
+13
-4
src/vs/workbench/parts/search/common/searchQuery.ts
src/vs/workbench/parts/search/common/searchQuery.ts
+2
-0
src/vs/workbench/parts/search/test/common/searchModel.test.ts
...vs/workbench/parts/search/test/common/searchModel.test.ts
+4
-2
src/vs/workbench/services/search/node/fileSearch.ts
src/vs/workbench/services/search/node/fileSearch.ts
+27
-13
src/vs/workbench/services/search/node/rawSearchService.ts
src/vs/workbench/services/search/node/rawSearchService.ts
+233
-8
src/vs/workbench/services/search/node/search.ts
src/vs/workbench/services/search/node/search.ts
+12
-3
src/vs/workbench/services/search/node/searchIpc.ts
src/vs/workbench/services/search/node/searchIpc.ts
+6
-0
src/vs/workbench/services/search/node/searchService.ts
src/vs/workbench/services/search/node/searchService.ts
+12
-2
src/vs/workbench/services/search/node/textSearch.ts
src/vs/workbench/services/search/node/textSearch.ts
+5
-5
src/vs/workbench/services/search/test/node/search.test.ts
src/vs/workbench/services/search/test/node/search.test.ts
+13
-12
src/vs/workbench/services/search/test/node/searchService.test.ts
...workbench/services/search/test/node/searchService.test.ts
+177
-22
src/vs/workbench/test/browser/parts/quickOpen/quickopen.perf.test.ts
...bench/test/browser/parts/quickOpen/quickopen.perf.test.ts
+23
-5
未找到文件。
src/vs/base/common/comparers.ts
浏览文件 @
0f776fe4
...
...
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
scorer
=
require
(
'
vs/base/common/scorer
'
);
import
strings
=
require
(
'
vs/base/common/strings
'
);
const
FileNameMatch
=
/^
(
.*
)\.([^
.
]
*
)
|
([^
.
]
+
)
$/
;
...
...
@@ -76,3 +77,63 @@ export function compareByPrefix(one: string, other: string, lookFor: string): nu
return
0
;
}
export
interface
IScorableResourceAccessor
<
T
>
{
getLabel
(
T
):
string
;
getResourcePath
(
T
):
string
;
}
export
function
compareByScore
<
T
>
(
elementA
:
T
,
elementB
:
T
,
accessor
:
IScorableResourceAccessor
<
T
>
,
lookFor
:
string
,
lookForNormalizedLower
:
string
,
scorerCache
?:
{
[
key
:
string
]:
number
}):
number
{
const
labelA
=
accessor
.
getLabel
(
elementA
);
const
labelB
=
accessor
.
getLabel
(
elementB
);
// treat prefix matches highest in any case
const
prefixCompare
=
compareByPrefix
(
labelA
,
labelB
,
lookFor
);
if
(
prefixCompare
)
{
return
prefixCompare
;
}
// Give higher importance to label score
const
labelAScore
=
scorer
.
score
(
labelA
,
lookFor
,
scorerCache
);
const
labelBScore
=
scorer
.
score
(
labelB
,
lookFor
,
scorerCache
);
// Useful for understanding the scoring
// elementA.setPrefix(labelAScore + ' ');
// elementB.setPrefix(labelBScore + ' ');
if
(
labelAScore
!==
labelBScore
)
{
return
labelAScore
>
labelBScore
?
-
1
:
1
;
}
// Score on full resource path comes next (if available)
let
resourcePathA
=
accessor
.
getResourcePath
(
elementA
);
let
resourcePathB
=
accessor
.
getResourcePath
(
elementB
);
if
(
resourcePathA
&&
resourcePathB
)
{
const
resourceAScore
=
scorer
.
score
(
resourcePathA
,
lookFor
,
scorerCache
);
const
resourceBScore
=
scorer
.
score
(
resourcePathB
,
lookFor
,
scorerCache
);
// Useful for understanding the scoring
// elementA.setPrefix(elementA.getPrefix() + ' ' + resourceAScore + ': ');
// elementB.setPrefix(elementB.getPrefix() + ' ' + resourceBScore + ': ');
if
(
resourceAScore
!==
resourceBScore
)
{
return
resourceAScore
>
resourceBScore
?
-
1
:
1
;
}
}
// At this place, the scores are identical so we check for string lengths and favor shorter ones
if
(
labelA
.
length
!==
labelB
.
length
)
{
return
labelA
.
length
<
labelB
.
length
?
-
1
:
1
;
}
if
(
resourcePathA
&&
resourcePathB
&&
resourcePathA
.
length
!==
resourcePathB
.
length
)
{
return
resourcePathA
.
length
<
resourcePathB
.
length
?
-
1
:
1
;
}
// Finally compare by label or resource path
if
(
labelA
===
labelB
&&
resourcePathA
&&
resourcePathB
)
{
return
compareAnything
(
resourcePathA
,
resourcePathB
,
lookForNormalizedLower
);
}
return
compareAnything
(
labelA
,
labelB
,
lookForNormalizedLower
);
}
src/vs/base/parts/quickopen/browser/quickOpenModel.ts
浏览文件 @
0f776fe4
...
...
@@ -16,12 +16,11 @@ import paths = require('vs/base/common/paths');
import
{
IQuickNavigateConfiguration
,
IModel
,
IDataSource
,
IFilter
,
IAccessiblityProvider
,
IRenderer
,
IRunner
,
Mode
}
from
'
vs/base/parts/quickopen/common/quickOpen
'
;
import
{
IActionProvider
}
from
'
vs/base/parts/tree/browser/actionsRenderer
'
;
import
{
Action
,
IAction
,
IActionRunner
}
from
'
vs/base/common/actions
'
;
import
{
compareAnything
,
compareBy
Prefix
}
from
'
vs/base/common/comparers
'
;
import
{
compareAnything
,
compareBy
Score
as
doCompareByScore
}
from
'
vs/base/common/comparers
'
;
import
{
ActionBar
,
IActionItem
}
from
'
vs/base/browser/ui/actionbar/actionbar
'
;
import
{
LegacyRenderer
,
ILegacyTemplateData
}
from
'
vs/base/parts/tree/browser/treeDefaults
'
;
import
{
HighlightedLabel
}
from
'
vs/base/browser/ui/highlightedlabel/highlightedLabel
'
;
import
DOM
=
require
(
'
vs/base/browser/dom
'
);
import
scorer
=
require
(
'
vs/base/common/scorer
'
);
export
interface
IContext
{
event
:
any
;
...
...
@@ -35,6 +34,18 @@ export interface IHighlight {
let
IDS
=
0
;
class
EntryAccessor
{
public
static
getLabel
(
entry
:
QuickOpenEntry
)
{
return
entry
.
getLabel
();
}
public
static
getResourcePath
(
entry
:
QuickOpenEntry
)
{
const
resource
=
entry
.
getResource
();
return
resource
&&
resource
.
fsPath
;
}
}
export
class
QuickOpenEntry
{
private
id
:
string
;
private
labelHighlights
:
IHighlight
[];
...
...
@@ -183,58 +194,7 @@ export class QuickOpenEntry {
}
public
static
compareByScore
(
elementA
:
QuickOpenEntry
,
elementB
:
QuickOpenEntry
,
lookFor
:
string
,
lookForNormalizedLower
:
string
,
scorerCache
?:
{
[
key
:
string
]:
number
}):
number
{
const
labelA
=
elementA
.
getLabel
();
const
labelB
=
elementB
.
getLabel
();
// treat prefix matches highest in any case
const
prefixCompare
=
compareByPrefix
(
labelA
,
labelB
,
lookFor
);
if
(
prefixCompare
)
{
return
prefixCompare
;
}
// Give higher importance to label score
const
labelAScore
=
scorer
.
score
(
labelA
,
lookFor
,
scorerCache
);
const
labelBScore
=
scorer
.
score
(
labelB
,
lookFor
,
scorerCache
);
// Useful for understanding the scoring
// elementA.setPrefix(labelAScore + ' ');
// elementB.setPrefix(labelBScore + ' ');
if
(
labelAScore
!==
labelBScore
)
{
return
labelAScore
>
labelBScore
?
-
1
:
1
;
}
// Score on full resource path comes next (if available)
let
resourceA
=
elementA
.
getResource
();
let
resourceB
=
elementB
.
getResource
();
if
(
resourceA
&&
resourceB
)
{
const
resourceAScore
=
scorer
.
score
(
resourceA
.
fsPath
,
lookFor
,
scorerCache
);
const
resourceBScore
=
scorer
.
score
(
resourceB
.
fsPath
,
lookFor
,
scorerCache
);
// Useful for understanding the scoring
// elementA.setPrefix(elementA.getPrefix() + ' ' + resourceAScore + ': ');
// elementB.setPrefix(elementB.getPrefix() + ' ' + resourceBScore + ': ');
if
(
resourceAScore
!==
resourceBScore
)
{
return
resourceAScore
>
resourceBScore
?
-
1
:
1
;
}
}
// At this place, the scores are identical so we check for string lengths and favor shorter ones
if
(
labelA
.
length
!==
labelB
.
length
)
{
return
labelA
.
length
<
labelB
.
length
?
-
1
:
1
;
}
if
(
resourceA
&&
resourceB
&&
resourceA
.
fsPath
.
length
!==
resourceB
.
fsPath
.
length
)
{
return
resourceA
.
fsPath
.
length
<
resourceB
.
fsPath
.
length
?
-
1
:
1
;
}
// Finally compare by label or resource path
if
(
labelA
===
labelB
&&
resourceA
&&
resourceB
)
{
return
compareAnything
(
resourceA
.
fsPath
,
resourceB
.
fsPath
,
lookForNormalizedLower
);
}
return
compareAnything
(
labelA
,
labelB
,
lookForNormalizedLower
);
return
doCompareByScore
(
elementA
,
elementB
,
EntryAccessor
,
lookFor
,
lookForNormalizedLower
,
scorerCache
);
}
/**
...
...
src/vs/platform/search/common/search.ts
浏览文件 @
0f776fe4
...
...
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
{
PPromise
}
from
'
vs/base/common/winjs.base
'
;
import
{
PPromise
,
TPromise
}
from
'
vs/base/common/winjs.base
'
;
import
uri
from
'
vs/base/common/uri
'
;
import
glob
=
require
(
'
vs/base/common/glob
'
);
import
{
IFilesConfiguration
}
from
'
vs/platform/files/common/files
'
;
...
...
@@ -19,6 +19,7 @@ export const ISearchService = createDecorator<ISearchService>(ID);
export
interface
ISearchService
{
_serviceBrand
:
any
;
search
(
query
:
ISearchQuery
):
PPromise
<
ISearchComplete
,
ISearchProgressItem
>
;
clearCache
(
cacheKey
:
string
):
TPromise
<
void
>
;
}
export
interface
IQueryOptions
{
...
...
@@ -28,6 +29,8 @@ export interface IQueryOptions {
excludePattern
?:
glob
.
IExpression
;
includePattern
?:
glob
.
IExpression
;
maxResults
?:
number
;
sortByScore
?:
boolean
;
cacheKey
?:
string
;
fileEncoding
?:
string
;
}
...
...
@@ -75,6 +78,19 @@ export interface ISearchComplete {
}
export
interface
ISearchStats
{
fromCache
:
boolean
;
resultCount
:
number
;
unsortedResultTime
?:
number
;
sortedResultTime
?:
number
;
}
export
interface
ICachedSearchStats
extends
ISearchStats
{
cacheLookupStartTime
:
number
;
cacheLookupResultTime
:
number
;
cacheEntryCount
:
number
;
}
export
interface
IUncachedSearchStats
extends
ISearchStats
{
fileWalkStartTime
:
number
;
fileWalkResultTime
:
number
;
directoriesWalked
:
number
;
...
...
src/vs/workbench/parts/search/browser/openAnythingHandler.ts
浏览文件 @
0f776fe4
...
...
@@ -16,6 +16,7 @@ import scorer = require('vs/base/common/scorer');
import
paths
=
require
(
'
vs/base/common/paths
'
);
import
labels
=
require
(
'
vs/base/common/labels
'
);
import
strings
=
require
(
'
vs/base/common/strings
'
);
import
uuid
=
require
(
'
vs/base/common/uuid
'
);
import
{
IRange
}
from
'
vs/editor/common/editorCommon
'
;
import
{
IAutoFocus
}
from
'
vs/base/parts/quickopen/common/quickOpen
'
;
import
{
QuickOpenEntry
,
QuickOpenModel
}
from
'
vs/base/parts/quickopen/browser/quickOpenModel
'
;
...
...
@@ -26,35 +27,52 @@ import * as openSymbolHandler from 'vs/workbench/parts/search/browser/openSymbol
/* tslint:enable:no-unused-variable */
import
{
IMessageService
,
Severity
}
from
'
vs/platform/message/common/message
'
;
import
{
IInstantiationService
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
ISearchStats
}
from
'
vs/platform/search/common/search
'
;
import
{
ISearchStats
,
ICachedSearchStats
,
IUncachedSearchStats
}
from
'
vs/platform/search/common/search
'
;
import
{
ITelemetryService
}
from
'
vs/platform/telemetry/common/telemetry
'
;
import
{
IWorkspaceContextService
}
from
'
vs/workbench/services/workspace/common/contextService
'
;
import
{
IConfigurationService
}
from
'
vs/platform/configuration/common/configuration
'
;
const
objects_assign
:
<
T
,
U
>
(
destination
:
T
,
source
:
U
)
=>
T
&
U
=
objects
.
assign
;
interface
ISearchWithRange
{
search
:
string
;
range
:
IRange
;
}
interface
ITimerEventData
{
fromCache
:
boolean
;
searchLength
:
number
;
unsortedResultDuration
:
number
;
sortedResultDuration
:
number
;
numberOfResultEntries
:
number
;
fileWalkStartDuration
?:
number
;
fileWalkResultDuration
?:
number
;
directoriesWalked
?:
number
;
filesWalked
?:
number
;
resultCount
:
number
;
symbols
:
{
fromCache
:
boolean
;
};
files
:
{
fromCache
:
boolean
;
unsortedResultDuration
:
number
;
sortedResultDuration
:
number
;
resultCount
:
number
;
}
&
({
fileWalkStartDuration
:
number
;
fileWalkResultDuration
:
number
;
directoriesWalked
:
number
;
filesWalked
:
number
;
}
|
{
cacheLookupStartDuration
:
number
;
cacheLookupResultDuration
:
number
;
cacheEntryCount
:
number
;
});
}
interface
ITelemetryData
{
fromCache
:
boolean
;
searchLength
:
number
;
searchStats
?:
ISearchStats
;
unsortedResultTime
:
number
;
sortedResultTime
:
number
;
numberOfResultEntries
:
number
;
resultCount
:
number
;
symbols
:
{
fromCache
:
boolean
;
};
files
:
ISearchStats
;
}
// OpenSymbolHandler is used from an extension and must be in the main bundle file so it can load
...
...
@@ -71,11 +89,12 @@ export class OpenAnythingHandler extends QuickOpenHandler {
private
openSymbolHandler
:
OpenSymbolHandler
;
private
openFileHandler
:
OpenFileHandler
;
private
r
esultsToSearchCache
:
{
[
searchValue
:
string
]:
QuickOpenEntry
[];
};
private
symbolR
esultsToSearchCache
:
{
[
searchValue
:
string
]:
QuickOpenEntry
[];
};
private
delayer
:
ThrottledDelayer
<
QuickOpenModel
>
;
private
pendingSearch
:
TPromise
<
QuickOpenModel
>
;
private
isClosed
:
boolean
;
private
scorerCache
:
{
[
key
:
string
]:
number
};
private
cacheKey
:
string
;
constructor
(
@
IMessageService
private
messageService
:
IMessageService
,
...
...
@@ -96,8 +115,9 @@ export class OpenAnythingHandler extends QuickOpenHandler {
skipSorting
:
true
// we sort combined with file results
});
this
.
r
esultsToSearchCache
=
Object
.
create
(
null
);
this
.
symbolR
esultsToSearchCache
=
Object
.
create
(
null
);
this
.
scorerCache
=
Object
.
create
(
null
);
this
.
cacheKey
=
uuid
.
generateUuid
();
this
.
delayer
=
new
ThrottledDelayer
<
QuickOpenModel
>
(
OpenAnythingHandler
.
SEARCH_DELAY
);
}
...
...
@@ -129,13 +149,7 @@ export class OpenAnythingHandler extends QuickOpenHandler {
}
// Check Cache first
let
cachedResults
=
this
.
getResultsFromCache
(
searchValue
,
searchWithRange
?
searchWithRange
.
range
:
null
);
if
(
cachedResults
)
{
const
[
viewResults
,
telemetry
]
=
cachedResults
;
timerEvent
.
data
=
this
.
createTimerEventData
(
startTime
,
telemetry
);
timerEvent
.
stop
();
return
TPromise
.
as
(
new
QuickOpenModel
(
viewResults
));
}
let
cachedSymbolResults
=
this
.
getSymbolResultsFromCache
(
searchValue
,
!!
searchWithRange
);
// The throttler needs a factory for its promises
let
promiseFactory
=
()
=>
{
...
...
@@ -144,7 +158,9 @@ export class OpenAnythingHandler extends QuickOpenHandler {
// Symbol Results (unless a range is specified)
let
resultPromises
:
TPromise
<
QuickOpenModel
>
[]
=
[];
if
(
!
searchWithRange
)
{
if
(
cachedSymbolResults
)
{
resultPromises
.
push
(
TPromise
.
as
(
new
QuickOpenModel
(
cachedSymbolResults
)));
}
else
if
(
!
searchWithRange
)
{
let
symbolSearchTimeoutPromiseFn
:
(
timeout
:
number
)
=>
TPromise
<
QuickOpenModel
>
=
(
timeout
)
=>
{
return
TPromise
.
timeout
(
timeout
).
then
(()
=>
{
...
...
@@ -172,7 +188,7 @@ export class OpenAnythingHandler extends QuickOpenHandler {
}
// File Results
resultPromises
.
push
(
this
.
openFileHandler
.
getResultsWithStats
(
searchValue
).
then
(([
results
,
stats
])
=>
{
resultPromises
.
push
(
this
.
openFileHandler
.
getResultsWithStats
(
searchValue
,
this
.
cacheKey
,
OpenAnythingHandler
.
MAX_DISPLAYED_RESULTS
).
then
(([
results
,
stats
])
=>
{
receivedFileResults
=
true
;
searchStats
=
stats
;
...
...
@@ -190,10 +206,11 @@ export class OpenAnythingHandler extends QuickOpenHandler {
}
// Combine symbol results and file results
let
result
=
[...
results
[
0
].
entries
,
...
results
[
1
].
entries
];
const
symbolResults
=
results
[
0
].
entries
;
let
result
=
[...
symbolResults
,
...
results
[
1
].
entries
];
// Cache for fast lookup
this
.
resultsToSearchCache
[
searchValue
]
=
result
;
//
//
Cache for fast lookup
this
.
symbolResultsToSearchCache
[
searchValue
]
=
symbolResults
;
// Sort
const
normalizedSearchValue
=
strings
.
stripWildcards
(
searchValue
).
toLowerCase
();
...
...
@@ -211,12 +228,14 @@ export class OpenAnythingHandler extends QuickOpenHandler {
});
timerEvent
.
data
=
this
.
createTimerEventData
(
startTime
,
{
fromCache
:
false
,
searchLength
:
searchValue
.
length
,
searchStats
:
searchStats
,
unsortedResultTime
,
sortedResultTime
,
numberOfResultEntries
:
result
.
length
resultCount
:
result
.
length
,
symbols
:
{
fromCache
:
!!
cachedSymbolResults
},
files
:
searchStats
});
timerEvent
.
stop
();
return
TPromise
.
as
<
QuickOpenModel
>
(
new
QuickOpenModel
(
viewResults
));
...
...
@@ -228,8 +247,8 @@ export class OpenAnythingHandler extends QuickOpenHandler {
return
this
.
pendingSearch
;
};
// Trigger through delayer to prevent accumulation while the user is typing
return
this
.
delayer
.
trigger
(
promiseFactory
);
// Trigger through delayer to prevent accumulation while the user is typing
(except when expecting results to come from cache)
return
cachedSymbolResults
?
promiseFactory
()
:
this
.
delayer
.
trigger
(
promiseFactory
);
}
private
extractRange
(
value
:
string
):
ISearchWithRange
{
...
...
@@ -280,14 +299,14 @@ export class OpenAnythingHandler extends QuickOpenHandler {
return
null
;
}
private
get
ResultsFromCache
(
searchValue
:
string
,
range
:
IRange
=
null
):
[
QuickOpenEntry
[],
ITelemetryData
]
{
private
get
SymbolResultsFromCache
(
searchValue
:
string
,
hasRange
:
boolean
):
QuickOpenEntry
[
]
{
if
(
paths
.
isAbsolute
(
searchValue
))
{
return
null
;
// bypass cache if user looks up an absolute path where matching goes directly on disk
}
// Find cache entries by prefix of search value
let
cachedEntries
:
QuickOpenEntry
[];
for
(
let
previousSearch
in
this
.
r
esultsToSearchCache
)
{
for
(
let
previousSearch
in
this
.
symbolR
esultsToSearchCache
)
{
// If we narrow down, we might be able to reuse the cached results
if
(
searchValue
.
indexOf
(
previousSearch
)
===
0
)
{
...
...
@@ -295,7 +314,7 @@ export class OpenAnythingHandler extends QuickOpenHandler {
continue
;
// since a path character widens the search for potential more matches, require it in previous search too
}
cachedEntries
=
this
.
r
esultsToSearchCache
[
previousSearch
];
cachedEntries
=
this
.
symbolR
esultsToSearchCache
[
previousSearch
];
break
;
}
}
...
...
@@ -304,17 +323,16 @@ export class OpenAnythingHandler extends QuickOpenHandler {
return
null
;
}
if
(
hasRange
)
{
return
[];
}
// Pattern match on results and adjust highlights
let
results
:
QuickOpenEntry
[]
=
[];
const
normalizedSearchValueLowercase
=
strings
.
stripWildcards
(
searchValue
).
toLowerCase
();
for
(
let
i
=
0
;
i
<
cachedEntries
.
length
;
i
++
)
{
let
entry
=
cachedEntries
[
i
];
// Check for file entries if range is used
if
(
range
&&
!
(
entry
instanceof
FileEntry
))
{
continue
;
}
// Check if this entry is a match for the search value
const
resource
=
entry
.
getResource
();
// can be null for symbol results!
let
targetToMatch
=
resource
?
labels
.
getPathLabel
(
resource
,
this
.
contextService
)
:
entry
.
getLabel
();
...
...
@@ -324,29 +342,8 @@ export class OpenAnythingHandler extends QuickOpenHandler {
results
.
push
(
entry
);
}
const
unsortedResultTime
=
Date
.
now
();
// Sort
const
compare
=
(
elementA
,
elementB
)
=>
QuickOpenEntry
.
compareByScore
(
elementA
,
elementB
,
searchValue
,
normalizedSearchValueLowercase
,
this
.
scorerCache
);
const
viewResults
=
arrays
.
top
(
results
,
compare
,
OpenAnythingHandler
.
MAX_DISPLAYED_RESULTS
);
const
sortedResultTime
=
Date
.
now
();
// Apply range and highlights
viewResults
.
forEach
(
entry
=>
{
if
(
entry
instanceof
FileEntry
)
{
entry
.
setRange
(
range
);
}
const
{
labelHighlights
,
descriptionHighlights
}
=
QuickOpenEntry
.
highlight
(
entry
,
searchValue
,
true
/* fuzzy highlight */
);
entry
.
setHighlights
(
labelHighlights
,
descriptionHighlights
);
});
return
[
viewResults
,
{
fromCache
:
true
,
searchLength
:
searchValue
.
length
,
unsortedResultTime
,
sortedResultTime
,
numberOfResultEntries
:
results
.
length
}];
return
results
;
}
public
getGroupLabel
():
string
{
...
...
@@ -366,8 +363,10 @@ export class OpenAnythingHandler extends QuickOpenHandler {
this
.
cancelPendingSearch
();
// Clear Cache
this
.
r
esultsToSearchCache
=
Object
.
create
(
null
);
this
.
symbolR
esultsToSearchCache
=
Object
.
create
(
null
);
this
.
scorerCache
=
Object
.
create
(
null
);
this
.
openFileHandler
.
clearCache
(
this
.
cacheKey
);
this
.
cacheKey
=
uuid
.
generateUuid
();
// Propagate
this
.
openSymbolHandler
.
onClose
(
canceled
);
...
...
@@ -382,19 +381,30 @@ export class OpenAnythingHandler extends QuickOpenHandler {
}
private
createTimerEventData
(
startTime
:
number
,
telemetry
:
ITelemetryData
):
ITimerEventData
{
const
data
:
ITimerEventData
=
{
fromCache
:
telemetry
.
fromCache
,
const
stats
=
telemetry
.
files
;
const
cached
=
stats
as
ICachedSearchStats
;
const
uncached
=
stats
as
IUncachedSearchStats
;
return
{
searchLength
:
telemetry
.
searchLength
,
unsortedResultDuration
:
telemetry
.
unsortedResultTime
-
startTime
,
sortedResultDuration
:
telemetry
.
sortedResultTime
-
startTime
,
numberOfResultEntries
:
telemetry
.
numberOfResultEntries
resultCount
:
telemetry
.
resultCount
,
symbols
:
telemetry
.
symbols
,
files
:
objects_assign
({
fromCache
:
stats
.
fromCache
,
unsortedResultDuration
:
stats
.
unsortedResultTime
-
startTime
,
sortedResultDuration
:
stats
.
sortedResultTime
-
startTime
,
resultCount
:
stats
.
resultCount
},
stats
.
fromCache
?
{
cacheLookupStartDuration
:
cached
.
cacheLookupStartTime
-
startTime
,
cacheLookupResultDuration
:
cached
.
cacheLookupResultTime
-
startTime
,
cacheEntryCount
:
cached
.
cacheEntryCount
}
:
{
fileWalkStartDuration
:
uncached
.
fileWalkStartTime
-
startTime
,
fileWalkResultDuration
:
uncached
.
fileWalkResultTime
-
startTime
,
directoriesWalked
:
uncached
.
directoriesWalked
,
filesWalked
:
uncached
.
filesWalked
})
};
const
stats
=
telemetry
.
searchStats
;
return
stats
?
objects
.
assign
(
data
,
{
fileWalkStartDuration
:
stats
.
fileWalkStartTime
-
startTime
,
fileWalkResultDuration
:
stats
.
fileWalkResultTime
-
startTime
,
directoriesWalked
:
stats
.
directoriesWalked
,
filesWalked
:
stats
.
filesWalked
})
:
data
;
}
}
\ No newline at end of file
src/vs/workbench/parts/search/browser/openFileHandler.ts
浏览文件 @
0f776fe4
...
...
@@ -104,7 +104,7 @@ export class OpenFileHandler extends QuickOpenHandler {
.
then
(
result
=>
result
[
0
]);
}
public
getResultsWithStats
(
searchValue
:
string
):
TPromise
<
[
QuickOpenModel
,
ISearchStats
]
>
{
public
getResultsWithStats
(
searchValue
:
string
,
cacheKey
?:
string
,
maxSortedResults
?:
number
):
TPromise
<
[
QuickOpenModel
,
ISearchStats
]
>
{
searchValue
=
searchValue
.
trim
();
let
promise
:
TPromise
<
[
QuickOpenEntry
[],
ISearchStats
]
>
;
...
...
@@ -112,18 +112,23 @@ export class OpenFileHandler extends QuickOpenHandler {
if
(
!
searchValue
)
{
promise
=
TPromise
.
as
(
<
[
QuickOpenEntry
[],
ISearchStats
]
>
[[],
undefined
]);
}
else
{
promise
=
this
.
doFindResults
(
searchValue
);
promise
=
this
.
doFindResults
(
searchValue
,
cacheKey
,
maxSortedResults
);
}
return
promise
.
then
(
result
=>
[
new
QuickOpenModel
(
result
[
0
]),
result
[
1
]]);
}
private
doFindResults
(
searchValue
:
string
):
TPromise
<
[
QuickOpenEntry
[],
ISearchStats
]
>
{
private
doFindResults
(
searchValue
:
string
,
cacheKey
?:
string
,
maxSortedResults
?:
number
):
TPromise
<
[
QuickOpenEntry
[],
ISearchStats
]
>
{
const
query
:
IQueryOptions
=
{
folderResources
:
this
.
contextService
.
getWorkspace
()
?
[
this
.
contextService
.
getWorkspace
().
resource
]
:
[],
extraFileResources
:
getOutOfWorkspaceEditorResources
(
this
.
editorGroupService
,
this
.
contextService
),
filePattern
:
searchValue
filePattern
:
searchValue
,
cacheKey
:
cacheKey
};
if
(
maxSortedResults
)
{
query
.
maxResults
=
maxSortedResults
;
query
.
sortByScore
=
true
;
}
return
this
.
searchService
.
search
(
this
.
queryBuilder
.
file
(
query
)).
then
((
complete
)
=>
{
let
results
:
QuickOpenEntry
[]
=
[];
...
...
@@ -140,6 +145,10 @@ export class OpenFileHandler extends QuickOpenHandler {
});
}
public
clearCache
(
cacheKey
:
string
):
TPromise
<
void
>
{
return
this
.
searchService
.
clearCache
(
cacheKey
);
}
public
getGroupLabel
():
string
{
return
nls
.
localize
(
'
searchResults
'
,
"
search results
"
);
}
...
...
src/vs/workbench/parts/search/common/searchQuery.ts
浏览文件 @
0f776fe4
...
...
@@ -59,6 +59,8 @@ export class QueryBuilder {
excludePattern
:
options
.
excludePattern
,
includePattern
:
options
.
includePattern
,
maxResults
:
options
.
maxResults
,
sortByScore
:
options
.
sortByScore
,
cacheKey
:
options
.
cacheKey
,
fileEncoding
:
options
.
fileEncoding
,
contentPattern
:
contentPattern
};
...
...
src/vs/workbench/parts/search/test/common/searchModel.test.ts
浏览文件 @
0f776fe4
...
...
@@ -14,7 +14,7 @@ import { SearchModel } from 'vs/workbench/parts/search/common/searchModel';
import
URI
from
'
vs/base/common/uri
'
;
import
{
IFileMatch
,
ILineMatch
}
from
'
vs/platform/search/common/search
'
;
import
{
ITelemetryService
}
from
'
vs/platform/telemetry/common/telemetry
'
;
import
{
ISearchService
,
ISearchComplete
,
ISearchProgressItem
,
ISearchStats
}
from
'
vs/platform/search/common/search
'
;
import
{
ISearchService
,
ISearchComplete
,
ISearchProgressItem
,
I
Uncached
SearchStats
}
from
'
vs/platform/search/common/search
'
;
import
{
Range
}
from
'
vs/editor/common/core/range
'
;
import
{
createMockModelService
}
from
'
vs/test/utils/servicesTestUtils
'
;
import
{
IModelService
}
from
'
vs/editor/common/services/modelService
'
;
...
...
@@ -24,7 +24,9 @@ suite('SearchModel', () => {
let
instantiationService
:
TestInstantiationService
;
let
restoreStubs
;
const
testSearchStats
:
ISearchStats
=
{
const
testSearchStats
:
IUncachedSearchStats
=
{
fromCache
:
false
,
resultCount
:
4
,
fileWalkStartTime
:
0
,
fileWalkResultTime
:
1
,
directoriesWalked
:
2
,
...
...
src/vs/workbench/services/search/node/fileSearch.ts
浏览文件 @
0f776fe4
...
...
@@ -13,11 +13,11 @@ import arrays = require('vs/base/common/arrays');
import
strings
=
require
(
'
vs/base/common/strings
'
);
import
types
=
require
(
'
vs/base/common/types
'
);
import
glob
=
require
(
'
vs/base/common/glob
'
);
import
{
IProgress
,
ISearchStats
}
from
'
vs/platform/search/common/search
'
;
import
{
IProgress
,
I
Uncached
SearchStats
}
from
'
vs/platform/search/common/search
'
;
import
extfs
=
require
(
'
vs/base/node/extfs
'
);
import
flow
=
require
(
'
vs/base/node/flow
'
);
import
{
I
Serialized
FileMatch
,
ISerializedSearchComplete
,
IRawSearch
,
ISearchEngine
}
from
'
./search
'
;
import
{
I
Raw
FileMatch
,
ISerializedSearchComplete
,
IRawSearch
,
ISearchEngine
}
from
'
./search
'
;
export
class
FileWalker
{
private
config
:
IRawSearch
;
...
...
@@ -59,7 +59,7 @@ export class FileWalker {
this
.
isCanceled
=
true
;
}
public
walk
(
rootFolders
:
string
[],
extraFiles
:
string
[],
onResult
:
(
result
:
I
SerializedFileMatch
,
size
:
number
)
=>
void
,
done
:
(
error
:
Error
,
isLimitHit
:
boolean
)
=>
void
):
void
{
public
walk
(
rootFolders
:
string
[],
extraFiles
:
string
[],
onResult
:
(
result
:
I
RawFileMatch
)
=>
void
,
done
:
(
error
:
Error
,
isLimitHit
:
boolean
)
=>
void
):
void
{
this
.
fileWalkStartTime
=
Date
.
now
();
// Support that the file pattern is a full path to a file that exists
...
...
@@ -70,7 +70,12 @@ export class FileWalker {
// Report result from file pattern if matching
if
(
exists
)
{
onResult
({
path
:
this
.
filePattern
},
size
);
this
.
resultCount
++
;
onResult
({
absolutePath
:
this
.
filePattern
,
pathLabel
:
this
.
filePattern
,
size
});
// Optimization: a match on an absolute path is a good result and we do not
// continue walking the entire root paths array for other matches because
...
...
@@ -106,7 +111,12 @@ export class FileWalker {
// Report result from file pattern if matching
if
(
match
)
{
onResult
({
path
:
match
},
size
);
this
.
resultCount
++
;
onResult
({
absolutePath
:
match
,
pathLabel
:
this
.
filePattern
,
size
});
}
return
this
.
doWalk
(
paths
.
normalize
(
absolutePath
),
''
,
files
,
onResult
,
perEntryCallback
);
...
...
@@ -118,12 +128,14 @@ export class FileWalker {
});
}
public
getStats
():
ISearchStats
{
public
getStats
():
I
Uncached
SearchStats
{
return
{
fromCache
:
false
,
fileWalkStartTime
:
this
.
fileWalkStartTime
,
fileWalkResultTime
:
Date
.
now
(),
directoriesWalked
:
this
.
directoriesWalked
,
filesWalked
:
this
.
filesWalked
filesWalked
:
this
.
filesWalked
,
resultCount
:
this
.
resultCount
};
}
...
...
@@ -149,7 +161,7 @@ export class FileWalker {
});
}
private
doWalk
(
absolutePath
:
string
,
relativeParentPathWithSlashes
:
string
,
files
:
string
[],
onResult
:
(
result
:
I
SerializedFileMatch
,
size
:
number
)
=>
void
,
done
:
(
error
:
Error
,
result
:
any
)
=>
void
):
void
{
private
doWalk
(
absolutePath
:
string
,
relativeParentPathWithSlashes
:
string
,
files
:
string
[],
onResult
:
(
result
:
I
RawFileMatch
)
=>
void
,
done
:
(
error
:
Error
,
result
:
any
)
=>
void
):
void
{
// Execute tasks on each file in parallel to optimize throughput
flow
.
parallel
(
files
,
(
file
:
string
,
clb
:
(
error
:
Error
)
=>
void
):
void
=>
{
...
...
@@ -242,7 +254,7 @@ export class FileWalker {
});
}
private
matchFile
(
onResult
:
(
result
:
I
SerializedFileMatch
,
size
:
number
)
=>
void
,
absolutePath
:
string
,
relativePathWithSlashes
:
string
,
size
?:
number
):
void
{
private
matchFile
(
onResult
:
(
result
:
I
RawFileMatch
)
=>
void
,
absolutePath
:
string
,
relativePathWithSlashes
:
string
,
size
?:
number
):
void
{
if
(
this
.
isFilePatternMatch
(
relativePathWithSlashes
)
&&
(
!
this
.
includePattern
||
glob
.
match
(
this
.
includePattern
,
relativePathWithSlashes
)))
{
this
.
resultCount
++
;
...
...
@@ -252,8 +264,10 @@ export class FileWalker {
if
(
!
this
.
isLimitHit
)
{
onResult
({
path
:
absolutePath
},
size
);
absolutePath
,
pathLabel
:
relativePathWithSlashes
,
size
});
}
}
}
...
...
@@ -296,7 +310,7 @@ export class FileWalker {
}
}
export
class
Engine
implements
ISearchEngine
{
export
class
Engine
implements
ISearchEngine
<
IRawFileMatch
>
{
private
rootFolders
:
string
[];
private
extraFiles
:
string
[];
private
walker
:
FileWalker
;
...
...
@@ -308,7 +322,7 @@ export class Engine implements ISearchEngine {
this
.
walker
=
new
FileWalker
(
config
);
}
public
search
(
onResult
:
(
result
:
I
Serialized
FileMatch
)
=>
void
,
onProgress
:
(
progress
:
IProgress
)
=>
void
,
done
:
(
error
:
Error
,
complete
:
ISerializedSearchComplete
)
=>
void
):
void
{
public
search
(
onResult
:
(
result
:
I
Raw
FileMatch
)
=>
void
,
onProgress
:
(
progress
:
IProgress
)
=>
void
,
done
:
(
error
:
Error
,
complete
:
ISerializedSearchComplete
)
=>
void
):
void
{
this
.
walker
.
walk
(
this
.
rootFolders
,
this
.
extraFiles
,
onResult
,
(
err
:
Error
,
isLimitHit
:
boolean
)
=>
{
done
(
err
,
{
limitHit
:
isLimitHit
,
...
...
src/vs/workbench/services/search/node/rawSearchService.ts
浏览文件 @
0f776fe4
...
...
@@ -10,20 +10,29 @@ import fs = require('fs');
import
gracefulFs
=
require
(
'
graceful-fs
'
);
gracefulFs
.
gracefulify
(
fs
);
import
{
PPromise
}
from
'
vs/base/common/winjs.base
'
;
import
arrays
=
require
(
'
vs/base/common/arrays
'
);
import
{
compareByScore
}
from
'
vs/base/common/comparers
'
;
import
objects
=
require
(
'
vs/base/common/objects
'
);
import
paths
=
require
(
'
vs/base/common/paths
'
);
import
scorer
=
require
(
'
vs/base/common/scorer
'
);
import
strings
=
require
(
'
vs/base/common/strings
'
);
import
{
PPromise
,
TPromise
}
from
'
vs/base/common/winjs.base
'
;
import
{
MAX_FILE_SIZE
}
from
'
vs/platform/files/common/files
'
;
import
{
FileWalker
,
Engine
as
FileSearchEngine
}
from
'
vs/workbench/services/search/node/fileSearch
'
;
import
{
Engine
as
TextSearchEngine
}
from
'
vs/workbench/services/search/node/textSearch
'
;
import
{
IRawSearchService
,
IRawSearch
,
ISerializedSearchProgressItem
,
ISerializedSearchComplete
,
ISearchEngine
}
from
'
./search
'
;
import
{
IRawSearchService
,
IRawSearch
,
IRawFileMatch
,
ISerializedFileMatch
,
ISerializedSearchProgressItem
,
ISerializedSearchComplete
,
ISearchEngine
}
from
'
./search
'
;
import
{
ICachedSearchStats
,
IProgress
}
from
'
vs/platform/search/common/search
'
;
export
type
IRawProgressItem
<
T
>
=
T
|
T
[]
|
IProgress
;
export
class
SearchService
implements
IRawSearchService
{
private
static
BATCH_SIZE
=
5
00
;
private
static
BATCH_SIZE
=
5
12
;
public
fileSearch
(
config
:
IRawSearch
):
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
{
let
engine
=
new
FileSearchEngine
(
config
);
private
caches
:
{
[
cacheKey
:
string
]:
Cache
;
}
=
Object
.
create
(
null
);
return
this
.
doSearch
(
engine
,
SearchService
.
BATCH_SIZE
);
public
fileSearch
(
config
:
IRawSearch
):
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
{
return
this
.
doFileSearch
(
FileSearchEngine
,
config
,
SearchService
.
BATCH_SIZE
);
}
public
textSearch
(
config
:
IRawSearch
):
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
{
...
...
@@ -39,8 +48,194 @@ export class SearchService implements IRawSearchService {
return
this
.
doSearch
(
engine
,
SearchService
.
BATCH_SIZE
);
}
public
doSearch
(
engine
:
ISearchEngine
,
batchSize
?:
number
):
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
{
public
doFileSearch
(
EngineClass
:
{
new
(
config
:
IRawSearch
):
ISearchEngine
<
IRawFileMatch
>
;
},
config
:
IRawSearch
,
batchSize
?:
number
):
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
{
if
(
config
.
sortByScore
)
{
const
cached
=
this
.
trySearchFromCache
(
config
,
batchSize
);
if
(
cached
)
{
return
cached
;
}
const
walkerConfig
=
config
.
maxResults
?
objects
.
assign
({},
config
,
{
maxResults
:
null
})
:
config
;
const
engine
=
new
EngineClass
(
walkerConfig
);
return
this
.
doSortedSearch
(
engine
,
config
,
batchSize
);
}
let
searchPromise
;
return
new
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
((
c
,
e
,
p
)
=>
{
const
engine
=
new
EngineClass
(
config
);
searchPromise
=
this
.
doSearch
(
engine
,
batchSize
)
.
then
(
c
,
e
,
progress
=>
{
if
(
Array
.
isArray
(
progress
))
{
p
(
progress
.
map
(
m
=>
({
path
:
m
.
absolutePath
})));
}
else
if
((
<
IRawFileMatch
>
progress
).
absolutePath
)
{
p
({
path
:
(
<
IRawFileMatch
>
progress
).
absolutePath
});
}
else
{
p
(
progress
);
}
});
},
()
=>
searchPromise
.
cancel
());
}
private
doSortedSearch
(
engine
:
ISearchEngine
<
IRawFileMatch
>
,
config
:
IRawSearch
,
batchSize
?:
number
):
PPromise
<
ISerializedSearchComplete
,
IRawProgressItem
<
IRawFileMatch
>>
{
let
searchPromise
;
return
new
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
((
c
,
e
,
p
)
=>
{
let
results
:
IRawFileMatch
[]
=
[];
let
unsortedResultTime
:
number
;
let
sortedResultTime
:
number
;
searchPromise
=
this
.
doSearch
(
engine
,
-
1
).
then
(
result
=>
{
const
maxResults
=
config
.
maxResults
;
result
.
limitHit
=
!!
maxResults
&&
results
.
length
>
maxResults
;
result
.
stats
.
unsortedResultTime
=
unsortedResultTime
||
Date
.
now
();
result
.
stats
.
sortedResultTime
=
sortedResultTime
||
Date
.
now
();
c
(
result
);
},
null
,
progress
=>
{
try
{
if
(
Array
.
isArray
(
progress
))
{
results
=
progress
;
let
scorerCache
;
if
(
config
.
cacheKey
)
{
const
cache
=
this
.
getOrCreateCache
(
config
.
cacheKey
);
cache
.
resultsToSearchCache
[
config
.
filePattern
]
=
results
;
scorerCache
=
cache
.
scorerCache
;
}
else
{
scorerCache
=
Object
.
create
(
null
);
}
unsortedResultTime
=
Date
.
now
();
const
sortedResults
=
this
.
sortResults
(
config
,
results
,
scorerCache
);
sortedResultTime
=
Date
.
now
();
this
.
sendProgress
(
sortedResults
,
p
,
batchSize
);
}
else
{
p
(
progress
);
}
}
catch
(
err
)
{
e
(
err
);
}
}).
then
(
null
,
e
);
},
()
=>
searchPromise
.
cancel
());
}
private
getOrCreateCache
(
cacheKey
:
string
):
Cache
{
const
existing
=
this
.
caches
[
cacheKey
];
if
(
existing
)
{
return
existing
;
}
return
this
.
caches
[
cacheKey
]
=
new
Cache
();
}
private
trySearchFromCache
(
config
:
IRawSearch
,
batchSize
?:
number
):
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
{
const
cache
=
config
.
cacheKey
&&
this
.
caches
[
config
.
cacheKey
];
if
(
!
cache
)
{
return
;
}
const
cacheLookupStartTime
=
Date
.
now
();
const
cached
=
this
.
getResultsFromCache
(
cache
,
config
.
filePattern
);
if
(
cached
)
{
const
cacheLookupResultTime
=
Date
.
now
();
const
[
results
,
cacheEntryCount
]
=
cached
;
let
clippedResults
;
if
(
config
.
sortByScore
)
{
clippedResults
=
this
.
sortResults
(
config
,
results
,
cache
);
}
else
if
(
config
.
maxResults
)
{
clippedResults
=
results
.
slice
(
0
,
config
.
maxResults
);
}
else
{
clippedResults
=
results
;
}
const
sortedResultTime
=
Date
.
now
();
let
canceled
=
false
;
return
new
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
((
c
,
e
,
p
)
=>
{
process
.
nextTick
(()
=>
{
// allow caller to register progress callback first
if
(
canceled
)
{
return
;
}
this
.
sendProgress
(
clippedResults
,
p
,
batchSize
);
const
maxResults
=
config
.
maxResults
;
const
stats
:
ICachedSearchStats
=
{
fromCache
:
true
,
cacheLookupStartTime
:
cacheLookupStartTime
,
cacheLookupResultTime
:
cacheLookupResultTime
,
cacheEntryCount
:
cacheEntryCount
,
resultCount
:
results
.
length
};
if
(
config
.
sortByScore
)
{
stats
.
unsortedResultTime
=
cacheLookupResultTime
;
stats
.
sortedResultTime
=
sortedResultTime
;
}
c
({
limitHit
:
!!
maxResults
&&
results
.
length
>
maxResults
,
stats
:
stats
});
});
},
()
=>
{
canceled
=
true
;
});
}
}
private
sortResults
(
config
:
IRawSearch
,
results
:
IRawFileMatch
[],
cache
:
Cache
):
ISerializedFileMatch
[]
{
const
filePattern
=
config
.
filePattern
;
const
normalizedSearchValue
=
strings
.
stripWildcards
(
filePattern
).
toLowerCase
();
const
compare
=
(
elementA
:
IRawFileMatch
,
elementB
:
IRawFileMatch
)
=>
compareByScore
(
elementA
,
elementB
,
FileMatchAccessor
,
filePattern
,
normalizedSearchValue
,
cache
.
scorerCache
);
const
filteredWrappers
=
arrays
.
top
(
results
,
compare
,
config
.
maxResults
);
return
filteredWrappers
.
map
(
result
=>
({
path
:
result
.
absolutePath
}));
}
private
sendProgress
(
results
:
ISerializedFileMatch
[],
progressCb
:
(
batch
:
ISerializedFileMatch
[])
=>
void
,
batchSize
?:
number
)
{
if
(
batchSize
&&
batchSize
>
0
)
{
for
(
let
i
=
0
;
i
<
results
.
length
;
i
+=
batchSize
)
{
progressCb
(
results
.
slice
(
i
,
i
+
batchSize
));
}
}
else
{
progressCb
(
results
);
}
}
private
getResultsFromCache
(
cache
:
Cache
,
searchValue
:
string
):
[
IRawFileMatch
[],
number
]
{
if
(
paths
.
isAbsolute
(
searchValue
))
{
return
null
;
// bypass cache if user looks up an absolute path where matching goes directly on disk
}
// Find cache entries by prefix of search value
const
hasPathSep
=
searchValue
.
indexOf
(
paths
.
nativeSep
)
>=
0
;
let
cachedEntries
:
IRawFileMatch
[];
for
(
let
previousSearch
in
cache
.
resultsToSearchCache
)
{
// If we narrow down, we might be able to reuse the cached results
if
(
strings
.
startsWith
(
searchValue
,
previousSearch
))
{
if
(
hasPathSep
&&
previousSearch
.
indexOf
(
paths
.
nativeSep
)
<
0
)
{
continue
;
// since a path character widens the search for potential more matches, require it in previous search too
}
cachedEntries
=
cache
.
resultsToSearchCache
[
previousSearch
];
break
;
}
}
if
(
!
cachedEntries
)
{
return
null
;
}
// Pattern match on results and adjust highlights
let
results
:
IRawFileMatch
[]
=
[];
const
normalizedSearchValue
=
searchValue
.
replace
(
/
\\
/g
,
'
/
'
);
// Normalize file patterns to forward slashes
const
normalizedSearchValueLowercase
=
strings
.
stripWildcards
(
normalizedSearchValue
).
toLowerCase
();
for
(
let
i
=
0
;
i
<
cachedEntries
.
length
;
i
++
)
{
let
entry
=
cachedEntries
[
i
];
// Check if this entry is a match for the search value
if
(
!
scorer
.
matches
(
entry
.
pathLabel
,
normalizedSearchValueLowercase
))
{
continue
;
}
results
.
push
(
entry
);
}
return
[
results
,
cachedEntries
.
length
];
}
private
doSearch
<
T
>
(
engine
:
ISearchEngine
<
T
>
,
batchSize
?:
number
):
PPromise
<
ISerializedSearchComplete
,
IRawProgressItem
<
T
>>
{
return
new
PPromise
<
ISerializedSearchComplete
,
IRawProgressItem
<
T
>>
((
c
,
e
,
p
)
=>
{
let
batch
=
[];
engine
.
search
((
match
)
=>
{
if
(
match
)
{
...
...
@@ -68,4 +263,34 @@ export class SearchService implements IRawSearchService {
});
},
()
=>
engine
.
cancel
());
}
public
clearCache
(
cacheKey
:
string
):
TPromise
<
void
>
{
delete
this
.
caches
[
cacheKey
];
return
TPromise
.
as
(
undefined
);
}
}
class
Cache
{
public
resultsToSearchCache
:
{
[
searchValue
:
string
]:
IRawFileMatch
[];
}
=
Object
.
create
(
null
);
public
scorerCache
:
{
[
key
:
string
]:
number
}
=
Object
.
create
(
null
);
}
interface
IFileMatch
extends
IRawFileMatch
{
label
?:
string
;
}
class
FileMatchAccessor
{
public
static
getLabel
(
match
:
IFileMatch
):
string
{
if
(
!
match
.
label
)
{
match
.
label
=
paths
.
basename
(
match
.
absolutePath
);
}
return
match
.
label
;
}
public
static
getResourcePath
(
match
:
IFileMatch
):
string
{
return
match
.
absolutePath
;
}
}
src/vs/workbench/services/search/node/search.ts
浏览文件 @
0f776fe4
...
...
@@ -5,7 +5,7 @@
'
use strict
'
;
import
{
PPromise
}
from
'
vs/base/common/winjs.base
'
;
import
{
PPromise
,
TPromise
}
from
'
vs/base/common/winjs.base
'
;
import
glob
=
require
(
'
vs/base/common/glob
'
);
import
{
IProgress
,
ILineMatch
,
IPatternInfo
,
ISearchStats
}
from
'
vs/platform/search/common/search
'
;
...
...
@@ -17,6 +17,8 @@ export interface IRawSearch {
includePattern
?:
glob
.
IExpression
;
contentPattern
?:
IPatternInfo
;
maxResults
?:
number
;
sortByScore
?:
boolean
;
cacheKey
?:
string
;
maxFilesize
?:
number
;
fileEncoding
?:
string
;
}
...
...
@@ -24,10 +26,17 @@ export interface IRawSearch {
export
interface
IRawSearchService
{
fileSearch
(
search
:
IRawSearch
):
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
;
textSearch
(
search
:
IRawSearch
):
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
;
clearCache
(
cacheKey
:
string
):
TPromise
<
void
>
;
}
export
interface
ISearchEngine
{
search
:
(
onResult
:
(
match
:
ISerializedFileMatch
)
=>
void
,
onProgress
:
(
progress
:
IProgress
)
=>
void
,
done
:
(
error
:
Error
,
complete
:
ISerializedSearchComplete
)
=>
void
)
=>
void
;
export
interface
IRawFileMatch
{
absolutePath
:
string
;
pathLabel
:
string
;
size
:
number
;
}
export
interface
ISearchEngine
<
T
>
{
search
:
(
onResult
:
(
match
:
T
)
=>
void
,
onProgress
:
(
progress
:
IProgress
)
=>
void
,
done
:
(
error
:
Error
,
complete
:
ISerializedSearchComplete
)
=>
void
)
=>
void
;
cancel
:
()
=>
void
;
}
...
...
src/vs/workbench/services/search/node/searchIpc.ts
浏览文件 @
0f776fe4
...
...
@@ -12,6 +12,7 @@ import { IRawSearchService, IRawSearch, ISerializedSearchComplete, ISerializedSe
export
interface
ISearchChannel
extends
IChannel
{
call
(
command
:
'
fileSearch
'
,
search
:
IRawSearch
):
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
;
call
(
command
:
'
textSearch
'
,
search
:
IRawSearch
):
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
;
call
(
command
:
'
clearCache
'
,
cacheKey
:
string
):
TPromise
<
void
>
;
call
(
command
:
string
,
arg
:
any
):
TPromise
<
any
>
;
}
...
...
@@ -23,6 +24,7 @@ export class SearchChannel implements ISearchChannel {
switch
(
command
)
{
case
'
fileSearch
'
:
return
this
.
service
.
fileSearch
(
arg
);
case
'
textSearch
'
:
return
this
.
service
.
textSearch
(
arg
);
case
'
clearCache
'
:
return
this
.
service
.
clearCache
(
arg
);
}
}
}
...
...
@@ -38,4 +40,8 @@ export class SearchChannelClient implements IRawSearchService {
textSearch
(
search
:
IRawSearch
):
PPromise
<
ISerializedSearchComplete
,
ISerializedSearchProgressItem
>
{
return
this
.
channel
.
call
(
'
textSearch
'
,
search
);
}
public
clearCache
(
cacheKey
:
string
):
TPromise
<
void
>
{
return
this
.
channel
.
call
(
'
clearCache
'
,
cacheKey
);
}
}
\ No newline at end of file
src/vs/workbench/services/search/node/searchService.ts
浏览文件 @
0f776fe4
...
...
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
{
PPromise
}
from
'
vs/base/common/winjs.base
'
;
import
{
PPromise
,
TPromise
}
from
'
vs/base/common/winjs.base
'
;
import
uri
from
'
vs/base/common/uri
'
;
import
glob
=
require
(
'
vs/base/common/glob
'
);
import
objects
=
require
(
'
vs/base/common/objects
'
);
...
...
@@ -189,6 +189,10 @@ export class SearchService implements ISearchService {
return
true
;
}
public
clearCache
(
cacheKey
:
string
):
TPromise
<
void
>
{
return
this
.
diskSearch
.
clearCache
(
cacheKey
);
}
}
export
class
DiskSearch
{
...
...
@@ -223,7 +227,9 @@ export class DiskSearch {
filePattern
:
query
.
filePattern
,
excludePattern
:
query
.
excludePattern
,
includePattern
:
query
.
includePattern
,
maxResults
:
query
.
maxResults
maxResults
:
query
.
maxResults
,
sortByScore
:
query
.
sortByScore
,
cacheKey
:
query
.
cacheKey
};
if
(
query
.
type
===
QueryType
.
Text
)
{
...
...
@@ -282,4 +288,8 @@ export class DiskSearch {
}
return
fileMatch
;
}
public
clearCache
(
cacheKey
:
string
):
TPromise
<
void
>
{
return
this
.
raw
.
clearCache
(
cacheKey
);
}
}
\ No newline at end of file
src/vs/workbench/services/search/node/textSearch.ts
浏览文件 @
0f776fe4
...
...
@@ -21,7 +21,7 @@ interface ReadLinesOptions {
encoding
:
string
;
}
export
class
Engine
implements
ISearchEngine
{
export
class
Engine
implements
ISearchEngine
<
ISerializedFileMatch
>
{
private
static
PROGRESS_FLUSH_CHUNK_SIZE
=
50
;
// optimization: number of files to process before emitting progress event
...
...
@@ -88,8 +88,8 @@ export class Engine implements ISearchEngine {
};
// Walk over the file system
this
.
walker
.
walk
(
this
.
rootFolders
,
this
.
extraFiles
,
(
result
,
size
)
=>
{
size
=
size
||
1
;
this
.
walker
.
walk
(
this
.
rootFolders
,
this
.
extraFiles
,
result
=>
{
const
size
=
result
.
size
||
1
;
this
.
total
+=
size
;
// If the result is empty or we have reached the limit or we are canceled, ignore it
...
...
@@ -126,7 +126,7 @@ export class Engine implements ISearchEngine {
}
if
(
fileMatch
===
null
)
{
fileMatch
=
new
FileMatch
(
result
.
p
ath
);
fileMatch
=
new
FileMatch
(
result
.
absoluteP
ath
);
}
if
(
lineMatch
===
null
)
{
...
...
@@ -141,7 +141,7 @@ export class Engine implements ISearchEngine {
};
// Read lines buffered to support large files
this
.
readlinesAsync
(
result
.
p
ath
,
perLineCallback
,
{
bufferLength
:
8096
,
encoding
:
this
.
fileEncoding
},
doneCallback
);
this
.
readlinesAsync
(
result
.
absoluteP
ath
,
perLineCallback
,
{
bufferLength
:
8096
,
encoding
:
this
.
fileEncoding
},
doneCallback
);
},
(
error
,
isLimitHit
)
=>
{
this
.
walkerIsDone
=
true
;
this
.
walkerError
=
error
;
...
...
src/vs/workbench/services/search/test/node/search.test.ts
浏览文件 @
0f776fe4
...
...
@@ -12,6 +12,7 @@ import {join, normalize} from 'vs/base/common/paths';
import
{
LineMatch
}
from
'
vs/platform/search/common/search
'
;
import
{
FileWalker
,
Engine
as
FileSearchEngine
}
from
'
vs/workbench/services/search/node/fileSearch
'
;
import
{
IRawFileMatch
}
from
'
vs/workbench/services/search/node/search
'
;
import
{
Engine
as
TextSearchEngine
}
from
'
vs/workbench/services/search/node/textSearch
'
;
function
count
(
lineMatches
:
LineMatch
[]):
number
{
...
...
@@ -149,7 +150,7 @@ suite('Search', () => {
});
let
count
=
0
;
let
res
;
let
res
:
IRawFileMatch
;
engine
.
search
((
result
)
=>
{
if
(
result
)
{
count
++
;
...
...
@@ -158,7 +159,7 @@ suite('Search', () => {
},
()
=>
{
},
(
error
)
=>
{
assert
.
ok
(
!
error
);
assert
.
equal
(
count
,
1
);
assert
.
ok
(
path
.
basename
(
res
.
path
)
===
'
site.less
'
);
assert
.
strictEqual
(
path
.
basename
(
res
.
absolutePath
),
'
site.less
'
);
done
();
});
});
...
...
@@ -246,7 +247,7 @@ suite('Search', () => {
});
let
count
=
0
;
let
res
;
let
res
:
IRawFileMatch
;
engine
.
search
((
result
)
=>
{
if
(
result
)
{
count
++
;
...
...
@@ -255,7 +256,7 @@ suite('Search', () => {
},
()
=>
{
},
(
error
)
=>
{
assert
.
ok
(
!
error
);
assert
.
equal
(
count
,
1
);
assert
.
equal
(
path
.
basename
(
res
.
p
ath
),
'
汉语.txt
'
);
assert
.
equal
(
path
.
basename
(
res
.
absoluteP
ath
),
'
汉语.txt
'
);
done
();
});
});
...
...
@@ -286,7 +287,7 @@ suite('Search', () => {
});
let
count
=
0
;
let
res
;
let
res
:
IRawFileMatch
;
engine
.
search
((
result
)
=>
{
if
(
result
)
{
count
++
;
...
...
@@ -295,7 +296,7 @@ suite('Search', () => {
},
()
=>
{
},
(
error
)
=>
{
assert
.
ok
(
!
error
);
assert
.
equal
(
count
,
1
);
assert
.
equal
(
path
.
basename
(
res
.
p
ath
),
'
site.css
'
);
assert
.
equal
(
path
.
basename
(
res
.
absoluteP
ath
),
'
site.css
'
);
done
();
});
});
...
...
@@ -308,7 +309,7 @@ suite('Search', () => {
});
let
count
=
0
;
let
res
;
let
res
:
IRawFileMatch
;
engine
.
search
((
result
)
=>
{
if
(
result
)
{
count
++
;
...
...
@@ -317,7 +318,7 @@ suite('Search', () => {
},
()
=>
{
},
(
error
)
=>
{
assert
.
ok
(
!
error
);
assert
.
equal
(
count
,
1
);
assert
.
equal
(
path
.
basename
(
res
.
p
ath
),
'
company.js
'
);
assert
.
equal
(
path
.
basename
(
res
.
absoluteP
ath
),
'
company.js
'
);
done
();
});
});
...
...
@@ -334,7 +335,7 @@ suite('Search', () => {
});
let
count
=
0
;
let
res
;
let
res
:
IRawFileMatch
;
engine
.
search
((
result
)
=>
{
if
(
result
)
{
count
++
;
...
...
@@ -343,7 +344,7 @@ suite('Search', () => {
},
()
=>
{
},
(
error
)
=>
{
assert
.
ok
(
!
error
);
assert
.
equal
(
count
,
1
);
assert
.
equal
(
path
.
basename
(
res
.
p
ath
),
'
company.js
'
);
assert
.
equal
(
path
.
basename
(
res
.
absoluteP
ath
),
'
company.js
'
);
done
();
});
});
...
...
@@ -361,7 +362,7 @@ suite('Search', () => {
});
let
count
=
0
;
let
res
;
let
res
:
IRawFileMatch
;
engine
.
search
((
result
)
=>
{
if
(
result
)
{
count
++
;
...
...
@@ -370,7 +371,7 @@ suite('Search', () => {
},
()
=>
{
},
(
error
)
=>
{
assert
.
ok
(
!
error
);
assert
.
equal
(
count
,
1
);
assert
.
equal
(
path
.
basename
(
res
.
p
ath
),
'
site.css
'
);
assert
.
equal
(
path
.
basename
(
res
.
absoluteP
ath
),
'
site.css
'
);
done
();
});
});
...
...
src/vs/workbench/services/search/test/node/searchService.test.ts
浏览文件 @
0f776fe4
...
...
@@ -7,31 +7,47 @@
import
assert
=
require
(
'
assert
'
);
import
{
IProgress
}
from
'
vs/platform/search/common/search
'
;
import
{
ISearchEngine
,
ISerializedFileMatch
,
ISerializedSearchComplete
}
from
'
vs/workbench/services/search/node/search
'
;
import
{
IProgress
,
IUncachedSearchStats
}
from
'
vs/platform/search/common/search
'
;
import
{
ISearchEngine
,
I
RawSearch
,
IRawFileMatch
,
I
SerializedFileMatch
,
ISerializedSearchComplete
}
from
'
vs/workbench/services/search/node/search
'
;
import
{
SearchService
as
RawSearchService
}
from
'
vs/workbench/services/search/node/rawSearchService
'
;
import
{
DiskSearch
}
from
'
vs/workbench/services/search/node/searchService
'
;
class
TestSearchEngine
implements
ISearchEngine
{
const
stats
:
IUncachedSearchStats
=
{
fromCache
:
false
,
resultCount
:
4
,
fileWalkStartTime
:
0
,
fileWalkResultTime
:
1
,
directoriesWalked
:
2
,
filesWalked
:
3
};
class
TestSearchEngine
implements
ISearchEngine
<
IRawFileMatch
>
{
constructor
(
private
result
:
()
=>
ISerializedFileMatch
)
{
public
static
last
:
TestSearchEngine
;
private
isCanceled
=
false
;
constructor
(
private
result
:
()
=>
IRawFileMatch
,
public
config
?:
IRawSearch
)
{
TestSearchEngine
.
last
=
this
;
}
public
search
(
onResult
:
(
match
:
I
Serialized
FileMatch
)
=>
void
,
onProgress
:
(
progress
:
IProgress
)
=>
void
,
done
:
(
error
:
Error
,
complete
:
ISerializedSearchComplete
)
=>
void
):
void
{
public
search
(
onResult
:
(
match
:
I
Raw
FileMatch
)
=>
void
,
onProgress
:
(
progress
:
IProgress
)
=>
void
,
done
:
(
error
:
Error
,
complete
:
ISerializedSearchComplete
)
=>
void
):
void
{
const
self
=
this
;
(
function
next
()
{
process
.
nextTick
(()
=>
{
if
(
self
.
isCanceled
)
{
done
(
null
,
{
limitHit
:
false
,
stats
:
stats
});
return
;
}
const
result
=
self
.
result
();
if
(
!
result
)
{
done
(
null
,
{
limitHit
:
false
,
stats
:
{
fileWalkStartTime
:
0
,
fileWalkResultTime
:
1
,
directoriesWalked
:
2
,
filesWalked
:
3
}
stats
:
stats
});
}
else
{
onResult
(
result
);
...
...
@@ -42,24 +58,39 @@ class TestSearchEngine implements ISearchEngine {
}
public
cancel
():
void
{
this
.
isCanceled
=
true
;
}
}
suite
(
'
SearchService
'
,
()
=>
{
const
rawSearch
:
IRawSearch
=
{
rootFolders
:
[
'
/some/where
'
],
filePattern
:
'
a
'
};
const
rawMatch
:
IRawFileMatch
=
{
absolutePath
:
'
/some/where
'
,
pathLabel
:
'
where
'
,
size
:
123
};
const
match
:
ISerializedFileMatch
=
{
path
:
'
/some/where
'
};
test
(
'
Individual results
'
,
function
()
{
const
path
=
'
/some/where
'
;
let
i
=
5
;
const
engine
=
new
TestSearchEngine
(()
=>
i
--
&&
{
path
}
);
const
Engine
=
TestSearchEngine
.
bind
(
null
,
()
=>
i
--
&&
rawMatch
);
const
service
=
new
RawSearchService
();
let
results
=
0
;
return
service
.
do
Search
(
engine
)
return
service
.
do
FileSearch
(
Engine
,
rawSearch
)
.
then
(()
=>
{
assert
.
strictEqual
(
results
,
5
);
},
null
,
value
=>
{
if
(
!
Array
.
isArray
(
value
))
{
assert
.
strictEqual
((
<
any
>
value
).
path
,
pat
h
);
assert
.
deepStrictEqual
(
value
,
matc
h
);
results
++
;
}
else
{
assert
.
fail
(
value
);
...
...
@@ -68,19 +99,18 @@ suite('SearchService', () => {
});
test
(
'
Batch results
'
,
function
()
{
const
path
=
'
/some/where
'
;
let
i
=
25
;
const
engine
=
new
TestSearchEngine
(()
=>
i
--
&&
{
path
}
);
const
Engine
=
TestSearchEngine
.
bind
(
null
,
()
=>
i
--
&&
rawMatch
);
const
service
=
new
RawSearchService
();
const
results
=
[];
return
service
.
do
Search
(
engine
,
10
)
return
service
.
do
FileSearch
(
Engine
,
rawSearch
,
10
)
.
then
(()
=>
{
assert
.
deepStrictEqual
(
results
,
[
10
,
10
,
5
]);
},
null
,
value
=>
{
if
(
Array
.
isArray
(
value
))
{
value
.
forEach
(
m
atch
=>
{
assert
.
strictEqual
(
match
.
path
,
pat
h
);
value
.
forEach
(
m
=>
{
assert
.
deepStrictEqual
(
m
,
matc
h
);
});
results
.
push
(
value
.
length
);
}
else
{
...
...
@@ -92,12 +122,12 @@ suite('SearchService', () => {
test
(
'
Collect batched results
'
,
function
()
{
const
path
=
'
/some/where
'
;
let
i
=
25
;
const
engine
=
new
TestSearchEngine
(()
=>
i
--
&&
{
path
}
);
const
Engine
=
TestSearchEngine
.
bind
(
null
,
()
=>
i
--
&&
rawMatch
);
const
service
=
new
RawSearchService
();
const
diskSearch
=
new
DiskSearch
(
false
);
const
progressResults
=
[];
return
DiskSearch
.
collectResults
(
service
.
do
Search
(
engine
,
10
))
return
DiskSearch
.
collectResults
(
service
.
do
FileSearch
(
Engine
,
rawSearch
,
10
))
.
then
(
result
=>
{
assert
.
strictEqual
(
result
.
results
.
length
,
25
,
'
Result
'
);
assert
.
strictEqual
(
progressResults
.
length
,
25
,
'
Progress
'
);
...
...
@@ -106,4 +136,129 @@ suite('SearchService', () => {
progressResults
.
push
(
match
);
});
});
test
(
'
Sorted results
'
,
function
()
{
const
paths
=
[
'
bab
'
,
'
bbc
'
,
'
abb
'
];
const
matches
=
paths
.
map
(
path
=>
({
absolutePath
:
`/some/where/
${
path
}
`
,
pathLabel
:
path
,
size
:
3
}));
let
i
=
0
;
const
Engine
=
TestSearchEngine
.
bind
(
null
,
()
=>
matches
.
shift
());
const
service
=
new
RawSearchService
();
const
results
=
[];
return
service
.
doFileSearch
(
Engine
,
{
rootFolders
:
[
'
/some/where
'
],
filePattern
:
'
bb
'
,
sortByScore
:
true
,
maxResults
:
2
},
1
).
then
(()
=>
{
assert
.
notStrictEqual
(
typeof
TestSearchEngine
.
last
.
config
.
maxResults
,
'
number
'
);
assert
.
deepStrictEqual
(
results
,
[
'
/some/where/bbc
'
,
'
/some/where/bab
'
]);
},
null
,
value
=>
{
if
(
Array
.
isArray
(
value
))
{
results
.
push
(...
value
.
map
(
v
=>
v
.
path
));
}
else
{
assert
.
fail
(
value
);
}
});
});
test
(
'
Sorted result batches
'
,
function
()
{
let
i
=
25
;
const
Engine
=
TestSearchEngine
.
bind
(
null
,
()
=>
i
--
&&
rawMatch
);
const
service
=
new
RawSearchService
();
const
results
=
[];
return
service
.
doFileSearch
(
Engine
,
{
rootFolders
:
[
'
/some/where
'
],
filePattern
:
'
a
'
,
sortByScore
:
true
,
maxResults
:
23
},
10
)
.
then
(()
=>
{
assert
.
deepStrictEqual
(
results
,
[
10
,
10
,
3
]);
},
null
,
value
=>
{
if
(
Array
.
isArray
(
value
))
{
value
.
forEach
(
m
=>
{
assert
.
deepStrictEqual
(
m
,
match
);
});
results
.
push
(
value
.
length
);
}
else
{
assert
.
fail
(
value
);
}
});
});
test
(
'
Cached results
'
,
function
()
{
const
paths
=
[
'
bcb
'
,
'
bbc
'
,
'
aab
'
];
const
matches
=
paths
.
map
(
path
=>
({
absolutePath
:
`/some/where/
${
path
}
`
,
pathLabel
:
path
,
size
:
3
}));
let
i
=
0
;
const
Engine
=
TestSearchEngine
.
bind
(
null
,
()
=>
matches
.
shift
());
const
service
=
new
RawSearchService
();
const
results
=
[];
return
service
.
doFileSearch
(
Engine
,
{
rootFolders
:
[
'
/some/where
'
],
filePattern
:
'
b
'
,
sortByScore
:
true
,
cacheKey
:
'
x
'
},
-
1
).
then
(
complete
=>
{
assert
.
strictEqual
(
complete
.
stats
.
fromCache
,
false
);
assert
.
deepStrictEqual
(
results
,
[
'
/some/where/bcb
'
,
'
/some/where/bbc
'
,
'
/some/where/aab
'
]);
},
null
,
value
=>
{
if
(
Array
.
isArray
(
value
))
{
results
.
push
(...
value
.
map
(
v
=>
v
.
path
));
}
else
{
assert
.
fail
(
value
);
}
}).
then
(()
=>
{
const
results
=
[];
return
service
.
doFileSearch
(
Engine
,
{
rootFolders
:
[
'
/some/where
'
],
filePattern
:
'
bc
'
,
sortByScore
:
true
,
cacheKey
:
'
x
'
},
-
1
).
then
(
complete
=>
{
assert
.
ok
(
complete
.
stats
.
fromCache
);
assert
.
deepStrictEqual
(
results
,
[
'
/some/where/bcb
'
,
'
/some/where/bbc
'
]);
},
null
,
value
=>
{
if
(
Array
.
isArray
(
value
))
{
results
.
push
(...
value
.
map
(
v
=>
v
.
path
));
}
else
{
assert
.
fail
(
value
);
}
});
}).
then
(()
=>
{
return
service
.
clearCache
(
'
x
'
);
}).
then
(()
=>
{
matches
.
push
({
absolutePath
:
'
/some/where/bc
'
,
pathLabel
:
'
bc
'
,
size
:
3
});
const
results
=
[];
return
service
.
doFileSearch
(
Engine
,
{
rootFolders
:
[
'
/some/where
'
],
filePattern
:
'
bc
'
,
sortByScore
:
true
,
cacheKey
:
'
x
'
},
-
1
).
then
(
complete
=>
{
assert
.
strictEqual
(
complete
.
stats
.
fromCache
,
false
);
assert
.
deepStrictEqual
(
results
,
[
'
/some/where/bc
'
]);
},
null
,
value
=>
{
if
(
Array
.
isArray
(
value
))
{
results
.
push
(...
value
.
map
(
v
=>
v
.
path
));
}
else
{
assert
.
fail
(
value
);
}
});
});
});
});
\ No newline at end of file
src/vs/workbench/test/browser/parts/quickOpen/quickopen.perf.test.ts
浏览文件 @
0f776fe4
...
...
@@ -36,7 +36,9 @@ suite('QuickOpen performance', () => {
test
(
'
Measure
'
,
()
=>
{
const
n
=
3
;
const
testWorkspaceArg
=
minimist
(
process
.
argv
)[
'
testWorkspace
'
];
const
argv
=
minimist
(
process
.
argv
);
const
testWorkspaceArg
=
argv
[
'
testWorkspace
'
];
const
verboseResults
=
argv
[
'
verboseResults
'
];
const
testWorkspacePath
=
testWorkspaceArg
?
path
.
join
(
process
.
cwd
(),
testWorkspaceArg
)
:
__dirname
;
const
telemetryService
=
new
TestTelemetryService
();
...
...
@@ -69,12 +71,15 @@ suite('QuickOpen performance', () => {
.
then
((
handler
:
QuickOpenHandler
)
=>
{
return
handler
.
getResults
(
'
a
'
).
then
(
result
=>
{
const
uncachedEvent
=
popEvent
();
assert
.
ok
(
!
uncachedEvent
.
data
.
fromCache
);
assert
.
strictEqual
(
uncachedEvent
.
data
.
symbols
.
fromCache
,
false
,
'
symbols.fromCache
'
);
assert
.
strictEqual
(
uncachedEvent
.
data
.
files
.
fromCache
,
false
,
'
files.fromCache
'
);
return
uncachedEvent
;
}).
then
(
uncachedEvent
=>
{
return
handler
.
getResults
(
'
ab
'
).
then
(
result
=>
{
const
cachedEvent
=
popEvent
();
assert
.
ok
(
cachedEvent
.
data
.
fromCache
);
assert
.
ok
(
cachedEvent
.
data
.
symbols
.
fromCache
,
'
symbolsFromCache
'
);
assert
.
ok
(
cachedEvent
.
data
.
files
.
fromCache
,
'
filesFromCache
'
);
handler
.
onClose
(
false
);
return
[
uncachedEvent
,
cachedEvent
];
});
});
...
...
@@ -90,6 +95,19 @@ suite('QuickOpen performance', () => {
return
event
;
}
function
printResult
(
data
)
{
if
(
verboseResults
)
{
console
.
log
(
JSON
.
stringify
(
data
,
null
,
'
'
)
+
'
,
'
);
}
else
{
console
.
log
(
JSON
.
stringify
({
filesfromCache
:
data
.
files
.
fromCache
,
searchLength
:
data
.
searchLength
,
sortedResultDuration
:
data
.
sortedResultDuration
,
filesResultCount
:
data
.
files
.
resultCount
,
})
+
'
,
'
);
}
}
return
measure
()
// Warm-up first
.
then
(()
=>
{
if
(
testWorkspaceArg
)
{
// Don't measure by default
...
...
@@ -101,14 +119,14 @@ suite('QuickOpen performance', () => {
}
return
measure
()
.
then
(([
uncachedEvent
,
cachedEvent
])
=>
{
console
.
log
(
JSON
.
stringify
(
uncachedEvent
.
data
)
+
'
,
'
);
printResult
(
uncachedEvent
.
data
);
cachedEvents
.
push
(
cachedEvent
);
return
iterate
();
});
})().
then
(()
=>
{
console
.
log
();
cachedEvents
.
forEach
(
cachedEvent
=>
{
console
.
log
(
JSON
.
stringify
(
cachedEvent
.
data
)
+
'
,
'
);
printResult
(
cachedEvent
.
data
);
});
});
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录