Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
7879818f
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,体验更适合开发者的 AI 搜索 >>
提交
7879818f
编写于
1月 10, 2020
作者:
J
Jackson Kearl
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Fix circular dependency issue
上级
35d37c78
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
444 addition
and
413 deletion
+444
-413
src/vs/workbench/contrib/search/browser/search.contribution.ts
...s/workbench/contrib/search/browser/search.contribution.ts
+2
-1
src/vs/workbench/contrib/search/browser/searchActions.ts
src/vs/workbench/contrib/search/browser/searchActions.ts
+1
-1
src/vs/workbench/contrib/search/browser/searchEditor.ts
src/vs/workbench/contrib/search/browser/searchEditor.ts
+6
-410
src/vs/workbench/contrib/search/browser/searchEditorCommands.ts
.../workbench/contrib/search/browser/searchEditorCommands.ts
+434
-0
src/vs/workbench/contrib/search/browser/searchView.ts
src/vs/workbench/contrib/search/browser/searchView.ts
+1
-1
未找到文件。
src/vs/workbench/contrib/search/browser/search.contribution.ts
浏览文件 @
7879818f
...
...
@@ -57,7 +57,8 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import
{
assertType
}
from
'
vs/base/common/types
'
;
import
{
SearchViewPaneContainer
}
from
'
vs/workbench/contrib/search/browser/searchViewlet
'
;
import
{
EditorDescriptor
,
Extensions
as
EditorExtensions
,
IEditorRegistry
}
from
'
vs/workbench/browser/editor
'
;
import
{
SearchEditor
,
SearchEditorInput
}
from
'
vs/workbench/contrib/search/browser/searchEditor
'
;
import
{
SearchEditorInput
}
from
'
vs/workbench/contrib/search/browser/searchEditorCommands
'
;
import
{
SearchEditor
}
from
'
vs/workbench/contrib/search/browser/searchEditor
'
;
import
{
SyncDescriptor
}
from
'
vs/platform/instantiation/common/descriptors
'
;
registerSingleton
(
ISearchWorkbenchService
,
SearchWorkbenchService
,
true
);
...
...
src/vs/workbench/contrib/search/browser/searchActions.ts
浏览文件 @
7879818f
...
...
@@ -29,7 +29,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import
{
SearchViewPaneContainer
}
from
'
vs/workbench/contrib/search/browser/searchViewlet
'
;
import
{
SearchPanel
}
from
'
vs/workbench/contrib/search/browser/searchPanel
'
;
import
{
ITreeNavigator
}
from
'
vs/base/browser/ui/tree/tree
'
;
import
{
createEditorFromSearchResult
,
refreshActiveEditorSearch
,
openNewSearchEditor
}
from
'
vs/workbench/contrib/search/browser/searchEditor
'
;
import
{
createEditorFromSearchResult
,
refreshActiveEditorSearch
,
openNewSearchEditor
}
from
'
vs/workbench/contrib/search/browser/searchEditor
Commands
'
;
import
{
IWorkspaceContextService
}
from
'
vs/platform/workspace/common/workspace
'
;
import
{
IProgressService
,
ProgressLocation
}
from
'
vs/platform/progress/common/progress
'
;
import
{
IQuickInputService
}
from
'
vs/platform/quickinput/common/quickInput
'
;
...
...
src/vs/workbench/contrib/search/browser/searchEditor.ts
浏览文件 @
7879818f
...
...
@@ -5,21 +5,16 @@
import
*
as
DOM
from
'
vs/base/browser/dom
'
;
import
{
StandardKeyboardEvent
}
from
'
vs/base/browser/keyboardEvent
'
;
import
{
coalesce
,
flatten
}
from
'
vs/base/common/arrays
'
;
import
{
CancellationToken
}
from
'
vs/base/common/cancellation
'
;
import
{
KeyCode
,
KeyMod
}
from
'
vs/base/common/keyCodes
'
;
import
*
as
network
from
'
vs/base/common/network
'
;
import
{
repeat
}
from
'
vs/base/common/strings
'
;
import
{
assertIsDefined
}
from
'
vs/base/common/types
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
'
vs/css!./media/searchEditor
'
;
import
{
isCodeEditor
}
from
'
vs/editor/browser/editorBrowser
'
;
import
{
CodeEditorWidget
,
ICodeEditorWidgetOptions
}
from
'
vs/editor/browser/widget/codeEditorWidget
'
;
import
type
{
IEditorOptions
}
from
'
vs/editor/common/config/editorOptions
'
;
import
{
Range
}
from
'
vs/editor/common/core/range
'
;
import
{
EndOfLinePreference
,
TrackedRangeStickiness
}
from
'
vs/editor/common/model
'
;
import
{
TrackedRangeStickiness
}
from
'
vs/editor/common/model
'
;
import
{
IModelService
}
from
'
vs/editor/common/services/modelService
'
;
import
{
IModeService
}
from
'
vs/editor/common/services/modeService
'
;
import
{
localize
}
from
'
vs/nls
'
;
import
{
ICommandService
}
from
'
vs/platform/commands/common/commands
'
;
import
{
IConfigurationService
}
from
'
vs/platform/configuration/common/configuration
'
;
...
...
@@ -28,84 +23,25 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import
{
ILabelService
}
from
'
vs/platform/label/common/label
'
;
import
{
IStorageService
}
from
'
vs/platform/storage/common/storage
'
;
import
{
ITelemetryService
}
from
'
vs/platform/telemetry/common/telemetry
'
;
import
{
searchEditorFindMatch
,
searchEditorFindMatchBorder
}
from
'
vs/platform/theme/common/colorRegistry
'
;
import
{
IThemeService
,
registerThemingParticipant
}
from
'
vs/platform/theme/common/themeService
'
;
import
{
IThemeService
}
from
'
vs/platform/theme/common/themeService
'
;
import
{
IWorkspaceContextService
}
from
'
vs/platform/workspace/common/workspace
'
;
import
{
BaseEditor
}
from
'
vs/workbench/browser/parts/editor/baseEditor
'
;
import
{
EditorInput
,
EditorOptions
}
from
'
vs/workbench/common/editor
'
;
import
{
UntitledTextEditorInput
}
from
'
vs/workbench/common/editor/untitledTextEditorInput
'
;
import
{
ExcludePatternInputWidget
,
PatternInputWidget
}
from
'
vs/workbench/contrib/search/browser/patternInputWidget
'
;
import
{
SearchWidget
}
from
'
vs/workbench/contrib/search/browser/searchWidget
'
;
import
{
ITextQueryBuilderOptions
,
QueryBuilder
}
from
'
vs/workbench/contrib/search/common/queryBuilder
'
;
import
{
getOutOfWorkspaceEditorResources
}
from
'
vs/workbench/contrib/search/common/search
'
;
import
{
FileMatch
,
Match
,
searchMatchComparer
,
SearchModel
,
SearchResult
}
from
'
vs/workbench/contrib/search/common/searchModel
'
;
import
{
IEditorService
}
from
'
vs/workbench/services/editor/common/editorService
'
;
import
{
SearchModel
}
from
'
vs/workbench/contrib/search/common/searchModel
'
;
import
{
IPatternInfo
,
ISearchConfigurationProperties
,
ITextQuery
}
from
'
vs/workbench/services/search/common/search
'
;
import
{
Delayer
}
from
'
vs/base/common/async
'
;
import
{
serializeSearchResultForEditor
,
SearchConfiguration
,
SearchEditorInput
}
from
'
vs/workbench/contrib/search/browser/searchEditorCommands
'
;
const
RESULT_LINE_REGEX
=
/^
(\s
+
)(\d
+
)(
:|
)(\s
+
)(
.*
)
$/
;
type
SearchConfiguration
=
{
query
:
string
,
includes
:
string
,
excludes
:
string
,
contextLines
:
number
,
wholeWord
:
boolean
,
caseSensitive
:
boolean
,
regexp
:
boolean
,
useIgnores
:
boolean
};
let
searchEditorInputInstances
=
0
;
export
class
SearchEditorInput
extends
EditorInput
{
static
readonly
ID
:
string
=
'
workbench.editorinputs.searchEditorInput
'
;
public
config
:
SearchConfiguration
;
private
instanceNumber
:
number
=
searchEditorInputInstances
++
;
constructor
(
config
:
SearchConfiguration
|
undefined
,
@
IModelService
private
readonly
modelService
:
IModelService
,
@
IModeService
private
readonly
modeService
:
IModeService
,
)
{
super
();
if
(
config
===
undefined
)
{
this
.
config
=
{
query
:
''
,
includes
:
''
,
excludes
:
''
,
contextLines
:
0
,
wholeWord
:
false
,
caseSensitive
:
false
,
regexp
:
false
,
useIgnores
:
true
};
}
else
{
this
.
config
=
config
;
}
const
searchResultMode
=
this
.
modeService
.
create
(
'
search-result
'
);
this
.
modelService
.
createModel
(
''
,
searchResultMode
,
this
.
getResource
());
}
getTypeId
():
string
{
return
SearchEditorInput
.
ID
;
}
getResource
():
URI
{
return
URI
.
from
({
scheme
:
'
code-search
'
,
fragment
:
`
${
this
.
instanceNumber
}
`
});
}
getName
():
string
{
return
this
.
config
.
query
?
localize
(
'
searchTitle.withQuery
'
,
"
Search: {0}
"
,
this
.
config
.
query
)
:
localize
(
'
searchTitle
'
,
"
Search
"
);
}
setConfig
(
config
:
SearchConfiguration
)
{
this
.
config
=
config
;
this
.
_onDidChangeLabel
.
fire
();
}
// Using \r\n on Windows inserts an extra newline between results.
const
lineDelimiter
=
'
\n
'
;
async
resolve
()
{
return
null
;
}
dispose
()
{
this
.
modelService
.
destroyModel
(
this
.
getResource
());
super
.
dispose
();
}
}
export
class
SearchEditor
extends
BaseEditor
{
static
readonly
ID
:
string
=
'
workbench.editor.searchEditor
'
;
...
...
@@ -353,343 +289,3 @@ export class SearchEditor extends BaseEditor {
return
this
.
searchResultEditor
.
getModel
();
}
}
// Using \r\n on Windows inserts an extra newline between results.
const
lineDelimiter
=
'
\n
'
;
const
translateRangeLines
=
(
n
:
number
)
=>
(
range
:
Range
)
=>
new
Range
(
range
.
startLineNumber
+
n
,
range
.
startColumn
,
range
.
endLineNumber
+
n
,
range
.
endColumn
);
const
matchToSearchResultFormat
=
(
match
:
Match
):
{
line
:
string
,
ranges
:
Range
[],
lineNumber
:
string
}[]
=>
{
const
getLinePrefix
=
(
i
:
number
)
=>
`
${
match
.
range
().
startLineNumber
+
i
}
`
;
const
fullMatchLines
=
match
.
fullPreviewLines
();
const
largestPrefixSize
=
fullMatchLines
.
reduce
((
largest
,
_
,
i
)
=>
Math
.
max
(
getLinePrefix
(
i
).
length
,
largest
),
0
);
const
results
:
{
line
:
string
,
ranges
:
Range
[],
lineNumber
:
string
}[]
=
[];
fullMatchLines
.
forEach
((
sourceLine
,
i
)
=>
{
const
lineNumber
=
getLinePrefix
(
i
);
const
paddingStr
=
repeat
(
'
'
,
largestPrefixSize
-
lineNumber
.
length
);
const
prefix
=
`
${
lineNumber
}
:
${
paddingStr
}
`
;
const
prefixOffset
=
prefix
.
length
;
const
line
=
(
prefix
+
sourceLine
).
replace
(
/
\r?\n?
$/
,
''
);
const
rangeOnThisLine
=
({
start
,
end
}:
{
start
?:
number
;
end
?:
number
;
})
=>
new
Range
(
1
,
(
start
??
1
)
+
prefixOffset
,
1
,
(
end
??
sourceLine
.
length
+
1
)
+
prefixOffset
);
const
matchRange
=
match
.
range
();
const
matchIsSingleLine
=
matchRange
.
startLineNumber
===
matchRange
.
endLineNumber
;
let
lineRange
;
if
(
matchIsSingleLine
)
{
lineRange
=
(
rangeOnThisLine
({
start
:
matchRange
.
startColumn
,
end
:
matchRange
.
endColumn
}));
}
else
if
(
i
===
0
)
{
lineRange
=
(
rangeOnThisLine
({
start
:
matchRange
.
startColumn
}));
}
else
if
(
i
===
fullMatchLines
.
length
-
1
)
{
lineRange
=
(
rangeOnThisLine
({
end
:
matchRange
.
endColumn
}));
}
else
{
lineRange
=
(
rangeOnThisLine
({}));
}
results
.
push
({
lineNumber
:
lineNumber
,
line
,
ranges
:
[
lineRange
]
});
});
return
results
;
};
type
SearchResultSerialization
=
{
text
:
string
[],
matchRanges
:
Range
[]
};
function
fileMatchToSearchResultFormat
(
fileMatch
:
FileMatch
,
labelFormatter
:
(
x
:
URI
)
=>
string
):
SearchResultSerialization
{
const
serializedMatches
=
flatten
(
fileMatch
.
matches
()
.
sort
(
searchMatchComparer
)
.
map
(
match
=>
matchToSearchResultFormat
(
match
)));
const
uriString
=
labelFormatter
(
fileMatch
.
resource
);
let
text
:
string
[]
=
[
`
${
uriString
}
:`
];
let
matchRanges
:
Range
[]
=
[];
const
targetLineNumberToOffset
:
Record
<
string
,
number
>
=
{};
const
context
:
{
line
:
string
,
lineNumber
:
number
}[]
=
[];
fileMatch
.
context
.
forEach
((
line
,
lineNumber
)
=>
context
.
push
({
line
,
lineNumber
}));
context
.
sort
((
a
,
b
)
=>
a
.
lineNumber
-
b
.
lineNumber
);
let
lastLine
:
number
|
undefined
=
undefined
;
const
seenLines
=
new
Set
<
string
>
();
serializedMatches
.
forEach
(
match
=>
{
if
(
!
seenLines
.
has
(
match
.
line
))
{
while
(
context
.
length
&&
context
[
0
].
lineNumber
<
+
match
.
lineNumber
)
{
const
{
line
,
lineNumber
}
=
context
.
shift
()
!
;
if
(
lastLine
!==
undefined
&&
lineNumber
!==
lastLine
+
1
)
{
text
.
push
(
''
);
}
text
.
push
(
`
${
lineNumber
}
${
line
}
`
);
lastLine
=
lineNumber
;
}
targetLineNumberToOffset
[
match
.
lineNumber
]
=
text
.
length
;
seenLines
.
add
(
match
.
line
);
text
.
push
(
match
.
line
);
lastLine
=
+
match
.
lineNumber
;
}
matchRanges
.
push
(...
match
.
ranges
.
map
(
translateRangeLines
(
targetLineNumberToOffset
[
match
.
lineNumber
])));
});
while
(
context
.
length
)
{
const
{
line
,
lineNumber
}
=
context
.
shift
()
!
;
text
.
push
(
`
${
lineNumber
}
${
line
}
`
);
}
return
{
text
,
matchRanges
};
}
const
flattenSearchResultSerializations
=
(
serializations
:
SearchResultSerialization
[]):
SearchResultSerialization
=>
{
let
text
:
string
[]
=
[];
let
matchRanges
:
Range
[]
=
[];
serializations
.
forEach
(
serialized
=>
{
serialized
.
matchRanges
.
map
(
translateRangeLines
(
text
.
length
)).
forEach
(
range
=>
matchRanges
.
push
(
range
));
serialized
.
text
.
forEach
(
line
=>
text
.
push
(
line
));
text
.
push
(
''
);
// new line
});
return
{
text
,
matchRanges
};
};
const
contentPatternToSearchResultHeader
=
(
pattern
:
ITextQuery
|
null
,
includes
:
string
,
excludes
:
string
,
contextLines
:
number
):
string
[]
=>
{
if
(
!
pattern
)
{
return
[];
}
const
removeNullFalseAndUndefined
=
<
T
>
(
a
:
(
T
|
null
|
false
|
undefined
)[])
=>
a
.
filter
(
a
=>
a
!==
false
&&
a
!==
null
&&
a
!==
undefined
)
as
T
[];
const
escapeNewlines
=
(
str
:
string
)
=>
str
.
replace
(
/
\\
/g
,
'
\\\\
'
).
replace
(
/
\n
/g
,
'
\\
n
'
);
return
removeNullFalseAndUndefined
([
`# Query:
${
escapeNewlines
(
pattern
.
contentPattern
.
pattern
)}
`
,
(
pattern
.
contentPattern
.
isCaseSensitive
||
pattern
.
contentPattern
.
isWordMatch
||
pattern
.
contentPattern
.
isRegExp
||
pattern
.
userDisabledExcludesAndIgnoreFiles
)
&&
`# Flags:
${
coalesce
([
pattern
.
contentPattern
.
isCaseSensitive
&&
'
CaseSensitive
'
,
pattern
.
contentPattern
.
isWordMatch
&&
'
WordMatch
'
,
pattern
.
contentPattern
.
isRegExp
&&
'
RegExp
'
,
pattern
.
userDisabledExcludesAndIgnoreFiles
&&
'
IgnoreExcludeSettings
'
]).
join
(
'
'
)}
`
,
includes
?
`# Including:
${
includes
}
`
:
undefined
,
excludes
?
`# Excluding:
${
excludes
}
`
:
undefined
,
contextLines
?
`# ContextLines:
${
contextLines
}
`
:
undefined
,
''
]);
};
type
SearchHeader
=
{
pattern
:
string
;
flags
:
{
regex
:
boolean
;
wholeWord
:
boolean
;
caseSensitive
:
boolean
;
ignoreExcludes
:
boolean
;
};
includes
:
string
;
excludes
:
string
;
context
:
number
|
undefined
;
};
const
searchHeaderToContentPattern
=
(
header
:
string
[]):
SearchHeader
=>
{
const
query
:
SearchHeader
=
{
pattern
:
''
,
flags
:
{
regex
:
false
,
caseSensitive
:
false
,
ignoreExcludes
:
false
,
wholeWord
:
false
},
includes
:
''
,
excludes
:
''
,
context
:
undefined
};
const
unescapeNewlines
=
(
str
:
string
)
=>
{
let
out
=
''
;
for
(
let
i
=
0
;
i
<
str
.
length
;
i
++
)
{
if
(
str
[
i
]
===
'
\\
'
)
{
i
++
;
const
escaped
=
str
[
i
];
if
(
escaped
===
'
n
'
)
{
out
+=
'
\n
'
;
}
else
if
(
escaped
===
'
\\
'
)
{
out
+=
'
\\
'
;
}
else
{
throw
Error
(
localize
(
'
invalidQueryStringError
'
,
"
All backslashes in Query string must be escaped (
\\\\
)
"
));
}
}
else
{
out
+=
str
[
i
];
}
}
return
out
;
};
const
parseYML
=
/^#
([^
:
]
*
)
:
(
.*
)
$/
;
for
(
const
line
of
header
)
{
const
parsed
=
parseYML
.
exec
(
line
);
if
(
!
parsed
)
{
continue
;
}
const
[,
key
,
value
]
=
parsed
;
switch
(
key
)
{
case
'
Query
'
:
query
.
pattern
=
unescapeNewlines
(
value
);
break
;
case
'
Including
'
:
query
.
includes
=
value
;
break
;
case
'
Excluding
'
:
query
.
excludes
=
value
;
break
;
case
'
ContextLines
'
:
query
.
context
=
+
value
;
break
;
case
'
Flags
'
:
{
query
.
flags
=
{
regex
:
value
.
indexOf
(
'
RegExp
'
)
!==
-
1
,
caseSensitive
:
value
.
indexOf
(
'
CaseSensitive
'
)
!==
-
1
,
ignoreExcludes
:
value
.
indexOf
(
'
IgnoreExcludeSettings
'
)
!==
-
1
,
wholeWord
:
value
.
indexOf
(
'
WordMatch
'
)
!==
-
1
};
}
}
}
return
query
;
};
const
serializeSearchResultForEditor
=
(
searchResult
:
SearchResult
,
rawIncludePattern
:
string
,
rawExcludePattern
:
string
,
contextLines
:
number
,
labelFormatter
:
(
x
:
URI
)
=>
string
,
includeHeader
:
boolean
):
SearchResultSerialization
=>
{
const
header
=
includeHeader
?
contentPatternToSearchResultHeader
(
searchResult
.
query
,
rawIncludePattern
,
rawExcludePattern
,
contextLines
)
:
[];
const
allResults
=
flattenSearchResultSerializations
(
flatten
(
searchResult
.
folderMatches
().
sort
(
searchMatchComparer
)
.
map
(
folderMatch
=>
folderMatch
.
matches
().
sort
(
searchMatchComparer
)
.
map
(
fileMatch
=>
fileMatchToSearchResultFormat
(
fileMatch
,
labelFormatter
)))));
return
{
matchRanges
:
allResults
.
matchRanges
.
map
(
translateRangeLines
(
header
.
length
)),
text
:
header
.
concat
(
allResults
.
text
)
};
};
export
const
refreshActiveEditorSearch
=
async
(
contextLines
:
number
|
undefined
,
editorService
:
IEditorService
,
instantiationService
:
IInstantiationService
,
contextService
:
IWorkspaceContextService
,
labelService
:
ILabelService
,
configurationService
:
IConfigurationService
)
=>
{
const
editorWidget
=
editorService
.
activeTextEditorWidget
;
if
(
!
isCodeEditor
(
editorWidget
))
{
return
;
}
const
textModel
=
editorWidget
.
getModel
();
if
(
!
textModel
)
{
return
;
}
const
header
=
textModel
.
getValueInRange
(
new
Range
(
1
,
1
,
5
,
1
),
EndOfLinePreference
.
LF
)
.
split
(
lineDelimiter
)
.
filter
(
line
=>
line
.
indexOf
(
'
#
'
)
===
0
);
const
contentPattern
=
searchHeaderToContentPattern
(
header
);
const
content
:
IPatternInfo
=
{
pattern
:
contentPattern
.
pattern
,
isRegExp
:
contentPattern
.
flags
.
regex
,
isCaseSensitive
:
contentPattern
.
flags
.
caseSensitive
,
isWordMatch
:
contentPattern
.
flags
.
wholeWord
};
contextLines
=
contextLines
??
contentPattern
.
context
??
0
;
const
options
:
ITextQueryBuilderOptions
=
{
_reason
:
'
searchEditor
'
,
extraFileResources
:
instantiationService
.
invokeFunction
(
getOutOfWorkspaceEditorResources
),
maxResults
:
10000
,
disregardIgnoreFiles
:
contentPattern
.
flags
.
ignoreExcludes
,
disregardExcludeSettings
:
contentPattern
.
flags
.
ignoreExcludes
,
excludePattern
:
contentPattern
.
excludes
,
includePattern
:
contentPattern
.
includes
,
previewOptions
:
{
matchLines
:
1
,
charsPerLine
:
1000
},
afterContext
:
contextLines
,
beforeContext
:
contextLines
,
isSmartCase
:
configurationService
.
getValue
<
ISearchConfigurationProperties
>
(
'
search
'
).
smartCase
,
expandPatterns
:
true
};
const
folderResources
=
contextService
.
getWorkspace
().
folders
;
let
query
:
ITextQuery
;
try
{
const
queryBuilder
=
instantiationService
.
createInstance
(
QueryBuilder
);
query
=
queryBuilder
.
text
(
content
,
folderResources
.
map
(
folder
=>
folder
.
uri
),
options
);
}
catch
(
err
)
{
return
;
}
const
searchModel
=
instantiationService
.
createInstance
(
SearchModel
);
await
searchModel
.
search
(
query
);
const
labelFormatter
=
(
uri
:
URI
):
string
=>
labelService
.
getUriLabel
(
uri
,
{
relative
:
true
});
const
results
=
serializeSearchResultForEditor
(
searchModel
.
searchResult
,
contentPattern
.
includes
,
contentPattern
.
excludes
,
contextLines
,
labelFormatter
,
false
);
textModel
.
setValue
(
results
.
text
.
join
(
lineDelimiter
));
textModel
.
deltaDecorations
([],
results
.
matchRanges
.
map
(
range
=>
({
range
,
options
:
{
className
:
'
searchEditorFindMatch
'
,
stickiness
:
TrackedRangeStickiness
.
NeverGrowsWhenTypingAtEdges
}
})));
};
export
const
openNewSearchEditor
=
async
(
editorService
:
IEditorService
,
instantiationService
:
IInstantiationService
)
=>
{
await
editorService
.
openEditor
(
instantiationService
.
createInstance
(
SearchEditorInput
,
undefined
),
{
pinned
:
true
});
};
export
const
createEditorFromSearchResult
=
async
(
searchResult
:
SearchResult
,
rawIncludePattern
:
string
,
rawExcludePattern
:
string
,
labelService
:
ILabelService
,
editorService
:
IEditorService
,
instantiationService
:
IInstantiationService
)
=>
{
if
(
!
searchResult
.
query
)
{
console
.
error
(
'
Expected searchResult.query to be defined. Got
'
,
searchResult
);
return
;
}
const
searchTerm
=
searchResult
.
query
.
contentPattern
.
pattern
.
replace
(
/
[^\w
-_.
]
/g
,
''
)
||
'
Search
'
;
const
labelFormatter
=
(
uri
:
URI
):
string
=>
labelService
.
getUriLabel
(
uri
,
{
relative
:
true
});
const
results
=
serializeSearchResultForEditor
(
searchResult
,
rawIncludePattern
,
rawExcludePattern
,
0
,
labelFormatter
,
false
);
const
contents
=
results
.
text
.
join
(
lineDelimiter
);
let
possible
=
{
contents
,
mode
:
'
search-result
'
,
resource
:
URI
.
from
({
scheme
:
network
.
Schemas
.
untitled
,
path
:
searchTerm
})
};
let
id
=
0
;
let
existing
=
editorService
.
getOpened
(
possible
);
while
(
existing
)
{
if
(
existing
instanceof
UntitledTextEditorInput
)
{
const
model
=
await
existing
.
resolve
();
const
existingContents
=
model
.
textEditorModel
.
getValue
(
EndOfLinePreference
.
LF
);
if
(
existingContents
===
contents
)
{
break
;
}
}
possible
.
resource
=
possible
.
resource
.
with
({
path
:
searchTerm
+
'
-
'
+
++
id
});
existing
=
editorService
.
getOpened
(
possible
);
}
const
editor
=
await
editorService
.
openEditor
(
instantiationService
.
createInstance
(
SearchEditorInput
,
{
query
:
searchResult
.
query
.
contentPattern
.
pattern
,
regexp
:
!!
searchResult
.
query
.
contentPattern
.
isRegExp
,
caseSensitive
:
!!
searchResult
.
query
.
contentPattern
.
isCaseSensitive
,
wholeWord
:
!!
searchResult
.
query
.
contentPattern
.
isWordMatch
,
includes
:
rawIncludePattern
,
excludes
:
rawExcludePattern
,
contextLines
:
0
,
useIgnores
:
!
searchResult
.
query
.
userDisabledExcludesAndIgnoreFiles
,
}),
{
pinned
:
true
})
as
SearchEditor
;
const
model
=
assertIsDefined
(
editor
.
getModel
());
model
.
setValue
(
contents
);
model
.
deltaDecorations
([],
results
.
matchRanges
.
map
(
range
=>
({
range
,
options
:
{
className
:
'
searchEditorFindMatch
'
,
stickiness
:
TrackedRangeStickiness
.
NeverGrowsWhenTypingAtEdges
}
})));
};
registerThemingParticipant
((
theme
,
collector
)
=>
{
collector
.
addRule
(
`.monaco-editor .searchEditorFindMatch { background-color:
${
theme
.
getColor
(
searchEditorFindMatch
)}
; }`
);
const
findMatchHighlightBorder
=
theme
.
getColor
(
searchEditorFindMatchBorder
);
if
(
findMatchHighlightBorder
)
{
collector
.
addRule
(
`.monaco-editor .searchEditorFindMatch { border: 1px
${
theme
.
type
===
'
hc
'
?
'
dotted
'
:
'
solid
'
}
${
findMatchHighlightBorder
}
; box-sizing: border-box; }`
);
}
});
src/vs/workbench/contrib/search/browser/searchEditorCommands.ts
0 → 100644
浏览文件 @
7879818f
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
{
coalesce
,
flatten
}
from
'
vs/base/common/arrays
'
;
import
*
as
network
from
'
vs/base/common/network
'
;
import
{
repeat
}
from
'
vs/base/common/strings
'
;
import
{
assertIsDefined
}
from
'
vs/base/common/types
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
'
vs/css!./media/searchEditor
'
;
import
{
isCodeEditor
}
from
'
vs/editor/browser/editorBrowser
'
;
import
{
Range
}
from
'
vs/editor/common/core/range
'
;
import
{
EndOfLinePreference
,
TrackedRangeStickiness
}
from
'
vs/editor/common/model
'
;
import
{
localize
}
from
'
vs/nls
'
;
import
{
IConfigurationService
}
from
'
vs/platform/configuration/common/configuration
'
;
import
{
IInstantiationService
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
ILabelService
}
from
'
vs/platform/label/common/label
'
;
import
{
searchEditorFindMatch
,
searchEditorFindMatchBorder
}
from
'
vs/platform/theme/common/colorRegistry
'
;
import
{
registerThemingParticipant
}
from
'
vs/platform/theme/common/themeService
'
;
import
{
IWorkspaceContextService
}
from
'
vs/platform/workspace/common/workspace
'
;
import
{
UntitledTextEditorInput
}
from
'
vs/workbench/common/editor/untitledTextEditorInput
'
;
import
{
ITextQueryBuilderOptions
,
QueryBuilder
}
from
'
vs/workbench/contrib/search/common/queryBuilder
'
;
import
{
getOutOfWorkspaceEditorResources
}
from
'
vs/workbench/contrib/search/common/search
'
;
import
{
FileMatch
,
Match
,
searchMatchComparer
,
SearchModel
,
SearchResult
}
from
'
vs/workbench/contrib/search/common/searchModel
'
;
import
{
IEditorService
}
from
'
vs/workbench/services/editor/common/editorService
'
;
import
{
IPatternInfo
,
ISearchConfigurationProperties
,
ITextQuery
}
from
'
vs/workbench/services/search/common/search
'
;
import
{
EditorInput
}
from
'
vs/workbench/common/editor
'
;
import
{
IModelService
}
from
'
vs/editor/common/services/modelService
'
;
import
{
IModeService
}
from
'
vs/editor/common/services/modeService
'
;
import
{
SearchEditor
}
from
'
vs/workbench/contrib/search/browser/searchEditor
'
;
export
type
SearchConfiguration
=
{
query
:
string
,
includes
:
string
,
excludes
:
string
,
contextLines
:
number
,
wholeWord
:
boolean
,
caseSensitive
:
boolean
,
regexp
:
boolean
,
useIgnores
:
boolean
};
let
searchEditorInputInstances
=
0
;
export
class
SearchEditorInput
extends
EditorInput
{
static
readonly
ID
:
string
=
'
workbench.editorinputs.searchEditorInput
'
;
public
config
:
SearchConfiguration
;
private
instanceNumber
:
number
=
searchEditorInputInstances
++
;
constructor
(
config
:
SearchConfiguration
|
undefined
,
@
IModelService
private
readonly
modelService
:
IModelService
,
@
IModeService
private
readonly
modeService
:
IModeService
,
)
{
super
();
if
(
config
===
undefined
)
{
this
.
config
=
{
query
:
''
,
includes
:
''
,
excludes
:
''
,
contextLines
:
0
,
wholeWord
:
false
,
caseSensitive
:
false
,
regexp
:
false
,
useIgnores
:
true
};
}
else
{
this
.
config
=
config
;
}
const
searchResultMode
=
this
.
modeService
.
create
(
'
search-result
'
);
this
.
modelService
.
createModel
(
''
,
searchResultMode
,
this
.
getResource
());
}
getTypeId
():
string
{
return
SearchEditorInput
.
ID
;
}
getResource
():
URI
{
return
URI
.
from
({
scheme
:
'
code-search
'
,
fragment
:
`
${
this
.
instanceNumber
}
`
});
}
getName
():
string
{
return
this
.
config
.
query
?
localize
(
'
searchTitle.withQuery
'
,
"
Search: {0}
"
,
this
.
config
.
query
)
:
localize
(
'
searchTitle
'
,
"
Search
"
);
}
setConfig
(
config
:
SearchConfiguration
)
{
this
.
config
=
config
;
this
.
_onDidChangeLabel
.
fire
();
}
async
resolve
()
{
return
null
;
}
dispose
()
{
this
.
modelService
.
destroyModel
(
this
.
getResource
());
super
.
dispose
();
}
}
// Using \r\n on Windows inserts an extra newline between results.
const
lineDelimiter
=
'
\n
'
;
const
translateRangeLines
=
(
n
:
number
)
=>
(
range
:
Range
)
=>
new
Range
(
range
.
startLineNumber
+
n
,
range
.
startColumn
,
range
.
endLineNumber
+
n
,
range
.
endColumn
);
const
matchToSearchResultFormat
=
(
match
:
Match
):
{
line
:
string
,
ranges
:
Range
[],
lineNumber
:
string
}[]
=>
{
const
getLinePrefix
=
(
i
:
number
)
=>
`
${
match
.
range
().
startLineNumber
+
i
}
`
;
const
fullMatchLines
=
match
.
fullPreviewLines
();
const
largestPrefixSize
=
fullMatchLines
.
reduce
((
largest
,
_
,
i
)
=>
Math
.
max
(
getLinePrefix
(
i
).
length
,
largest
),
0
);
const
results
:
{
line
:
string
,
ranges
:
Range
[],
lineNumber
:
string
}[]
=
[];
fullMatchLines
.
forEach
((
sourceLine
,
i
)
=>
{
const
lineNumber
=
getLinePrefix
(
i
);
const
paddingStr
=
repeat
(
'
'
,
largestPrefixSize
-
lineNumber
.
length
);
const
prefix
=
`
${
lineNumber
}
:
${
paddingStr
}
`
;
const
prefixOffset
=
prefix
.
length
;
const
line
=
(
prefix
+
sourceLine
).
replace
(
/
\r?\n?
$/
,
''
);
const
rangeOnThisLine
=
({
start
,
end
}:
{
start
?:
number
;
end
?:
number
;
})
=>
new
Range
(
1
,
(
start
??
1
)
+
prefixOffset
,
1
,
(
end
??
sourceLine
.
length
+
1
)
+
prefixOffset
);
const
matchRange
=
match
.
range
();
const
matchIsSingleLine
=
matchRange
.
startLineNumber
===
matchRange
.
endLineNumber
;
let
lineRange
;
if
(
matchIsSingleLine
)
{
lineRange
=
(
rangeOnThisLine
({
start
:
matchRange
.
startColumn
,
end
:
matchRange
.
endColumn
}));
}
else
if
(
i
===
0
)
{
lineRange
=
(
rangeOnThisLine
({
start
:
matchRange
.
startColumn
}));
}
else
if
(
i
===
fullMatchLines
.
length
-
1
)
{
lineRange
=
(
rangeOnThisLine
({
end
:
matchRange
.
endColumn
}));
}
else
{
lineRange
=
(
rangeOnThisLine
({}));
}
results
.
push
({
lineNumber
:
lineNumber
,
line
,
ranges
:
[
lineRange
]
});
});
return
results
;
};
type
SearchResultSerialization
=
{
text
:
string
[],
matchRanges
:
Range
[]
};
function
fileMatchToSearchResultFormat
(
fileMatch
:
FileMatch
,
labelFormatter
:
(
x
:
URI
)
=>
string
):
SearchResultSerialization
{
const
serializedMatches
=
flatten
(
fileMatch
.
matches
()
.
sort
(
searchMatchComparer
)
.
map
(
match
=>
matchToSearchResultFormat
(
match
)));
const
uriString
=
labelFormatter
(
fileMatch
.
resource
);
let
text
:
string
[]
=
[
`
${
uriString
}
:`
];
let
matchRanges
:
Range
[]
=
[];
const
targetLineNumberToOffset
:
Record
<
string
,
number
>
=
{};
const
context
:
{
line
:
string
,
lineNumber
:
number
}[]
=
[];
fileMatch
.
context
.
forEach
((
line
,
lineNumber
)
=>
context
.
push
({
line
,
lineNumber
}));
context
.
sort
((
a
,
b
)
=>
a
.
lineNumber
-
b
.
lineNumber
);
let
lastLine
:
number
|
undefined
=
undefined
;
const
seenLines
=
new
Set
<
string
>
();
serializedMatches
.
forEach
(
match
=>
{
if
(
!
seenLines
.
has
(
match
.
line
))
{
while
(
context
.
length
&&
context
[
0
].
lineNumber
<
+
match
.
lineNumber
)
{
const
{
line
,
lineNumber
}
=
context
.
shift
()
!
;
if
(
lastLine
!==
undefined
&&
lineNumber
!==
lastLine
+
1
)
{
text
.
push
(
''
);
}
text
.
push
(
`
${
lineNumber
}
${
line
}
`
);
lastLine
=
lineNumber
;
}
targetLineNumberToOffset
[
match
.
lineNumber
]
=
text
.
length
;
seenLines
.
add
(
match
.
line
);
text
.
push
(
match
.
line
);
lastLine
=
+
match
.
lineNumber
;
}
matchRanges
.
push
(...
match
.
ranges
.
map
(
translateRangeLines
(
targetLineNumberToOffset
[
match
.
lineNumber
])));
});
while
(
context
.
length
)
{
const
{
line
,
lineNumber
}
=
context
.
shift
()
!
;
text
.
push
(
`
${
lineNumber
}
${
line
}
`
);
}
return
{
text
,
matchRanges
};
}
const
flattenSearchResultSerializations
=
(
serializations
:
SearchResultSerialization
[]):
SearchResultSerialization
=>
{
let
text
:
string
[]
=
[];
let
matchRanges
:
Range
[]
=
[];
serializations
.
forEach
(
serialized
=>
{
serialized
.
matchRanges
.
map
(
translateRangeLines
(
text
.
length
)).
forEach
(
range
=>
matchRanges
.
push
(
range
));
serialized
.
text
.
forEach
(
line
=>
text
.
push
(
line
));
text
.
push
(
''
);
// new line
});
return
{
text
,
matchRanges
};
};
const
contentPatternToSearchResultHeader
=
(
pattern
:
ITextQuery
|
null
,
includes
:
string
,
excludes
:
string
,
contextLines
:
number
):
string
[]
=>
{
if
(
!
pattern
)
{
return
[];
}
const
removeNullFalseAndUndefined
=
<
T
>
(
a
:
(
T
|
null
|
false
|
undefined
)[])
=>
a
.
filter
(
a
=>
a
!==
false
&&
a
!==
null
&&
a
!==
undefined
)
as
T
[];
const
escapeNewlines
=
(
str
:
string
)
=>
str
.
replace
(
/
\\
/g
,
'
\\\\
'
).
replace
(
/
\n
/g
,
'
\\
n
'
);
return
removeNullFalseAndUndefined
([
`# Query:
${
escapeNewlines
(
pattern
.
contentPattern
.
pattern
)}
`
,
(
pattern
.
contentPattern
.
isCaseSensitive
||
pattern
.
contentPattern
.
isWordMatch
||
pattern
.
contentPattern
.
isRegExp
||
pattern
.
userDisabledExcludesAndIgnoreFiles
)
&&
`# Flags:
${
coalesce
([
pattern
.
contentPattern
.
isCaseSensitive
&&
'
CaseSensitive
'
,
pattern
.
contentPattern
.
isWordMatch
&&
'
WordMatch
'
,
pattern
.
contentPattern
.
isRegExp
&&
'
RegExp
'
,
pattern
.
userDisabledExcludesAndIgnoreFiles
&&
'
IgnoreExcludeSettings
'
]).
join
(
'
'
)}
`
,
includes
?
`# Including:
${
includes
}
`
:
undefined
,
excludes
?
`# Excluding:
${
excludes
}
`
:
undefined
,
contextLines
?
`# ContextLines:
${
contextLines
}
`
:
undefined
,
''
]);
};
type
SearchHeader
=
{
pattern
:
string
;
flags
:
{
regex
:
boolean
;
wholeWord
:
boolean
;
caseSensitive
:
boolean
;
ignoreExcludes
:
boolean
;
};
includes
:
string
;
excludes
:
string
;
context
:
number
|
undefined
;
};
const
searchHeaderToContentPattern
=
(
header
:
string
[]):
SearchHeader
=>
{
const
query
:
SearchHeader
=
{
pattern
:
''
,
flags
:
{
regex
:
false
,
caseSensitive
:
false
,
ignoreExcludes
:
false
,
wholeWord
:
false
},
includes
:
''
,
excludes
:
''
,
context
:
undefined
};
const
unescapeNewlines
=
(
str
:
string
)
=>
{
let
out
=
''
;
for
(
let
i
=
0
;
i
<
str
.
length
;
i
++
)
{
if
(
str
[
i
]
===
'
\\
'
)
{
i
++
;
const
escaped
=
str
[
i
];
if
(
escaped
===
'
n
'
)
{
out
+=
'
\n
'
;
}
else
if
(
escaped
===
'
\\
'
)
{
out
+=
'
\\
'
;
}
else
{
throw
Error
(
localize
(
'
invalidQueryStringError
'
,
"
All backslashes in Query string must be escaped (
\\\\
)
"
));
}
}
else
{
out
+=
str
[
i
];
}
}
return
out
;
};
const
parseYML
=
/^#
([^
:
]
*
)
:
(
.*
)
$/
;
for
(
const
line
of
header
)
{
const
parsed
=
parseYML
.
exec
(
line
);
if
(
!
parsed
)
{
continue
;
}
const
[,
key
,
value
]
=
parsed
;
switch
(
key
)
{
case
'
Query
'
:
query
.
pattern
=
unescapeNewlines
(
value
);
break
;
case
'
Including
'
:
query
.
includes
=
value
;
break
;
case
'
Excluding
'
:
query
.
excludes
=
value
;
break
;
case
'
ContextLines
'
:
query
.
context
=
+
value
;
break
;
case
'
Flags
'
:
{
query
.
flags
=
{
regex
:
value
.
indexOf
(
'
RegExp
'
)
!==
-
1
,
caseSensitive
:
value
.
indexOf
(
'
CaseSensitive
'
)
!==
-
1
,
ignoreExcludes
:
value
.
indexOf
(
'
IgnoreExcludeSettings
'
)
!==
-
1
,
wholeWord
:
value
.
indexOf
(
'
WordMatch
'
)
!==
-
1
};
}
}
}
return
query
;
};
export
const
serializeSearchResultForEditor
=
(
searchResult
:
SearchResult
,
rawIncludePattern
:
string
,
rawExcludePattern
:
string
,
contextLines
:
number
,
labelFormatter
:
(
x
:
URI
)
=>
string
,
includeHeader
:
boolean
):
SearchResultSerialization
=>
{
const
header
=
includeHeader
?
contentPatternToSearchResultHeader
(
searchResult
.
query
,
rawIncludePattern
,
rawExcludePattern
,
contextLines
)
:
[];
const
allResults
=
flattenSearchResultSerializations
(
flatten
(
searchResult
.
folderMatches
().
sort
(
searchMatchComparer
)
.
map
(
folderMatch
=>
folderMatch
.
matches
().
sort
(
searchMatchComparer
)
.
map
(
fileMatch
=>
fileMatchToSearchResultFormat
(
fileMatch
,
labelFormatter
)))));
return
{
matchRanges
:
allResults
.
matchRanges
.
map
(
translateRangeLines
(
header
.
length
)),
text
:
header
.
concat
(
allResults
.
text
)
};
};
export
const
refreshActiveEditorSearch
=
async
(
contextLines
:
number
|
undefined
,
editorService
:
IEditorService
,
instantiationService
:
IInstantiationService
,
contextService
:
IWorkspaceContextService
,
labelService
:
ILabelService
,
configurationService
:
IConfigurationService
)
=>
{
const
editorWidget
=
editorService
.
activeTextEditorWidget
;
if
(
!
isCodeEditor
(
editorWidget
))
{
return
;
}
const
textModel
=
editorWidget
.
getModel
();
if
(
!
textModel
)
{
return
;
}
const
header
=
textModel
.
getValueInRange
(
new
Range
(
1
,
1
,
5
,
1
),
EndOfLinePreference
.
LF
)
.
split
(
lineDelimiter
)
.
filter
(
line
=>
line
.
indexOf
(
'
#
'
)
===
0
);
const
contentPattern
=
searchHeaderToContentPattern
(
header
);
const
content
:
IPatternInfo
=
{
pattern
:
contentPattern
.
pattern
,
isRegExp
:
contentPattern
.
flags
.
regex
,
isCaseSensitive
:
contentPattern
.
flags
.
caseSensitive
,
isWordMatch
:
contentPattern
.
flags
.
wholeWord
};
contextLines
=
contextLines
??
contentPattern
.
context
??
0
;
const
options
:
ITextQueryBuilderOptions
=
{
_reason
:
'
searchEditor
'
,
extraFileResources
:
instantiationService
.
invokeFunction
(
getOutOfWorkspaceEditorResources
),
maxResults
:
10000
,
disregardIgnoreFiles
:
contentPattern
.
flags
.
ignoreExcludes
,
disregardExcludeSettings
:
contentPattern
.
flags
.
ignoreExcludes
,
excludePattern
:
contentPattern
.
excludes
,
includePattern
:
contentPattern
.
includes
,
previewOptions
:
{
matchLines
:
1
,
charsPerLine
:
1000
},
afterContext
:
contextLines
,
beforeContext
:
contextLines
,
isSmartCase
:
configurationService
.
getValue
<
ISearchConfigurationProperties
>
(
'
search
'
).
smartCase
,
expandPatterns
:
true
};
const
folderResources
=
contextService
.
getWorkspace
().
folders
;
let
query
:
ITextQuery
;
try
{
const
queryBuilder
=
instantiationService
.
createInstance
(
QueryBuilder
);
query
=
queryBuilder
.
text
(
content
,
folderResources
.
map
(
folder
=>
folder
.
uri
),
options
);
}
catch
(
err
)
{
return
;
}
const
searchModel
=
instantiationService
.
createInstance
(
SearchModel
);
await
searchModel
.
search
(
query
);
const
labelFormatter
=
(
uri
:
URI
):
string
=>
labelService
.
getUriLabel
(
uri
,
{
relative
:
true
});
const
results
=
serializeSearchResultForEditor
(
searchModel
.
searchResult
,
contentPattern
.
includes
,
contentPattern
.
excludes
,
contextLines
,
labelFormatter
,
false
);
textModel
.
setValue
(
results
.
text
.
join
(
lineDelimiter
));
textModel
.
deltaDecorations
([],
results
.
matchRanges
.
map
(
range
=>
({
range
,
options
:
{
className
:
'
searchEditorFindMatch
'
,
stickiness
:
TrackedRangeStickiness
.
NeverGrowsWhenTypingAtEdges
}
})));
};
export
const
openNewSearchEditor
=
async
(
editorService
:
IEditorService
,
instantiationService
:
IInstantiationService
)
=>
{
await
editorService
.
openEditor
(
instantiationService
.
createInstance
(
SearchEditorInput
,
undefined
),
{
pinned
:
true
});
};
export
const
createEditorFromSearchResult
=
async
(
searchResult
:
SearchResult
,
rawIncludePattern
:
string
,
rawExcludePattern
:
string
,
labelService
:
ILabelService
,
editorService
:
IEditorService
,
instantiationService
:
IInstantiationService
)
=>
{
if
(
!
searchResult
.
query
)
{
console
.
error
(
'
Expected searchResult.query to be defined. Got
'
,
searchResult
);
return
;
}
const
searchTerm
=
searchResult
.
query
.
contentPattern
.
pattern
.
replace
(
/
[^\w
-_.
]
/g
,
''
)
||
'
Search
'
;
const
labelFormatter
=
(
uri
:
URI
):
string
=>
labelService
.
getUriLabel
(
uri
,
{
relative
:
true
});
const
results
=
serializeSearchResultForEditor
(
searchResult
,
rawIncludePattern
,
rawExcludePattern
,
0
,
labelFormatter
,
false
);
const
contents
=
results
.
text
.
join
(
lineDelimiter
);
let
possible
=
{
contents
,
mode
:
'
search-result
'
,
resource
:
URI
.
from
({
scheme
:
network
.
Schemas
.
untitled
,
path
:
searchTerm
})
};
let
id
=
0
;
let
existing
=
editorService
.
getOpened
(
possible
);
while
(
existing
)
{
if
(
existing
instanceof
UntitledTextEditorInput
)
{
const
model
=
await
existing
.
resolve
();
const
existingContents
=
model
.
textEditorModel
.
getValue
(
EndOfLinePreference
.
LF
);
if
(
existingContents
===
contents
)
{
break
;
}
}
possible
.
resource
=
possible
.
resource
.
with
({
path
:
searchTerm
+
'
-
'
+
++
id
});
existing
=
editorService
.
getOpened
(
possible
);
}
const
editor
=
await
editorService
.
openEditor
(
instantiationService
.
createInstance
(
SearchEditorInput
,
{
query
:
searchResult
.
query
.
contentPattern
.
pattern
,
regexp
:
!!
searchResult
.
query
.
contentPattern
.
isRegExp
,
caseSensitive
:
!!
searchResult
.
query
.
contentPattern
.
isCaseSensitive
,
wholeWord
:
!!
searchResult
.
query
.
contentPattern
.
isWordMatch
,
includes
:
rawIncludePattern
,
excludes
:
rawExcludePattern
,
contextLines
:
0
,
useIgnores
:
!
searchResult
.
query
.
userDisabledExcludesAndIgnoreFiles
,
}),
{
pinned
:
true
})
as
SearchEditor
;
const
model
=
assertIsDefined
(
editor
.
getModel
());
model
.
setValue
(
contents
);
model
.
deltaDecorations
([],
results
.
matchRanges
.
map
(
range
=>
({
range
,
options
:
{
className
:
'
searchEditorFindMatch
'
,
stickiness
:
TrackedRangeStickiness
.
NeverGrowsWhenTypingAtEdges
}
})));
};
registerThemingParticipant
((
theme
,
collector
)
=>
{
collector
.
addRule
(
`.monaco-editor .searchEditorFindMatch { background-color:
${
theme
.
getColor
(
searchEditorFindMatch
)}
; }`
);
const
findMatchHighlightBorder
=
theme
.
getColor
(
searchEditorFindMatchBorder
);
if
(
findMatchHighlightBorder
)
{
collector
.
addRule
(
`.monaco-editor .searchEditorFindMatch { border: 1px
${
theme
.
type
===
'
hc
'
?
'
dotted
'
:
'
solid
'
}
${
findMatchHighlightBorder
}
; box-sizing: border-box; }`
);
}
});
src/vs/workbench/contrib/search/browser/searchView.ts
浏览文件 @
7879818f
...
...
@@ -64,7 +64,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
import
{
MultiCursorSelectionController
}
from
'
vs/editor/contrib/multicursor/multicursor
'
;
import
{
Selection
}
from
'
vs/editor/common/core/selection
'
;
import
{
SIDE_BAR_BACKGROUND
,
PANEL_BACKGROUND
}
from
'
vs/workbench/common/theme
'
;
import
{
createEditorFromSearchResult
}
from
'
vs/workbench/contrib/search/browser/searchEditor
'
;
import
{
createEditorFromSearchResult
}
from
'
vs/workbench/contrib/search/browser/searchEditor
Commands
'
;
import
{
ILabelService
}
from
'
vs/platform/label/common/label
'
;
import
{
Color
,
RGBA
}
from
'
vs/base/common/color
'
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录