Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
8bd5387d
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 搜索 >>
提交
8bd5387d
编写于
10月 08, 2018
作者:
R
Rob Lourens
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Implement multiline search for #13155
上级
17d36c47
变更
10
隐藏空白更改
内联
并排
Showing
10 changed file
with
161 addition
and
78 deletion
+161
-78
src/vs/base/common/strings.ts
src/vs/base/common/strings.ts
+17
-1
src/vs/base/test/common/strings.test.ts
src/vs/base/test/common/strings.test.ts
+12
-0
src/vs/editor/common/model/textModelSearch.ts
src/vs/editor/common/model/textModelSearch.ts
+29
-29
src/vs/editor/test/common/model/textModelSearch.test.ts
src/vs/editor/test/common/model/textModelSearch.test.ts
+14
-1
src/vs/platform/search/common/search.ts
src/vs/platform/search/common/search.ts
+23
-10
src/vs/platform/search/test/common/search.test.ts
src/vs/platform/search/test/common/search.test.ts
+25
-1
src/vs/workbench/parts/search/common/queryBuilder.ts
src/vs/workbench/parts/search/common/queryBuilder.ts
+21
-5
src/vs/workbench/parts/search/common/searchModel.ts
src/vs/workbench/parts/search/common/searchModel.ts
+1
-11
src/vs/workbench/services/search/node/ripgrepTextSearch.ts
src/vs/workbench/services/search/node/ripgrepTextSearch.ts
+18
-9
src/vs/workbench/services/search/node/searchService.ts
src/vs/workbench/services/search/node/searchService.ts
+1
-11
未找到文件。
src/vs/base/common/strings.ts
浏览文件 @
8bd5387d
...
...
@@ -686,4 +686,20 @@ export function containsUppercaseCharacter(target: string, ignoreEscapedChars =
export
function
uppercaseFirstLetter
(
str
:
string
):
string
{
return
str
.
charAt
(
0
).
toUpperCase
()
+
str
.
slice
(
1
);
}
\ No newline at end of file
}
export
function
getNLines
(
str
:
string
,
n
=
1
):
string
{
if
(
n
===
0
)
{
return
''
;
}
let
idx
=
-
1
;
do
{
idx
=
str
.
indexOf
(
'
\n
'
,
idx
+
1
);
n
--
;
}
while
(
n
>
0
&&
idx
>=
0
);
return
idx
>=
0
?
str
.
substr
(
0
,
idx
)
:
str
;
}
src/vs/base/test/common/strings.test.ts
浏览文件 @
8bd5387d
...
...
@@ -391,4 +391,16 @@ suite('Strings', () => {
assert
.
equal
(
strings
.
uppercaseFirstLetter
(
inStr
),
result
,
`Wrong result for
${
inStr
}
`
);
});
});
test
(
'
getNLines
'
,
()
=>
{
assert
.
equal
(
strings
.
getNLines
(
''
,
5
),
''
);
assert
.
equal
(
strings
.
getNLines
(
'
foo
'
,
5
),
'
foo
'
);
assert
.
equal
(
strings
.
getNLines
(
'
foo
\n
bar
'
,
5
),
'
foo
\n
bar
'
);
assert
.
equal
(
strings
.
getNLines
(
'
foo
\n
bar
'
,
2
),
'
foo
\n
bar
'
);
assert
.
equal
(
strings
.
getNLines
(
'
foo
\n
bar
'
,
1
),
'
foo
'
);
assert
.
equal
(
strings
.
getNLines
(
'
foo
\n
bar
'
),
'
foo
'
);
assert
.
equal
(
strings
.
getNLines
(
'
foo
\n
bar
\n
something
'
,
2
),
'
foo
\n
bar
'
);
assert
.
equal
(
strings
.
getNLines
(
'
foo
'
,
0
),
''
);
});
});
src/vs/editor/common/model/textModelSearch.ts
浏览文件 @
8bd5387d
...
...
@@ -26,34 +26,6 @@ export class SearchParams {
this
.
wordSeparators
=
wordSeparators
;
}
private
static
_isMultilineRegexSource
(
searchString
:
string
):
boolean
{
if
(
!
searchString
||
searchString
.
length
===
0
)
{
return
false
;
}
for
(
let
i
=
0
,
len
=
searchString
.
length
;
i
<
len
;
i
++
)
{
const
chCode
=
searchString
.
charCodeAt
(
i
);
if
(
chCode
===
CharCode
.
Backslash
)
{
// move to next char
i
++
;
if
(
i
>=
len
)
{
// string ends with a \
break
;
}
const
nextChCode
=
searchString
.
charCodeAt
(
i
);
if
(
nextChCode
===
CharCode
.
n
||
nextChCode
===
CharCode
.
r
||
nextChCode
===
CharCode
.
W
)
{
return
true
;
}
}
}
return
false
;
}
public
parseSearchRequest
():
SearchData
{
if
(
this
.
searchString
===
''
)
{
return
null
;
...
...
@@ -62,7 +34,7 @@ export class SearchParams {
// Try to create a RegExp out of the params
let
multiline
:
boolean
;
if
(
this
.
isRegex
)
{
multiline
=
SearchParams
.
_
isMultilineRegexSource
(
this
.
searchString
);
multiline
=
isMultilineRegexSource
(
this
.
searchString
);
}
else
{
multiline
=
(
this
.
searchString
.
indexOf
(
'
\n
'
)
>=
0
);
}
...
...
@@ -93,6 +65,34 @@ export class SearchParams {
}
}
export
function
isMultilineRegexSource
(
searchString
:
string
):
boolean
{
if
(
!
searchString
||
searchString
.
length
===
0
)
{
return
false
;
}
for
(
let
i
=
0
,
len
=
searchString
.
length
;
i
<
len
;
i
++
)
{
const
chCode
=
searchString
.
charCodeAt
(
i
);
if
(
chCode
===
CharCode
.
Backslash
)
{
// move to next char
i
++
;
if
(
i
>=
len
)
{
// string ends with a \
break
;
}
const
nextChCode
=
searchString
.
charCodeAt
(
i
);
if
(
nextChCode
===
CharCode
.
n
||
nextChCode
===
CharCode
.
r
||
nextChCode
===
CharCode
.
W
)
{
return
true
;
}
}
}
return
false
;
}
export
class
SearchData
{
/**
...
...
src/vs/editor/test/common/model/textModelSearch.test.ts
浏览文件 @
8bd5387d
...
...
@@ -7,7 +7,7 @@ import { Position } from 'vs/editor/common/core/position';
import
{
FindMatch
,
EndOfLineSequence
}
from
'
vs/editor/common/model
'
;
import
{
Range
}
from
'
vs/editor/common/core/range
'
;
import
{
TextModel
}
from
'
vs/editor/common/model/textModel
'
;
import
{
TextModelSearch
,
SearchParams
,
SearchData
}
from
'
vs/editor/common/model/textModelSearch
'
;
import
{
TextModelSearch
,
SearchParams
,
SearchData
,
isMultilineRegexSource
}
from
'
vs/editor/common/model/textModelSearch
'
;
import
{
getMapForWordSeparators
}
from
'
vs/editor/common/controller/wordCharacterClassifier
'
;
import
{
USUAL_WORD_SEPARATORS
}
from
'
vs/editor/common/model/wordHelper
'
;
...
...
@@ -720,4 +720,17 @@ suite('TextModelSearch', () => {
]
);
});
test
(
'
isMultilineRegexSource
'
,
()
=>
{
assert
(
!
isMultilineRegexSource
(
'
foo
'
));
assert
(
!
isMultilineRegexSource
(
''
));
assert
(
!
isMultilineRegexSource
(
'
foo
\\
sbar
'
));
assert
(
!
isMultilineRegexSource
(
'
\\\\
notnewline
'
));
assert
(
isMultilineRegexSource
(
'
foo
\\
nbar
'
));
assert
(
isMultilineRegexSource
(
'
foo
\\
nbar
\\
s
'
));
assert
(
isMultilineRegexSource
(
'
foo
\\
r
\\
n
'
));
assert
(
isMultilineRegexSource
(
'
\\
n
'
));
assert
(
isMultilineRegexSource
(
'
foo
\\
W
'
));
});
});
src/vs/platform/search/common/search.ts
浏览文件 @
8bd5387d
...
...
@@ -13,6 +13,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import
{
IFilesConfiguration
}
from
'
vs/platform/files/common/files
'
;
import
{
createDecorator
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
CancellationToken
}
from
'
vs/base/common/cancellation
'
;
import
{
getNLines
}
from
'
vs/base/common/strings
'
;
export
const
VIEW_ID
=
'
workbench.view.search
'
;
...
...
@@ -235,41 +236,53 @@ export class TextSearchResult implements ITextSearchResult {
range
:
ISearchRange
;
preview
:
ITextSearchResultPreview
;
constructor
(
fullLine
:
string
,
range
:
ISearchRange
,
previewOptions
?:
ITextSearchPreviewOptions
)
{
constructor
(
text
:
string
,
range
:
ISearchRange
,
previewOptions
?:
ITextSearchPreviewOptions
)
{
this
.
range
=
range
;
if
(
previewOptions
)
{
text
=
getNLines
(
text
,
previewOptions
.
matchLines
);
const
leadingChars
=
Math
.
floor
(
previewOptions
.
charsPerLine
/
5
);
const
endColumnByTrimmedLines
=
(
range
.
startLineNumber
+
previewOptions
.
matchLines
-
1
)
===
range
.
endLineNumber
?
// if single line...
range
.
endColumn
:
previewOptions
.
charsPerLine
;
// This doesn't handle all previewOptions correctly
const
previewStart
=
Math
.
max
(
range
.
startColumn
-
leadingChars
,
0
);
const
previewEnd
=
previewOptions
.
charsPerLine
+
previewStart
;
const
endOfMatchRangeInPreview
=
Math
.
min
(
previewEnd
,
range
.
endColumn
-
previewStart
);
const
endByCharsPerLine
=
previewOptions
.
charsPerLine
+
previewStart
;
const
trimmedEndOfMatchRangeInPreview
=
Math
.
min
(
endByCharsPerLine
,
endColumnByTrimmedLines
-
previewStart
);
this
.
preview
=
{
text
:
fullLine
.
substring
(
previewStart
,
previewEnd
),
match
:
new
OneLineRange
(
0
,
range
.
startColumn
-
previewStart
,
e
ndOfMatchRangeInPreview
)
text
:
text
.
substring
(
previewStart
,
endByCharsPerLine
),
match
:
new
OneLineRange
(
0
,
range
.
startColumn
-
previewStart
,
trimmedE
ndOfMatchRangeInPreview
)
};
}
else
{
this
.
preview
=
{
text
:
fullLine
,
text
:
text
,
match
:
new
OneLineRange
(
0
,
range
.
startColumn
,
range
.
endColumn
)
};
}
}
}
export
class
OneLine
Range
implements
ISearchRange
{
export
class
Search
Range
implements
ISearchRange
{
startLineNumber
:
number
;
startColumn
:
number
;
endLineNumber
:
number
;
endColumn
:
number
;
constructor
(
lineNumber
:
number
,
startColumn
:
number
,
endColumn
:
number
)
{
this
.
startLineNumber
=
l
ineNumber
;
constructor
(
startLineNumber
:
number
,
startColumn
:
number
,
endLineNumber
:
number
,
endColumn
:
number
)
{
this
.
startLineNumber
=
startL
ineNumber
;
this
.
startColumn
=
startColumn
;
this
.
endLineNumber
=
l
ineNumber
;
this
.
endLineNumber
=
endL
ineNumber
;
this
.
endColumn
=
endColumn
;
}
}
export
class
OneLineRange
extends
SearchRange
{
constructor
(
lineNumber
:
number
,
startColumn
:
number
,
endColumn
:
number
)
{
super
(
lineNumber
,
startColumn
,
lineNumber
,
endColumn
);
}
}
export
interface
ISearchConfigurationProperties
{
exclude
:
glob
.
IExpression
;
useRipgrep
:
boolean
;
...
...
src/vs/platform/search/test/common/search.test.ts
浏览文件 @
8bd5387d
...
...
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
*
as
assert
from
'
assert
'
;
import
{
ITextSearchPreviewOptions
,
OneLineRange
,
TextSearchResult
}
from
'
vs/platform/search/common/search
'
;
import
{
ITextSearchPreviewOptions
,
OneLineRange
,
TextSearchResult
,
SearchRange
}
from
'
vs/platform/search/common/search
'
;
suite
(
'
TextSearchResult
'
,
()
=>
{
...
...
@@ -78,4 +78,28 @@ suite('TextSearchResult', () => {
assert
.
deepEqual
(
result
.
range
,
range
);
assertPreviewRangeText
(
'
b
'
,
result
);
});
test
(
'
one line of multiline match
'
,
()
=>
{
const
previewOptions
:
ITextSearchPreviewOptions
=
{
matchLines
:
1
,
charsPerLine
:
10000
};
const
range
=
new
SearchRange
(
5
,
4
,
6
,
3
);
const
result
=
new
TextSearchResult
(
'
foo bar
\n
foo bar
'
,
range
,
previewOptions
);
assert
.
deepEqual
(
result
.
range
,
range
);
assertPreviewRangeText
(
'
bar
'
,
result
);
});
// test('all lines of multiline match', () => {
// const previewOptions: ITextSearchPreviewOptions = {
// matchLines: 5,
// charsPerLine: 10000
// };
// const range = new SearchRange(5, 4, 6, 3);
// const result = new TextSearchResult('foo bar\nfoo bar', range, previewOptions);
// assert.deepEqual(result.range, range);
// assertPreviewRangeText('bar\nfoo', result);
// });
});
\ No newline at end of file
src/vs/workbench/parts/search/common/queryBuilder.ts
浏览文件 @
8bd5387d
...
...
@@ -17,6 +17,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/
import
{
IPatternInfo
,
IQueryOptions
,
IFolderQuery
,
ISearchQuery
,
QueryType
,
ISearchConfiguration
,
getExcludes
,
pathIncludedInQuery
}
from
'
vs/platform/search/common/search
'
;
import
{
IConfigurationService
}
from
'
vs/platform/configuration/common/configuration
'
;
import
{
IEnvironmentService
}
from
'
vs/platform/environment/common/environment
'
;
import
{
isMultilineRegexSource
}
from
'
vs/editor/common/model/textModelSearch
'
;
export
interface
ISearchPathPattern
{
searchPath
:
uri
;
...
...
@@ -77,7 +78,8 @@ export class QueryBuilder {
const
ignoreSymlinks
=
!
this
.
configurationService
.
getValue
<
ISearchConfiguration
>
().
search
.
followSymlinks
;
if
(
contentPattern
)
{
this
.
resolveSmartCaseToCaseSensitive
(
contentPattern
);
contentPattern
.
isCaseSensitive
=
this
.
isCaseSensitive
(
contentPattern
);
contentPattern
.
isMultiline
=
this
.
isMultiline
(
contentPattern
);
contentPattern
.
wordSeparators
=
this
.
configurationService
.
getValue
<
ISearchConfiguration
>
().
editor
.
wordSeparators
;
}
...
...
@@ -113,19 +115,33 @@ export class QueryBuilder {
}
/**
*
Fix th
e isCaseSensitive flag based on the query and the isSmartCase flag, for search providers that don't support smart case natively.
*
Resolv
e isCaseSensitive flag based on the query and the isSmartCase flag, for search providers that don't support smart case natively.
*/
private
resolveSmartCaseToCaseSensitive
(
contentPattern
:
IPatternInfo
):
void
{
private
isCaseSensitive
(
contentPattern
:
IPatternInfo
):
boolean
{
if
(
contentPattern
.
isSmartCase
)
{
if
(
contentPattern
.
isRegExp
)
{
// Consider it case sensitive if it contains an unescaped capital letter
if
(
strings
.
containsUppercaseCharacter
(
contentPattern
.
pattern
,
true
))
{
contentPattern
.
isCaseSensitive
=
true
;
return
true
;
}
}
else
if
(
strings
.
containsUppercaseCharacter
(
contentPattern
.
pattern
))
{
contentPattern
.
isCaseSensitive
=
true
;
return
true
;
}
}
return
contentPattern
.
isCaseSensitive
;
}
private
isMultiline
(
contentPattern
:
IPatternInfo
):
boolean
{
if
(
contentPattern
.
isMultiline
)
{
return
true
;
}
if
(
contentPattern
.
isRegExp
&&
isMultilineRegexSource
(
contentPattern
.
pattern
))
{
return
true
;
}
return
false
;
}
/**
...
...
src/vs/workbench/parts/search/common/searchModel.ts
浏览文件 @
8bd5387d
...
...
@@ -992,19 +992,9 @@ export class RangeHighlightDecorations implements IDisposable {
});
}
/**
* While search doesn't support multiline matches, collapse editor matches to a single line
*/
export
function
editorMatchToTextSearchResult
(
match
:
FindMatch
,
model
:
ITextModel
,
previewOptions
:
ITextSearchPreviewOptions
):
TextSearchResult
{
let
endLineNumber
=
match
.
range
.
endLineNumber
-
1
;
let
endCol
=
match
.
range
.
endColumn
-
1
;
if
(
match
.
range
.
endLineNumber
!==
match
.
range
.
startLineNumber
)
{
endLineNumber
=
match
.
range
.
startLineNumber
-
1
;
endCol
=
model
.
getLineLength
(
match
.
range
.
startLineNumber
);
}
return
new
TextSearchResult
(
model
.
getLineContent
(
match
.
range
.
startLineNumber
),
new
Range
(
match
.
range
.
startLineNumber
-
1
,
match
.
range
.
startColumn
-
1
,
endLineNumber
,
endCol
),
new
Range
(
match
.
range
.
startLineNumber
-
1
,
match
.
range
.
startColumn
-
1
,
match
.
range
.
endLineNumber
-
1
,
match
.
range
.
endColumn
-
1
),
previewOptions
);
}
src/vs/workbench/services/search/node/ripgrepTextSearch.ts
浏览文件 @
8bd5387d
...
...
@@ -285,19 +285,24 @@ export class RipgrepParser extends EventEmitter {
private
submatchToResult
(
parsedLine
:
any
,
match
:
any
,
uri
:
URI
):
TextSearchResult
{
const
lineNumber
=
parsedLine
.
data
.
line_number
-
1
;
let
matchText
=
bytesOrTextToString
(
parsedLine
.
data
.
lines
);
let
start
=
match
.
start
;
let
end
=
match
.
end
;
let
lineText
=
bytesOrTextToString
(
parsedLine
.
data
.
lines
);
let
matchText
=
bytesOrTextToString
(
match
.
match
);
const
newlineMatches
=
matchText
.
match
(
/
\n
/g
);
const
newlines
=
newlineMatches
?
newlineMatches
.
length
:
0
;
let
startCol
=
match
.
start
;
const
endLineNumber
=
lineNumber
+
newlines
;
let
endCol
=
match
.
end
-
(
lineText
.
lastIndexOf
(
'
\n
'
,
lineText
.
length
-
2
)
+
1
);
if
(
lineNumber
===
0
)
{
if
(
strings
.
startsWithUTF8BOM
(
match
Text
))
{
matchText
=
strings
.
stripUTF8BOM
(
match
Text
);
start
-=
3
;
end
-=
3
;
if
(
strings
.
startsWithUTF8BOM
(
line
Text
))
{
lineText
=
strings
.
stripUTF8BOM
(
line
Text
);
start
Col
-=
3
;
end
Col
-=
3
;
}
}
const
range
=
new
Range
(
lineNumber
,
start
,
lineNumber
,
end
);
return
new
TextSearchResult
(
match
Text
,
range
,
this
.
previewOptions
);
const
range
=
new
Range
(
lineNumber
,
start
Col
,
endLineNumber
,
endCol
);
return
new
TextSearchResult
(
line
Text
,
range
,
this
.
previewOptions
);
}
private
getFileMatch
(
relativeOrAbsolutePath
:
string
):
FileMatch
{
...
...
@@ -490,6 +495,10 @@ function getRgArgs(config: IRawSearch) {
args
.
push
(
'
--json
'
);
if
(
config
.
contentPattern
.
isMultiline
)
{
args
.
push
(
'
--multiline
'
);
}
// Folder to search
args
.
push
(
'
--
'
);
...
...
src/vs/workbench/services/search/node/searchService.ts
浏览文件 @
8bd5387d
...
...
@@ -589,19 +589,9 @@ export class DiskSearch implements ISearchResultProvider {
}
}
/**
* While search doesn't support multiline matches, collapse editor matches to a single line
*/
function
editorMatchToTextSearchResult
(
match
:
FindMatch
,
model
:
ITextModel
,
previewOptions
:
ITextSearchPreviewOptions
):
TextSearchResult
{
let
endLineNumber
=
match
.
range
.
endLineNumber
-
1
;
let
endCol
=
match
.
range
.
endColumn
-
1
;
if
(
match
.
range
.
endLineNumber
!==
match
.
range
.
startLineNumber
)
{
endLineNumber
=
match
.
range
.
startLineNumber
-
1
;
endCol
=
model
.
getLineLength
(
match
.
range
.
startLineNumber
);
}
return
new
TextSearchResult
(
model
.
getLineContent
(
match
.
range
.
startLineNumber
),
new
Range
(
match
.
range
.
startLineNumber
-
1
,
match
.
range
.
startColumn
-
1
,
endLineNumber
,
endCol
),
new
Range
(
match
.
range
.
startLineNumber
-
1
,
match
.
range
.
startColumn
-
1
,
match
.
range
.
endLineNumber
-
1
,
match
.
range
.
endColumn
-
1
),
previewOptions
);
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录