Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
c7dbab59
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,发现更多精彩内容 >>
未验证
提交
c7dbab59
编写于
12月 28, 2020
作者:
R
Raymond Zhao
提交者:
GitHub
12月 28, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Emmet create new html-matcher override instead of using LS (#113508)
上级
4b6280aa
变更
11
显示空白变更内容
内联
并排
Showing
11 changed file
with
307 addition
and
157 deletion
+307
-157
extensions/emmet/src/balance.ts
extensions/emmet/src/balance.ts
+25
-23
extensions/emmet/src/emmetCommon.ts
extensions/emmet/src/emmetCommon.ts
+8
-6
extensions/emmet/src/matchTag.ts
extensions/emmet/src/matchTag.ts
+17
-13
extensions/emmet/src/parseDocument.ts
extensions/emmet/src/parseDocument.ts
+46
-0
extensions/emmet/src/removeTag.ts
extensions/emmet/src/removeTag.ts
+28
-14
extensions/emmet/src/splitJoinTag.ts
extensions/emmet/src/splitJoinTag.ts
+17
-15
extensions/emmet/src/typings/EmmetFlatNode.d.ts
extensions/emmet/src/typings/EmmetFlatNode.d.ts
+79
-0
extensions/emmet/src/typings/emmetio__css-parser.d.ts
extensions/emmet/src/typings/emmetio__css-parser.d.ts
+2
-0
extensions/emmet/src/typings/emmetio__html-matcher.d.ts
extensions/emmet/src/typings/emmetio__html-matcher.d.ts
+2
-0
extensions/emmet/src/updateTag.ts
extensions/emmet/src/updateTag.ts
+21
-23
extensions/emmet/src/util.ts
extensions/emmet/src/util.ts
+62
-63
未找到文件。
extensions/emmet/src/balance.ts
浏览文件 @
c7dbab59
...
...
@@ -4,9 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import
*
as
vscode
from
'
vscode
'
;
import
{
getHtml
NodeLS
,
offsetRangeToSelection
,
toLSTextDocument
,
validate
}
from
'
./util
'
;
import
{
parseMarkupDocument
}
from
'
./parseMarkup
Document
'
;
import
{
TextDocument
as
LSTextDocument
}
from
'
vscode-html-languageservic
e
'
;
import
{
getHtml
FlatNode
,
offsetRangeToSelection
,
validate
}
from
'
./util
'
;
import
{
getRootNode
}
from
'
./parse
Document
'
;
import
{
HtmlNode
as
HtmlFlatNode
}
from
'
EmmetFlatNod
e
'
;
let
balanceOutStack
:
Array
<
vscode
.
Selection
[]
>
=
[];
let
lastBalancedSelections
:
vscode
.
Selection
[]
=
[];
...
...
@@ -24,16 +24,16 @@ function balance(out: boolean) {
return
;
}
const
editor
=
vscode
.
window
.
activeTextEditor
;
const
document
=
toLSTextDocument
(
editor
.
document
)
;
const
htmlDocument
=
parseMarkupDocument
(
document
);
if
(
!
htmlDocument
)
{
const
document
=
editor
.
document
;
const
rootNode
=
<
HtmlFlatNode
>
getRootNode
(
document
,
true
);
if
(
!
rootNode
)
{
return
;
}
const
rangeFn
=
out
?
getRangeToBalanceOut
:
getRangeToBalanceIn
;
let
newSelections
:
vscode
.
Selection
[]
=
[];
editor
.
selections
.
forEach
(
selection
=>
{
const
range
=
rangeFn
(
document
,
selection
);
const
range
=
rangeFn
(
document
,
rootNode
,
selection
);
newSelections
.
push
(
range
);
});
...
...
@@ -57,17 +57,18 @@ function balance(out: boolean) {
lastBalancedSelections
=
editor
.
selections
;
}
function
getRangeToBalanceOut
(
document
:
LSTextDocument
,
selection
:
vscode
.
Selection
):
vscode
.
Selection
{
const
nodeToBalance
=
getHtmlNodeLS
(
document
,
selection
.
start
,
false
);
function
getRangeToBalanceOut
(
document
:
vscode
.
TextDocument
,
rootNode
:
HtmlFlatNode
,
selection
:
vscode
.
Selection
):
vscode
.
Selection
{
const
offset
=
document
.
offsetAt
(
selection
.
start
);
const
nodeToBalance
=
getHtmlFlatNode
(
document
.
getText
(),
rootNode
,
offset
,
false
);
if
(
!
nodeToBalance
)
{
return
selection
;
}
if
(
!
nodeToBalance
.
endTagStart
||
!
nodeToBalance
.
startTagEnd
)
{
if
(
!
nodeToBalance
.
open
||
!
nodeToBalance
.
close
)
{
return
offsetRangeToSelection
(
document
,
nodeToBalance
.
start
,
nodeToBalance
.
end
);
}
const
innerSelection
=
offsetRangeToSelection
(
document
,
nodeToBalance
.
startTagEnd
,
nodeToBalance
.
endTagS
tart
);
const
outerSelection
=
offsetRangeToSelection
(
document
,
nodeToBalance
.
start
,
nodeToBalanc
e
.
end
);
const
innerSelection
=
offsetRangeToSelection
(
document
,
nodeToBalance
.
open
.
end
,
nodeToBalance
.
close
.
s
tart
);
const
outerSelection
=
offsetRangeToSelection
(
document
,
nodeToBalance
.
open
.
start
,
nodeToBalance
.
clos
e
.
end
);
if
(
innerSelection
.
contains
(
selection
)
&&
!
innerSelection
.
isEqual
(
selection
))
{
return
innerSelection
;
...
...
@@ -78,34 +79,35 @@ function getRangeToBalanceOut(document: LSTextDocument, selection: vscode.Select
return
selection
;
}
function
getRangeToBalanceIn
(
document
:
LSTextDocument
,
selection
:
vscode
.
Selection
):
vscode
.
Selection
{
const
nodeToBalance
=
getHtmlNodeLS
(
document
,
selection
.
start
,
true
);
function
getRangeToBalanceIn
(
document
:
vscode
.
TextDocument
,
rootNode
:
HtmlFlatNode
,
selection
:
vscode
.
Selection
):
vscode
.
Selection
{
const
offset
=
document
.
offsetAt
(
selection
.
start
);
const
nodeToBalance
=
getHtmlFlatNode
(
document
.
getText
(),
rootNode
,
offset
,
true
);
if
(
!
nodeToBalance
)
{
return
selection
;
}
const
selectionStart
=
document
.
offsetAt
(
selection
.
start
);
const
selectionEnd
=
document
.
offsetAt
(
selection
.
end
);
if
(
nodeToBalance
.
endTagStart
!==
undefined
&&
nodeToBalance
.
startTagEnd
!==
undefined
)
{
if
(
nodeToBalance
.
open
&&
nodeToBalance
.
close
)
{
const
entireNodeSelected
=
selectionStart
===
nodeToBalance
.
start
&&
selectionEnd
===
nodeToBalance
.
end
;
const
startInOpenTag
=
selectionStart
>
nodeToBalance
.
start
&&
selectionStart
<
nodeToBalance
.
startTagE
nd
;
const
startInCloseTag
=
selectionStart
>
nodeToBalance
.
endTagStart
&&
selectionStart
<
nodeToBalanc
e
.
end
;
const
startInOpenTag
=
selectionStart
>
nodeToBalance
.
open
.
start
&&
selectionStart
<
nodeToBalance
.
open
.
e
nd
;
const
startInCloseTag
=
selectionStart
>
nodeToBalance
.
close
.
start
&&
selectionStart
<
nodeToBalance
.
clos
e
.
end
;
if
(
entireNodeSelected
||
startInOpenTag
||
startInCloseTag
)
{
return
offsetRangeToSelection
(
document
,
nodeToBalance
.
startTagEnd
,
nodeToBalance
.
endTagS
tart
);
return
offsetRangeToSelection
(
document
,
nodeToBalance
.
open
.
end
,
nodeToBalance
.
close
.
s
tart
);
}
}
if
(
!
nodeToBalance
.
children
.
length
)
{
if
(
!
nodeToBalance
.
firstChild
)
{
return
selection
;
}
const
firstChild
=
nodeToBalance
.
children
[
0
]
;
const
firstChild
=
nodeToBalance
.
firstChild
;
if
(
selectionStart
===
firstChild
.
start
&&
selectionEnd
===
firstChild
.
end
&&
firstChild
.
endTagStart
!==
undefined
&&
firstChild
.
startTagEnd
!==
undefined
)
{
return
offsetRangeToSelection
(
document
,
firstChild
.
startTagEnd
,
firstChild
.
endTagS
tart
);
&&
firstChild
.
open
&&
firstChild
.
close
)
{
return
offsetRangeToSelection
(
document
,
firstChild
.
open
.
end
,
firstChild
.
close
.
s
tart
);
}
return
offsetRangeToSelection
(
document
,
firstChild
.
start
,
firstChild
.
end
);
...
...
extensions/emmet/src/emmetCommon.ts
浏览文件 @
c7dbab59
...
...
@@ -17,9 +17,9 @@ import { fetchEditPoint } from './editPoint';
import
{
fetchSelectItem
}
from
'
./selectItem
'
;
import
{
evaluateMathExpression
}
from
'
./evaluateMathExpression
'
;
import
{
incrementDecrement
}
from
'
./incrementDecrement
'
;
import
{
LANGUAGE_MODES
,
getMappingForIncludedLanguages
,
updateEmmetExtensionsPath
,
getPathBaseName
,
toLSTextDocument
,
getSyntaxes
,
getEmmetMode
}
from
'
./util
'
;
import
{
LANGUAGE_MODES
,
getMappingForIncludedLanguages
,
updateEmmetExtensionsPath
,
getPathBaseName
,
getSyntaxes
,
getEmmetMode
}
from
'
./util
'
;
import
{
reflectCssValue
}
from
'
./reflectCssValue
'
;
import
{
addFileTo
MarkupParseCache
,
removeFileFromMarkupParseCache
}
from
'
./parseMarkup
Document
'
;
import
{
addFileTo
ParseCache
,
removeFileFromParseCache
}
from
'
./parse
Document
'
;
export
function
activateEmmetExtension
(
context
:
vscode
.
ExtensionContext
)
{
registerCompletionProviders
(
context
);
...
...
@@ -149,15 +149,17 @@ export function activateEmmetExtension(context: vscode.ExtensionContext) {
context
.
subscriptions
.
push
(
vscode
.
workspace
.
onDidOpenTextDocument
((
e
)
=>
{
const
emmetMode
=
getEmmetMode
(
e
.
languageId
,
[])
??
''
;
if
(
getSyntaxes
().
markup
.
includes
(
emmetMode
))
{
addFileToMarkupParseCache
(
toLSTextDocument
(
e
));
const
syntaxes
=
getSyntaxes
();
if
(
syntaxes
.
markup
.
includes
(
emmetMode
)
||
syntaxes
.
stylesheet
.
includes
(
emmetMode
))
{
addFileToParseCache
(
e
);
}
}));
context
.
subscriptions
.
push
(
vscode
.
workspace
.
onDidCloseTextDocument
((
e
)
=>
{
const
emmetMode
=
getEmmetMode
(
e
.
languageId
,
[])
??
''
;
if
(
getSyntaxes
().
markup
.
includes
(
emmetMode
))
{
removeFileFromMarkupParseCache
(
toLSTextDocument
(
e
));
const
syntaxes
=
getSyntaxes
();
if
(
syntaxes
.
markup
.
includes
(
emmetMode
)
||
syntaxes
.
stylesheet
.
includes
(
emmetMode
))
{
removeFileFromParseCache
(
e
);
}
}));
}
...
...
extensions/emmet/src/matchTag.ts
浏览文件 @
c7dbab59
...
...
@@ -4,8 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import
*
as
vscode
from
'
vscode
'
;
import
{
toLSTextDocument
,
validate
,
getHtmlNodeLS
,
offsetRangeToSelection
}
from
'
./util
'
;
import
{
TextDocument
as
LSTextDocument
}
from
'
vscode-html-languageservice
'
;
import
{
validate
,
getHtmlFlatNode
,
offsetRangeToSelection
}
from
'
./util
'
;
import
{
getRootNode
}
from
'
./parseDocument
'
;
import
{
HtmlNode
as
HtmlFlatNode
}
from
'
EmmetFlatNode
'
;
export
function
matchTag
()
{
if
(
!
validate
(
false
)
||
!
vscode
.
window
.
activeTextEditor
)
{
...
...
@@ -13,11 +14,15 @@ export function matchTag() {
}
const
editor
=
vscode
.
window
.
activeTextEditor
;
const
document
=
toLSTextDocument
(
editor
.
document
);
const
document
=
editor
.
document
;
const
rootNode
=
<
HtmlFlatNode
>
getRootNode
(
document
,
true
);
if
(
!
rootNode
)
{
return
;
}
let
updatedSelections
:
vscode
.
Selection
[]
=
[];
editor
.
selections
.
forEach
(
selection
=>
{
const
updatedSelection
=
getUpdatedSelections
(
document
,
selection
.
start
);
const
updatedSelection
=
getUpdatedSelections
(
document
,
rootNode
,
selection
.
start
);
if
(
updatedSelection
)
{
updatedSelections
.
push
(
updatedSelection
);
}
...
...
@@ -28,22 +33,21 @@ export function matchTag() {
}
}
function
getUpdatedSelections
(
document
:
LSTextDocument
,
position
:
vscode
.
Position
):
vscode
.
Selection
|
undefined
{
const
currentNode
=
getHtmlNodeLS
(
document
,
position
,
true
);
function
getUpdatedSelections
(
document
:
vscode
.
TextDocument
,
rootNode
:
HtmlFlatNode
,
position
:
vscode
.
Position
):
vscode
.
Selection
|
undefined
{
const
offset
=
document
.
offsetAt
(
position
);
const
currentNode
=
getHtmlFlatNode
(
document
.
getText
(),
rootNode
,
offset
,
true
);
if
(
!
currentNode
)
{
return
;
}
const
offset
=
document
.
offsetAt
(
position
);
// If no closing tag or cursor is between open and close tag, then no-op
if
(
currentNode
.
endTagStart
===
undefined
||
currentNode
.
startTagEnd
===
undefined
||
(
offset
>
currentNode
.
startTagEnd
&&
offset
<
currentNode
.
endTagStart
))
{
// If no opening/closing tag or cursor is between open and close tag, then no-op
if
(
!
currentNode
.
open
||
!
currentNode
.
close
||
(
offset
>
currentNode
.
open
.
end
&&
offset
<
currentNode
.
close
.
start
))
{
return
;
}
// Place cursor inside the close tag if cursor is inside the open tag, else place it inside the open tag
const
finalOffset
=
(
offset
<=
currentNode
.
startTagEnd
)
?
currentNode
.
endTagS
tart
+
2
:
currentNode
.
start
+
1
;
const
finalOffset
=
(
offset
<=
currentNode
.
open
.
end
)
?
currentNode
.
close
.
s
tart
+
2
:
currentNode
.
start
+
1
;
return
offsetRangeToSelection
(
document
,
finalOffset
,
finalOffset
);
}
extensions/emmet/src/parse
Markup
Document.ts
→
extensions/emmet/src/parseDocument.ts
浏览文件 @
c7dbab59
...
...
@@ -3,20 +3,22 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
{
HTMLDocument
,
TextDocument
as
LSTextDocument
}
from
'
vscode-html-languageservice
'
;
import
{
getLanguageService
}
from
'
./util
'
;
import
{
TextDocument
}
from
'
vscode
'
;
import
{
Node
as
FlatNode
}
from
'
EmmetFlatNode
'
;
import
parse
from
'
@emmetio/html-matcher
'
;
import
parseStylesheet
from
'
@emmetio/css-parser
'
;
import
{
isStyleSheet
}
from
'
./util
'
;
type
Pair
<
K
,
V
>
=
{
key
:
K
;
value
:
V
;
};
// Map(filename, Pair(fileVersion,
p
arsedContent))
const
_parseCache
=
new
Map
<
string
,
Pair
<
number
,
HTMLDocument
>
|
undefined
>
();
// Map(filename, Pair(fileVersion,
rootNodeOfP
arsedContent))
const
_parseCache
=
new
Map
<
string
,
Pair
<
number
,
FlatNode
>
|
undefined
>
();
export
function
parseMarkupDocument
(
document
:
LSTextDocument
,
useCache
:
boolean
=
true
):
HTMLDocument
{
const
languageService
=
getLanguageService
();
const
key
=
document
.
uri
;
export
function
getRootNode
(
document
:
TextDocument
,
useCache
:
boolean
):
FlatNode
{
const
key
=
document
.
uri
.
toString
();
const
result
=
_parseCache
.
get
(
key
);
const
documentVersion
=
document
.
version
;
if
(
useCache
&&
result
)
{
...
...
@@ -25,19 +27,20 @@ export function parseMarkupDocument(document: LSTextDocument, useCache: boolean
}
}
const
parsedDocument
=
languageService
.
parseHTMLDocument
(
document
);
const
parseContent
=
isStyleSheet
(
document
.
languageId
)
?
parseStylesheet
:
parse
;
const
rootNode
=
parseContent
(
document
.
getText
());
if
(
useCache
)
{
_parseCache
.
set
(
key
,
{
key
:
documentVersion
,
value
:
parsedDocument
});
_parseCache
.
set
(
key
,
{
key
:
documentVersion
,
value
:
rootNode
});
}
return
parsedDocument
;
return
rootNode
;
}
export
function
addFileTo
MarkupParseCache
(
document
:
LS
TextDocument
)
{
const
filename
=
document
.
uri
;
export
function
addFileTo
ParseCache
(
document
:
TextDocument
)
{
const
filename
=
document
.
uri
.
toString
()
;
_parseCache
.
set
(
filename
,
undefined
);
}
export
function
removeFileFrom
MarkupParseCache
(
document
:
LS
TextDocument
)
{
const
filename
=
document
.
uri
;
export
function
removeFileFrom
ParseCache
(
document
:
TextDocument
)
{
const
filename
=
document
.
uri
.
toString
()
;
_parseCache
.
delete
(
filename
);
}
extensions/emmet/src/removeTag.ts
浏览文件 @
c7dbab59
...
...
@@ -4,16 +4,24 @@
*--------------------------------------------------------------------------------------------*/
import
*
as
vscode
from
'
vscode
'
;
import
{
validate
,
getHtmlNodeLS
,
toLSTextDocument
,
offsetRangeToVsRange
}
from
'
./util
'
;
import
{
getRootNode
}
from
'
./parseDocument
'
;
import
{
validate
,
getHtmlFlatNode
,
offsetRangeToVsRange
}
from
'
./util
'
;
import
{
HtmlNode
as
HtmlFlatNode
}
from
'
EmmetFlatNode
'
;
export
function
removeTag
()
{
if
(
!
validate
(
false
)
||
!
vscode
.
window
.
activeTextEditor
)
{
return
;
}
const
editor
=
vscode
.
window
.
activeTextEditor
;
const
document
=
editor
.
document
;
const
rootNode
=
<
HtmlFlatNode
>
getRootNode
(
document
,
true
);
if
(
!
rootNode
)
{
return
;
}
let
finalRangesToRemove
=
editor
.
selections
.
reverse
()
.
reduce
<
vscode
.
Range
[]
>
((
prev
,
selection
)
=>
prev
.
concat
(
getRangesToRemove
(
editor
.
document
,
selection
)),
[]);
prev
.
concat
(
getRangesToRemove
(
editor
.
document
,
rootNode
,
selection
)),
[]);
return
editor
.
edit
(
editBuilder
=>
{
finalRangesToRemove
.
forEach
(
range
=>
{
...
...
@@ -27,20 +35,25 @@ export function removeTag() {
* It finds the node to remove based on the selection's start position
* and then removes that node, reindenting the content in between.
*/
function
getRangesToRemove
(
document
:
vscode
.
TextDocument
,
selection
:
vscode
.
Selection
):
vscode
.
Range
[]
{
const
lsDocument
=
toLSTextDocument
(
documen
t
);
const
nodeToUpdate
=
getHtml
NodeLS
(
lsDocument
,
selection
.
star
t
,
true
);
function
getRangesToRemove
(
document
:
vscode
.
TextDocument
,
rootNode
:
HtmlFlatNode
,
selection
:
vscode
.
Selection
):
vscode
.
Range
[]
{
const
offset
=
document
.
offsetAt
(
selection
.
star
t
);
const
nodeToUpdate
=
getHtml
FlatNode
(
document
.
getText
(),
rootNode
,
offse
t
,
true
);
if
(
!
nodeToUpdate
)
{
return
[];
}
const
openTagRange
=
offsetRangeToVsRange
(
lsDocument
,
nodeToUpdate
.
start
,
nodeToUpdate
.
startTagEnd
??
nodeToUpdate
.
end
);
let
openTagRange
:
vscode
.
Range
|
undefined
;
if
(
nodeToUpdate
.
open
)
{
openTagRange
=
offsetRangeToVsRange
(
document
,
nodeToUpdate
.
open
.
start
,
nodeToUpdate
.
open
.
end
);
}
let
closeTagRange
:
vscode
.
Range
|
undefined
;
if
(
nodeToUpdate
.
endTagStart
!==
undefined
)
{
closeTagRange
=
offsetRangeToVsRange
(
lsDocument
,
nodeToUpdate
.
endTagStart
,
nodeToUpdat
e
.
end
);
if
(
nodeToUpdate
.
close
)
{
closeTagRange
=
offsetRangeToVsRange
(
document
,
nodeToUpdate
.
close
.
start
,
nodeToUpdate
.
clos
e
.
end
);
}
let
rangesToRemove
=
[
openTagRange
];
let
rangesToRemove
=
[];
if
(
openTagRange
)
{
rangesToRemove
.
push
(
openTagRange
);
if
(
closeTagRange
)
{
const
indentAmountToRemove
=
calculateIndentAmountToRemove
(
document
,
openTagRange
,
closeTagRange
);
for
(
let
i
=
openTagRange
.
start
.
line
+
1
;
i
<
closeTagRange
.
start
.
line
;
i
++
)
{
...
...
@@ -48,6 +61,7 @@ function getRangesToRemove(document: vscode.TextDocument, selection: vscode.Sele
}
rangesToRemove
.
push
(
closeTagRange
);
}
}
return
rangesToRemove
;
}
...
...
extensions/emmet/src/splitJoinTag.ts
浏览文件 @
c7dbab59
...
...
@@ -4,8 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import
*
as
vscode
from
'
vscode
'
;
import
{
validate
,
getEmmetMode
,
getEmmetConfiguration
,
toLSTextDocument
,
getHtmlNodeLS
,
offsetRangeToVsRange
}
from
'
./util
'
;
import
{
Node
as
LSNode
,
TextDocument
as
LSTextDocument
}
from
'
vscode-html-languageservice
'
;
import
{
validate
,
getEmmetMode
,
getEmmetConfiguration
,
getHtmlFlatNode
,
offsetRangeToVsRange
}
from
'
./util
'
;
import
{
HtmlNode
as
HtmlFlatNode
}
from
'
EmmetFlatNode
'
;
import
{
getRootNode
}
from
'
./parseDocument
'
;
export
function
splitJoinTag
()
{
if
(
!
validate
(
false
)
||
!
vscode
.
window
.
activeTextEditor
)
{
...
...
@@ -13,29 +14,30 @@ export function splitJoinTag() {
}
const
editor
=
vscode
.
window
.
activeTextEditor
;
const
document
=
toLSTextDocument
(
editor
.
document
);
const
document
=
editor
.
document
;
const
rootNode
=
<
HtmlFlatNode
>
getRootNode
(
editor
.
document
,
true
);
if
(
!
rootNode
)
{
return
;
}
return
editor
.
edit
(
editBuilder
=>
{
editor
.
selections
.
reverse
().
forEach
(
selection
=>
{
const
nodeToUpdate
=
getHtmlNodeLS
(
document
,
selection
.
start
,
true
);
const
documentText
=
document
.
getText
();
const
offset
=
document
.
offsetAt
(
selection
.
start
);
const
nodeToUpdate
=
getHtmlFlatNode
(
documentText
,
rootNode
,
offset
,
true
);
if
(
nodeToUpdate
)
{
const
textEdit
=
getRangesToReplace
(
document
,
nodeToUpdate
);
if
(
textEdit
)
{
editBuilder
.
replace
(
textEdit
.
range
,
textEdit
.
newText
);
}
}
});
});
}
function
getRangesToReplace
(
document
:
LSTextDocument
,
nodeToUpdate
:
LSNode
):
vscode
.
TextEdit
|
undefined
{
function
getRangesToReplace
(
document
:
vscode
.
TextDocument
,
nodeToUpdate
:
HtmlFlatNode
):
vscode
.
TextEdit
{
let
rangeToReplace
:
vscode
.
Range
;
let
textToReplaceWith
:
string
;
if
(
!
nodeToUpdate
?.
tag
)
{
return
;
}
if
(
nodeToUpdate
.
endTagStart
===
undefined
||
nodeToUpdate
.
startTagEnd
===
undefined
)
{
if
(
!
nodeToUpdate
.
open
||
!
nodeToUpdate
.
close
)
{
// Split Tag
const
nodeText
=
document
.
getText
().
substring
(
nodeToUpdate
.
start
,
nodeToUpdate
.
end
);
const
m
=
nodeText
.
match
(
/
(\s
*
\/)?
>$/
);
...
...
@@ -43,10 +45,10 @@ function getRangesToReplace(document: LSTextDocument, nodeToUpdate: LSNode): vsc
const
start
=
m
?
end
-
m
[
0
].
length
:
end
;
rangeToReplace
=
offsetRangeToVsRange
(
document
,
start
,
end
);
textToReplaceWith
=
`></
${
nodeToUpdate
.
tag
}
>`
;
textToReplaceWith
=
`></
${
nodeToUpdate
.
name
}
>`
;
}
else
{
// Join Tag
const
start
=
nodeToUpdate
.
startTagE
nd
-
1
;
const
start
=
nodeToUpdate
.
open
.
e
nd
-
1
;
const
end
=
nodeToUpdate
.
end
;
rangeToReplace
=
offsetRangeToVsRange
(
document
,
start
,
end
);
textToReplaceWith
=
'
/>
'
;
...
...
extensions/emmet/src/typings/EmmetFlatNode.d.ts
0 → 100644
浏览文件 @
c7dbab59
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
declare
module
'
EmmetFlatNode
'
{
export
interface
Node
{
start
:
number
end
:
number
type
:
string
parent
:
Node
|
undefined
firstChild
:
Node
|
undefined
nextSibling
:
Node
|
undefined
previousSibling
:
Node
|
undefined
children
:
Node
[]
}
export
interface
Token
{
start
:
number
end
:
number
stream
:
string
toString
():
string
}
export
interface
CssToken
extends
Token
{
size
:
number
item
(
number
:
number
):
any
type
:
string
}
export
interface
HtmlToken
extends
Token
{
value
:
string
}
export
interface
Attribute
extends
Token
{
name
:
Token
value
:
Token
}
export
interface
HtmlNode
extends
Node
{
name
:
string
open
:
Token
|
undefined
close
:
Token
|
undefined
parent
:
HtmlNode
|
undefined
firstChild
:
HtmlNode
|
undefined
nextSibling
:
HtmlNode
|
undefined
previousSibling
:
HtmlNode
|
undefined
children
:
HtmlNode
[]
attributes
:
Attribute
[]
}
export
interface
CssNode
extends
Node
{
name
:
string
parent
:
CssNode
|
undefined
firstChild
:
CssNode
|
undefined
nextSibling
:
CssNode
|
undefined
previousSibling
:
CssNode
|
undefined
children
:
CssNode
[]
}
export
interface
Rule
extends
CssNode
{
selectorToken
:
Token
contentStartToken
:
Token
contentEndToken
:
Token
}
export
interface
Property
extends
CssNode
{
valueToken
:
Token
separator
:
string
parent
:
Rule
terminatorToken
:
Token
separatorToken
:
Token
value
:
string
}
export
interface
Stylesheet
extends
Node
{
comments
:
Token
[]
}
}
extensions/emmet/src/typings/emmetio__css-parser.d.ts
浏览文件 @
c7dbab59
...
...
@@ -5,8 +5,10 @@
declare
module
'
@emmetio/css-parser
'
{
import
{
BufferStream
,
Stylesheet
}
from
'
EmmetNode
'
;
import
{
Stylesheet
as
FlatStylesheet
}
from
'
EmmetFlatNode
'
;
function
parseStylesheet
(
stream
:
BufferStream
):
Stylesheet
;
function
parseStylesheet
(
stream
:
string
):
FlatStylesheet
;
export
default
parseStylesheet
;
}
...
...
extensions/emmet/src/typings/emmetio__html-matcher.d.ts
浏览文件 @
c7dbab59
...
...
@@ -5,8 +5,10 @@
declare
module
'
@emmetio/html-matcher
'
{
import
{
BufferStream
,
HtmlNode
}
from
'
EmmetNode
'
;
import
{
HtmlNode
as
HtmlFlatNode
}
from
'
EmmetFlatNode
'
;
function
parse
(
stream
:
BufferStream
):
HtmlNode
;
function
parse
(
stream
:
string
):
HtmlFlatNode
;
export
default
parse
;
}
...
...
extensions/emmet/src/updateTag.ts
浏览文件 @
c7dbab59
...
...
@@ -4,8 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import
*
as
vscode
from
'
vscode
'
;
import
{
getHtmlNodeLS
,
toLSTextDocument
,
validate
}
from
'
./util
'
;
import
{
TextDocument
as
LSTextDocument
,
Node
as
LSNode
}
from
'
vscode-html-languageservice
'
;
import
{
getHtmlFlatNode
,
validate
}
from
'
./util
'
;
import
{
HtmlNode
as
HtmlFlatNode
}
from
'
EmmetFlatNode
'
;
import
{
getRootNode
}
from
'
./parseDocument
'
;
export
function
updateTag
(
tagName
:
string
):
Thenable
<
boolean
>
|
undefined
{
if
(
!
validate
(
false
)
||
!
vscode
.
window
.
activeTextEditor
)
{
...
...
@@ -13,9 +14,15 @@ export function updateTag(tagName: string): Thenable<boolean> | undefined {
}
const
editor
=
vscode
.
window
.
activeTextEditor
;
const
document
=
editor
.
document
;
const
rootNode
=
<
HtmlFlatNode
>
getRootNode
(
document
,
true
);
if
(
!
rootNode
)
{
return
;
}
const
rangesToUpdate
=
editor
.
selections
.
reverse
()
.
reduce
<
vscode
.
Range
[]
>
((
prev
,
selection
)
=>
prev
.
concat
(
getRangesToUpdate
(
editor
,
selection
)),
[]);
prev
.
concat
(
getRangesToUpdate
(
document
,
selection
,
rootNode
)),
[]);
return
editor
.
edit
(
editBuilder
=>
{
rangesToUpdate
.
forEach
(
range
=>
{
...
...
@@ -24,34 +31,25 @@ export function updateTag(tagName: string): Thenable<boolean> | undefined {
});
}
function
getPositionFromOffset
(
offset
:
number
|
undefined
,
document
:
LSTextDocument
):
vscode
.
Position
|
undefined
{
if
(
offset
===
undefined
)
{
return
undefined
;
}
const
pos
=
document
.
positionAt
(
offset
);
return
new
vscode
.
Position
(
pos
.
line
,
pos
.
character
);
}
function
getRangesFromNode
(
node
:
LSNode
,
document
:
LSTextDocument
):
vscode
.
Range
[]
{
const
start
=
getPositionFromOffset
(
node
.
start
,
document
)
!
;
const
startTagEnd
=
getPositionFromOffset
(
node
.
startTagEnd
,
document
);
const
end
=
getPositionFromOffset
(
node
.
end
,
document
)
!
;
const
endTagStart
=
getPositionFromOffset
(
node
.
endTagStart
,
document
);
function
getRangesFromNode
(
node
:
HtmlFlatNode
,
document
:
vscode
.
TextDocument
):
vscode
.
Range
[]
{
let
ranges
:
vscode
.
Range
[]
=
[];
if
(
startTagEnd
)
{
if
(
node
.
open
)
{
const
start
=
document
.
positionAt
(
node
.
open
.
start
);
ranges
.
push
(
new
vscode
.
Range
(
start
.
translate
(
0
,
1
),
start
.
translate
(
0
,
1
).
translate
(
0
,
node
.
tag
!
.
length
??
0
)));
start
.
translate
(
0
,
1
).
translate
(
0
,
node
.
name
.
length
)));
}
if
(
endTagStart
)
{
if
(
node
.
close
)
{
const
endTagStart
=
document
.
positionAt
(
node
.
close
.
start
);
const
end
=
document
.
positionAt
(
node
.
close
.
end
);
ranges
.
push
(
new
vscode
.
Range
(
endTagStart
.
translate
(
0
,
2
),
end
.
translate
(
0
,
-
1
)));
}
return
ranges
;
}
function
getRangesToUpdate
(
editor
:
vscode
.
TextEditor
,
selection
:
vscode
.
Selection
):
vscode
.
Range
[]
{
const
document
=
toLSTextDocument
(
editor
.
document
);
const
nodeToUpdate
=
getHtmlNodeLS
(
document
,
selection
.
start
,
true
);
function
getRangesToUpdate
(
document
:
vscode
.
TextDocument
,
selection
:
vscode
.
Selection
,
rootNode
:
HtmlFlatNode
):
vscode
.
Range
[]
{
const
documentText
=
document
.
getText
();
const
offset
=
document
.
offsetAt
(
selection
.
start
);
const
nodeToUpdate
=
getHtmlFlatNode
(
documentText
,
rootNode
,
offset
,
true
);
if
(
!
nodeToUpdate
)
{
return
[];
}
...
...
extensions/emmet/src/util.ts
浏览文件 @
c7dbab59
...
...
@@ -7,13 +7,12 @@ import * as vscode from 'vscode';
import
parse
from
'
@emmetio/html-matcher
'
;
import
parseStylesheet
from
'
@emmetio/css-parser
'
;
import
{
Node
,
HtmlNode
,
CssToken
,
Property
,
Rule
,
Stylesheet
}
from
'
EmmetNode
'
;
import
{
Node
as
FlatNode
,
HtmlNode
as
HtmlFlatNode
}
from
'
EmmetFlatNode
'
;
import
{
DocumentStreamReader
}
from
'
./bufferStream
'
;
import
*
as
EmmetHelper
from
'
vscode-emmet-helper
'
;
import
{
Position
as
LSPosition
,
getLanguageService
as
getLanguageServiceInternal
,
LanguageService
,
LanguageServiceOptions
,
TextDocument
as
LSTextDocument
,
Node
as
LSNode
}
from
'
vscode-html-languageservice
'
;
import
{
parseMarkupDocument
}
from
'
./parseMarkupDocument
'
;
import
{
TextDocument
as
LSTextDocument
}
from
'
vscode-languageserver-textdocument
'
;
let
_emmetHelper
:
typeof
EmmetHelper
;
let
_languageService
:
LanguageService
;
let
_currentExtensionsPath
:
string
|
undefined
=
undefined
;
let
_homeDir
:
vscode
.
Uri
|
undefined
;
...
...
@@ -33,16 +32,6 @@ export function getEmmetHelper() {
return
_emmetHelper
;
}
export
function
getLanguageService
(
options
?:
LanguageServiceOptions
):
LanguageService
{
if
(
!
options
)
{
if
(
!
_languageService
)
{
_languageService
=
getLanguageServiceInternal
();
}
return
_languageService
;
}
return
getLanguageServiceInternal
(
options
);
}
/**
* Update Emmet Helper to use user snippets from the extensionsPath setting
*/
...
...
@@ -323,7 +312,7 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
/**
* Returns node corresponding to given position in the given root node
*/
export
function
getNode
(
root
:
Node
|
undefined
,
position
:
vscode
.
Position
,
includeNodeBoundary
:
boolean
)
{
export
function
getNode
(
root
:
Node
|
undefined
,
position
:
vscode
.
Position
,
includeNodeBoundary
:
boolean
)
:
Node
|
null
{
if
(
!
root
)
{
return
null
;
}
...
...
@@ -348,6 +337,46 @@ export function getNode(root: Node | undefined, position: vscode.Position, inclu
return
foundNode
;
}
export
function
getFlatNode
(
root
:
FlatNode
|
undefined
,
offset
:
number
,
includeNodeBoundary
:
boolean
):
FlatNode
|
undefined
{
if
(
!
root
)
{
return
;
}
function
getFlatNodeChild
(
child
:
FlatNode
|
undefined
):
FlatNode
|
undefined
{
if
(
!
child
)
{
return
;
}
const
nodeStart
=
child
.
start
;
const
nodeEnd
=
child
.
end
;
if
((
nodeStart
<
offset
&&
nodeEnd
>
offset
)
||
(
includeNodeBoundary
&&
nodeStart
<=
offset
&&
nodeEnd
>=
offset
))
{
return
getFlatNodeChildren
(
child
.
children
)
??
child
;
}
else
if
(
'
close
'
in
<
any
>
child
)
{
// We have an HTML node in this case.
// In case this node is an invalid unpaired HTML node,
// we still want to search its children
const
htmlChild
=
<
HtmlFlatNode
>
child
;
if
(
htmlChild
.
open
&&
!
htmlChild
.
close
)
{
return
getFlatNodeChildren
(
htmlChild
.
children
);
}
}
return
;
}
function
getFlatNodeChildren
(
children
:
FlatNode
[]):
FlatNode
|
undefined
{
for
(
let
i
=
0
;
i
<
children
.
length
;
i
++
)
{
const
foundChild
=
getFlatNodeChild
(
children
[
i
]);
if
(
foundChild
)
{
return
foundChild
;
}
}
return
;
}
return
getFlatNodeChildren
(
root
.
children
);
}
export
const
allowedMimeTypesInScriptTag
=
[
'
text/html
'
,
'
text/plain
'
,
'
text/x-template
'
,
'
text/template
'
,
'
text/ng-template
'
];
/**
...
...
@@ -380,37 +409,24 @@ export function getHtmlNode(document: vscode.TextDocument, root: Node | undefine
/**
* Finds the HTML node within an HTML document at a given position
*/
export
function
getHtmlNodeLS
(
document
:
LSTextDocument
,
position
:
vscode
.
Position
,
includeNodeBoundary
:
boolean
):
LSNode
|
undefined
{
const
documentText
=
document
.
getText
();
const
offset
=
document
.
offsetAt
(
position
);
let
selectionStartOffset
=
offset
;
if
(
includeNodeBoundary
&&
documentText
.
charAt
(
offset
)
===
'
<
'
)
{
selectionStartOffset
++
;
}
else
if
(
includeNodeBoundary
&&
documentText
.
charAt
(
offset
)
===
'
>
'
)
{
selectionStartOffset
--
;
}
return
getHtmlNodeLSInternal
(
document
,
selectionStartOffset
);
}
function
getHtmlNodeLSInternal
(
document
:
LSTextDocument
,
offset
:
number
,
isInTemplateNode
:
boolean
=
false
):
LSNode
|
undefined
{
const
useCache
=
!
isInTemplateNode
;
const
parsedDocument
=
parseMarkupDocument
(
document
,
useCache
);
const
currentNode
:
LSNode
=
parsedDocument
.
findNodeAt
(
offset
);
if
(
!
currentNode
.
tag
)
{
return
;
}
export
function
getHtmlFlatNode
(
documentText
:
string
,
root
:
FlatNode
|
undefined
,
offset
:
number
,
includeNodeBoundary
:
boolean
):
HtmlFlatNode
|
undefined
{
const
currentNode
:
HtmlFlatNode
|
undefined
=
<
HtmlFlatNode
|
undefined
>
getFlatNode
(
root
,
offset
,
includeNodeBoundary
);
if
(
!
currentNode
)
{
return
;
}
const
isTemplateScript
=
isNodeTemplateScriptLS
(
currentNode
);
const
isTemplateScript
=
currentNode
.
name
===
'
script
'
&&
(
currentNode
.
attributes
&&
currentNode
.
attributes
.
some
(
x
=>
x
.
name
.
toString
()
===
'
type
'
&&
allowedMimeTypesInScriptTag
.
includes
(
x
.
value
.
toString
())));
if
(
isTemplateScript
&&
currentNode
.
startTagEnd
&&
offset
>
currentNode
.
startTagE
nd
&&
(
!
currentNode
.
endTagStart
||
offset
<
currentNode
.
endTagS
tart
))
{
&&
currentNode
.
open
&&
offset
>
currentNode
.
open
.
e
nd
&&
(
!
currentNode
.
close
||
offset
<
currentNode
.
close
.
s
tart
))
{
// blank out the rest of the document and search for the node within
const
documentText
=
document
.
getText
(
);
const
beforePadding
=
'
'
.
repeat
(
currentNode
.
startTagEnd
)
;
const
scriptBodyText
=
beforePadding
+
documentText
.
substring
(
currentNode
.
startTagEnd
,
currentNode
.
endTagStart
??
currentNode
.
end
);
const
scriptBodyDocument
=
LSTextDocument
.
create
(
document
.
uri
,
document
.
languageId
,
document
.
version
,
scriptBodyText
);
const
scriptBodyNode
=
getHtml
NodeLSInternal
(
scriptBodyDocument
,
offset
,
true
);
const
beforePadding
=
'
'
.
repeat
(
currentNode
.
open
.
end
);
const
endToUse
=
currentNode
.
close
?
currentNode
.
close
.
start
:
currentNode
.
end
;
const
scriptBodyText
=
beforePadding
+
documentText
.
substring
(
currentNode
.
open
.
end
,
endToUse
);
const
innerRoot
:
HtmlFlatNode
=
parse
(
scriptBodyText
);
const
scriptBodyNode
=
getHtml
FlatNode
(
scriptBodyText
,
innerRoot
,
offset
,
includeNodeBoundary
);
if
(
scriptBodyNode
)
{
scriptBodyNode
.
parent
=
currentNode
;
currentNode
.
children
.
push
(
scriptBodyNode
);
...
...
@@ -420,33 +436,16 @@ function getHtmlNodeLSInternal(document: LSTextDocument, offset: number, isInTem
return
currentNode
;
}
/**
* Returns whether the node is a <script> node
* that we want to search through and parse for more potential HTML nodes
*/
function
isNodeTemplateScriptLS
(
node
:
LSNode
):
boolean
{
if
(
node
.
tag
===
'
script
'
&&
node
.
attributes
&&
node
.
attributes
[
'
type
'
])
{
let
scriptType
=
node
.
attributes
[
'
type
'
];
scriptType
=
scriptType
.
substring
(
1
,
scriptType
.
length
-
1
);
return
allowedMimeTypesInScriptTag
.
includes
(
scriptType
);
}
return
false
;
}
function
toVsPosition
(
position
:
LSPosition
):
vscode
.
Position
{
return
new
vscode
.
Position
(
position
.
line
,
position
.
character
);
}
export
function
offsetRangeToSelection
(
document
:
LSTextDocument
,
start
:
number
,
end
:
number
):
vscode
.
Selection
{
export
function
offsetRangeToSelection
(
document
:
vscode
.
TextDocument
,
start
:
number
,
end
:
number
):
vscode
.
Selection
{
const
startPos
=
document
.
positionAt
(
start
);
const
endPos
=
document
.
positionAt
(
end
);
return
new
vscode
.
Selection
(
toVsPosition
(
startPos
),
toVsPosition
(
endPos
)
);
return
new
vscode
.
Selection
(
startPos
,
endPos
);
}
export
function
offsetRangeToVsRange
(
document
:
LS
TextDocument
,
start
:
number
,
end
:
number
):
vscode
.
Range
{
export
function
offsetRangeToVsRange
(
document
:
vscode
.
TextDocument
,
start
:
number
,
end
:
number
):
vscode
.
Range
{
const
startPos
=
document
.
positionAt
(
start
);
const
endPos
=
document
.
positionAt
(
end
);
return
new
vscode
.
Range
(
toVsPosition
(
startPos
),
toVsPosition
(
endPos
)
);
return
new
vscode
.
Range
(
startPos
,
endPos
);
}
/**
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录