Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
8d7504f7
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,发现更多精彩内容 >>
提交
8d7504f7
编写于
9月 13, 2016
作者:
M
Martin Aeschlimann
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
[html] add link provider
上级
99ebc306
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
298 addition
and
6 deletion
+298
-6
extensions/html/server/package.json
extensions/html/server/package.json
+2
-1
extensions/html/server/src/service/htmlLanguageService.ts
extensions/html/server/src/service/htmlLanguageService.ts
+19
-2
extensions/html/server/src/service/services/htmlLinks.ts
extensions/html/server/src/service/services/htmlLinks.ts
+114
-0
extensions/html/server/src/service/test/links.test.ts
extensions/html/server/src/service/test/links.test.ts
+71
-0
extensions/html/server/src/service/utils/paths.ts
extensions/html/server/src/service/utils/paths.ts
+76
-0
extensions/html/server/src/service/utils/strings.ts
extensions/html/server/src/service/utils/strings.ts
+16
-3
未找到文件。
extensions/html/server/package.json
浏览文件 @
8d7504f7
...
...
@@ -9,7 +9,8 @@
},
"dependencies"
:
{
"vscode-languageserver"
:
"^2.4.0-next.12"
,
"vscode-nls"
:
"^1.0.4"
"vscode-nls"
:
"^1.0.4"
,
"vscode-uri"
:
"^0.0.7"
},
"scripts"
:
{
"compile"
:
"gulp compile-extension:json-server"
,
...
...
extensions/html/server/src/service/htmlLanguageService.ts
浏览文件 @
8d7504f7
...
...
@@ -6,12 +6,28 @@
import
{
parse
}
from
'
./parser/htmlParser
'
;
import
{
doComplete
}
from
'
./services/htmlCompletion
'
;
import
{
format
}
from
'
./services/htmlFormatter
'
;
import
{
provideLinks
}
from
'
./services/htmlLinks
'
;
import
{
findDocumentHighlights
}
from
'
./services/htmlHighlighting
'
;
import
{
TextDocument
,
Position
,
CompletionItem
,
CompletionList
,
Hover
,
Range
,
SymbolInformation
,
Diagnostic
,
TextEdit
,
DocumentHighlight
,
FormattingOptions
,
MarkedString
}
from
'
vscode-languageserver-types
'
;
export
{
TextDocument
,
Position
,
CompletionItem
,
CompletionList
,
Hover
,
Range
,
SymbolInformation
,
Diagnostic
,
TextEdit
,
DocumentHighlight
,
FormattingOptions
,
MarkedString
};
export
class
DocumentLink
{
/**
* The range this link applies to.
*/
range
:
Range
;
/**
* The uri this link points to.
*/
target
:
string
;
}
export
interface
HTMLFormatConfiguration
{
tabSize
:
number
;
insertSpaces
:
boolean
;
...
...
@@ -38,8 +54,8 @@ export interface LanguageService {
doValidation
(
document
:
TextDocument
,
htmlDocument
:
HTMLDocument
):
Diagnostic
[];
findDocumentHighlights
(
document
:
TextDocument
,
position
:
Position
,
htmlDocument
:
HTMLDocument
):
DocumentHighlight
[];
doComplete
(
document
:
TextDocument
,
position
:
Position
,
htmlDocument
:
HTMLDocument
):
CompletionList
;
// doHover(document: TextDocument, position: Position, doc: HTMLDocument): Hover;
format
(
document
:
TextDocument
,
range
:
Range
,
options
:
HTMLFormatConfiguration
):
TextEdit
[];
provideLinks
(
document
:
TextDocument
,
workspacePath
:
string
):
DocumentLink
[];
}
export
function
getLanguageService
()
:
LanguageService
{
...
...
@@ -49,6 +65,7 @@ export function getLanguageService() : LanguageService {
parseHTMLDocument
:
(
document
)
=>
parse
(
document
.
getText
()),
doComplete
,
format
,
findDocumentHighlights
findDocumentHighlights
,
provideLinks
};
}
\ No newline at end of file
extensions/html/server/src/service/services/htmlLinks.ts
0 → 100644
浏览文件 @
8d7504f7
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
{
TokenType
,
createScanner
}
from
'
../parser/htmlScanner
'
;
import
{
TextDocument
,
Range
}
from
'
vscode-languageserver-types
'
;
import
*
as
paths
from
'
../utils/paths
'
;
import
*
as
strings
from
'
../utils/strings
'
;
import
Uri
from
'
vscode-uri
'
;
import
{
DocumentLink
}
from
'
../htmlLanguageService
'
;
function
_stripQuotes
(
url
:
string
):
string
{
return
url
.
replace
(
/^'
([^
'
]
+
)
'$/
,(
substr
,
match1
)
=>
match1
)
.
replace
(
/^"
([^
"
]
+
)
"$/
,(
substr
,
match1
)
=>
match1
);
}
export
function
_getWorkspaceUrl
(
modelAbsoluteUri
:
Uri
,
rootAbsoluteUrlStr
:
string
,
tokenContent
:
string
):
string
{
tokenContent
=
_stripQuotes
(
tokenContent
);
if
(
/^
\s
*javascript
\:
/i
.
test
(
tokenContent
)
||
/^
\s
*
\#
/i
.
test
(
tokenContent
))
{
return
null
;
}
if
(
/^
\s
*https
?
:
\/\/
/i
.
test
(
tokenContent
)
||
/^
\s
*file:
\/\/
/i
.
test
(
tokenContent
))
{
// Absolute link that needs no treatment
return
tokenContent
.
replace
(
/^
\s
*/g
,
''
);
}
if
(
/^
\s
*
\/\/
/i
.
test
(
tokenContent
))
{
// Absolute link (that does not name the protocol)
let
pickedScheme
=
'
http
'
;
if
(
modelAbsoluteUri
.
scheme
===
'
https
'
)
{
pickedScheme
=
'
https
'
;
}
return
pickedScheme
+
'
:
'
+
tokenContent
.
replace
(
/^
\s
*/g
,
''
);
}
let
modelPath
=
paths
.
dirname
(
modelAbsoluteUri
.
path
);
let
alternativeResultPath
:
string
=
null
;
if
(
tokenContent
.
length
>
0
&&
tokenContent
.
charAt
(
0
)
===
'
/
'
)
{
alternativeResultPath
=
tokenContent
;
}
else
{
alternativeResultPath
=
paths
.
join
(
modelPath
,
tokenContent
);
alternativeResultPath
=
alternativeResultPath
.
replace
(
/^
(\/\.\.)
+/
,
''
);
}
let
potentialResult
=
modelAbsoluteUri
.
with
({
path
:
alternativeResultPath
}).
toString
();
if
(
rootAbsoluteUrlStr
&&
strings
.
startsWith
(
modelAbsoluteUri
.
toString
(),
rootAbsoluteUrlStr
))
{
// The `rootAbsoluteUrl` is set and matches our current model
// We need to ensure that this `potentialResult` does not escape `rootAbsoluteUrl`
let
commonPrefixLength
=
strings
.
commonPrefixLength
(
rootAbsoluteUrlStr
,
potentialResult
);
if
(
strings
.
endsWith
(
rootAbsoluteUrlStr
,
'
/
'
))
{
commonPrefixLength
=
potentialResult
.
lastIndexOf
(
'
/
'
,
commonPrefixLength
)
+
1
;
}
return
rootAbsoluteUrlStr
+
potentialResult
.
substr
(
commonPrefixLength
);
}
return
potentialResult
;
}
function
createLink
(
document
:
TextDocument
,
rootAbsoluteUrl
:
string
,
tokenContent
:
string
,
startOffset
:
number
,
endOffset
:
number
):
DocumentLink
{
let
documentUri
=
Uri
.
parse
(
document
.
uri
);
let
workspaceUrl
=
_getWorkspaceUrl
(
documentUri
,
rootAbsoluteUrl
,
tokenContent
);
if
(
!
workspaceUrl
)
{
return
null
;
}
return
{
range
:
Range
.
create
(
document
.
positionAt
(
startOffset
),
document
.
positionAt
(
endOffset
)),
target
:
workspaceUrl
};
}
export
function
provideLinks
(
document
:
TextDocument
,
workspacePath
:
string
):
DocumentLink
[]
{
let
newLinks
:
DocumentLink
[]
=
[];
let
rootAbsoluteUrl
:
string
=
null
;
if
(
workspacePath
)
{
// The workspace can be null in the no folder opened case
let
strRootAbsoluteUrl
=
workspacePath
;
if
(
strRootAbsoluteUrl
.
charAt
(
strRootAbsoluteUrl
.
length
-
1
)
===
'
/
'
)
{
rootAbsoluteUrl
=
strRootAbsoluteUrl
;
}
else
{
rootAbsoluteUrl
=
strRootAbsoluteUrl
+
'
/
'
;
}
}
let
scanner
=
createScanner
(
document
.
getText
(),
0
);
let
token
=
scanner
.
scan
();
let
afterHrefOrSrc
=
false
;
while
(
token
!==
TokenType
.
EOS
)
{
switch
(
token
)
{
case
TokenType
.
AttributeName
:
let
tokenContent
=
scanner
.
getTokenText
();
afterHrefOrSrc
=
tokenContent
===
'
src
'
||
tokenContent
===
'
href
'
;
break
;
case
TokenType
.
AttributeValue
:
if
(
afterHrefOrSrc
)
{
let
tokenContent
=
scanner
.
getTokenText
();
let
link
=
createLink
(
document
,
rootAbsoluteUrl
,
tokenContent
,
scanner
.
getTokenOffset
(),
scanner
.
getTokenEnd
());
if
(
link
)
{
newLinks
.
push
(
link
);
}
afterHrefOrSrc
=
false
;
}
break
;
}
}
return
newLinks
;
}
\ No newline at end of file
extensions/html/server/src/service/test/links.test.ts
0 → 100644
浏览文件 @
8d7504f7
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
*
as
assert
from
'
assert
'
;
import
*
as
htmlLinks
from
'
../services/htmlLinks
'
;
import
{
CompletionList
,
TextDocument
,
TextEdit
,
Position
,
CompletionItemKind
}
from
'
vscode-languageserver-types
'
;
import
Uri
from
'
vscode-uri
'
;
suite
(
'
HTML Link Detection
'
,
()
=>
{
function
testLinkCreation
(
modelUrl
:
string
,
rootUrl
:
string
,
tokenContent
:
string
,
expected
:
string
):
void
{
var
_modelUrl
=
Uri
.
parse
(
modelUrl
);
var
actual
=
htmlLinks
.
_getWorkspaceUrl
(
_modelUrl
,
rootUrl
,
tokenContent
);
assert
.
equal
(
actual
,
expected
);
}
test
(
'
Link creation
'
,
()
=>
{
testLinkCreation
(
'
inmemory://model/1
'
,
null
,
'
javascript:void;
'
,
null
);
testLinkCreation
(
'
inmemory://model/1
'
,
null
,
'
\t
javascript:alert(7);
'
,
null
);
testLinkCreation
(
'
inmemory://model/1
'
,
null
,
'
#relative
'
,
null
);
testLinkCreation
(
'
inmemory://model/1
'
,
null
,
'
file:///C:
\\
Alex
\\
src
\\
path
\\
to
\\
file.txt
'
,
'
file:///C:
\\
Alex
\\
src
\\
path
\\
to
\\
file.txt
'
);
testLinkCreation
(
'
inmemory://model/1
'
,
null
,
'
http://www.microsoft.com/
'
,
'
http://www.microsoft.com/
'
);
testLinkCreation
(
'
inmemory://model/1
'
,
null
,
'
https://www.microsoft.com/
'
,
'
https://www.microsoft.com/
'
);
testLinkCreation
(
'
inmemory://model/1
'
,
null
,
'
//www.microsoft.com/
'
,
'
http://www.microsoft.com/
'
);
testLinkCreation
(
'
inmemory://model/1
'
,
null
,
'
../../a.js
'
,
'
inmemory://model/a.js
'
);
testLinkCreation
(
'
inmemory://model/1
'
,
'
inmemory://model/
'
,
'
javascript:void;
'
,
null
);
testLinkCreation
(
'
inmemory://model/1
'
,
'
inmemory://model/
'
,
'
\t
javascript:alert(7);
'
,
null
);
testLinkCreation
(
'
inmemory://model/1
'
,
'
inmemory://model/
'
,
'
#relative
'
,
null
);
testLinkCreation
(
'
inmemory://model/1
'
,
'
inmemory://model/
'
,
'
file:///C:
\\
Alex
\\
src
\\
path
\\
to
\\
file.txt
'
,
'
file:///C:
\\
Alex
\\
src
\\
path
\\
to
\\
file.txt
'
);
testLinkCreation
(
'
inmemory://model/1
'
,
'
inmemory://model/
'
,
'
http://www.microsoft.com/
'
,
'
http://www.microsoft.com/
'
);
testLinkCreation
(
'
inmemory://model/1
'
,
'
inmemory://model/
'
,
'
https://www.microsoft.com/
'
,
'
https://www.microsoft.com/
'
);
testLinkCreation
(
'
inmemory://model/1
'
,
'
inmemory://model/
'
,
'
//www.microsoft.com/
'
,
'
http://www.microsoft.com/
'
);
testLinkCreation
(
'
inmemory://model/1
'
,
'
inmemory://model/
'
,
'
../../a.js
'
,
'
inmemory://model/a.js
'
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
null
,
'
javascript:void;
'
,
null
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
null
,
'
\t
javascript:alert(7);
'
,
null
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
null
,
'
#relative
'
,
null
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
null
,
'
file:///C:
\\
Alex
\\
src
\\
path
\\
to
\\
file.txt
'
,
'
file:///C:
\\
Alex
\\
src
\\
path
\\
to
\\
file.txt
'
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
null
,
'
http://www.microsoft.com/
'
,
'
http://www.microsoft.com/
'
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
null
,
'
https://www.microsoft.com/
'
,
'
https://www.microsoft.com/
'
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
null
,
'
//www.microsoft.com/
'
,
'
http://www.microsoft.com/
'
);
//testLinkCreation('file:///C:/Alex/src/path/to/file.txt', null, 'a.js', 'file:///C:/Alex/src/path/to/a.js');
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
null
,
'
/a.js
'
,
'
file:///a.js
'
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
'
file:///C:/Alex/src/
'
,
'
javascript:void;
'
,
null
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
'
file:///C:/Alex/src/
'
,
'
\t
javascript:alert(7);
'
,
null
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
'
file:///C:/Alex/src/
'
,
'
#relative
'
,
null
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
null
,
'
file:///C:
\\
Alex
\\
src
\\
path
\\
to
\\
file.txt
'
,
'
file:///C:
\\
Alex
\\
src
\\
path
\\
to
\\
file.txt
'
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
'
file:///C:/Alex/src/
'
,
'
http://www.microsoft.com/
'
,
'
http://www.microsoft.com/
'
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
'
file:///C:/Alex/src/
'
,
'
https://www.microsoft.com/
'
,
'
https://www.microsoft.com/
'
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
'
file:///C:/Alex/src/
'
,
'
https://www.microsoft.com/?q=1#h
'
,
'
https://www.microsoft.com/?q=1#h
'
);
testLinkCreation
(
'
file:///C:/Alex/src/path/to/file.txt
'
,
'
file:///C:/Alex/src/
'
,
'
//www.microsoft.com/
'
,
'
http://www.microsoft.com/
'
);
//testLinkCreation('file:///C:/Alex/src/path/to/file.txt', 'file:///C:/Alex/src/', 'a.js', 'file:///C:/Alex/src/path/to/a.js');
//testLinkCreation('file:///C:/Alex/src/path/to/file.txt', 'file:///C:/Alex/src/', '/a.js', 'file:///C:/Alex/src/a.js');
testLinkCreation
(
'
https://www.test.com/path/to/file.txt
'
,
null
,
'
file:///C:
\\
Alex
\\
src
\\
path
\\
to
\\
file.txt
'
,
'
file:///C:
\\
Alex
\\
src
\\
path
\\
to
\\
file.txt
'
);
testLinkCreation
(
'
https://www.test.com/path/to/file.txt
'
,
null
,
'
//www.microsoft.com/
'
,
'
https://www.microsoft.com/
'
);
testLinkCreation
(
'
https://www.test.com/path/to/file.txt
'
,
'
https://www.test.com
'
,
'
//www.microsoft.com/
'
,
'
https://www.microsoft.com/
'
);
// invalid uris don't throw
testLinkCreation
(
'
https://www.test.com/path/to/file.txt
'
,
'
https://www.test.com
'
,
'
%
'
,
'
https://www.test.com/path/to/%25
'
);
// Bug #18314: Ctrl + Click does not open existing file if folder's name starts with 'c' character
// testLinkCreation('file:///c:/Alex/working_dir/18314-link-detection/test.html', 'file:///c:/Alex/working_dir/18314-link-detection/', '/class/class.js', 'file:///c:/Alex/working_dir/18314-link-detection/class/class.js');
});
});
\ No newline at end of file
extensions/html/server/src/service/utils/paths.ts
0 → 100644
浏览文件 @
8d7504f7
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
export
const
enum
CharCode
{
Slash
=
47
,
Backslash
=
92
}
/**
* @returns the directory name of a path.
*/
export
function
dirname
(
path
:
string
):
string
{
var
idx
=
~
path
.
lastIndexOf
(
'
/
'
)
||
~
path
.
lastIndexOf
(
'
\\
'
);
if
(
idx
===
0
)
{
return
'
.
'
;
}
else
if
(
~
idx
===
0
)
{
return
path
[
0
];
}
else
{
return
path
.
substring
(
0
,
~
idx
);
}
}
/**
* @returns the base name of a path.
*/
export
function
basename
(
path
:
string
):
string
{
var
idx
=
~
path
.
lastIndexOf
(
'
/
'
)
||
~
path
.
lastIndexOf
(
'
\\
'
);
if
(
idx
===
0
)
{
return
path
;
}
else
if
(
~
idx
===
path
.
length
-
1
)
{
return
basename
(
path
.
substring
(
0
,
path
.
length
-
1
));
}
else
{
return
path
.
substr
(
~
idx
+
1
);
}
}
/**
* @returns {{.far}} from boo.far or the empty string.
*/
export
function
extname
(
path
:
string
):
string
{
path
=
basename
(
path
);
var
idx
=
~
path
.
lastIndexOf
(
'
.
'
);
return
idx
?
path
.
substring
(
~
idx
)
:
''
;
}
export
const
join
:
(...
parts
:
string
[])
=>
string
=
function
()
{
// Not using a function with var-args because of how TS compiles
// them to JS - it would result in 2*n runtime cost instead
// of 1*n, where n is parts.length.
let
value
=
''
;
for
(
let
i
=
0
;
i
<
arguments
.
length
;
i
++
)
{
let
part
=
arguments
[
i
];
if
(
i
>
0
)
{
// add the separater between two parts unless
// there already is one
let
last
=
value
.
charCodeAt
(
value
.
length
-
1
);
if
(
last
!==
CharCode
.
Slash
&&
last
!==
CharCode
.
Backslash
)
{
let
next
=
part
.
charCodeAt
(
0
);
if
(
next
!==
CharCode
.
Slash
&&
next
!==
CharCode
.
Backslash
)
{
value
+=
'
/
'
;
}
}
}
value
+=
part
;
}
return
value
;
};
extensions/html/server/src/service/utils/strings.ts
浏览文件 @
8d7504f7
...
...
@@ -32,6 +32,19 @@ export function endsWith(haystack: string, needle: string): boolean {
}
}
export
function
convertSimple2RegExpPattern
(
pattern
:
string
):
string
{
return
pattern
.
replace
(
/
[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]
/g
,
'
\\
$&
'
).
replace
(
/
[\*]
/g
,
'
.*
'
);
}
/**
* @returns the length of the common prefix of the two strings.
*/
export
function
commonPrefixLength
(
a
:
string
,
b
:
string
):
number
{
let
i
:
number
,
len
=
Math
.
min
(
a
.
length
,
b
.
length
);
for
(
i
=
0
;
i
<
len
;
i
++
)
{
if
(
a
.
charCodeAt
(
i
)
!==
b
.
charCodeAt
(
i
))
{
return
i
;
}
}
return
len
;
}
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录