Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
15ba9aee
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,发现更多精彩内容 >>
未验证
提交
15ba9aee
编写于
12月 29, 2020
作者:
R
Raymond Zhao
提交者:
GitHub
12月 29, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Emmet improve Expand Abbreviation perf (#113558)
上级
b5cd082c
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
82 addition
and
155 deletion
+82
-155
extensions/emmet/src/abbreviationActions.ts
extensions/emmet/src/abbreviationActions.ts
+50
-35
extensions/emmet/src/defaultCompletionProvider.ts
extensions/emmet/src/defaultCompletionProvider.ts
+9
-9
extensions/emmet/src/test/partialParsingStylesheet.test.ts
extensions/emmet/src/test/partialParsingStylesheet.test.ts
+5
-4
extensions/emmet/src/util.ts
extensions/emmet/src/util.ts
+18
-107
未找到文件。
extensions/emmet/src/abbreviationActions.ts
浏览文件 @
15ba9aee
...
@@ -4,8 +4,9 @@
...
@@ -4,8 +4,9 @@
*--------------------------------------------------------------------------------------------*/
*--------------------------------------------------------------------------------------------*/
import
*
as
vscode
from
'
vscode
'
;
import
*
as
vscode
from
'
vscode
'
;
import
{
Node
,
HtmlNode
,
Rule
,
Property
,
Stylesheet
}
from
'
EmmetNode
'
;
import
{
Node
,
HtmlNode
,
Rule
,
Property
,
Stylesheet
}
from
'
EmmetFlatNode
'
;
import
{
getEmmetHelper
,
getNode
,
getInnerRange
,
getMappingForIncludedLanguages
,
parseDocument
,
validate
,
getEmmetConfiguration
,
isStyleSheet
,
getEmmetMode
,
parsePartialStylesheet
,
isStyleAttribute
,
getEmbeddedCssNodeIfAny
,
allowedMimeTypesInScriptTag
,
toLSTextDocument
}
from
'
./util
'
;
import
{
getEmmetHelper
,
getFlatNode
,
getMappingForIncludedLanguages
,
validate
,
getEmmetConfiguration
,
isStyleSheet
,
getEmmetMode
,
parsePartialStylesheet
,
isStyleAttribute
,
getEmbeddedCssNodeIfAny
,
allowedMimeTypesInScriptTag
,
toLSTextDocument
}
from
'
./util
'
;
import
{
getRootNode
as
parseDocument
}
from
'
./parseDocument
'
;
const
trimRegex
=
/
[\u
00a0
]
*
[\d
#
\-\*\u
2022
]
+
\.?
/
;
const
trimRegex
=
/
[\u
00a0
]
*
[\d
#
\-\*\u
2022
]
+
\.?
/
;
const
hexColorRegex
=
/^#
[\d
a-fA-F
]{0,6}
$/
;
const
hexColorRegex
=
/^#
[\d
a-fA-F
]{0,6}
$/
;
...
@@ -59,7 +60,7 @@ function doWrapping(individualLines: boolean, args: any) {
...
@@ -59,7 +60,7 @@ function doWrapping(individualLines: boolean, args: any) {
args
[
'
language
'
]
=
editor
.
document
.
languageId
;
args
[
'
language
'
]
=
editor
.
document
.
languageId
;
}
}
const
syntax
=
getSyntaxFromArgs
(
args
)
||
'
html
'
;
const
syntax
=
getSyntaxFromArgs
(
args
)
||
'
html
'
;
const
rootNode
=
parseDocument
(
editor
.
document
,
fals
e
);
const
rootNode
=
parseDocument
(
editor
.
document
,
tru
e
);
let
inPreview
=
false
;
let
inPreview
=
false
;
let
currentValue
=
''
;
let
currentValue
=
''
;
...
@@ -68,31 +69,40 @@ function doWrapping(individualLines: boolean, args: any) {
...
@@ -68,31 +69,40 @@ function doWrapping(individualLines: boolean, args: any) {
// Fetch general information for the succesive expansions. i.e. the ranges to replace and its contents
// Fetch general information for the succesive expansions. i.e. the ranges to replace and its contents
const
rangesToReplace
:
PreviewRangesWithContent
[]
=
editor
.
selections
.
sort
((
a
:
vscode
.
Selection
,
b
:
vscode
.
Selection
)
=>
{
return
a
.
start
.
compareTo
(
b
.
start
);
}).
map
(
selection
=>
{
const
rangesToReplace
:
PreviewRangesWithContent
[]
=
editor
.
selections
.
sort
((
a
:
vscode
.
Selection
,
b
:
vscode
.
Selection
)
=>
{
return
a
.
start
.
compareTo
(
b
.
start
);
}).
map
(
selection
=>
{
let
rangeToReplace
:
vscode
.
Range
=
selection
.
isReversed
?
new
vscode
.
Range
(
selection
.
active
,
selection
.
anchor
)
:
selection
;
let
rangeToReplace
:
vscode
.
Range
=
selection
.
isReversed
?
new
vscode
.
Range
(
selection
.
active
,
selection
.
anchor
)
:
selection
;
const
document
=
editor
.
document
;
if
(
!
rangeToReplace
.
isSingleLine
&&
rangeToReplace
.
end
.
character
===
0
)
{
if
(
!
rangeToReplace
.
isSingleLine
&&
rangeToReplace
.
end
.
character
===
0
)
{
const
previousLine
=
rangeToReplace
.
end
.
line
-
1
;
const
previousLine
=
rangeToReplace
.
end
.
line
-
1
;
const
lastChar
=
editor
.
document
.
lineAt
(
previousLine
).
text
.
length
;
const
lastChar
=
document
.
lineAt
(
previousLine
).
text
.
length
;
rangeToReplace
=
new
vscode
.
Range
(
rangeToReplace
.
start
,
new
vscode
.
Position
(
previousLine
,
lastChar
));
rangeToReplace
=
new
vscode
.
Range
(
rangeToReplace
.
start
,
new
vscode
.
Position
(
previousLine
,
lastChar
));
}
else
if
(
rangeToReplace
.
isEmpty
)
{
}
else
if
(
rangeToReplace
.
isEmpty
)
{
const
{
active
}
=
selection
;
const
{
active
}
=
selection
;
const
currentNode
=
getNode
(
rootNode
,
active
,
true
);
const
activeOffset
=
document
.
offsetAt
(
active
);
if
(
currentNode
&&
(
currentNode
.
start
.
line
===
active
.
line
||
currentNode
.
end
.
line
===
active
.
line
))
{
const
currentNode
=
getFlatNode
(
rootNode
,
activeOffset
,
true
);
rangeToReplace
=
new
vscode
.
Range
(
currentNode
.
start
,
currentNode
.
end
);
if
(
currentNode
)
{
const
currentNodeStart
=
document
.
positionAt
(
currentNode
.
start
);
const
currentNodeEnd
=
document
.
positionAt
(
currentNode
.
end
);
if
(
currentNodeStart
.
line
===
active
.
line
||
currentNodeEnd
.
line
===
active
.
line
)
{
rangeToReplace
=
new
vscode
.
Range
(
currentNodeStart
,
currentNodeEnd
);
}
else
{
rangeToReplace
=
new
vscode
.
Range
(
rangeToReplace
.
start
.
line
,
0
,
rangeToReplace
.
start
.
line
,
document
.
lineAt
(
rangeToReplace
.
start
.
line
).
text
.
length
);
}
}
else
{
}
else
{
rangeToReplace
=
new
vscode
.
Range
(
rangeToReplace
.
start
.
line
,
0
,
rangeToReplace
.
start
.
line
,
editor
.
document
.
lineAt
(
rangeToReplace
.
start
.
line
).
text
.
length
);
rangeToReplace
=
new
vscode
.
Range
(
rangeToReplace
.
start
.
line
,
0
,
rangeToReplace
.
start
.
line
,
document
.
lineAt
(
rangeToReplace
.
start
.
line
).
text
.
length
);
}
}
}
}
const
firstLineOfSelection
=
editor
.
document
.
lineAt
(
rangeToReplace
.
start
).
text
.
substr
(
rangeToReplace
.
start
.
character
);
const
firstLineOfSelection
=
document
.
lineAt
(
rangeToReplace
.
start
).
text
.
substr
(
rangeToReplace
.
start
.
character
);
const
matches
=
firstLineOfSelection
.
match
(
/^
(\s
*
)
/
);
const
matches
=
firstLineOfSelection
.
match
(
/^
(\s
*
)
/
);
const
extraWhitespaceSelected
=
matches
?
matches
[
1
].
length
:
0
;
const
extraWhitespaceSelected
=
matches
?
matches
[
1
].
length
:
0
;
rangeToReplace
=
new
vscode
.
Range
(
rangeToReplace
.
start
.
line
,
rangeToReplace
.
start
.
character
+
extraWhitespaceSelected
,
rangeToReplace
.
end
.
line
,
rangeToReplace
.
end
.
character
);
rangeToReplace
=
new
vscode
.
Range
(
rangeToReplace
.
start
.
line
,
rangeToReplace
.
start
.
character
+
extraWhitespaceSelected
,
rangeToReplace
.
end
.
line
,
rangeToReplace
.
end
.
character
);
let
textToWrapInPreview
:
string
[];
let
textToWrapInPreview
:
string
[];
const
textToReplace
=
editor
.
document
.
getText
(
rangeToReplace
);
const
textToReplace
=
document
.
getText
(
rangeToReplace
);
if
(
individualLines
)
{
if
(
individualLines
)
{
textToWrapInPreview
=
textToReplace
.
split
(
'
\n
'
).
map
(
x
=>
x
.
trim
());
textToWrapInPreview
=
textToReplace
.
split
(
'
\n
'
).
map
(
x
=>
x
.
trim
());
}
else
{
}
else
{
const
wholeFirstLine
=
editor
.
document
.
lineAt
(
rangeToReplace
.
start
).
text
;
const
wholeFirstLine
=
document
.
lineAt
(
rangeToReplace
.
start
).
text
;
const
otherMatches
=
wholeFirstLine
.
match
(
/^
(\s
*
)
/
);
const
otherMatches
=
wholeFirstLine
.
match
(
/^
(\s
*
)
/
);
const
precedingWhitespace
=
otherMatches
?
otherMatches
[
1
]
:
''
;
const
precedingWhitespace
=
otherMatches
?
otherMatches
[
1
]
:
''
;
textToWrapInPreview
=
rangeToReplace
.
isSingleLine
?
[
textToReplace
]
:
[
'
\n\t
'
+
textToReplace
.
split
(
'
\n
'
+
precedingWhitespace
).
join
(
'
\n\t
'
)
+
'
\n
'
];
textToWrapInPreview
=
rangeToReplace
.
isSingleLine
?
[
textToReplace
]
:
[
'
\n\t
'
+
textToReplace
.
split
(
'
\n
'
+
precedingWhitespace
).
join
(
'
\n\t
'
)
+
'
\n
'
];
...
@@ -327,7 +337,7 @@ export function expandEmmetAbbreviation(args: any): Thenable<boolean | undefined
...
@@ -327,7 +337,7 @@ export function expandEmmetAbbreviation(args: any): Thenable<boolean | undefined
if
(
editor
.
selections
.
length
===
1
&&
isStyleSheet
(
editor
.
document
.
languageId
)
&&
usePartialParsing
&&
editor
.
document
.
lineCount
>
1000
)
{
if
(
editor
.
selections
.
length
===
1
&&
isStyleSheet
(
editor
.
document
.
languageId
)
&&
usePartialParsing
&&
editor
.
document
.
lineCount
>
1000
)
{
rootNode
=
parsePartialStylesheet
(
editor
.
document
,
editor
.
selection
.
isReversed
?
editor
.
selection
.
anchor
:
editor
.
selection
.
active
);
rootNode
=
parsePartialStylesheet
(
editor
.
document
,
editor
.
selection
.
isReversed
?
editor
.
selection
.
anchor
:
editor
.
selection
.
active
);
}
else
{
}
else
{
rootNode
=
parseDocument
(
editor
.
document
,
fals
e
);
rootNode
=
parseDocument
(
editor
.
document
,
tru
e
);
}
}
return
rootNode
;
return
rootNode
;
...
@@ -342,24 +352,25 @@ export function expandEmmetAbbreviation(args: any): Thenable<boolean | undefined
...
@@ -342,24 +352,25 @@ export function expandEmmetAbbreviation(args: any): Thenable<boolean | undefined
if
(
!
helper
.
isAbbreviationValid
(
syntax
,
abbreviation
))
{
if
(
!
helper
.
isAbbreviationValid
(
syntax
,
abbreviation
))
{
return
;
return
;
}
}
let
currentNode
=
getNode
(
getRootNode
(),
position
,
true
);
const
offset
=
editor
.
document
.
offsetAt
(
position
);
let
currentNode
=
getFlatNode
(
getRootNode
(),
offset
,
true
);
let
validateLocation
=
true
;
let
validateLocation
=
true
;
let
syntaxToUse
=
syntax
;
let
syntaxToUse
=
syntax
;
if
(
editor
.
document
.
languageId
===
'
html
'
)
{
if
(
editor
.
document
.
languageId
===
'
html
'
)
{
if
(
isStyleAttribute
(
currentNode
,
position
))
{
if
(
isStyleAttribute
(
currentNode
,
offset
))
{
syntaxToUse
=
'
css
'
;
syntaxToUse
=
'
css
'
;
validateLocation
=
false
;
validateLocation
=
false
;
}
else
{
}
else
{
const
embeddedCssNode
=
getEmbeddedCssNodeIfAny
(
editor
.
document
,
currentNode
,
position
);
const
embeddedCssNode
=
getEmbeddedCssNodeIfAny
(
editor
.
document
,
currentNode
,
position
);
if
(
embeddedCssNode
)
{
if
(
embeddedCssNode
)
{
currentNode
=
get
Node
(
embeddedCssNode
,
position
,
true
);
currentNode
=
get
FlatNode
(
embeddedCssNode
,
offset
,
true
);
syntaxToUse
=
'
css
'
;
syntaxToUse
=
'
css
'
;
}
}
}
}
}
}
if
(
validateLocation
&&
!
isValidLocationForEmmetAbbreviation
(
editor
.
document
,
getRootNode
(),
currentNode
,
syntaxToUse
,
position
,
rangeToReplace
))
{
if
(
validateLocation
&&
!
isValidLocationForEmmetAbbreviation
(
editor
.
document
,
getRootNode
(),
currentNode
,
syntaxToUse
,
offset
,
rangeToReplace
))
{
return
;
return
;
}
}
...
@@ -393,10 +404,10 @@ function fallbackTab(): Thenable<boolean | undefined> {
...
@@ -393,10 +404,10 @@ function fallbackTab(): Thenable<boolean | undefined> {
* @param position position to validate
* @param position position to validate
* @param abbreviationRange The range of the abbreviation for which given position is being validated
* @param abbreviationRange The range of the abbreviation for which given position is being validated
*/
*/
export
function
isValidLocationForEmmetAbbreviation
(
document
:
vscode
.
TextDocument
,
rootNode
:
Node
|
undefined
,
currentNode
:
Node
|
null
,
syntax
:
string
,
position
:
vscode
.
Position
,
abbreviationRange
:
vscode
.
Range
):
boolean
{
export
function
isValidLocationForEmmetAbbreviation
(
document
:
vscode
.
TextDocument
,
rootNode
:
Node
|
undefined
,
currentNode
:
Node
|
undefined
,
syntax
:
string
,
offset
:
number
,
abbreviationRange
:
vscode
.
Range
):
boolean
{
if
(
isStyleSheet
(
syntax
))
{
if
(
isStyleSheet
(
syntax
))
{
const
stylesheet
=
<
Stylesheet
>
rootNode
;
const
stylesheet
=
<
Stylesheet
>
rootNode
;
if
(
stylesheet
&&
(
stylesheet
.
comments
||
[]).
some
(
x
=>
position
.
isAfterOrEqual
(
x
.
start
)
&&
position
.
isBeforeOrEqual
(
x
.
end
)
))
{
if
(
stylesheet
&&
(
stylesheet
.
comments
||
[]).
some
(
x
=>
offset
>=
x
.
start
&&
offset
<=
x
.
end
))
{
return
false
;
return
false
;
}
}
// Continue validation only if the file was parse-able and the currentNode has been found
// Continue validation only if the file was parse-able and the currentNode has been found
...
@@ -419,14 +430,14 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
...
@@ -419,14 +430,14 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
const
propertyNode
=
<
Property
>
currentNode
;
const
propertyNode
=
<
Property
>
currentNode
;
if
(
propertyNode
.
terminatorToken
if
(
propertyNode
.
terminatorToken
&&
propertyNode
.
separator
&&
propertyNode
.
separator
&&
position
.
isAfterOrEqual
(
propertyNode
.
separatorToken
.
end
)
&&
offset
>=
propertyNode
.
separatorToken
.
end
&&
position
.
isBeforeOrEqual
(
propertyNode
.
terminatorToken
.
start
)
&&
offset
<=
propertyNode
.
terminatorToken
.
start
&&
abbreviation
.
indexOf
(
'
:
'
)
===
-
1
)
{
&&
abbreviation
.
indexOf
(
'
:
'
)
===
-
1
)
{
return
hexColorRegex
.
test
(
abbreviation
)
||
abbreviation
===
'
!
'
;
return
hexColorRegex
.
test
(
abbreviation
)
||
abbreviation
===
'
!
'
;
}
}
if
(
!
propertyNode
.
terminatorToken
if
(
!
propertyNode
.
terminatorToken
&&
propertyNode
.
separator
&&
propertyNode
.
separator
&&
position
.
isAfterOrEqual
(
propertyNode
.
separatorToken
.
end
)
&&
offset
>=
propertyNode
.
separatorToken
.
end
&&
abbreviation
.
indexOf
(
'
:
'
)
===
-
1
)
{
&&
abbreviation
.
indexOf
(
'
:
'
)
===
-
1
)
{
return
hexColorRegex
.
test
(
abbreviation
)
||
abbreviation
===
'
!
'
;
return
hexColorRegex
.
test
(
abbreviation
)
||
abbreviation
===
'
!
'
;
}
}
...
@@ -444,7 +455,7 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
...
@@ -444,7 +455,7 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
const
currentCssNode
=
<
Rule
>
currentNode
;
const
currentCssNode
=
<
Rule
>
currentNode
;
// Position is valid if it occurs after the `{` that marks beginning of rule contents
// Position is valid if it occurs after the `{` that marks beginning of rule contents
if
(
position
.
isAfter
(
currentCssNode
.
contentStartToken
.
end
)
)
{
if
(
offset
>
currentCssNode
.
contentStartToken
.
end
)
{
return
true
;
return
true
;
}
}
...
@@ -453,12 +464,16 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
...
@@ -453,12 +464,16 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
// But we should assume it is a valid location for css properties under the parent rule
// But we should assume it is a valid location for css properties under the parent rule
if
(
currentCssNode
.
parent
if
(
currentCssNode
.
parent
&&
(
currentCssNode
.
parent
.
type
===
'
rule
'
||
currentCssNode
.
parent
.
type
===
'
at-rule
'
)
&&
(
currentCssNode
.
parent
.
type
===
'
rule
'
||
currentCssNode
.
parent
.
type
===
'
at-rule
'
)
&&
currentCssNode
.
selectorToken
&&
currentCssNode
.
selectorToken
)
{
&&
position
.
line
!==
currentCssNode
.
selectorToken
.
end
.
line
const
position
=
document
.
positionAt
(
offset
);
&&
currentCssNode
.
selectorToken
.
start
.
character
===
abbreviationRange
.
start
.
character
const
tokenStartPos
=
document
.
positionAt
(
currentCssNode
.
selectorToken
.
start
);
&&
currentCssNode
.
selectorToken
.
start
.
line
===
abbreviationRange
.
start
.
line
const
tokenEndPos
=
document
.
positionAt
(
currentCssNode
.
selectorToken
.
end
);
)
{
if
(
position
.
line
!==
tokenEndPos
.
line
return
true
;
&&
tokenStartPos
.
character
===
abbreviationRange
.
start
.
character
&&
tokenStartPos
.
line
===
abbreviationRange
.
start
.
line
)
{
return
true
;
}
}
}
return
false
;
return
false
;
...
@@ -469,7 +484,7 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
...
@@ -469,7 +484,7 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
const
escape
=
'
\\
'
;
const
escape
=
'
\\
'
;
const
question
=
'
?
'
;
const
question
=
'
?
'
;
const
currentHtmlNode
=
<
HtmlNode
>
currentNode
;
const
currentHtmlNode
=
<
HtmlNode
>
currentNode
;
let
start
=
new
vscode
.
Position
(
0
,
0
)
;
let
start
=
0
;
if
(
currentHtmlNode
)
{
if
(
currentHtmlNode
)
{
if
(
currentHtmlNode
.
name
===
'
script
'
)
{
if
(
currentHtmlNode
.
name
===
'
script
'
)
{
...
@@ -487,27 +502,27 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
...
@@ -487,27 +502,27 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
return
false
;
return
false
;
}
}
const
innerRange
=
getInnerRange
(
currentHtmlNode
);
// Fix for https://github.com/microsoft/vscode/issues/28829
// Fix for https://github.com/microsoft/vscode/issues/28829
if
(
!
innerRange
||
!
innerRange
.
contains
(
position
))
{
if
(
!
currentHtmlNode
.
open
||
!
currentHtmlNode
.
close
||
!
(
currentHtmlNode
.
open
.
end
<=
offset
&&
offset
<=
currentHtmlNode
.
close
.
start
))
{
return
false
;
return
false
;
}
}
// Fix for https://github.com/microsoft/vscode/issues/35128
// Fix for https://github.com/microsoft/vscode/issues/35128
// Find the position up till where we will backtrack looking for unescaped < or >
// Find the position up till where we will backtrack looking for unescaped < or >
// to decide if current position is valid for emmet expansion
// to decide if current position is valid for emmet expansion
start
=
innerRange
.
start
;
start
=
currentHtmlNode
.
open
.
end
;
let
lastChildBeforePosition
=
currentHtmlNode
.
firstChild
;
let
lastChildBeforePosition
=
currentHtmlNode
.
firstChild
;
while
(
lastChildBeforePosition
)
{
while
(
lastChildBeforePosition
)
{
if
(
lastChildBeforePosition
.
end
.
isAfter
(
position
)
)
{
if
(
lastChildBeforePosition
.
end
>
offset
)
{
break
;
break
;
}
}
start
=
lastChildBeforePosition
.
end
;
start
=
lastChildBeforePosition
.
end
;
lastChildBeforePosition
=
lastChildBeforePosition
.
nextSibling
;
lastChildBeforePosition
=
lastChildBeforePosition
.
nextSibling
;
}
}
}
}
let
textToBackTrack
=
document
.
getText
(
new
vscode
.
Range
(
start
.
line
,
start
.
character
,
abbreviationRange
.
start
.
line
,
abbreviationRange
.
start
.
character
));
const
startPos
=
document
.
positionAt
(
start
);
let
textToBackTrack
=
document
.
getText
(
new
vscode
.
Range
(
startPos
.
line
,
startPos
.
character
,
abbreviationRange
.
start
.
line
,
abbreviationRange
.
start
.
character
));
// Worse case scenario is when cursor is inside a big chunk of text which needs to backtracked
// Worse case scenario is when cursor is inside a big chunk of text which needs to backtracked
// Backtrack only 500 offsets to ensure we dont waste time doing this
// Backtrack only 500 offsets to ensure we dont waste time doing this
...
...
extensions/emmet/src/defaultCompletionProvider.ts
浏览文件 @
15ba9aee
...
@@ -4,10 +4,11 @@
...
@@ -4,10 +4,11 @@
*--------------------------------------------------------------------------------------------*/
*--------------------------------------------------------------------------------------------*/
import
*
as
vscode
from
'
vscode
'
;
import
*
as
vscode
from
'
vscode
'
;
import
{
Node
,
Stylesheet
}
from
'
EmmetNode
'
;
import
{
Node
,
Stylesheet
}
from
'
Emmet
Flat
Node
'
;
import
{
isValidLocationForEmmetAbbreviation
,
getSyntaxFromArgs
}
from
'
./abbreviationActions
'
;
import
{
isValidLocationForEmmetAbbreviation
,
getSyntaxFromArgs
}
from
'
./abbreviationActions
'
;
import
{
getEmmetHelper
,
getMappingForIncludedLanguages
,
parsePartialStylesheet
,
getEmmetConfiguration
,
getEmmetMode
,
isStyleSheet
,
parseDocument
,
ge
tNode
,
allowedMimeTypesInScriptTag
,
trimQuotes
,
toLSTextDocument
}
from
'
./util
'
;
import
{
getEmmetHelper
,
getMappingForIncludedLanguages
,
parsePartialStylesheet
,
getEmmetConfiguration
,
getEmmetMode
,
isStyleSheet
,
getFla
tNode
,
allowedMimeTypesInScriptTag
,
trimQuotes
,
toLSTextDocument
}
from
'
./util
'
;
import
{
getLanguageService
,
TokenType
,
Range
as
LSRange
}
from
'
vscode-html-languageservice
'
;
import
{
getLanguageService
,
TokenType
,
Range
as
LSRange
}
from
'
vscode-html-languageservice
'
;
import
{
getRootNode
}
from
'
./parseDocument
'
;
export
class
DefaultCompletionItemProvider
implements
vscode
.
CompletionItemProvider
{
export
class
DefaultCompletionItemProvider
implements
vscode
.
CompletionItemProvider
{
...
@@ -62,8 +63,8 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
...
@@ -62,8 +63,8 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
const
helper
=
getEmmetHelper
();
const
helper
=
getEmmetHelper
();
let
validateLocation
=
syntax
===
'
html
'
||
syntax
===
'
jsx
'
||
syntax
===
'
xml
'
;
let
validateLocation
=
syntax
===
'
html
'
||
syntax
===
'
jsx
'
||
syntax
===
'
xml
'
;
let
rootNode
:
Node
|
undefined
=
undefined
;
let
rootNode
:
Node
|
undefined
;
let
currentNode
:
Node
|
null
=
null
;
let
currentNode
:
Node
|
undefined
;
const
lsDoc
=
toLSTextDocument
(
document
);
const
lsDoc
=
toLSTextDocument
(
document
);
position
=
document
.
validatePosition
(
position
);
position
=
document
.
validatePosition
(
position
);
...
@@ -145,19 +146,18 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
...
@@ -145,19 +146,18 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
return
;
return
;
}
}
const
offset
=
document
.
offsetAt
(
position
);
if
(
isStyleSheet
(
document
.
languageId
)
&&
context
.
triggerKind
!==
vscode
.
CompletionTriggerKind
.
TriggerForIncompleteCompletions
)
{
if
(
isStyleSheet
(
document
.
languageId
)
&&
context
.
triggerKind
!==
vscode
.
CompletionTriggerKind
.
TriggerForIncompleteCompletions
)
{
validateLocation
=
true
;
validateLocation
=
true
;
let
usePartialParsing
=
vscode
.
workspace
.
getConfiguration
(
'
emmet
'
)[
'
optimizeStylesheetParsing
'
]
===
true
;
let
usePartialParsing
=
vscode
.
workspace
.
getConfiguration
(
'
emmet
'
)[
'
optimizeStylesheetParsing
'
]
===
true
;
rootNode
=
usePartialParsing
&&
document
.
lineCount
>
1000
?
parsePartialStylesheet
(
document
,
position
)
:
<
Stylesheet
>
parseDocument
(
document
,
fals
e
);
rootNode
=
usePartialParsing
&&
document
.
lineCount
>
1000
?
parsePartialStylesheet
(
document
,
position
)
:
<
Stylesheet
>
getRootNode
(
document
,
tru
e
);
if
(
!
rootNode
)
{
if
(
!
rootNode
)
{
return
;
return
;
}
}
currentNode
=
get
Node
(
rootNode
,
position
,
true
);
currentNode
=
get
FlatNode
(
rootNode
,
offset
,
true
);
}
}
if
(
validateLocation
&&
!
isValidLocationForEmmetAbbreviation
(
document
,
rootNode
,
currentNode
,
syntax
,
offset
,
toRange
(
extractAbbreviationResults
.
abbreviationRange
)))
{
if
(
validateLocation
&&
!
isValidLocationForEmmetAbbreviation
(
document
,
rootNode
,
currentNode
,
syntax
,
position
,
toRange
(
extractAbbreviationResults
.
abbreviationRange
)))
{
return
;
return
;
}
}
...
...
extensions/emmet/src/test/partialParsingStylesheet.test.ts
浏览文件 @
15ba9aee
...
@@ -7,15 +7,16 @@ import 'mocha';
...
@@ -7,15 +7,16 @@ import 'mocha';
import
*
as
assert
from
'
assert
'
;
import
*
as
assert
from
'
assert
'
;
import
{
withRandomFileEditor
}
from
'
./testUtils
'
;
import
{
withRandomFileEditor
}
from
'
./testUtils
'
;
import
*
as
vscode
from
'
vscode
'
;
import
*
as
vscode
from
'
vscode
'
;
import
{
parsePartialStylesheet
,
getNode
}
from
'
../util
'
;
import
{
parsePartialStylesheet
,
get
Flat
Node
}
from
'
../util
'
;
import
{
isValidLocationForEmmetAbbreviation
}
from
'
../abbreviationActions
'
;
import
{
isValidLocationForEmmetAbbreviation
}
from
'
../abbreviationActions
'
;
suite
(
'
Tests for partial parse of Stylesheets
'
,
()
=>
{
suite
(
'
Tests for partial parse of Stylesheets
'
,
()
=>
{
function
isValid
(
doc
:
vscode
.
TextDocument
,
range
:
vscode
.
Range
,
syntax
:
string
):
boolean
{
function
isValid
(
doc
:
vscode
.
TextDocument
,
range
:
vscode
.
Range
,
syntax
:
string
):
boolean
{
const
rootNode
=
parsePartialStylesheet
(
doc
,
range
.
end
);
const
rootNode
=
parsePartialStylesheet
(
doc
,
range
.
end
);
const
currentNode
=
getNode
(
rootNode
,
range
.
end
,
true
);
const
endOffset
=
doc
.
offsetAt
(
range
.
end
);
return
isValidLocationForEmmetAbbreviation
(
doc
,
rootNode
,
currentNode
,
syntax
,
range
.
end
,
range
);
const
currentNode
=
getFlatNode
(
rootNode
,
endOffset
,
true
);
return
isValidLocationForEmmetAbbreviation
(
doc
,
rootNode
,
currentNode
,
syntax
,
endOffset
,
range
);
}
}
test
(
'
Ignore block comment inside rule
'
,
function
():
any
{
test
(
'
Ignore block comment inside rule
'
,
function
():
any
{
...
@@ -257,4 +258,4 @@ ment */{
...
@@ -257,4 +258,4 @@ ment */{
});
});
});
});
\ No newline at end of file
extensions/emmet/src/util.ts
浏览文件 @
15ba9aee
...
@@ -6,8 +6,7 @@
...
@@ -6,8 +6,7 @@
import
*
as
vscode
from
'
vscode
'
;
import
*
as
vscode
from
'
vscode
'
;
import
parse
from
'
@emmetio/html-matcher
'
;
import
parse
from
'
@emmetio/html-matcher
'
;
import
parseStylesheet
from
'
@emmetio/css-parser
'
;
import
parseStylesheet
from
'
@emmetio/css-parser
'
;
import
{
Node
,
HtmlNode
,
Stylesheet
}
from
'
EmmetNode
'
;
import
{
Node
as
FlatNode
,
HtmlNode
as
HtmlFlatNode
,
Property
as
FlatProperty
,
Rule
as
FlatRule
,
CssToken
as
FlatCssToken
,
Stylesheet
as
FlatStylesheet
}
from
'
EmmetFlatNode
'
;
import
{
Node
as
FlatNode
,
HtmlNode
as
HtmlFlatNode
,
Property
as
FlatProperty
,
Rule
as
FlatRule
,
CssToken
as
FlatCssToken
}
from
'
EmmetFlatNode
'
;
import
{
DocumentStreamReader
}
from
'
./bufferStream
'
;
import
{
DocumentStreamReader
}
from
'
./bufferStream
'
;
import
*
as
EmmetHelper
from
'
vscode-emmet-helper
'
;
import
*
as
EmmetHelper
from
'
vscode-emmet-helper
'
;
import
{
TextDocument
as
LSTextDocument
}
from
'
vscode-languageserver-textdocument
'
;
import
{
TextDocument
as
LSTextDocument
}
from
'
vscode-languageserver-textdocument
'
;
...
@@ -138,21 +137,6 @@ export function getEmmetMode(language: string, excludedLanguages: string[]): str
...
@@ -138,21 +137,6 @@ export function getEmmetMode(language: string, excludedLanguages: string[]): str
return
;
return
;
}
}
/**
* Parses the given document using emmet parsing modules
*/
export
function
parseDocument
(
document
:
vscode
.
TextDocument
,
showError
:
boolean
=
true
):
Node
|
undefined
{
let
parseContent
=
isStyleSheet
(
document
.
languageId
)
?
parseStylesheet
:
parse
;
try
{
return
parseContent
(
new
DocumentStreamReader
(
document
));
}
catch
(
e
)
{
if
(
showError
)
{
vscode
.
window
.
showErrorMessage
(
'
Emmet: Failed to parse the file
'
);
}
}
return
undefined
;
}
const
closeBrace
=
125
;
const
closeBrace
=
125
;
const
openBrace
=
123
;
const
openBrace
=
123
;
const
slash
=
47
;
const
slash
=
47
;
...
@@ -164,7 +148,7 @@ const star = 42;
...
@@ -164,7 +148,7 @@ const star = 42;
* @param document vscode.TextDocument
* @param document vscode.TextDocument
* @param position vscode.Position
* @param position vscode.Position
*/
*/
export
function
parsePartialStylesheet
(
document
:
vscode
.
TextDocument
,
position
:
vscode
.
Position
):
Stylesheet
|
undefined
{
export
function
parsePartialStylesheet
(
document
:
vscode
.
TextDocument
,
position
:
vscode
.
Position
):
Flat
Stylesheet
|
undefined
{
const
isCSS
=
document
.
languageId
===
'
css
'
;
const
isCSS
=
document
.
languageId
===
'
css
'
;
let
startPosition
=
new
vscode
.
Position
(
0
,
0
);
let
startPosition
=
new
vscode
.
Position
(
0
,
0
);
let
endPosition
=
new
vscode
.
Position
(
document
.
lineCount
-
1
,
document
.
lineAt
(
document
.
lineCount
-
1
).
text
.
length
);
let
endPosition
=
new
vscode
.
Position
(
document
.
lineCount
-
1
,
document
.
lineAt
(
document
.
lineCount
-
1
).
text
.
length
);
...
@@ -304,7 +288,10 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
...
@@ -304,7 +288,10 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
}
}
try
{
try
{
return
parseStylesheet
(
new
DocumentStreamReader
(
document
,
startPosition
,
new
vscode
.
Range
(
startPosition
,
endPosition
)));
const
startOffset
=
document
.
offsetAt
(
startPosition
);
const
endOffset
=
document
.
offsetAt
(
endPosition
);
const
buffer
=
'
'
.
repeat
(
startOffset
)
+
document
.
getText
().
substring
(
startOffset
,
endOffset
);
return
parseStylesheet
(
buffer
);
}
catch
(
e
)
{
}
catch
(
e
)
{
return
;
return
;
}
}
...
@@ -313,31 +300,6 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
...
@@ -313,31 +300,6 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
/**
/**
* Returns node corresponding to given position in the given root node
* Returns node corresponding to given position in the given root node
*/
*/
export
function
getNode
(
root
:
Node
|
undefined
,
position
:
vscode
.
Position
,
includeNodeBoundary
:
boolean
):
Node
|
null
{
if
(
!
root
)
{
return
null
;
}
let
currentNode
=
root
.
firstChild
;
let
foundNode
:
Node
|
null
=
null
;
while
(
currentNode
)
{
const
nodeStart
:
vscode
.
Position
=
currentNode
.
start
;
const
nodeEnd
:
vscode
.
Position
=
currentNode
.
end
;
if
((
nodeStart
.
isBefore
(
position
)
&&
nodeEnd
.
isAfter
(
position
))
||
(
includeNodeBoundary
&&
(
nodeStart
.
isBeforeOrEqual
(
position
)
&&
nodeEnd
.
isAfterOrEqual
(
position
))))
{
foundNode
=
currentNode
;
// Dig deeper
currentNode
=
currentNode
.
firstChild
;
}
else
{
currentNode
=
currentNode
.
nextSibling
;
}
}
return
foundNode
;
}
export
function
getFlatNode
(
root
:
FlatNode
|
undefined
,
offset
:
number
,
includeNodeBoundary
:
boolean
):
FlatNode
|
undefined
{
export
function
getFlatNode
(
root
:
FlatNode
|
undefined
,
offset
:
number
,
includeNodeBoundary
:
boolean
):
FlatNode
|
undefined
{
if
(
!
root
)
{
if
(
!
root
)
{
return
;
return
;
...
@@ -380,35 +342,9 @@ export function getFlatNode(root: FlatNode | undefined, offset: number, includeN
...
@@ -380,35 +342,9 @@ export function getFlatNode(root: FlatNode | undefined, offset: number, includeN
export
const
allowedMimeTypesInScriptTag
=
[
'
text/html
'
,
'
text/plain
'
,
'
text/x-template
'
,
'
text/template
'
,
'
text/ng-template
'
];
export
const
allowedMimeTypesInScriptTag
=
[
'
text/html
'
,
'
text/plain
'
,
'
text/x-template
'
,
'
text/template
'
,
'
text/ng-template
'
];
/**
* Returns HTML node corresponding to given position in the given root node
* If position is inside a script tag of type template, then it will be parsed to find the inner HTML node as well
*/
export
function
getHtmlNode
(
document
:
vscode
.
TextDocument
,
root
:
Node
|
undefined
,
position
:
vscode
.
Position
,
includeNodeBoundary
:
boolean
):
HtmlNode
|
undefined
{
let
currentNode
=
<
HtmlNode
>
getNode
(
root
,
position
,
includeNodeBoundary
);
if
(
!
currentNode
)
{
return
;
}
const
isTemplateScript
=
currentNode
.
name
===
'
script
'
&&
(
currentNode
.
attributes
&&
currentNode
.
attributes
.
some
(
x
=>
x
.
name
.
toString
()
===
'
type
'
&&
allowedMimeTypesInScriptTag
.
indexOf
(
x
.
value
.
toString
())
>
-
1
));
if
(
isTemplateScript
&&
currentNode
.
close
&&
(
position
.
isAfter
(
currentNode
.
open
.
end
)
&&
position
.
isBefore
(
currentNode
.
close
.
start
)))
{
let
buffer
=
new
DocumentStreamReader
(
document
,
currentNode
.
open
.
end
,
new
vscode
.
Range
(
currentNode
.
open
.
end
,
currentNode
.
close
.
start
));
try
{
let
scriptInnerNodes
=
parse
(
buffer
);
currentNode
=
<
HtmlNode
>
getNode
(
scriptInnerNodes
,
position
,
includeNodeBoundary
)
||
currentNode
;
}
catch
(
e
)
{
}
}
return
currentNode
;
}
/**
/**
* Finds the HTML node within an HTML document at a given position
* Finds the HTML node within an HTML document at a given position
* If position is inside a script tag of type template, then it will be parsed to find the inner HTML node as well
*/
*/
export
function
getHtmlFlatNode
(
documentText
:
string
,
root
:
FlatNode
|
undefined
,
offset
:
number
,
includeNodeBoundary
:
boolean
):
HtmlFlatNode
|
undefined
{
export
function
getHtmlFlatNode
(
documentText
:
string
,
root
:
FlatNode
|
undefined
,
offset
:
number
,
includeNodeBoundary
:
boolean
):
HtmlFlatNode
|
undefined
{
const
currentNode
:
HtmlFlatNode
|
undefined
=
<
HtmlFlatNode
|
undefined
>
getFlatNode
(
root
,
offset
,
includeNodeBoundary
);
const
currentNode
:
HtmlFlatNode
|
undefined
=
<
HtmlFlatNode
|
undefined
>
getFlatNode
(
root
,
offset
,
includeNodeBoundary
);
...
@@ -449,31 +385,6 @@ export function offsetRangeToVsRange(document: vscode.TextDocument, start: numbe
...
@@ -449,31 +385,6 @@ export function offsetRangeToVsRange(document: vscode.TextDocument, start: numbe
return
new
vscode
.
Range
(
startPos
,
endPos
);
return
new
vscode
.
Range
(
startPos
,
endPos
);
}
}
/**
* Returns inner range of an html node.
*/
export
function
getInnerRange
(
currentNode
:
HtmlNode
):
vscode
.
Range
|
undefined
{
if
(
!
currentNode
.
close
)
{
return
undefined
;
}
return
new
vscode
.
Range
(
currentNode
.
open
.
end
,
currentNode
.
close
.
start
);
}
/**
* Returns the deepest non comment node under given node
*/
export
function
getDeepestNode
(
node
:
Node
|
undefined
):
Node
|
undefined
{
if
(
!
node
||
!
node
.
children
||
node
.
children
.
length
===
0
||
!
node
.
children
.
find
(
x
=>
x
.
type
!==
'
comment
'
))
{
return
node
;
}
for
(
let
i
=
node
.
children
.
length
-
1
;
i
>=
0
;
i
--
)
{
if
(
node
.
children
[
i
].
type
!==
'
comment
'
)
{
return
getDeepestNode
(
node
.
children
[
i
]);
}
}
return
undefined
;
}
/**
/**
* Returns the deepest non comment node under given node
* Returns the deepest non comment node under given node
*/
*/
...
@@ -696,18 +607,18 @@ export function getCssPropertyFromDocument(editor: vscode.TextEditor, position:
...
@@ -696,18 +607,18 @@ export function getCssPropertyFromDocument(editor: vscode.TextEditor, position:
}
}
export
function
getEmbeddedCssNodeIfAny
(
document
:
vscode
.
TextDocument
,
currentNode
:
Node
|
null
,
position
:
vscode
.
Position
):
Node
|
undefined
{
export
function
getEmbeddedCssNodeIfAny
(
document
:
vscode
.
TextDocument
,
currentNode
:
FlatNode
|
undefined
,
position
:
vscode
.
Position
):
Flat
Node
|
undefined
{
if
(
!
currentNode
)
{
if
(
!
currentNode
)
{
return
;
return
;
}
}
const
currentHtmlNode
=
<
HtmlNode
>
currentNode
;
const
currentHtmlNode
=
<
Html
Flat
Node
>
currentNode
;
if
(
currentHtmlNode
&&
currentHtmlNode
.
close
)
{
if
(
currentHtmlNode
&&
currentHtmlNode
.
open
&&
currentHtmlNode
.
close
)
{
const
innerRange
=
getInnerRange
(
currentHtmlNode
);
const
offset
=
document
.
offsetAt
(
position
);
if
(
innerRange
&&
innerRange
.
contains
(
position
)
)
{
if
(
currentHtmlNode
.
open
.
end
<=
offset
&&
offset
<=
currentHtmlNode
.
close
.
start
)
{
if
(
currentHtmlNode
.
name
===
'
style
'
if
(
currentHtmlNode
.
name
===
'
style
'
&&
currentHtmlNode
.
open
.
end
.
isBefore
(
position
)
&&
currentHtmlNode
.
open
.
end
<
offset
&&
currentHtmlNode
.
close
.
start
.
isAfter
(
position
)
)
{
&&
currentHtmlNode
.
close
.
start
>
offset
)
{
let
buffer
=
new
DocumentStreamReader
(
document
,
currentHtmlNode
.
open
.
end
,
new
vscode
.
Range
(
currentHtmlNode
.
open
.
end
,
currentHtmlNode
.
close
.
start
)
);
const
buffer
=
'
'
.
repeat
(
currentHtmlNode
.
open
.
end
)
+
document
.
getText
().
substring
(
currentHtmlNode
.
open
.
end
,
currentHtmlNode
.
close
.
start
);
return
parseStylesheet
(
buffer
);
return
parseStylesheet
(
buffer
);
}
}
}
}
...
@@ -715,17 +626,17 @@ export function getEmbeddedCssNodeIfAny(document: vscode.TextDocument, currentNo
...
@@ -715,17 +626,17 @@ export function getEmbeddedCssNodeIfAny(document: vscode.TextDocument, currentNo
return
;
return
;
}
}
export
function
isStyleAttribute
(
currentNode
:
Node
|
null
,
position
:
vscode
.
Position
):
boolean
{
export
function
isStyleAttribute
(
currentNode
:
FlatNode
|
undefined
,
offset
:
number
):
boolean
{
if
(
!
currentNode
)
{
if
(
!
currentNode
)
{
return
false
;
return
false
;
}
}
const
currentHtmlNode
=
<
HtmlNode
>
currentNode
;
const
currentHtmlNode
=
<
Html
Flat
Node
>
currentNode
;
const
index
=
(
currentHtmlNode
.
attributes
||
[]).
findIndex
(
x
=>
x
.
name
.
toString
()
===
'
style
'
);
const
index
=
(
currentHtmlNode
.
attributes
||
[]).
findIndex
(
x
=>
x
.
name
.
toString
()
===
'
style
'
);
if
(
index
===
-
1
)
{
if
(
index
===
-
1
)
{
return
false
;
return
false
;
}
}
const
styleAttribute
=
currentHtmlNode
.
attributes
[
index
];
const
styleAttribute
=
currentHtmlNode
.
attributes
[
index
];
return
position
.
isAfterOrEqual
(
styleAttribute
.
value
.
start
)
&&
position
.
isBeforeOrEqual
(
styleAttribute
.
value
.
end
)
;
return
offset
>=
styleAttribute
.
value
.
start
&&
offset
<=
styleAttribute
.
value
.
end
;
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录