mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
* format
* update external options type
* add command flag for command
* Allow commands in Notebooks
Co-authored-by: chgagnon <chgagnon@microsoft.com>
(cherry picked from commit e151668c81)
This commit is contained in:
@@ -128,8 +128,10 @@ export default class WebViewComponent extends ComponentBase<WebViewProperties> i
|
|||||||
if (!link) {
|
if (!link) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (WebViewComponent.standardSupportedLinkSchemes.indexOf(link.scheme) >= 0 || this.enableCommandUris && link.scheme === 'command') {
|
if (WebViewComponent.standardSupportedLinkSchemes.indexOf(link.scheme) >= 0) {
|
||||||
this._openerService.open(link);
|
this._openerService.open(link);
|
||||||
|
} else if (this.enableCommandUris && link.scheme === 'command') {
|
||||||
|
this._openerService.open(link, { allowCommands: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export class LinkHandlerDirective {
|
|||||||
this.openerService.open(uri, { openExternal: true }).catch(onUnexpectedError);
|
this.openerService.open(uri, { openExternal: true }).catch(onUnexpectedError);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.openerService.open(uri).catch(onUnexpectedError);
|
this.openerService.open(uri, { allowCommands: true }).catch(onUnexpectedError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export class MarkdownRenderer {
|
|||||||
if (!markdown) {
|
if (!markdown) {
|
||||||
element = document.createElement('span');
|
element = document.createElement('span');
|
||||||
} else {
|
} else {
|
||||||
element = renderMarkdown(markdown, { ...this._getRenderOptions(disposeables), ...options }, markedOptions);
|
element = renderMarkdown(markdown, { ...this._getRenderOptions(markdown, disposeables), ...options }, markedOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -67,7 +67,7 @@ export class MarkdownRenderer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _getRenderOptions(disposeables: DisposableStore): MarkdownRenderOptions {
|
protected _getRenderOptions(markdown: IMarkdownString, disposeables: DisposableStore): MarkdownRenderOptions {
|
||||||
return {
|
return {
|
||||||
baseUrl: this._options.baseUrl,
|
baseUrl: this._options.baseUrl,
|
||||||
codeBlockRenderer: async (languageAlias, value) => {
|
codeBlockRenderer: async (languageAlias, value) => {
|
||||||
@@ -105,7 +105,7 @@ export class MarkdownRenderer {
|
|||||||
},
|
},
|
||||||
asyncRenderCallback: () => this._onDidRenderAsync.fire(),
|
asyncRenderCallback: () => this._onDidRenderAsync.fire(),
|
||||||
actionHandler: {
|
actionHandler: {
|
||||||
callback: (content) => this._openerService.open(content, { fromUserGesture: true }).catch(onUnexpectedError),
|
callback: (content) => this._openerService.open(content, { fromUserGesture: true, allowContributedOpeners: true, allowCommands: markdown.isTrusted }).catch(onUnexpectedError),
|
||||||
disposeables
|
disposeables
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,10 +21,15 @@ class CommandOpener implements IOpener {
|
|||||||
|
|
||||||
constructor(@ICommandService private readonly _commandService: ICommandService) { }
|
constructor(@ICommandService private readonly _commandService: ICommandService) { }
|
||||||
|
|
||||||
async open(target: URI | string) {
|
async open(target: URI | string, options?: OpenOptions): Promise<boolean> {
|
||||||
if (!matchesScheme(target, Schemas.command)) {
|
if (!matchesScheme(target, Schemas.command)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!options?.allowCommands) {
|
||||||
|
// silently ignore commands when command-links are disabled, also
|
||||||
|
// surpress other openers by returning TRUE
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// run command or bail out if command isn't known
|
// run command or bail out if command isn't known
|
||||||
if (typeof target === 'string') {
|
if (typeof target === 'string') {
|
||||||
target = URI.parse(target);
|
target = URI.parse(target);
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ class MessageWidget {
|
|||||||
this._codeLink.setAttribute('href', `${code.target.toString()}`);
|
this._codeLink.setAttribute('href', `${code.target.toString()}`);
|
||||||
|
|
||||||
this._codeLink.onclick = (e) => {
|
this._codeLink.onclick = (e) => {
|
||||||
this._openerService.open(code.target);
|
this._openerService.open(code.target, { allowCommands: true });
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -536,7 +536,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
|||||||
this._codeLink.setAttribute('href', code.target.toString());
|
this._codeLink.setAttribute('href', code.target.toString());
|
||||||
|
|
||||||
this._codeLink.onclick = (e) => {
|
this._codeLink.onclick = (e) => {
|
||||||
this._openerService.open(code.target);
|
this._openerService.open(code.target, { allowCommands: true });
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ export class LinkDetector implements IEditorContribution {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.openerService.open(uri, { openToSide, fromUserGesture });
|
return this.openerService.open(uri, { openToSide, fromUserGesture, allowContributedOpeners: true, allowCommands: true });
|
||||||
|
|
||||||
}, err => {
|
}, err => {
|
||||||
const messageOrError =
|
const messageOrError =
|
||||||
|
|||||||
@@ -81,20 +81,10 @@ suite('OpenerService', function () {
|
|||||||
const id = `aCommand${Math.random()}`;
|
const id = `aCommand${Math.random()}`;
|
||||||
CommandsRegistry.registerCommand(id, function () { });
|
CommandsRegistry.registerCommand(id, function () { });
|
||||||
|
|
||||||
|
assert.strictEqual(lastCommand, undefined);
|
||||||
await openerService.open(URI.parse('command:' + id));
|
await openerService.open(URI.parse('command:' + id));
|
||||||
assert.equal(lastCommand!.id, id);
|
|
||||||
assert.equal(lastCommand!.args.length, 0);
|
|
||||||
|
|
||||||
await openerService.open(URI.parse('command:' + id).with({ query: '123' }));
|
assert.strictEqual(lastCommand, undefined);
|
||||||
assert.equal(lastCommand!.id, id);
|
|
||||||
assert.equal(lastCommand!.args.length, 1);
|
|
||||||
assert.equal(lastCommand!.args[0], '123');
|
|
||||||
|
|
||||||
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);
|
|
||||||
assert.equal(lastCommand!.args[1], true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('links are protected by validators', async function () {
|
test('links are protected by validators', async function () {
|
||||||
@@ -108,6 +98,33 @@ suite('OpenerService', function () {
|
|||||||
assert.equal(httpsResult, false);
|
assert.equal(httpsResult, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('delegate to commandsService, command:someid', async function () {
|
||||||
|
const openerService = new OpenerService(editorService, commandService);
|
||||||
|
|
||||||
|
const id = `aCommand${Math.random()}`;
|
||||||
|
CommandsRegistry.registerCommand(id, function () { });
|
||||||
|
|
||||||
|
await openerService.open(URI.parse('command:' + id).with({ query: '\"123\"' }), { allowCommands: true });
|
||||||
|
assert.strictEqual(lastCommand!.id, id);
|
||||||
|
assert.strictEqual(lastCommand!.args.length, 1);
|
||||||
|
assert.strictEqual(lastCommand!.args[0], '123');
|
||||||
|
|
||||||
|
await openerService.open(URI.parse('command:' + id), { allowCommands: true });
|
||||||
|
assert.strictEqual(lastCommand!.id, id);
|
||||||
|
assert.strictEqual(lastCommand!.args.length, 0);
|
||||||
|
|
||||||
|
await openerService.open(URI.parse('command:' + id).with({ query: '123' }), { allowCommands: true });
|
||||||
|
assert.strictEqual(lastCommand!.id, id);
|
||||||
|
assert.strictEqual(lastCommand!.args.length, 1);
|
||||||
|
assert.strictEqual(lastCommand!.args[0], 123);
|
||||||
|
|
||||||
|
await openerService.open(URI.parse('command:' + id).with({ query: JSON.stringify([12, true]) }), { allowCommands: true });
|
||||||
|
assert.strictEqual(lastCommand!.id, id);
|
||||||
|
assert.strictEqual(lastCommand!.args.length, 2);
|
||||||
|
assert.strictEqual(lastCommand!.args[0], 12);
|
||||||
|
assert.strictEqual(lastCommand!.args[1], true);
|
||||||
|
});
|
||||||
|
|
||||||
test('links validated by validators go to openers', async function () {
|
test('links validated by validators go to openers', async function () {
|
||||||
const openerService = new OpenerService(editorService, commandService);
|
const openerService = new OpenerService(editorService, commandService);
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export class Link extends Disposable {
|
|||||||
|
|
||||||
this._register(onOpen(e => {
|
this._register(onOpen(e => {
|
||||||
EventHelper.stop(e, true);
|
EventHelper.stop(e, true);
|
||||||
openerService.open(link.href);
|
openerService.open(link.href, { allowCommands: true });
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.applyStyles();
|
this.applyStyles();
|
||||||
|
|||||||
@@ -29,9 +29,18 @@ type OpenInternalOptions = {
|
|||||||
* action, such as keyboard or mouse usage.
|
* action, such as keyboard or mouse usage.
|
||||||
*/
|
*/
|
||||||
readonly fromUserGesture?: boolean;
|
readonly fromUserGesture?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow command links to be handled.
|
||||||
|
*/
|
||||||
|
readonly allowCommands?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type OpenExternalOptions = { readonly openExternal?: boolean; readonly allowTunneling?: boolean };
|
export type OpenExternalOptions = {
|
||||||
|
readonly openExternal?: boolean;
|
||||||
|
readonly allowTunneling?: boolean;
|
||||||
|
readonly allowContributedOpeners?: boolean | string;
|
||||||
|
};
|
||||||
|
|
||||||
export type OpenOptions = OpenInternalOptions & OpenExternalOptions;
|
export type OpenOptions = OpenInternalOptions & OpenExternalOptions;
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||||||
private onDidClickLink(handle: extHostProtocol.WebviewHandle, link: string): void {
|
private onDidClickLink(handle: extHostProtocol.WebviewHandle, link: string): void {
|
||||||
const webview = this.getWebview(handle);
|
const webview = this.getWebview(handle);
|
||||||
if (this.isSupportedLink(webview, URI.parse(link))) {
|
if (this.isSupportedLink(webview, URI.parse(link))) {
|
||||||
this._openerService.open(link, { fromUserGesture: true });
|
this._openerService.open(link, { fromUserGesture: true, allowContributedOpeners: true, allowCommands: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ export class NotificationTemplateRenderer extends Disposable {
|
|||||||
private renderMessage(notification: INotificationViewItem): boolean {
|
private renderMessage(notification: INotificationViewItem): boolean {
|
||||||
clearNode(this.template.message);
|
clearNode(this.template.message);
|
||||||
this.template.message.appendChild(NotificationMessageRenderer.render(notification.message, {
|
this.template.message.appendChild(NotificationMessageRenderer.render(notification.message, {
|
||||||
callback: link => this.openerService.open(URI.parse(link)),
|
callback: link => this.openerService.open(URI.parse(link), { allowCommands: true }),
|
||||||
toDispose: this.inputDisposables
|
toDispose: this.inputDisposables
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -581,7 +581,7 @@ export abstract class ViewPane extends Pane implements IView {
|
|||||||
button.label = node.label;
|
button.label = node.label;
|
||||||
button.onDidClick(_ => {
|
button.onDidClick(_ => {
|
||||||
this.telemetryService.publicLog2<{ viewId: string, uri: string }, WelcomeActionClassification>('views.welcomeAction', { viewId: this.id, uri: node.href });
|
this.telemetryService.publicLog2<{ viewId: string, uri: string }, WelcomeActionClassification>('views.welcomeAction', { viewId: this.id, uri: node.href });
|
||||||
this.openerService.open(node.href);
|
this.openerService.open(node.href, { allowCommands: true });
|
||||||
}, null, disposables);
|
}, null, disposables);
|
||||||
disposables.add(button);
|
disposables.add(button);
|
||||||
disposables.add(attachButtonStyler(button, this.themeService));
|
disposables.add(attachButtonStyler(button, this.themeService));
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export class CommentNodeRenderer implements IListRenderer<ITreeNode<CommentNode>
|
|||||||
inline: true,
|
inline: true,
|
||||||
actionHandler: {
|
actionHandler: {
|
||||||
callback: (content) => {
|
callback: (content) => {
|
||||||
this.openerService.open(content).catch(onUnexpectedError);
|
this.openerService.open(content, { allowCommands: node.element.comment.body.isTrusted }).catch(onUnexpectedError);
|
||||||
},
|
},
|
||||||
disposeables: disposables
|
disposeables: disposables
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -625,11 +625,12 @@ export class ExtensionEditor extends EditorPane {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Only allow links with specific schemes
|
// Only allow links with specific schemes
|
||||||
if (matchesScheme(link, Schemas.http) || matchesScheme(link, Schemas.https) || matchesScheme(link, Schemas.mailto)
|
if (matchesScheme(link, Schemas.http) || matchesScheme(link, Schemas.https) || matchesScheme(link, Schemas.mailto)) {
|
||||||
|| (matchesScheme(link, Schemas.command) && URI.parse(link).path === ShowCurrentReleaseNotesActionId)
|
|
||||||
) {
|
|
||||||
this.openerService.open(link);
|
this.openerService.open(link);
|
||||||
}
|
}
|
||||||
|
if (matchesScheme(link, Schemas.command) && URI.parse(link).path === ShowCurrentReleaseNotesActionId) {
|
||||||
|
this.openerService.open(link, { allowCommands: true }); // TODO@sandy081 use commands service
|
||||||
|
}
|
||||||
}, null, this.contentDisposables));
|
}, null, this.contentDisposables));
|
||||||
|
|
||||||
return webview;
|
return webview;
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ class MarkerWidget extends Disposable {
|
|||||||
|
|
||||||
this._register(onOpen(e => {
|
this._register(onOpen(e => {
|
||||||
dom.EventHelper.stop(e, true);
|
dom.EventHelper.stop(e, true);
|
||||||
this._openerService.open(codeUri);
|
this._openerService.open(codeUri, { allowCommands: true });
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const code = new HighlightedLabel(dom.append(this._codeLink, dom.$('.marker-code')), false);
|
const code = new HighlightedLabel(dom.append(this._codeLink, dom.$('.marker-code')), false);
|
||||||
|
|||||||
@@ -424,7 +424,7 @@ var requirejs = (function() {
|
|||||||
|
|
||||||
if (matchesScheme(link, Schemas.http) || matchesScheme(link, Schemas.https) || matchesScheme(link, Schemas.mailto)
|
if (matchesScheme(link, Schemas.http) || matchesScheme(link, Schemas.https) || matchesScheme(link, Schemas.mailto)
|
||||||
|| matchesScheme(link, Schemas.command)) {
|
|| matchesScheme(link, Schemas.command)) {
|
||||||
this.openerService.open(link, { fromUserGesture: true });
|
this.openerService.open(link, { fromUserGesture: true, allowContributedOpeners: true, allowCommands: true });
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -755,7 +755,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre
|
|||||||
};
|
};
|
||||||
this._onDidClickSettingLink.fire(e);
|
this._onDidClickSettingLink.fire(e);
|
||||||
} else {
|
} else {
|
||||||
this._openerService.open(content).catch(onUnexpectedError);
|
this._openerService.open(content, { allowCommands: true }).catch(onUnexpectedError);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disposeables
|
disposeables
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ class HelpItem extends HelpItemBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async takeAction(extensionDescription: IExtensionDescription, url: string): Promise<void> {
|
protected async takeAction(extensionDescription: IExtensionDescription, url: string): Promise<void> {
|
||||||
await this.openerService.open(URI.parse(url));
|
await this.openerService.open(URI.parse(url), { allowCommands: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ export class WalkThroughPart extends EditorPane {
|
|||||||
this.notificationService.info(localize('walkThrough.gitNotFound', "It looks like Git is not installed on your system."));
|
this.notificationService.info(localize('walkThrough.gitNotFound', "It looks like Git is not installed on your system."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.openerService.open(this.addFrom(uri));
|
this.openerService.open(this.addFrom(uri), { allowCommands: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
private addFrom(uri: URI) {
|
private addFrom(uri: URI) {
|
||||||
|
|||||||
Reference in New Issue
Block a user