Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
261ef3e7
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,发现更多精彩内容 >>
未验证
提交
261ef3e7
编写于
11月 15, 2019
作者:
J
Johannes Rieken
提交者:
GitHub
11月 15, 2019
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #84812 from microsoft/joh/opener
OpenerService supporting URI and URL
上级
3be1521d
e80c62bd
变更
17
隐藏空白更改
内联
并排
Showing
17 changed file
with
189 addition
and
164 deletion
+189
-164
src/vs/base/browser/markdownRenderer.ts
src/vs/base/browser/markdownRenderer.ts
+5
-5
src/vs/editor/browser/services/openerService.ts
src/vs/editor/browser/services/openerService.ts
+102
-83
src/vs/editor/contrib/hover/modesContentHover.ts
src/vs/editor/contrib/hover/modesContentHover.ts
+1
-1
src/vs/editor/contrib/hover/modesGlyphHover.ts
src/vs/editor/contrib/hover/modesGlyphHover.ts
+1
-1
src/vs/editor/contrib/links/getLinks.ts
src/vs/editor/contrib/links/getLinks.ts
+2
-10
src/vs/editor/contrib/markdown/markdownRenderer.ts
src/vs/editor/contrib/markdown/markdownRenderer.ts
+2
-11
src/vs/editor/test/browser/services/openerService.test.ts
src/vs/editor/test/browser/services/openerService.test.ts
+28
-13
src/vs/platform/opener/common/opener.ts
src/vs/platform/opener/common/opener.ts
+12
-5
src/vs/workbench/api/browser/mainThreadWindow.ts
src/vs/workbench/api/browser/mainThreadWindow.ts
+10
-2
src/vs/workbench/api/common/extHost.protocol.ts
src/vs/workbench/api/common/extHost.protocol.ts
+1
-1
src/vs/workbench/api/common/extHostRequireInterceptor.ts
src/vs/workbench/api/common/extHostRequireInterceptor.ts
+2
-2
src/vs/workbench/api/common/extHostWindow.ts
src/vs/workbench/api/common/extHostWindow.ts
+3
-1
src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts
.../workbench/contrib/comments/browser/commentsTreeViewer.ts
+1
-7
src/vs/workbench/contrib/preferences/browser/settingsTree.ts
src/vs/workbench/contrib/preferences/browser/settingsTree.ts
+1
-10
src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts
...workbench/contrib/terminal/browser/terminalLinkHandler.ts
+1
-2
src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts
...s/workbench/contrib/url/common/trustedDomainsValidator.ts
+9
-6
src/vs/workbench/services/url/electron-browser/urlService.ts
src/vs/workbench/services/url/electron-browser/urlService.ts
+8
-4
未找到文件。
src/vs/base/browser/markdownRenderer.ts
浏览文件 @
261ef3e7
...
...
@@ -50,19 +50,19 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
const
_href
=
function
(
href
:
string
,
isDomUri
:
boolean
):
string
{
const
data
=
markdown
.
uris
&&
markdown
.
uris
[
href
];
if
(
!
data
)
{
return
href
;
return
href
;
// no uri exists
}
let
uri
=
URI
.
revive
(
data
);
if
(
URI
.
parse
(
href
).
toString
()
===
uri
.
toString
())
{
return
href
;
// no tranformation performed
}
if
(
isDomUri
)
{
uri
=
DOM
.
asDomUri
(
uri
);
}
if
(
uri
.
query
)
{
uri
=
uri
.
with
({
query
:
_uriMassage
(
uri
.
query
)
});
}
if
(
data
)
{
href
=
uri
.
toString
(
true
);
}
return
href
;
return
uri
.
toString
();
};
// signal to code-block render that the
...
...
src/vs/editor/browser/services/openerService.ts
浏览文件 @
261ef3e7
...
...
@@ -4,58 +4,132 @@
*--------------------------------------------------------------------------------------------*/
import
*
as
dom
from
'
vs/base/browser/dom
'
;
import
{
Disposable
,
IDisposable
}
from
'
vs/base/common/lifecycle
'
;
import
{
IDisposable
}
from
'
vs/base/common/lifecycle
'
;
import
{
LinkedList
}
from
'
vs/base/common/linkedList
'
;
import
{
parse
}
from
'
vs/base/common/marshalling
'
;
import
{
Schemas
}
from
'
vs/base/common/network
'
;
import
*
as
resources
from
'
vs/base/common/resources
'
;
import
{
equalsIgnoreCase
}
from
'
vs/base/common/strings
'
;
import
{
normalizePath
}
from
'
vs/base/common/resources
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
ICodeEditorService
}
from
'
vs/editor/browser/services/codeEditorService
'
;
import
{
CommandsRegistry
,
ICommandService
}
from
'
vs/platform/commands/common/commands
'
;
import
{
IOpener
,
IOpenerService
,
IValidator
,
IExternalUriResolver
,
OpenOptions
,
ResolveExternalUriOptions
,
IResolvedExternalUri
,
IExternalOpener
}
from
'
vs/platform/opener/common/opener
'
;
import
{
IOpener
,
IOpenerService
,
IValidator
,
IExternalUriResolver
,
OpenOptions
,
ResolveExternalUriOptions
,
IResolvedExternalUri
,
IExternalOpener
,
matchesScheme
}
from
'
vs/platform/opener/common/opener
'
;
import
{
EditorOpenContext
}
from
'
vs/platform/editor/common/editor
'
;
export
class
OpenerService
extends
Disposable
implements
IOpenerService
{
class
CommandOpener
implements
IOpener
{
constructor
(@
ICommandService
private
readonly
_commandService
:
ICommandService
)
{
}
async
open
(
target
:
URI
|
string
)
{
if
(
!
matchesScheme
(
target
,
Schemas
.
command
))
{
return
false
;
}
// run command or bail out if command isn't known
if
(
typeof
target
===
'
string
'
)
{
target
=
URI
.
parse
(
target
);
}
if
(
!
CommandsRegistry
.
getCommand
(
target
.
path
))
{
throw
new
Error
(
`command '
${
target
.
path
}
' NOT known`
);
}
// execute as command
let
args
:
any
=
[];
try
{
args
=
parse
(
target
.
query
);
if
(
!
Array
.
isArray
(
args
))
{
args
=
[
args
];
}
}
catch
(
e
)
{
// ignore error
}
await
this
.
_commandService
.
executeCommand
(
target
.
path
,
...
args
);
return
true
;
}
}
class
EditorOpener
implements
IOpener
{
constructor
(@
ICodeEditorService
private
readonly
_editorService
:
ICodeEditorService
)
{
}
async
open
(
target
:
URI
|
string
,
options
:
OpenOptions
)
{
if
(
typeof
target
===
'
string
'
)
{
target
=
URI
.
parse
(
target
);
}
let
selection
:
{
startLineNumber
:
number
;
startColumn
:
number
;
}
|
undefined
=
undefined
;
const
match
=
/^L
?(\d
+
)(?:
,
(\d
+
))?
/
.
exec
(
target
.
fragment
);
if
(
match
)
{
// support file:///some/file.js#73,84
// support file:///some/file.js#L73
selection
=
{
startLineNumber
:
parseInt
(
match
[
1
]),
startColumn
:
match
[
2
]
?
parseInt
(
match
[
2
])
:
1
};
// remove fragment
target
=
target
.
with
({
fragment
:
''
});
}
if
(
target
.
scheme
===
Schemas
.
file
)
{
target
=
normalizePath
(
target
);
// workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954)
}
await
this
.
_editorService
.
openCodeEditor
(
{
resource
:
target
,
options
:
{
selection
,
context
:
options
?.
fromUserGesture
?
EditorOpenContext
.
USER
:
EditorOpenContext
.
API
}
},
this
.
_editorService
.
getFocusedCodeEditor
(),
options
?.
openToSide
);
return
true
;
}
}
export
class
OpenerService
implements
IOpenerService
{
_serviceBrand
:
undefined
;
private
readonly
_openers
=
new
LinkedList
<
IOpener
>
();
private
readonly
_validators
=
new
LinkedList
<
IValidator
>
();
private
readonly
_resolvers
=
new
LinkedList
<
IExternalUriResolver
>
();
private
_externalOpener
:
IExternalOpener
;
constructor
(
@
ICodeEditorService
private
readonly
_
editorService
:
ICodeEditorService
,
@
ICommandService
private
readonly
_
commandService
:
ICommandService
,
@
ICodeEditorService
editorService
:
ICodeEditorService
,
@
ICommandService
commandService
:
ICommandService
,
)
{
super
();
// Default external opener is going through window.open()
this
.
_externalOpener
=
{
openExternal
:
href
=>
{
dom
.
windowOpenNoOpener
(
href
);
return
Promise
.
resolve
(
true
);
}
};
// Default opener: maito, http(s), command, and catch-all-editors
this
.
_openers
.
push
({
open
:
async
(
target
:
URI
|
string
,
options
?:
OpenOptions
)
=>
{
if
(
options
?.
openExternal
||
matchesScheme
(
target
,
Schemas
.
mailto
)
||
matchesScheme
(
target
,
Schemas
.
http
)
||
matchesScheme
(
target
,
Schemas
.
https
))
{
// open externally
await
this
.
_doOpenExternal
(
target
,
options
);
return
true
;
}
return
false
;
}
});
this
.
_openers
.
push
(
new
CommandOpener
(
commandService
));
this
.
_openers
.
push
(
new
EditorOpener
(
editorService
));
}
registerOpener
(
opener
:
IOpener
):
IDisposable
{
const
remove
=
this
.
_openers
.
push
(
opener
);
const
remove
=
this
.
_openers
.
unshift
(
opener
);
return
{
dispose
:
remove
};
}
registerValidator
(
validator
:
IValidator
):
IDisposable
{
const
remove
=
this
.
_validators
.
push
(
validator
);
return
{
dispose
:
remove
};
}
registerExternalUriResolver
(
resolver
:
IExternalUriResolver
):
IDisposable
{
const
remove
=
this
.
_resolvers
.
push
(
resolver
);
return
{
dispose
:
remove
};
}
...
...
@@ -63,30 +137,24 @@ export class OpenerService extends Disposable implements IOpenerService {
this
.
_externalOpener
=
externalOpener
;
}
async
open
(
resource
:
URI
,
options
?:
OpenOptions
):
Promise
<
boolean
>
{
// no scheme ?!?
if
(
!
resource
.
scheme
)
{
return
Promise
.
resolve
(
false
);
}
async
open
(
target
:
URI
|
string
,
options
?:
OpenOptions
):
Promise
<
boolean
>
{
// check with contributed validators
for
(
const
validator
of
this
.
_validators
.
toArray
())
{
if
(
!
(
await
validator
.
shouldOpen
(
resource
)))
{
if
(
!
(
await
validator
.
shouldOpen
(
target
)))
{
return
false
;
}
}
// check with contributed openers
for
(
const
opener
of
this
.
_openers
.
toArray
())
{
const
handled
=
await
opener
.
open
(
resource
,
options
);
const
handled
=
await
opener
.
open
(
target
,
options
);
if
(
handled
)
{
return
true
;
}
}
// use default openers
return
this
.
_doOpen
(
resource
,
options
);
return
false
;
}
async
resolveExternalUri
(
resource
:
URI
,
options
?:
ResolveExternalUriOptions
):
Promise
<
IResolvedExternalUri
>
{
...
...
@@ -100,68 +168,19 @@ export class OpenerService extends Disposable implements IOpenerService {
return
{
resolved
:
resource
,
dispose
:
()
=>
{
}
};
}
private
async
_doOpen
(
resource
:
URI
,
options
:
OpenOptions
|
undefined
):
Promise
<
boolean
>
{
const
{
scheme
,
path
,
query
,
fragment
}
=
resource
;
private
async
_doOpenExternal
(
resource
:
URI
|
string
,
options
:
OpenOptions
|
undefined
):
Promise
<
boolean
>
{
if
(
options
?.
openExternal
||
equalsIgnoreCase
(
scheme
,
Schemas
.
mailto
)
||
equalsIgnoreCase
(
scheme
,
Schemas
.
http
)
||
equalsIgnoreCase
(
scheme
,
Schemas
.
https
))
{
// open externally
return
this
.
_doOpenExternal
(
resource
,
options
);
}
if
(
equalsIgnoreCase
(
scheme
,
Schemas
.
command
))
{
// run command or bail out if command isn't known
if
(
!
CommandsRegistry
.
getCommand
(
path
))
{
throw
new
Error
(
`command '
${
path
}
' NOT known`
);
}
// execute as command
let
args
:
any
=
[];
try
{
args
=
parse
(
query
);
if
(
!
Array
.
isArray
(
args
))
{
args
=
[
args
];
}
}
catch
(
e
)
{
// ignore error
}
//todo@joh IExternalUriResolver should support `uri: URI | string`
const
uri
=
typeof
resource
===
'
string
'
?
URI
.
parse
(
resource
)
:
resource
;
const
{
resolved
}
=
await
this
.
resolveExternalUri
(
uri
,
options
);
await
this
.
_commandService
.
executeCommand
(
path
,
...
args
);
return
true
;
if
(
typeof
resource
===
'
string
'
&&
uri
.
toString
()
===
resolved
.
toString
())
{
// open the url-string AS IS
return
this
.
_externalOpener
.
openExternal
(
resource
);
}
else
{
// open URI using the toString(noEncode)+encodeURI-trick
return
this
.
_externalOpener
.
openExternal
(
encodeURI
(
resolved
.
toString
(
true
)));
}
// finally open in editor
let
selection
:
{
startLineNumber
:
number
;
startColumn
:
number
;
}
|
undefined
=
undefined
;
const
match
=
/^L
?(\d
+
)(?:
,
(\d
+
))?
/
.
exec
(
fragment
);
if
(
match
)
{
// support file:///some/file.js#73,84
// support file:///some/file.js#L73
selection
=
{
startLineNumber
:
parseInt
(
match
[
1
]),
startColumn
:
match
[
2
]
?
parseInt
(
match
[
2
])
:
1
};
// remove fragment
resource
=
resource
.
with
({
fragment
:
''
});
}
if
(
resource
.
scheme
===
Schemas
.
file
)
{
resource
=
resources
.
normalizePath
(
resource
);
// workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954)
}
await
this
.
_editorService
.
openCodeEditor
(
{
resource
,
options
:
{
selection
,
context
:
options
?.
fromUserGesture
?
EditorOpenContext
.
USER
:
EditorOpenContext
.
API
}
},
this
.
_editorService
.
getFocusedCodeEditor
(),
options
?.
openToSide
);
return
true
;
}
private
async
_doOpenExternal
(
resource
:
URI
,
options
:
OpenOptions
|
undefined
):
Promise
<
boolean
>
{
const
{
resolved
}
=
await
this
.
resolveExternalUri
(
resource
,
options
);
// TODO@Jo neither encodeURI nor toString(true) should be needed
// once we go with URL and not URI
return
this
.
_externalOpener
.
openExternal
(
encodeURI
(
resolved
.
toString
(
true
)));
}
dispose
()
{
...
...
src/vs/editor/contrib/hover/modesContentHover.ts
浏览文件 @
261ef3e7
...
...
@@ -207,7 +207,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
private
readonly
_themeService
:
IThemeService
,
private
readonly
_keybindingService
:
IKeybindingService
,
private
readonly
_modeService
:
IModeService
,
private
readonly
_openerService
:
IOpenerService
|
null
=
NullOpenerService
,
private
readonly
_openerService
:
IOpenerService
=
NullOpenerService
,
)
{
super
(
ModesContentHoverWidget
.
ID
,
editor
);
...
...
src/vs/editor/contrib/hover/modesGlyphHover.ts
浏览文件 @
261ef3e7
...
...
@@ -97,7 +97,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget {
constructor
(
editor
:
ICodeEditor
,
modeService
:
IModeService
,
openerService
:
IOpenerService
|
null
=
NullOpenerService
,
openerService
:
IOpenerService
=
NullOpenerService
,
)
{
super
(
ModesGlyphHoverWidget
.
ID
,
editor
);
...
...
src/vs/editor/contrib/links/getLinks.ts
浏览文件 @
261ef3e7
...
...
@@ -44,17 +44,9 @@ export class Link implements ILink {
return
this
.
_link
.
tooltip
;
}
resolve
(
token
:
CancellationToken
):
Promise
<
URI
>
{
async
resolve
(
token
:
CancellationToken
):
Promise
<
URI
|
string
>
{
if
(
this
.
_link
.
url
)
{
try
{
if
(
typeof
this
.
_link
.
url
===
'
string
'
)
{
return
Promise
.
resolve
(
URI
.
parse
(
this
.
_link
.
url
));
}
else
{
return
Promise
.
resolve
(
this
.
_link
.
url
);
}
}
catch
(
e
)
{
return
Promise
.
reject
(
new
Error
(
'
invalid
'
));
}
return
this
.
_link
.
url
;
}
if
(
typeof
this
.
_provider
.
resolveLink
===
'
function
'
)
{
...
...
src/vs/editor/contrib/markdown/markdownRenderer.ts
浏览文件 @
261ef3e7
...
...
@@ -7,7 +7,6 @@ import { IMarkdownString } from 'vs/base/common/htmlContent';
import
{
renderMarkdown
,
MarkdownRenderOptions
}
from
'
vs/base/browser/markdownRenderer
'
;
import
{
IOpenerService
,
NullOpenerService
}
from
'
vs/platform/opener/common/opener
'
;
import
{
IModeService
}
from
'
vs/editor/common/services/modeService
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
onUnexpectedError
}
from
'
vs/base/common/errors
'
;
import
{
tokenizeToString
}
from
'
vs/editor/common/modes/textToHtmlTokenizer
'
;
import
{
ICodeEditor
}
from
'
vs/editor/browser/editorBrowser
'
;
...
...
@@ -29,7 +28,7 @@ export class MarkdownRenderer extends Disposable {
constructor
(
private
readonly
_editor
:
ICodeEditor
,
@
IModeService
private
readonly
_modeService
:
IModeService
,
@
optional
(
IOpenerService
)
private
readonly
_openerService
:
IOpenerService
|
null
=
NullOpenerService
,
@
optional
(
IOpenerService
)
private
readonly
_openerService
:
IOpenerService
=
NullOpenerService
,
)
{
super
();
}
...
...
@@ -64,15 +63,7 @@ export class MarkdownRenderer extends Disposable {
codeBlockRenderCallback
:
()
=>
this
.
_onDidRenderCodeBlock
.
fire
(),
actionHandler
:
{
callback
:
(
content
)
=>
{
let
uri
:
URI
|
undefined
;
try
{
uri
=
URI
.
parse
(
content
);
}
catch
{
// ignore
}
if
(
uri
&&
this
.
_openerService
)
{
this
.
_openerService
.
open
(
uri
,
{
fromUserGesture
:
true
}).
catch
(
onUnexpectedError
);
}
this
.
_openerService
.
open
(
content
,
{
fromUserGesture
:
true
}).
catch
(
onUnexpectedError
);
},
disposeables
}
...
...
src/vs/editor/test/browser/services/openerService.test.ts
浏览文件 @
261ef3e7
...
...
@@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri';
import
{
OpenerService
}
from
'
vs/editor/browser/services/openerService
'
;
import
{
TestCodeEditorService
}
from
'
vs/editor/test/browser/editorTestServices
'
;
import
{
CommandsRegistry
,
ICommandService
,
NullCommandService
}
from
'
vs/platform/commands/common/commands
'
;
import
{
matchesScheme
}
from
'
vs/platform/opener/common/opener
'
;
suite
(
'
OpenerService
'
,
function
()
{
const
editorService
=
new
TestCodeEditorService
();
...
...
@@ -28,27 +29,27 @@ suite('OpenerService', function () {
lastCommand
=
undefined
;
});
test
(
'
delegate to editorService, scheme:///fff
'
,
function
()
{
test
(
'
delegate to editorService, scheme:///fff
'
,
async
function
()
{
const
openerService
=
new
OpenerService
(
editorService
,
NullCommandService
);
openerService
.
open
(
URI
.
parse
(
'
another:///somepath
'
));
await
openerService
.
open
(
URI
.
parse
(
'
another:///somepath
'
));
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
,
undefined
);
});
test
(
'
delegate to editorService, scheme:///fff#L123
'
,
function
()
{
test
(
'
delegate to editorService, scheme:///fff#L123
'
,
async
function
()
{
const
openerService
=
new
OpenerService
(
editorService
,
NullCommandService
);
openerService
.
open
(
URI
.
parse
(
'
file:///somepath#L23
'
));
await
openerService
.
open
(
URI
.
parse
(
'
file:///somepath#L23
'
));
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
startLineNumber
,
23
);
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
startColumn
,
1
);
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
endLineNumber
,
undefined
);
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
endColumn
,
undefined
);
assert
.
equal
(
editorService
.
lastInput
!
.
resource
.
fragment
,
''
);
openerService
.
open
(
URI
.
parse
(
'
another:///somepath#L23
'
));
await
openerService
.
open
(
URI
.
parse
(
'
another:///somepath#L23
'
));
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
startLineNumber
,
23
);
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
startColumn
,
1
);
openerService
.
open
(
URI
.
parse
(
'
another:///somepath#L23,45
'
));
await
openerService
.
open
(
URI
.
parse
(
'
another:///somepath#L23,45
'
));
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
startLineNumber
,
23
);
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
startColumn
,
45
);
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
endLineNumber
,
undefined
);
...
...
@@ -56,17 +57,17 @@ suite('OpenerService', function () {
assert
.
equal
(
editorService
.
lastInput
!
.
resource
.
fragment
,
''
);
});
test
(
'
delegate to editorService, scheme:///fff#123,123
'
,
function
()
{
test
(
'
delegate to editorService, scheme:///fff#123,123
'
,
async
function
()
{
const
openerService
=
new
OpenerService
(
editorService
,
NullCommandService
);
openerService
.
open
(
URI
.
parse
(
'
file:///somepath#23
'
));
await
openerService
.
open
(
URI
.
parse
(
'
file:///somepath#23
'
));
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
startLineNumber
,
23
);
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
startColumn
,
1
);
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
endLineNumber
,
undefined
);
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
endColumn
,
undefined
);
assert
.
equal
(
editorService
.
lastInput
!
.
resource
.
fragment
,
''
);
openerService
.
open
(
URI
.
parse
(
'
file:///somepath#23,45
'
));
await
openerService
.
open
(
URI
.
parse
(
'
file:///somepath#23,45
'
));
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
startLineNumber
,
23
);
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
startColumn
,
45
);
assert
.
equal
(
editorService
.
lastInput
!
.
options
!
.
selection
!
.
endLineNumber
,
undefined
);
...
...
@@ -74,22 +75,22 @@ suite('OpenerService', function () {
assert
.
equal
(
editorService
.
lastInput
!
.
resource
.
fragment
,
''
);
});
test
(
'
delegate to commandsService, command:someid
'
,
function
()
{
test
(
'
delegate to commandsService, command:someid
'
,
async
function
()
{
const
openerService
=
new
OpenerService
(
editorService
,
commandService
);
const
id
=
`aCommand
${
Math
.
random
()}
`
;
CommandsRegistry
.
registerCommand
(
id
,
function
()
{
});
openerService
.
open
(
URI
.
parse
(
'
command:
'
+
id
));
await
openerService
.
open
(
URI
.
parse
(
'
command:
'
+
id
));
assert
.
equal
(
lastCommand
!
.
id
,
id
);
assert
.
equal
(
lastCommand
!
.
args
.
length
,
0
);
openerService
.
open
(
URI
.
parse
(
'
command:
'
+
id
).
with
({
query
:
'
123
'
}));
await
openerService
.
open
(
URI
.
parse
(
'
command:
'
+
id
).
with
({
query
:
'
123
'
}));
assert
.
equal
(
lastCommand
!
.
id
,
id
);
assert
.
equal
(
lastCommand
!
.
args
.
length
,
1
);
assert
.
equal
(
lastCommand
!
.
args
[
0
],
'
123
'
);
openerService
.
open
(
URI
.
parse
(
'
command:
'
+
id
).
with
({
query
:
JSON
.
stringify
([
12
,
true
])
}));
await
openerService
.
open
(
URI
.
parse
(
'
command:
'
+
id
).
with
({
query
:
JSON
.
stringify
([
12
,
true
])
}));
assert
.
equal
(
lastCommand
!
.
id
,
id
);
assert
.
equal
(
lastCommand
!
.
args
.
length
,
2
);
assert
.
equal
(
lastCommand
!
.
args
[
0
],
12
);
...
...
@@ -199,4 +200,18 @@ suite('OpenerService', function () {
assert
.
equal
(
v1
,
2
);
assert
.
equal
(
v2
,
0
);
});
test
(
'
matchesScheme
'
,
function
()
{
assert
.
ok
(
matchesScheme
(
'
https://microsoft.com
'
,
'
https
'
));
assert
.
ok
(
matchesScheme
(
'
http://microsoft.com
'
,
'
http
'
));
assert
.
ok
(
matchesScheme
(
'
hTTPs://microsoft.com
'
,
'
https
'
));
assert
.
ok
(
matchesScheme
(
'
httP://microsoft.com
'
,
'
http
'
));
assert
.
ok
(
matchesScheme
(
URI
.
parse
(
'
https://microsoft.com
'
),
'
https
'
));
assert
.
ok
(
matchesScheme
(
URI
.
parse
(
'
http://microsoft.com
'
),
'
http
'
));
assert
.
ok
(
matchesScheme
(
URI
.
parse
(
'
hTTPs://microsoft.com
'
),
'
https
'
));
assert
.
ok
(
matchesScheme
(
URI
.
parse
(
'
httP://microsoft.com
'
),
'
http
'
));
assert
.
ok
(
!
matchesScheme
(
URI
.
parse
(
'
https://microsoft.com
'
),
'
http
'
));
assert
.
ok
(
!
matchesScheme
(
URI
.
parse
(
'
htt://microsoft.com
'
),
'
http
'
));
assert
.
ok
(
!
matchesScheme
(
URI
.
parse
(
'
z://microsoft.com
'
),
'
http
'
));
});
});
src/vs/platform/opener/common/opener.ts
浏览文件 @
261ef3e7
...
...
@@ -6,6 +6,7 @@
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
createDecorator
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
IDisposable
,
Disposable
}
from
'
vs/base/common/lifecycle
'
;
import
{
equalsIgnoreCase
,
startsWithIgnoreCase
}
from
'
vs/base/common/strings
'
;
export
const
IOpenerService
=
createDecorator
<
IOpenerService
>
(
'
openerService
'
);
...
...
@@ -35,8 +36,7 @@ export interface IResolvedExternalUri extends IDisposable {
}
export
interface
IOpener
{
open
(
resource
:
URI
,
options
?:
OpenInternalOptions
):
Promise
<
boolean
>
;
open
(
resource
:
URI
,
options
?:
OpenExternalOptions
):
Promise
<
boolean
>
;
open
(
resource
:
URI
|
string
,
options
?:
OpenInternalOptions
|
OpenExternalOptions
):
Promise
<
boolean
>
;
}
export
interface
IExternalOpener
{
...
...
@@ -44,7 +44,7 @@ export interface IExternalOpener {
}
export
interface
IValidator
{
shouldOpen
(
resource
:
URI
):
Promise
<
boolean
>
;
shouldOpen
(
resource
:
URI
|
string
):
Promise
<
boolean
>
;
}
export
interface
IExternalUriResolver
{
...
...
@@ -83,8 +83,7 @@ export interface IOpenerService {
* @param resource A resource
* @return A promise that resolves when the opening is done.
*/
open
(
resource
:
URI
,
options
?:
OpenInternalOptions
):
Promise
<
boolean
>
;
open
(
resource
:
URI
,
options
?:
OpenExternalOptions
):
Promise
<
boolean
>
;
open
(
resource
:
URI
|
string
,
options
?:
OpenInternalOptions
|
OpenExternalOptions
):
Promise
<
boolean
>
;
/**
* Resolve a resource to its external form.
...
...
@@ -101,3 +100,11 @@ export const NullOpenerService: IOpenerService = Object.freeze({
async
open
()
{
return
false
;
},
async
resolveExternalUri
(
uri
:
URI
)
{
return
{
resolved
:
uri
,
dispose
()
{
}
};
},
});
export
function
matchesScheme
(
target
:
URI
|
string
,
scheme
:
string
)
{
if
(
URI
.
isUri
(
target
))
{
return
equalsIgnoreCase
(
target
.
scheme
,
scheme
);
}
else
{
return
startsWithIgnoreCase
(
target
,
scheme
+
'
:
'
);
}
}
src/vs/workbench/api/browser/mainThreadWindow.ts
浏览文件 @
261ef3e7
...
...
@@ -42,9 +42,17 @@ export class MainThreadWindow implements MainThreadWindowShape {
return
Promise
.
resolve
(
this
.
hostService
.
hasFocus
);
}
async
$openUri
(
uriComponents
:
UriComponents
,
options
:
IOpenUriOptions
):
Promise
<
boolean
>
{
async
$openUri
(
uriComponents
:
UriComponents
,
uriString
:
string
|
undefined
,
options
:
IOpenUriOptions
):
Promise
<
boolean
>
{
const
uri
=
URI
.
from
(
uriComponents
);
return
this
.
openerService
.
open
(
uri
,
{
openExternal
:
true
,
allowTunneling
:
options
.
allowTunneling
});
let
target
:
URI
|
string
;
if
(
uriString
&&
URI
.
parse
(
uriString
).
toString
()
===
uri
.
toString
())
{
// called with string and no transformation happened -> keep string
target
=
uriString
;
}
else
{
// called with URI or transformed -> use uri
target
=
uri
;
}
return
this
.
openerService
.
open
(
target
,
{
openExternal
:
true
,
allowTunneling
:
options
.
allowTunneling
});
}
async
$asExternalUri
(
uriComponents
:
UriComponents
,
options
:
IOpenUriOptions
):
Promise
<
UriComponents
>
{
...
...
src/vs/workbench/api/common/extHost.protocol.ts
浏览文件 @
261ef3e7
...
...
@@ -759,7 +759,7 @@ export interface IOpenUriOptions {
export
interface
MainThreadWindowShape
extends
IDisposable
{
$getWindowVisibility
():
Promise
<
boolean
>
;
$openUri
(
uri
:
UriComponents
,
options
:
IOpenUriOptions
):
Promise
<
boolean
>
;
$openUri
(
uri
:
UriComponents
,
uriString
:
string
|
undefined
,
options
:
IOpenUriOptions
):
Promise
<
boolean
>
;
$asExternalUri
(
uri
:
UriComponents
,
options
:
IOpenUriOptions
):
Promise
<
UriComponents
>
;
}
...
...
src/vs/workbench/api/common/extHostRequireInterceptor.ts
浏览文件 @
261ef3e7
...
...
@@ -245,9 +245,9 @@ class OpenNodeModuleFactory implements INodeModuleFactory {
return
this
.
callOriginal
(
target
,
options
);
}
if
(
uri
.
scheme
===
'
http
'
||
uri
.
scheme
===
'
https
'
)
{
return
mainThreadWindow
.
$openUri
(
uri
,
{
allowTunneling
:
true
});
return
mainThreadWindow
.
$openUri
(
uri
,
target
,
{
allowTunneling
:
true
});
}
else
if
(
uri
.
scheme
===
'
mailto
'
||
uri
.
scheme
===
this
.
_appUriScheme
)
{
return
mainThreadWindow
.
$openUri
(
uri
,
{});
return
mainThreadWindow
.
$openUri
(
uri
,
target
,
{});
}
return
this
.
callOriginal
(
target
,
options
);
};
...
...
src/vs/workbench/api/common/extHostWindow.ts
浏览文件 @
261ef3e7
...
...
@@ -39,7 +39,9 @@ export class ExtHostWindow implements ExtHostWindowShape {
}
openUri
(
stringOrUri
:
string
|
URI
,
options
:
IOpenUriOptions
):
Promise
<
boolean
>
{
let
uriAsString
:
string
|
undefined
;
if
(
typeof
stringOrUri
===
'
string
'
)
{
uriAsString
=
stringOrUri
;
try
{
stringOrUri
=
URI
.
parse
(
stringOrUri
);
}
catch
(
e
)
{
...
...
@@ -51,7 +53,7 @@ export class ExtHostWindow implements ExtHostWindowShape {
}
else
if
(
stringOrUri
.
scheme
===
Schemas
.
command
)
{
return
Promise
.
reject
(
`Invalid scheme '
${
stringOrUri
.
scheme
}
'`
);
}
return
this
.
_proxy
.
$openUri
(
stringOrUri
,
options
);
return
this
.
_proxy
.
$openUri
(
stringOrUri
,
uriAsString
,
options
);
}
async
asExternalUri
(
uri
:
URI
,
options
:
IOpenUriOptions
):
Promise
<
URI
>
{
...
...
src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts
浏览文件 @
261ef3e7
...
...
@@ -8,7 +8,6 @@ import * as nls from 'vs/nls';
import
{
renderMarkdown
}
from
'
vs/base/browser/markdownRenderer
'
;
import
{
onUnexpectedError
}
from
'
vs/base/common/errors
'
;
import
{
IDisposable
,
DisposableStore
}
from
'
vs/base/common/lifecycle
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
IOpenerService
}
from
'
vs/platform/opener/common/opener
'
;
import
{
IResourceLabel
,
ResourceLabels
}
from
'
vs/workbench/browser/labels
'
;
import
{
CommentNode
,
CommentsModel
,
ResourceWithCommentThreads
}
from
'
vs/workbench/contrib/comments/common/commentModel
'
;
...
...
@@ -127,12 +126,7 @@ export class CommentNodeRenderer implements IListRenderer<ITreeNode<CommentNode>
inline
:
true
,
actionHandler
:
{
callback
:
(
content
)
=>
{
try
{
const
uri
=
URI
.
parse
(
content
);
this
.
openerService
.
open
(
uri
).
catch
(
onUnexpectedError
);
}
catch
(
err
)
{
// ignore
}
this
.
openerService
.
open
(
content
).
catch
(
onUnexpectedError
);
},
disposeables
:
disposables
}
...
...
src/vs/workbench/contrib/preferences/browser/settingsTree.ts
浏览文件 @
261ef3e7
...
...
@@ -28,7 +28,6 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import
{
dispose
,
IDisposable
,
Disposable
,
DisposableStore
}
from
'
vs/base/common/lifecycle
'
;
import
{
ISpliceable
}
from
'
vs/base/common/sequence
'
;
import
{
escapeRegExpCharacters
,
startsWith
}
from
'
vs/base/common/strings
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
localize
}
from
'
vs/nls
'
;
import
{
IClipboardService
}
from
'
vs/platform/clipboard/common/clipboardService
'
;
import
{
ICommandService
}
from
'
vs/platform/commands/common/commands
'
;
...
...
@@ -485,15 +484,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre
};
this
.
_onDidClickSettingLink
.
fire
(
e
);
}
else
{
let
uri
:
URI
|
undefined
;
try
{
uri
=
URI
.
parse
(
content
);
}
catch
(
err
)
{
// ignore
}
if
(
uri
)
{
this
.
_openerService
.
open
(
uri
).
catch
(
onUnexpectedError
);
}
this
.
_openerService
.
open
(
content
).
catch
(
onUnexpectedError
);
}
},
disposeables
...
...
src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts
浏览文件 @
261ef3e7
...
...
@@ -266,8 +266,7 @@ export class TerminalLinkHandler {
}
private
_handleHypertextLink
(
url
:
string
):
void
{
const
uri
=
URI
.
parse
(
url
);
this
.
_openerService
.
open
(
uri
,
{
allowTunneling
:
!!
(
this
.
_processManager
&&
this
.
_processManager
.
remoteAuthority
)
});
this
.
_openerService
.
open
(
url
,
{
allowTunneling
:
!!
(
this
.
_processManager
&&
this
.
_processManager
.
remoteAuthority
)
});
}
private
_isLinkActivationModifierDown
(
event
:
MouseEvent
):
boolean
{
...
...
src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts
浏览文件 @
261ef3e7
...
...
@@ -5,11 +5,10 @@
import
{
Schemas
}
from
'
vs/base/common/network
'
;
import
Severity
from
'
vs/base/common/severity
'
;
import
{
equalsIgnoreCase
}
from
'
vs/base/common/strings
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
{
localize
}
from
'
vs/nls
'
;
import
{
IDialogService
}
from
'
vs/platform/dialogs/common/dialogs
'
;
import
{
IOpenerService
}
from
'
vs/platform/opener/common/opener
'
;
import
{
IOpenerService
,
matchesScheme
}
from
'
vs/platform/opener/common/opener
'
;
import
{
IProductService
}
from
'
vs/platform/product/common/productService
'
;
import
{
IQuickInputService
}
from
'
vs/platform/quickinput/common/quickInput
'
;
import
{
IStorageService
}
from
'
vs/platform/storage/common/storage
'
;
...
...
@@ -21,6 +20,7 @@ import {
import
{
IEditorService
}
from
'
vs/workbench/services/editor/common/editorService
'
;
import
{
IClipboardService
}
from
'
vs/platform/clipboard/common/clipboardService
'
;
export
class
OpenerValidatorContributions
implements
IWorkbenchContribution
{
constructor
(
@
IOpenerService
private
readonly
_openerService
:
IOpenerService
,
...
...
@@ -34,13 +34,16 @@ export class OpenerValidatorContributions implements IWorkbenchContribution {
this
.
_openerService
.
registerValidator
({
shouldOpen
:
r
=>
this
.
validateLink
(
r
)
});
}
async
validateLink
(
resource
:
URI
):
Promise
<
boolean
>
{
const
{
scheme
,
authority
,
path
,
query
,
fragment
}
=
resource
;
if
(
!
equalsIgnoreCase
(
scheme
,
Schemas
.
http
)
&&
!
equalsIgnoreCase
(
scheme
,
Schemas
.
https
))
{
async
validateLink
(
resource
:
URI
|
string
):
Promise
<
boolean
>
{
if
(
!
matchesScheme
(
resource
,
Schemas
.
http
)
&&
!
matchesScheme
(
resource
,
Schemas
.
https
))
{
return
true
;
}
if
(
typeof
resource
===
'
string
'
)
{
resource
=
URI
.
parse
(
resource
);
}
const
{
scheme
,
authority
,
path
,
query
,
fragment
}
=
resource
;
const
domainToOpen
=
`
${
scheme
}
://
${
authority
}
`
;
const
{
defaultTrustedDomains
,
trustedDomains
}
=
readTrustedDomains
(
this
.
_storageService
,
this
.
_productService
);
const
allTrustedDomains
=
[...
defaultTrustedDomains
,
...
trustedDomains
];
...
...
src/vs/workbench/services/url/electron-browser/urlService.ts
浏览文件 @
261ef3e7
...
...
@@ -8,7 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import
{
IMainProcessService
}
from
'
vs/platform/ipc/electron-browser/mainProcessService
'
;
import
{
URLHandlerChannel
}
from
'
vs/platform/url/common/urlIpc
'
;
import
{
URLService
}
from
'
vs/platform/url/node/urlService
'
;
import
{
IOpenerService
}
from
'
vs/platform/opener/common/opener
'
;
import
{
IOpenerService
,
IOpener
,
matchesScheme
}
from
'
vs/platform/opener/common/opener
'
;
import
product
from
'
vs/platform/product/common/product
'
;
import
{
registerSingleton
}
from
'
vs/platform/instantiation/common/extensions
'
;
import
{
IElectronEnvironmentService
}
from
'
vs/workbench/services/electron/electron-browser/electronEnvironmentService
'
;
...
...
@@ -20,7 +20,7 @@ export interface IRelayOpenURLOptions extends IOpenURLOptions {
openExternal
?:
boolean
;
}
export
class
RelayURLService
extends
URLService
implements
IURLHandler
{
export
class
RelayURLService
extends
URLService
implements
IURLHandler
,
IOpener
{
private
urlService
:
IURLService
;
...
...
@@ -51,11 +51,15 @@ export class RelayURLService extends URLService implements IURLHandler {
return
uri
.
with
({
query
});
}
async
open
(
resource
:
URI
,
options
?:
IRelayOpenURLOptions
):
Promise
<
boolean
>
{
if
(
resource
.
scheme
!==
product
.
urlProtocol
)
{
async
open
(
resource
:
URI
|
string
,
options
?:
IRelayOpenURLOptions
):
Promise
<
boolean
>
{
if
(
!
matchesScheme
(
resource
,
product
.
urlProtocol
))
{
return
false
;
}
if
(
typeof
resource
===
'
string
'
)
{
resource
=
URI
.
parse
(
resource
);
}
return
await
this
.
urlService
.
open
(
resource
,
options
);
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录