Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
9da0fa19
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,发现更多精彩内容 >>
提交
9da0fa19
编写于
7月 22, 2017
作者:
R
Rob Lourens
提交者:
GitHub
7月 22, 2017
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #29964 from keegancsmith/foldermatch
Group search results by root folders
上级
ad92e3f1
1d51bf4b
变更
9
隐藏空白更改
内联
并排
Showing
9 changed file
with
346 addition
and
108 deletion
+346
-108
src/vs/workbench/browser/labels.ts
src/vs/workbench/browser/labels.ts
+7
-3
src/vs/workbench/parts/search/browser/media/searchviewlet.css
...vs/workbench/parts/search/browser/media/searchviewlet.css
+10
-3
src/vs/workbench/parts/search/browser/searchActions.ts
src/vs/workbench/parts/search/browser/searchActions.ts
+2
-2
src/vs/workbench/parts/search/browser/searchResultsView.ts
src/vs/workbench/parts/search/browser/searchResultsView.ts
+73
-9
src/vs/workbench/parts/search/browser/searchViewlet.ts
src/vs/workbench/parts/search/browser/searchViewlet.ts
+22
-10
src/vs/workbench/parts/search/common/searchModel.ts
src/vs/workbench/parts/search/common/searchModel.ts
+207
-62
src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts
...workbench/parts/search/test/browser/searchViewlet.test.ts
+2
-1
src/vs/workbench/parts/search/test/common/searchModel.test.ts
...vs/workbench/parts/search/test/common/searchModel.test.ts
+22
-18
src/vs/workbench/parts/search/test/common/searchResult.test.ts
...s/workbench/parts/search/test/common/searchResult.test.ts
+1
-0
未找到文件。
src/vs/workbench/browser/labels.ts
浏览文件 @
9da0fa19
...
...
@@ -12,7 +12,7 @@ import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import
{
IModeService
}
from
'
vs/editor/common/services/modeService
'
;
import
{
IEditorInput
}
from
'
vs/platform/editor/common/editor
'
;
import
{
toResource
}
from
'
vs/workbench/common/editor
'
;
import
{
getPathLabel
}
from
'
vs/base/common/labels
'
;
import
{
getPathLabel
,
IRootProvider
}
from
'
vs/base/common/labels
'
;
import
{
PLAINTEXT_MODE_ID
}
from
'
vs/editor/common/modes/modesRegistry
'
;
import
{
IWorkspaceContextService
}
from
'
vs/platform/workspace/common/workspace
'
;
import
{
IConfigurationService
}
from
'
vs/platform/configuration/common/configuration
'
;
...
...
@@ -168,6 +168,7 @@ export class EditorLabel extends ResourceLabel {
export
interface
IFileLabelOptions
extends
IResourceLabelOptions
{
hideLabel
?:
boolean
;
hidePath
?:
boolean
;
root
?:
uri
;
}
export
class
FileLabel
extends
ResourceLabel
{
...
...
@@ -188,11 +189,14 @@ export class FileLabel extends ResourceLabel {
public
setFile
(
resource
:
uri
,
options
:
IFileLabelOptions
=
Object
.
create
(
null
)):
void
{
const
hidePath
=
options
.
hidePath
||
(
resource
.
scheme
===
Schemas
.
untitled
&&
!
this
.
untitledEditorService
.
hasAssociatedFilePath
(
resource
));
const
rootProvider
:
IRootProvider
=
options
.
root
?
{
getRoot
():
uri
{
return
options
.
root
;
},
getWorkspace
():
{
roots
:
uri
[];
}
{
return
{
roots
:
[
options
.
root
]
};
},
}
:
this
.
contextService
;
this
.
setLabel
({
resource
,
name
:
!
options
.
hideLabel
?
paths
.
basename
(
resource
.
fsPath
)
:
void
0
,
description
:
!
hidePath
?
getPathLabel
(
paths
.
dirname
(
resource
.
fsPath
),
this
.
contextService
,
this
.
environmentService
)
:
void
0
description
:
!
hidePath
?
getPathLabel
(
paths
.
dirname
(
resource
.
fsPath
),
rootProvider
,
this
.
environmentService
)
:
void
0
},
options
);
}
}
...
...
src/vs/workbench/parts/search/browser/media/searchviewlet.css
浏览文件 @
9da0fa19
...
...
@@ -170,6 +170,7 @@
overflow
:
hidden
;
}
.search-viewlet
.foldermatch
,
.search-viewlet
.filematch
{
display
:
flex
;
position
:
relative
;
...
...
@@ -177,10 +178,12 @@
padding
:
0
;
}
.search-viewlet
.foldermatch
.monaco-icon-label
,
.search-viewlet
.filematch
.monaco-icon-label
{
flex
:
1
;
}
.search-viewlet
.foldermatch
.directory
,
.search-viewlet
.filematch
.directory
{
opacity
:
0.7
;
font-size
:
0.9em
;
...
...
@@ -266,9 +269,11 @@
margin-right
:
12px
;
}
.search-viewlet
>
.results
>
.monaco-tree
.monaco-tree-row
:hover
.content
.monaco-count-badge
,
.search-viewlet
>
.results
>
.monaco-tree.focused
.monaco-tree-row.focused
.content
.monaco-count-badge
{
display
:
none
;
.search-viewlet
>
.results
>
.monaco-tree
.monaco-tree-row
:hover
.content
.filematch
.monaco-count-badge
,
.search-viewlet
>
.results
>
.monaco-tree
.monaco-tree-row
:hover
.content
.linematch
.monaco-count-badge
,
.search-viewlet
>
.results
>
.monaco-tree.focused
.monaco-tree-row.focused
.content
.filematch
.monaco-count-badge
,
.search-viewlet
>
.results
>
.monaco-tree.focused
.monaco-tree-row.focused
.content
.linematch
.monaco-count-badge
{
display
:
none
;
}
.search-viewlet
.focused
.monaco-tree-row.selected
:not
(
.highlighted
)
>
.content.actions
.action-remove
,
...
...
@@ -382,6 +387,7 @@
opacity
:
.5
;
}
.vs-dark
.search-viewlet
.foldermatch
,
.vs-dark
.search-viewlet
.filematch
{
padding
:
0
;
}
...
...
@@ -398,6 +404,7 @@
/* High Contrast Theming */
.hc-black
.monaco-workbench
.search-viewlet
.foldermatch
,
.hc-black
.monaco-workbench
.search-viewlet
.filematch
,
.hc-black
.monaco-workbench
.search-viewlet
.linematch
{
line-height
:
20px
;
...
...
src/vs/workbench/parts/search/browser/searchActions.ts
浏览文件 @
9da0fa19
...
...
@@ -15,7 +15,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import
{
ITree
}
from
'
vs/base/parts/tree/browser/tree
'
;
import
{
INavigator
}
from
'
vs/base/common/iterator
'
;
import
{
SearchViewlet
}
from
'
vs/workbench/parts/search/browser/searchViewlet
'
;
import
{
SearchResult
,
Match
,
FileMatch
,
FileMatchO
rMatch
}
from
'
vs/workbench/parts/search/common/searchModel
'
;
import
{
Match
,
FileMatch
,
FileMatchOrMatch
,
Folde
rMatch
}
from
'
vs/workbench/parts/search/common/searchModel
'
;
import
{
IReplaceService
}
from
'
vs/workbench/parts/search/common/replace
'
;
import
*
as
Constants
from
'
vs/workbench/parts/search/common/constants
'
;
import
{
CollapseAllAction
as
TreeCollapseAction
}
from
'
vs/base/parts/tree/browser/treeDefaults
'
;
...
...
@@ -421,7 +421,7 @@ export class RemoveAction extends AbstractSearchAndReplaceAction {
let
elementToRefresh
:
any
;
if
(
this
.
element
instanceof
FileMatch
)
{
let
parent
:
SearchResult
=
<
SearchResult
>
this
.
element
.
parent
();
let
parent
:
FolderMatch
=
<
FolderMatch
>
this
.
element
.
parent
();
parent
.
remove
(
<
FileMatch
>
this
.
element
);
elementToRefresh
=
parent
;
}
else
{
...
...
src/vs/workbench/parts/search/browser/searchResultsView.ts
浏览文件 @
9da0fa19
...
...
@@ -13,7 +13,7 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import
{
CountBadge
}
from
'
vs/base/browser/ui/countBadge/countBadge
'
;
import
{
FileLabel
}
from
'
vs/workbench/browser/labels
'
;
import
{
ITree
,
IDataSource
,
ISorter
,
IAccessibilityProvider
,
IFilter
,
IRenderer
}
from
'
vs/base/parts/tree/browser/tree
'
;
import
{
Match
,
SearchResult
,
FileMatch
,
FileMatchOrMatch
,
SearchModel
}
from
'
vs/workbench/parts/search/common/searchModel
'
;
import
{
Match
,
SearchResult
,
FileMatch
,
FileMatchOrMatch
,
SearchModel
,
FolderMatch
}
from
'
vs/workbench/parts/search/common/searchModel
'
;
import
{
IWorkspaceContextService
}
from
'
vs/platform/workspace/common/workspace
'
;
import
{
Range
}
from
'
vs/editor/common/core/range
'
;
import
{
SearchViewlet
}
from
'
vs/workbench/parts/search/browser/searchViewlet
'
;
...
...
@@ -22,12 +22,19 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import
{
attachBadgeStyler
}
from
'
vs/platform/theme/common/styler
'
;
import
{
IThemeService
}
from
'
vs/platform/theme/common/themeService
'
;
import
{
getPathLabel
}
from
'
vs/base/common/labels
'
;
import
{
FileKind
}
from
"
vs/platform/files/common/files
"
;
export
class
SearchDataSource
implements
IDataSource
{
private
static
AUTOEXPAND_CHILD_LIMIT
=
10
;
constructor
(
private
includeFolderMatch
:
boolean
=
true
)
{
}
public
getId
(
tree
:
ITree
,
element
:
any
):
string
{
if
(
element
instanceof
FolderMatch
)
{
return
element
.
id
();
}
if
(
element
instanceof
FileMatch
)
{
return
element
.
id
();
}
...
...
@@ -42,8 +49,14 @@ export class SearchDataSource implements IDataSource {
private
_getChildren
(
element
:
any
):
any
[]
{
if
(
element
instanceof
FileMatch
)
{
return
element
.
matches
();
}
else
if
(
element
instanceof
SearchResult
)
{
}
else
if
(
element
instanceof
FolderMatch
)
{
return
element
.
matches
();
}
else
if
(
element
instanceof
SearchResult
)
{
if
(
this
.
includeFolderMatch
)
{
return
element
.
folderMatches
().
filter
(
fm
=>
!
fm
.
isEmpty
());
}
else
{
return
element
.
matches
();
}
}
return
[];
...
...
@@ -54,7 +67,7 @@ export class SearchDataSource implements IDataSource {
}
public
hasChildren
(
tree
:
ITree
,
element
:
any
):
boolean
{
return
element
instanceof
FileMatch
||
element
instanceof
SearchResult
;
return
element
instanceof
FileMatch
||
element
instanceof
FolderMatch
||
element
instanceof
SearchResult
;
}
public
getParent
(
tree
:
ITree
,
element
:
any
):
TPromise
<
any
>
{
...
...
@@ -63,6 +76,8 @@ export class SearchDataSource implements IDataSource {
if
(
element
instanceof
Match
)
{
value
=
element
.
parent
();
}
else
if
(
element
instanceof
FileMatch
)
{
value
=
this
.
includeFolderMatch
?
element
.
parent
()
:
element
.
parent
().
parent
();
}
else
if
(
element
instanceof
FolderMatch
)
{
value
=
element
.
parent
();
}
...
...
@@ -71,13 +86,20 @@ export class SearchDataSource implements IDataSource {
public
shouldAutoexpand
(
tree
:
ITree
,
element
:
any
):
boolean
{
const
numChildren
=
this
.
_getChildren
(
element
).
length
;
return
numChildren
>
0
&&
numChildren
<
SearchDataSource
.
AUTOEXPAND_CHILD_LIMIT
;
if
(
numChildren
<=
0
)
{
return
false
;
}
return
numChildren
<
SearchDataSource
.
AUTOEXPAND_CHILD_LIMIT
||
element
instanceof
FolderMatch
;
}
}
export
class
SearchSorter
implements
ISorter
{
public
compare
(
tree
:
ITree
,
elementA
:
FileMatchOrMatch
,
elementB
:
FileMatchOrMatch
):
number
{
if
(
elementA
instanceof
FolderMatch
&&
elementB
instanceof
FolderMatch
)
{
return
elementA
.
index
()
-
elementB
.
index
();
}
if
(
elementA
instanceof
FileMatch
&&
elementB
instanceof
FileMatch
)
{
return
elementA
.
resource
().
fsPath
.
localeCompare
(
elementB
.
resource
().
fsPath
)
||
elementA
.
name
().
localeCompare
(
elementB
.
name
());
}
...
...
@@ -90,6 +112,11 @@ export class SearchSorter implements ISorter {
}
}
interface
IFolderMatchTemplate
{
label
:
FileLabel
;
badge
:
CountBadge
;
}
interface
IFileMatchTemplate
{
label
:
FileLabel
;
badge
:
CountBadge
;
...
...
@@ -107,6 +134,7 @@ interface IMatchTemplate {
export
class
SearchRenderer
extends
Disposable
implements
IRenderer
{
private
static
FOLDER_MATCH_TEMPLATE_ID
=
'
folderMatch
'
;
private
static
FILE_MATCH_TEMPLATE_ID
=
'
fileMatch
'
;
private
static
MATCH_TEMPLATE_ID
=
'
match
'
;
...
...
@@ -125,7 +153,9 @@ export class SearchRenderer extends Disposable implements IRenderer {
}
public
getTemplateId
(
tree
:
ITree
,
element
:
any
):
string
{
if
(
element
instanceof
FileMatch
)
{
if
(
element
instanceof
FolderMatch
)
{
return
SearchRenderer
.
FOLDER_MATCH_TEMPLATE_ID
;
}
else
if
(
element
instanceof
FileMatch
)
{
return
SearchRenderer
.
FILE_MATCH_TEMPLATE_ID
;
}
else
if
(
element
instanceof
Match
)
{
return
SearchRenderer
.
MATCH_TEMPLATE_ID
;
...
...
@@ -134,6 +164,10 @@ export class SearchRenderer extends Disposable implements IRenderer {
}
public
renderTemplate
(
tree
:
ITree
,
templateId
:
string
,
container
:
HTMLElement
):
any
{
if
(
templateId
===
SearchRenderer
.
FOLDER_MATCH_TEMPLATE_ID
)
{
return
this
.
renderFolderMatchTemplate
(
tree
,
templateId
,
container
);
}
if
(
templateId
===
SearchRenderer
.
FILE_MATCH_TEMPLATE_ID
)
{
return
this
.
renderFileMatchTemplate
(
tree
,
templateId
,
container
);
}
...
...
@@ -146,13 +180,23 @@ export class SearchRenderer extends Disposable implements IRenderer {
}
public
renderElement
(
tree
:
ITree
,
element
:
any
,
templateId
:
string
,
templateData
:
any
):
void
{
if
(
SearchRenderer
.
FILE_MATCH_TEMPLATE_ID
===
templateId
)
{
if
(
SearchRenderer
.
FOLDER_MATCH_TEMPLATE_ID
===
templateId
)
{
this
.
renderFolderMatch
(
tree
,
<
FolderMatch
>
element
,
<
IFolderMatchTemplate
>
templateData
);
}
else
if
(
SearchRenderer
.
FILE_MATCH_TEMPLATE_ID
===
templateId
)
{
this
.
renderFileMatch
(
tree
,
<
FileMatch
>
element
,
<
IFileMatchTemplate
>
templateData
);
}
else
if
(
SearchRenderer
.
MATCH_TEMPLATE_ID
===
templateId
)
{
this
.
renderMatch
(
tree
,
<
Match
>
element
,
<
IMatchTemplate
>
templateData
);
}
}
private
renderFolderMatchTemplate
(
tree
:
ITree
,
templateId
:
string
,
container
:
HTMLElement
):
IFolderMatchTemplate
{
let
folderMatchElement
=
DOM
.
append
(
container
,
DOM
.
$
(
'
.foldermatch
'
));
const
label
=
this
.
instantiationService
.
createInstance
(
FileLabel
,
folderMatchElement
,
void
0
);
const
badge
=
new
CountBadge
(
DOM
.
append
(
folderMatchElement
,
DOM
.
$
(
'
.badge
'
)));
this
.
_register
(
attachBadgeStyler
(
badge
,
this
.
themeService
));
return
{
label
,
badge
};
}
private
renderFileMatchTemplate
(
tree
:
ITree
,
templateId
:
string
,
container
:
HTMLElement
):
IFileMatchTemplate
{
let
fileMatchElement
=
DOM
.
append
(
container
,
DOM
.
$
(
'
.filematch
'
));
const
label
=
this
.
instantiationService
.
createInstance
(
FileLabel
,
fileMatchElement
,
void
0
);
...
...
@@ -182,8 +226,21 @@ export class SearchRenderer extends Disposable implements IRenderer {
};
}
private
renderFolderMatch
(
tree
:
ITree
,
folderMatch
:
FolderMatch
,
templateData
:
IFolderMatchTemplate
):
void
{
if
(
folderMatch
.
hasRoot
())
{
templateData
.
label
.
setFile
(
folderMatch
.
resource
(),
{
fileKind
:
FileKind
.
ROOT_FOLDER
});
}
else
{
templateData
.
label
.
setValue
(
nls
.
localize
(
'
searchFolderMatch.other.label
'
,
"
Other files
"
));
}
let
count
=
folderMatch
.
fileCount
();
templateData
.
badge
.
setCount
(
count
);
templateData
.
badge
.
setTitleFormat
(
count
>
1
?
nls
.
localize
(
'
searchFileMatches
'
,
"
{0} files found
"
,
count
)
:
nls
.
localize
(
'
searchFileMatch
'
,
"
{0} file found
"
,
count
));
}
private
renderFileMatch
(
tree
:
ITree
,
fileMatch
:
FileMatch
,
templateData
:
IFileMatchTemplate
):
void
{
templateData
.
label
.
setFile
(
fileMatch
.
resource
());
const
folderMatch
=
fileMatch
.
parent
();
const
root
=
folderMatch
.
hasRoot
()
?
folderMatch
.
resource
()
:
undefined
;
templateData
.
label
.
setFile
(
fileMatch
.
resource
(),
{
root
});
let
count
=
fileMatch
.
count
();
templateData
.
badge
.
setCount
(
count
);
templateData
.
badge
.
setTitleFormat
(
count
>
1
?
nls
.
localize
(
'
searchMatches
'
,
"
{0} matches found
"
,
count
)
:
nls
.
localize
(
'
searchMatch
'
,
"
{0} match found
"
,
count
));
...
...
@@ -220,6 +277,9 @@ export class SearchRenderer extends Disposable implements IRenderer {
}
public
disposeTemplate
(
tree
:
ITree
,
templateId
:
string
,
templateData
:
any
):
void
{
if
(
SearchRenderer
.
FOLDER_MATCH_TEMPLATE_ID
===
templateId
)
{
(
<
IFolderMatchTemplate
>
templateData
).
label
.
dispose
();
}
if
(
SearchRenderer
.
FILE_MATCH_TEMPLATE_ID
===
templateId
)
{
(
<
IFileMatchTemplate
>
templateData
).
label
.
dispose
();
}
...
...
@@ -232,6 +292,10 @@ export class SearchAccessibilityProvider implements IAccessibilityProvider {
}
public
getAriaLabel
(
tree
:
ITree
,
element
:
FileMatchOrMatch
):
string
{
if
(
element
instanceof
FolderMatch
)
{
return
nls
.
localize
(
'
folderMatchAriaLabel
'
,
"
{0} matches in folder root {1}, Search result
"
,
element
.
count
(),
element
.
name
());
}
if
(
element
instanceof
FileMatch
)
{
const
path
=
getPathLabel
(
element
.
resource
(),
this
.
contextService
)
||
element
.
resource
().
fsPath
;
...
...
@@ -256,6 +320,6 @@ export class SearchAccessibilityProvider implements IAccessibilityProvider {
export
class
SearchFilter
implements
IFilter
{
public
isVisible
(
tree
:
ITree
,
element
:
any
):
boolean
{
return
!
(
element
instanceof
FileMatch
)
||
element
.
matches
().
length
>
0
;
return
!
(
element
instanceof
FileMatch
||
element
instanceof
FolderMatch
)
||
element
.
matches
().
length
>
0
;
}
}
\ No newline at end of file
}
src/vs/workbench/parts/search/browser/searchViewlet.ts
浏览文件 @
9da0fa19
...
...
@@ -442,7 +442,7 @@ export class SearchViewlet extends Viewlet {
this
.
results
=
div
;
this
.
results
.
addClass
(
'
show-file-icons
'
);
let
dataSource
=
new
SearchDataSource
();
let
dataSource
=
new
SearchDataSource
(
this
.
contextService
.
hasMultiFolderWorkspace
()
);
let
renderer
=
this
.
instantiationService
.
createInstance
(
SearchRenderer
,
this
.
getActionRunner
(),
this
);
this
.
tree
=
new
Tree
(
div
.
getHTMLElement
(),
{
...
...
@@ -1168,7 +1168,7 @@ export class SearchViewlet extends Viewlet {
const
msgWasHidden
=
this
.
messages
.
isHidden
();
if
(
fileCount
>
0
)
{
const
div
=
this
.
clearMessage
();
$
(
div
).
p
({
text
:
this
.
buildResultCountMessage
(
this
.
viewModel
.
searchResult
.
count
(),
fileCount
)
});
$
(
div
).
p
({
text
:
this
.
buildResultCountMessage
(
this
.
viewModel
.
searchResult
.
count
(),
fileCount
,
this
.
viewModel
.
searchResult
.
folderCount
()
)
});
if
(
msgWasHidden
)
{
this
.
reLayout
();
}
...
...
@@ -1177,15 +1177,27 @@ export class SearchViewlet extends Viewlet {
}
}
private
buildResultCountMessage
(
resultCount
:
number
,
fileCount
:
number
):
string
{
if
(
resultCount
===
1
&&
fileCount
===
1
)
{
return
nls
.
localize
(
'
search.file.result
'
,
"
{0} result in {1} file
"
,
resultCount
,
fileCount
);
}
else
if
(
resultCount
===
1
)
{
return
nls
.
localize
(
'
search.files.result
'
,
"
{0} result in {1} files
"
,
resultCount
,
fileCount
);
}
else
if
(
fileCount
===
1
)
{
return
nls
.
localize
(
'
search.file.results
'
,
"
{0} results in {1} file
"
,
resultCount
,
fileCount
);
private
buildResultCountMessage
(
resultCount
:
number
,
fileCount
:
number
,
folderCount
:
number
):
string
{
if
(
folderCount
===
1
)
{
if
(
resultCount
===
1
&&
fileCount
===
1
)
{
return
nls
.
localize
(
'
search.folder.file.result
'
,
"
{0} result in {1} file in {2} folder
"
,
resultCount
,
fileCount
,
folderCount
);
}
else
if
(
resultCount
===
1
)
{
return
nls
.
localize
(
'
search.folder.files.result
'
,
"
{0} result in {1} files in {2} folder
"
,
resultCount
,
fileCount
,
folderCount
);
}
else
if
(
fileCount
===
1
)
{
return
nls
.
localize
(
'
search.folder.file.results
'
,
"
{0} results in {1} file in {2} folder
"
,
resultCount
,
fileCount
,
folderCount
);
}
else
{
return
nls
.
localize
(
'
search.folder.files.results
'
,
"
{0} results in {1} files in {2} folder
"
,
resultCount
,
fileCount
,
folderCount
);
}
}
else
{
return
nls
.
localize
(
'
search.files.results
'
,
"
{0} results in {1} files
"
,
resultCount
,
fileCount
);
if
(
resultCount
===
1
&&
fileCount
===
1
)
{
return
nls
.
localize
(
'
search.folders.file.result
'
,
"
{0} result in {1} file in {2} folders
"
,
resultCount
,
fileCount
,
folderCount
);
}
else
if
(
resultCount
===
1
)
{
return
nls
.
localize
(
'
search.folders.files.result
'
,
"
{0} result in {1} files in {2} folders
"
,
resultCount
,
fileCount
,
folderCount
);
}
else
if
(
fileCount
===
1
)
{
return
nls
.
localize
(
'
search.folders.file.results
'
,
"
{0} results in {1} file in {2} folders
"
,
resultCount
,
fileCount
,
folderCount
);
}
else
{
return
nls
.
localize
(
'
search.folders.files.results
'
,
"
{0} results in {1} files in {2} folders
"
,
resultCount
,
fileCount
,
folderCount
);
}
}
}
...
...
src/vs/workbench/parts/search/common/searchModel.ts
浏览文件 @
9da0fa19
...
...
@@ -11,7 +11,7 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import
{
IDisposable
,
Disposable
}
from
'
vs/base/common/lifecycle
'
;
import
{
TPromise
,
PPromise
}
from
'
vs/base/common/winjs.base
'
;
import
URI
from
'
vs/base/common/uri
'
;
import
{
values
,
ResourceMap
}
from
'
vs/base/common/map
'
;
import
{
values
,
ResourceMap
,
TrieMap
}
from
'
vs/base/common/map
'
;
import
Event
,
{
Emitter
,
fromPromise
,
stopwatch
,
any
}
from
'
vs/base/common/event
'
;
import
{
ISearchService
,
ISearchProgressItem
,
ISearchComplete
,
ISearchQuery
,
IPatternInfo
,
IFileMatch
}
from
'
vs/platform/search/common/search
'
;
import
{
ReplacePattern
}
from
'
vs/platform/search/common/replace
'
;
...
...
@@ -132,7 +132,7 @@ export class FileMatch extends Disposable {
private
_updateScheduler
:
RunOnceScheduler
;
private
_modelDecorations
:
string
[]
=
[];
constructor
(
private
_query
:
IPatternInfo
,
private
_maxResults
:
number
,
private
_parent
:
SearchResult
,
private
rawMatch
:
IFileMatch
,
constructor
(
private
_query
:
IPatternInfo
,
private
_maxResults
:
number
,
private
_parent
:
FolderMatch
,
private
rawMatch
:
IFileMatch
,
@
IModelService
private
modelService
:
IModelService
,
@
IReplaceService
private
replaceService
:
IReplaceService
)
{
super
();
this
.
_resource
=
this
.
rawMatch
.
resource
;
...
...
@@ -252,7 +252,7 @@ export class FileMatch extends Disposable {
return
this
.
resource
().
toString
();
}
public
parent
():
SearchResult
{
public
parent
():
FolderMatch
{
return
this
.
_parent
;
}
...
...
@@ -334,45 +334,66 @@ export interface IChangeEvent {
removed
?:
boolean
;
}
export
class
SearchResult
extends
Disposable
{
export
class
FolderMatch
extends
Disposable
{
private
_onChange
=
this
.
_register
(
new
Emitter
<
IChangeEvent
>
());
public
onChange
:
Event
<
IChangeEvent
>
=
this
.
_onChange
.
event
;
private
_onDispose
=
this
.
_register
(
new
Emitter
<
void
>
());
public
onDispose
:
Event
<
void
>
=
this
.
_onDispose
.
event
;
private
_fileMatches
:
ResourceMap
<
FileMatch
>
;
private
_unDisposedFileMatches
:
ResourceMap
<
FileMatch
>
;
private
_query
:
IPatternInfo
=
null
;
private
_maxResults
:
number
;
private
_showHighlights
:
boolean
;
private
_replacingAll
:
boolean
=
false
;
private
_rangeHighlightDecorations
:
RangeHighlightDecorations
;
constructor
(
private
_searchModel
:
SearchModel
,
@
IReplaceService
private
replaceService
:
IReplaceService
,
@
ITelemetryService
private
telemetryService
:
ITelemetryService
,
constructor
(
private
_resource
:
URI
,
private
_id
:
string
,
private
_index
:
number
,
private
_query
:
ISearchQuery
,
private
_parent
:
SearchResult
,
private
_searchModel
:
SearchModel
,
@
IReplaceService
private
replaceService
:
IReplaceService
,
@
ITelemetryService
private
telemetryService
:
ITelemetryService
,
@
IInstantiationService
private
instantiationService
:
IInstantiationService
)
{
super
();
this
.
_fileMatches
=
new
ResourceMap
<
FileMatch
>
();
this
.
_unDisposedFileMatches
=
new
ResourceMap
<
FileMatch
>
();
this
.
_rangeHighlightDecorations
=
this
.
instantiationService
.
createInstance
(
RangeHighlightDecorations
);
}
public
set
query
(
query
:
IPatternInfo
)
{
this
.
_query
=
query
;
public
get
searchModel
():
SearchModel
{
return
this
.
_searchModel
;
}
public
set
maxResults
(
maxResults
:
number
)
{
this
.
_maxResults
=
maxResul
ts
;
public
get
showHighlights
():
boolean
{
return
this
.
_parent
.
showHighligh
ts
;
}
public
get
searchModel
():
SearchModel
{
return
this
.
_searchModel
;
public
set
replacingAll
(
b
:
boolean
)
{
this
.
_replacingAll
=
b
;
}
public
id
():
string
{
return
this
.
_id
;
}
public
resource
():
URI
{
return
this
.
_resource
;
}
public
add
(
raw
:
IFileMatch
[],
silent
:
boolean
=
false
):
void
{
public
index
():
number
{
return
this
.
_index
;
}
public
name
():
string
{
return
paths
.
basename
(
this
.
resource
().
fsPath
);
}
public
parent
():
SearchResult
{
return
this
.
_parent
;
}
public
hasRoot
():
boolean
{
return
this
.
_resource
.
fsPath
!==
''
;
}
public
add
(
raw
:
IFileMatch
[],
silent
:
boolean
):
void
{
let
changed
:
FileMatch
[]
=
[];
raw
.
forEach
((
rawFileMatch
)
=>
{
if
(
!
this
.
_fileMatches
.
has
(
rawFileMatch
.
resource
))
{
let
fileMatch
=
this
.
instantiationService
.
createInstance
(
FileMatch
,
this
.
_query
,
this
.
_
maxResults
,
this
,
rawFileMatch
);
let
fileMatch
=
this
.
instantiationService
.
createInstance
(
FileMatch
,
this
.
_query
.
contentPattern
,
this
.
_query
.
maxResults
,
this
,
rawFileMatch
);
this
.
doAdd
(
fileMatch
);
changed
.
push
(
fileMatch
);
let
disposable
=
fileMatch
.
onChange
(()
=>
this
.
onFileChange
(
fileMatch
));
...
...
@@ -400,31 +421,179 @@ export class SearchResult extends Disposable {
});
}
public
matches
():
FileMatch
[]
{
return
this
.
_fileMatches
.
values
();
}
public
isEmpty
():
boolean
{
return
this
.
fileCount
()
===
0
;
}
public
fileCount
():
number
{
return
this
.
_fileMatches
.
size
;
}
public
count
():
number
{
return
this
.
matches
().
reduce
<
number
>
((
prev
,
match
)
=>
prev
+
match
.
count
(),
0
);
}
private
onFileChange
(
fileMatch
:
FileMatch
):
void
{
let
added
:
boolean
=
false
;
let
removed
:
boolean
=
false
;
if
(
!
this
.
_fileMatches
.
has
(
fileMatch
.
resource
()))
{
this
.
doAdd
(
fileMatch
);
added
=
true
;
}
if
(
fileMatch
.
count
()
===
0
)
{
this
.
doRemove
(
fileMatch
,
false
,
false
);
added
=
false
;
removed
=
true
;
}
if
(
!
this
.
_replacingAll
)
{
this
.
_onChange
.
fire
({
elements
:
[
fileMatch
],
added
:
added
,
removed
:
removed
});
}
}
private
doAdd
(
fileMatch
:
FileMatch
):
void
{
this
.
_fileMatches
.
set
(
fileMatch
.
resource
(),
fileMatch
);
if
(
this
.
_unDisposedFileMatches
.
has
(
fileMatch
.
resource
()))
{
this
.
_unDisposedFileMatches
.
delete
(
fileMatch
.
resource
());
}
}
private
doRemove
(
fileMatch
:
FileMatch
,
dispose
:
boolean
=
true
,
trigger
:
boolean
=
true
):
void
{
this
.
_fileMatches
.
delete
(
fileMatch
.
resource
());
if
(
dispose
)
{
fileMatch
.
dispose
();
}
else
{
this
.
_unDisposedFileMatches
.
set
(
fileMatch
.
resource
(),
fileMatch
);
}
if
(
trigger
)
{
this
.
_onChange
.
fire
({
elements
:
[
fileMatch
],
removed
:
true
});
}
}
private
disposeMatches
():
void
{
this
.
_fileMatches
.
values
().
forEach
((
fileMatch
:
FileMatch
)
=>
fileMatch
.
dispose
());
this
.
_unDisposedFileMatches
.
values
().
forEach
((
fileMatch
:
FileMatch
)
=>
fileMatch
.
dispose
());
this
.
_fileMatches
.
clear
();
this
.
_unDisposedFileMatches
.
clear
();
}
public
dispose
():
void
{
this
.
disposeMatches
();
this
.
_onDispose
.
fire
();
super
.
dispose
();
}
}
export
class
SearchResult
extends
Disposable
{
private
_onChange
=
this
.
_register
(
new
Emitter
<
IChangeEvent
>
());
public
onChange
:
Event
<
IChangeEvent
>
=
this
.
_onChange
.
event
;
private
_folderMatches
:
FolderMatch
[]
=
[];
private
_folderMatchesMap
:
TrieMap
<
FolderMatch
>
=
new
TrieMap
<
FolderMatch
>
(
TrieMap
.
PathSplitter
);
private
_query
:
ISearchQuery
=
null
;
private
_showHighlights
:
boolean
;
private
_rangeHighlightDecorations
:
RangeHighlightDecorations
;
constructor
(
private
_searchModel
:
SearchModel
,
@
IReplaceService
private
replaceService
:
IReplaceService
,
@
ITelemetryService
private
telemetryService
:
ITelemetryService
,
@
IInstantiationService
private
instantiationService
:
IInstantiationService
)
{
super
();
this
.
_rangeHighlightDecorations
=
this
.
instantiationService
.
createInstance
(
RangeHighlightDecorations
);
}
public
set
query
(
query
:
ISearchQuery
)
{
// When updating the query we could change the roots, so ensure we clean up the old roots first.
this
.
clear
();
this
.
_query
=
query
;
const
otherFiles
=
URI
.
parse
(
''
);
this
.
_folderMatches
=
(
query
.
folderQueries
||
[]).
map
((
fq
)
=>
fq
.
folder
).
concat
([
otherFiles
]).
map
((
resource
,
index
)
=>
{
const
id
=
resource
.
toString
()
||
'
otherFiles
'
;
const
folderMatch
=
this
.
instantiationService
.
createInstance
(
FolderMatch
,
resource
,
id
,
index
,
query
,
this
,
this
.
_searchModel
);
const
disposable
=
folderMatch
.
onChange
((
event
)
=>
this
.
_onChange
.
fire
(
event
));
folderMatch
.
onDispose
(()
=>
disposable
.
dispose
());
return
folderMatch
;
});
// otherFiles is the fallback for missing values in the TrieMap. So we do not insert it.
this
.
_folderMatches
.
slice
(
0
,
this
.
folderMatches
.
length
-
1
)
.
forEach
(
fm
=>
this
.
_folderMatchesMap
.
insert
(
fm
.
resource
().
fsPath
,
fm
));
}
public
get
searchModel
():
SearchModel
{
return
this
.
_searchModel
;
}
public
add
(
allRaw
:
IFileMatch
[],
silent
:
boolean
=
false
):
void
{
// Split up raw into a list per folder so we can do a batch add per folder.
let
rawPerFolder
=
new
ResourceMap
<
IFileMatch
[]
>
();
this
.
_folderMatches
.
forEach
((
folderMatch
)
=>
rawPerFolder
.
set
(
folderMatch
.
resource
(),
[]));
allRaw
.
forEach
(
rawFileMatch
=>
{
let
folderMatch
=
this
.
getFolderMatch
(
rawFileMatch
.
resource
);
rawPerFolder
.
get
(
folderMatch
.
resource
()).
push
(
rawFileMatch
);
});
rawPerFolder
.
forEach
((
raw
)
=>
{
if
(
!
raw
.
length
)
{
return
;
}
let
folderMatch
=
this
.
getFolderMatch
(
raw
[
0
].
resource
);
folderMatch
.
add
(
raw
,
silent
);
});
}
public
clear
():
void
{
this
.
_folderMatches
.
forEach
((
folderMatch
)
=>
folderMatch
.
clear
());
this
.
disposeMatches
();
}
public
remove
(
match
:
FileMatch
):
void
{
this
.
getFolderMatch
(
match
.
resource
()).
remove
(
match
);
}
public
replace
(
match
:
FileMatch
):
TPromise
<
any
>
{
return
this
.
getFolderMatch
(
match
.
resource
()).
replace
(
match
);
}
public
replaceAll
(
progressRunner
:
IProgressRunner
):
TPromise
<
any
>
{
this
.
_
replacingAll
=
true
;
this
.
replacingAll
=
true
;
const
promise
=
this
.
replaceService
.
replace
(
this
.
matches
(),
progressRunner
);
const
onDone
=
stopwatch
(
fromPromise
(
promise
));
onDone
(
duration
=>
this
.
telemetryService
.
publicLog
(
'
replaceAll.started
'
,
{
duration
}));
return
promise
.
then
(()
=>
{
this
.
_
replacingAll
=
false
;
this
.
replacingAll
=
false
;
this
.
clear
();
},
()
=>
{
this
.
_
replacingAll
=
false
;
this
.
replacingAll
=
false
;
});
}
public
folderMatches
():
FolderMatch
[]
{
return
this
.
_folderMatches
.
concat
();
}
public
matches
():
FileMatch
[]
{
return
this
.
_fileMatches
.
values
();
let
matches
:
FileMatch
[][]
=
[];
this
.
_folderMatches
.
forEach
((
folderMatch
)
=>
{
matches
.
push
(
folderMatch
.
matches
());
});
return
[].
concat
(...
matches
);
}
public
isEmpty
():
boolean
{
return
this
.
fileCount
()
===
0
;
return
this
.
_folderMatches
.
every
((
folderMatch
)
=>
folderMatch
.
isEmpty
())
;
}
public
fileCount
():
number
{
return
this
.
_fileMatches
.
size
;
return
this
.
folderMatches
().
reduce
<
number
>
((
prev
,
match
)
=>
prev
+
match
.
fileCount
(),
0
);
}
public
folderCount
():
number
{
return
this
.
folderMatches
().
reduce
<
number
>
((
prev
,
match
)
=>
prev
+
(
match
.
fileCount
()
>
0
?
1
:
0
),
0
);
}
public
count
():
number
{
...
...
@@ -461,48 +630,25 @@ export class SearchResult extends Disposable {
return
this
.
_rangeHighlightDecorations
;
}
private
onFileChange
(
fileMatch
:
FileMatch
):
void
{
let
added
:
boolean
=
false
;
let
removed
:
boolean
=
false
;
if
(
!
this
.
_fileMatches
.
has
(
fileMatch
.
resource
()))
{
this
.
doAdd
(
fileMatch
);
added
=
true
;
}
if
(
fileMatch
.
count
()
===
0
)
{
this
.
doRemove
(
fileMatch
,
false
,
false
);
added
=
false
;
removed
=
true
;
}
if
(
!
this
.
_replacingAll
)
{
this
.
_onChange
.
fire
({
elements
:
[
fileMatch
],
added
:
added
,
removed
:
removed
});
}
private
getFolderMatch
(
resource
:
URI
):
FolderMatch
{
const
folderMatch
=
this
.
_folderMatchesMap
.
findSubstr
(
resource
.
fsPath
);
return
folderMatch
?
folderMatch
:
this
.
otherFiles
;
}
private
doAdd
(
fileMatch
:
FileMatch
):
void
{
this
.
_fileMatches
.
set
(
fileMatch
.
resource
(),
fileMatch
);
if
(
this
.
_unDisposedFileMatches
.
has
(
fileMatch
.
resource
()))
{
this
.
_unDisposedFileMatches
.
delete
(
fileMatch
.
resource
());
}
private
get
otherFiles
():
FolderMatch
{
return
this
.
_folderMatches
[
this
.
_folderMatches
.
length
-
1
];
}
private
doRemove
(
fileMatch
:
FileMatch
,
dispose
:
boolean
=
true
,
trigger
:
boolean
=
true
):
void
{
this
.
_fileMatches
.
delete
(
fileMatch
.
resource
());
if
(
dispose
)
{
fileMatch
.
dispose
();
}
else
{
this
.
_unDisposedFileMatches
.
set
(
fileMatch
.
resource
(),
fileMatch
);
}
if
(
trigger
)
{
this
.
_onChange
.
fire
({
elements
:
[
fileMatch
],
removed
:
true
});
}
private
set
replacingAll
(
running
:
boolean
)
{
this
.
_folderMatches
.
forEach
((
folderMatch
)
=>
{
folderMatch
.
replacingAll
=
running
;
});
}
private
disposeMatches
():
void
{
this
.
_fileMatches
.
values
().
forEach
((
fileMatch
:
FileMatch
)
=>
fileMatch
.
dispose
());
this
.
_unDisposedFileMatches
.
values
().
forEach
((
fileMatch
:
FileMatch
)
=>
fileMatch
.
dispose
());
this
.
_fileMatches
.
clear
();
this
.
_unDisposedFileMatches
.
clear
();
this
.
_folderMatches
.
forEach
(
folderMatch
=>
folderMatch
.
dispose
());
this
.
_folderMatches
=
[];
this
.
_folderMatchesMap
=
new
TrieMap
<
FolderMatch
>
(
TrieMap
.
PathSplitter
);
this
.
_rangeHighlightDecorations
.
removeHighlightRange
();
}
...
...
@@ -566,8 +712,7 @@ export class SearchModel extends Disposable {
this
.
searchResult
.
clear
();
this
.
_searchResult
.
query
=
this
.
_searchQuery
.
contentPattern
;
this
.
_searchResult
.
maxResults
=
this
.
_searchQuery
.
maxResults
;
this
.
_searchResult
.
query
=
this
.
_searchQuery
;
this
.
_replacePattern
=
new
ReplacePattern
(
this
.
_replaceString
,
this
.
_searchQuery
.
contentPattern
);
const
onDone
=
fromPromise
(
this
.
currentRequest
);
...
...
src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts
浏览文件 @
9da0fa19
...
...
@@ -25,7 +25,8 @@ suite('Search - Viewlet', () => {
test
(
'
Data Source
'
,
function
()
{
let
ds
=
new
SearchDataSource
();
let
result
=
instantiation
.
createInstance
(
SearchResult
,
null
);
let
result
:
SearchResult
=
instantiation
.
createInstance
(
SearchResult
,
null
);
result
.
query
=
{
type
:
1
,
folderQueries
:
[{
folder
:
uri
.
parse
(
'
file://c:/
'
)
}]
};
result
.
add
([{
resource
:
uri
.
parse
(
'
file:///c:/foo
'
),
lineMatches
:
[{
lineNumber
:
1
,
preview
:
'
bar
'
,
offsetAndLengths
:
[[
0
,
1
]]
}]
...
...
src/vs/workbench/parts/search/test/common/searchModel.test.ts
浏览文件 @
9da0fa19
...
...
@@ -11,7 +11,7 @@ import { DeferredPPromise } from 'vs/base/test/common/utils';
import
{
PPromise
}
from
'
vs/base/common/winjs.base
'
;
import
{
SearchModel
}
from
'
vs/workbench/parts/search/common/searchModel
'
;
import
URI
from
'
vs/base/common/uri
'
;
import
{
IFileMatch
,
ILineMatch
,
ISearchService
,
ISearchComplete
,
ISearchProgressItem
,
IUncachedSearchStats
}
from
'
vs/platform/search/common/search
'
;
import
{
IFileMatch
,
I
FolderQuery
,
I
LineMatch
,
ISearchService
,
ISearchComplete
,
ISearchProgressItem
,
IUncachedSearchStats
}
from
'
vs/platform/search/common/search
'
;
import
{
ITelemetryService
}
from
'
vs/platform/telemetry/common/telemetry
'
;
import
{
NullTelemetryService
}
from
'
vs/platform/telemetry/common/telemetryUtils
'
;
import
{
Range
}
from
'
vs/editor/common/core/range
'
;
...
...
@@ -57,6 +57,10 @@ suite('SearchModel', () => {
filesWalked
:
3
};
const
folderQueries
:
IFolderQuery
[]
=
[
{
folder
:
URI
.
parse
(
'
file://c:/
'
)
}
];
setup
(()
=>
{
restoreStubs
=
[];
instantiationService
=
new
TestInstantiationService
();
...
...
@@ -76,8 +80,8 @@ suite('SearchModel', () => {
let
results
=
[
aRawMatch
(
'
file://c:/1
'
,
aLineMatch
(
'
preview 1
'
,
1
,
[[
1
,
3
],
[
4
,
7
]])),
aRawMatch
(
'
file://c:/2
'
,
aLineMatch
(
'
preview 2
'
))];
instantiationService
.
stub
(
ISearchService
,
'
search
'
,
PPromise
.
as
({
results
:
results
}));
let
testObject
=
instantiationService
.
createInstance
(
SearchModel
);
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
});
let
testObject
:
SearchModel
=
instantiationService
.
createInstance
(
SearchModel
);
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
,
folderQueries
});
let
actual
=
testObject
.
searchResult
.
matches
();
...
...
@@ -103,7 +107,7 @@ suite('SearchModel', () => {
instantiationService
.
stub
(
ISearchService
,
'
search
'
,
promise
);
let
testObject
=
instantiationService
.
createInstance
(
SearchModel
);
let
result
=
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
});
let
result
=
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
,
folderQueries
});
promise
.
progress
(
results
[
0
]);
promise
.
progress
(
results
[
1
]);
...
...
@@ -137,7 +141,7 @@ suite('SearchModel', () => {
instantiationService
.
stub
(
ISearchService
,
'
search
'
,
PPromise
.
as
({
results
:
results
}));
let
testObject
=
instantiationService
.
createInstance
(
SearchModel
);
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
});
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
,
folderQueries
});
assert
.
ok
(
target
.
calledOnce
);
const
data
=
target
.
args
[
0
];
...
...
@@ -154,7 +158,7 @@ suite('SearchModel', () => {
instantiationService
.
stub
(
ISearchService
,
'
search
'
,
PPromise
.
as
({
results
:
[]
}));
let
testObject
=
instantiationService
.
createInstance
(
SearchModel
);
const
result
=
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
});
const
result
=
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
,
folderQueries
});
setTimeout
(()
=>
{
result
.
done
(()
=>
{
...
...
@@ -176,7 +180,7 @@ suite('SearchModel', () => {
instantiationService
.
stub
(
ISearchService
,
'
search
'
,
promise
);
let
testObject
=
instantiationService
.
createInstance
(
SearchModel
);
let
result
=
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
});
let
result
=
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
,
folderQueries
});
promise
.
progress
(
aRawMatch
(
'
file://c:/1
'
,
aLineMatch
(
'
some preview
'
)));
promise
.
complete
({
results
:
[],
stats
:
testSearchStats
});
...
...
@@ -202,7 +206,7 @@ suite('SearchModel', () => {
instantiationService
.
stub
(
ISearchService
,
'
search
'
,
promise
);
let
testObject
=
instantiationService
.
createInstance
(
SearchModel
);
let
result
=
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
});
let
result
=
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
,
folderQueries
});
promise
.
error
(
'
error
'
);
...
...
@@ -227,7 +231,7 @@ suite('SearchModel', () => {
instantiationService
.
stub
(
ISearchService
,
'
search
'
,
promise
);
let
testObject
=
instantiationService
.
createInstance
(
SearchModel
);
let
result
=
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
});
let
result
=
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
,
folderQueries
});
promise
.
cancel
();
...
...
@@ -245,12 +249,12 @@ suite('SearchModel', () => {
let
results
=
[
aRawMatch
(
'
file://c:/1
'
,
aLineMatch
(
'
preview 1
'
,
1
,
[[
1
,
3
],
[
4
,
7
]])),
aRawMatch
(
'
file://c:/2
'
,
aLineMatch
(
'
preview 2
'
))];
instantiationService
.
stub
(
ISearchService
,
'
search
'
,
PPromise
.
as
({
results
:
results
}));
let
testObject
:
SearchModel
=
instantiationService
.
createInstance
(
SearchModel
);
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
});
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
,
folderQueries
});
assert
.
ok
(
!
testObject
.
searchResult
.
isEmpty
());
instantiationService
.
stub
(
ISearchService
,
'
search
'
,
new
DeferredPPromise
<
ISearchComplete
,
ISearchProgressItem
>
());
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
});
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
,
folderQueries
});
assert
.
ok
(
testObject
.
searchResult
.
isEmpty
());
});
...
...
@@ -259,9 +263,9 @@ suite('SearchModel', () => {
instantiationService
.
stub
(
ISearchService
,
'
search
'
,
new
DeferredPPromise
((
c
,
e
,
p
)
=>
{
},
target
));
let
testObject
:
SearchModel
=
instantiationService
.
createInstance
(
SearchModel
);
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
});
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
,
folderQueries
});
instantiationService
.
stub
(
ISearchService
,
'
search
'
,
new
DeferredPPromise
<
ISearchComplete
,
ISearchProgressItem
>
());
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
});
testObject
.
search
({
contentPattern
:
{
pattern
:
'
somestring
'
},
type
:
1
,
folderQueries
});
assert
.
ok
(
target
.
calledOnce
);
});
...
...
@@ -271,24 +275,24 @@ suite('SearchModel', () => {
instantiationService
.
stub
(
ISearchService
,
'
search
'
,
PPromise
.
as
({
results
:
results
}));
let
testObject
:
SearchModel
=
instantiationService
.
createInstance
(
SearchModel
);
testObject
.
search
({
contentPattern
:
{
pattern
:
'
re
'
},
type
:
1
});
testObject
.
search
({
contentPattern
:
{
pattern
:
'
re
'
},
type
:
1
,
folderQueries
});
testObject
.
replaceString
=
'
hello
'
;
let
match
=
testObject
.
searchResult
.
matches
()[
0
].
matches
()[
0
];
assert
.
equal
(
'
hello
'
,
match
.
replaceString
);
testObject
.
search
({
contentPattern
:
{
pattern
:
'
re
'
,
isRegExp
:
true
},
type
:
1
});
testObject
.
search
({
contentPattern
:
{
pattern
:
'
re
'
,
isRegExp
:
true
},
type
:
1
,
folderQueries
});
match
=
testObject
.
searchResult
.
matches
()[
0
].
matches
()[
0
];
assert
.
equal
(
'
hello
'
,
match
.
replaceString
);
testObject
.
search
({
contentPattern
:
{
pattern
:
'
re(?:vi)
'
,
isRegExp
:
true
},
type
:
1
});
testObject
.
search
({
contentPattern
:
{
pattern
:
'
re(?:vi)
'
,
isRegExp
:
true
},
type
:
1
,
folderQueries
});
match
=
testObject
.
searchResult
.
matches
()[
0
].
matches
()[
0
];
assert
.
equal
(
'
hello
'
,
match
.
replaceString
);
testObject
.
search
({
contentPattern
:
{
pattern
:
'
r(e)(?:vi)
'
,
isRegExp
:
true
},
type
:
1
});
testObject
.
search
({
contentPattern
:
{
pattern
:
'
r(e)(?:vi)
'
,
isRegExp
:
true
},
type
:
1
,
folderQueries
});
match
=
testObject
.
searchResult
.
matches
()[
0
].
matches
()[
0
];
assert
.
equal
(
'
hello
'
,
match
.
replaceString
);
testObject
.
search
({
contentPattern
:
{
pattern
:
'
r(e)(?:vi)
'
,
isRegExp
:
true
},
type
:
1
});
testObject
.
search
({
contentPattern
:
{
pattern
:
'
r(e)(?:vi)
'
,
isRegExp
:
true
},
type
:
1
,
folderQueries
});
testObject
.
replaceString
=
'
hello$1
'
;
match
=
testObject
.
searchResult
.
matches
()[
0
].
matches
()[
0
];
assert
.
equal
(
'
helloe
'
,
match
.
replaceString
);
...
...
src/vs/workbench/parts/search/test/common/searchResult.test.ts
浏览文件 @
9da0fa19
...
...
@@ -368,6 +368,7 @@ suite('SearchResult', () => {
function
aSearchResult
():
SearchResult
{
let
searchModel
=
instantiationService
.
createInstance
(
SearchModel
);
searchModel
.
searchResult
.
query
=
{
type
:
1
,
folderQueries
:
[{
folder
:
URI
.
parse
(
'
file://c:/
'
)
}]
};
return
searchModel
.
searchResult
;
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录