Skip to content

Commit 283c952

Browse files
committed
opener service uses IOpener for its built-in openers
1 parent 4a61f33 commit 283c952

1 file changed

Lines changed: 80 additions & 62 deletions

File tree

src/vs/editor/browser/services/openerService.ts

Lines changed: 80 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,69 @@ import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/c
1616
import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener } from 'vs/platform/opener/common/opener';
1717
import { EditorOpenContext } from 'vs/platform/editor/common/editor';
1818

19+
class CommandOpener implements IOpener {
20+
21+
constructor(@ICommandService private readonly _commandService: ICommandService) { }
22+
23+
async open(uri: URI, ) {
24+
// run command or bail out if command isn't known
25+
if (!equalsIgnoreCase(uri.scheme, Schemas.command)) {
26+
return false;
27+
}
28+
29+
if (!CommandsRegistry.getCommand(uri.path)) {
30+
throw new Error(`command '${uri.path}' NOT known`);
31+
}
32+
33+
// execute as command
34+
let args: any = [];
35+
try {
36+
args = parse(uri.query);
37+
if (!Array.isArray(args)) {
38+
args = [args];
39+
}
40+
} catch (e) {
41+
// ignore error
42+
}
43+
await this._commandService.executeCommand(uri.path, ...args);
44+
return true;
45+
}
46+
}
47+
48+
class EditorOpener implements IOpener {
49+
50+
constructor(@ICodeEditorService private readonly _editorService: ICodeEditorService) { }
51+
52+
async open(uri: URI, options?: OpenOptions): Promise<boolean> {
53+
54+
// finally open in editor
55+
let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined;
56+
const match = /^L?(\d+)(?:,(\d+))?/.exec(uri.fragment);
57+
if (match) {
58+
// support file:///some/file.js#73,84
59+
// support file:///some/file.js#L73
60+
selection = {
61+
startLineNumber: parseInt(match[1]),
62+
startColumn: match[2] ? parseInt(match[2]) : 1
63+
};
64+
// remove fragment
65+
uri = uri.with({ fragment: '' });
66+
}
67+
68+
if (uri.scheme === Schemas.file) {
69+
uri = resources.normalizePath(uri); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954)
70+
}
71+
72+
await this._editorService.openCodeEditor(
73+
{ resource: uri, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } },
74+
this._editorService.getFocusedCodeEditor(),
75+
options?.openToSide
76+
);
77+
78+
return true;
79+
}
80+
}
81+
1982
export class OpenerService extends Disposable implements IOpenerService {
2083

2184
_serviceBrand: undefined;
@@ -26,23 +89,35 @@ export class OpenerService extends Disposable implements IOpenerService {
2689
private _externalOpener: IExternalOpener;
2790

2891
constructor(
29-
@ICodeEditorService private readonly _editorService: ICodeEditorService,
30-
@ICommandService private readonly _commandService: ICommandService,
92+
@ICodeEditorService editorService: ICodeEditorService,
93+
@ICommandService commandService: ICommandService,
3194
) {
3295
super();
3396

3497
// Default external opener is going through window.open()
3598
this._externalOpener = {
3699
openExternal: href => {
37100
dom.windowOpenNoOpener(href);
38-
39101
return Promise.resolve(true);
40102
}
41103
};
104+
105+
//
106+
this._openers.push({ // default: open externally
107+
open: async (uri: URI, options: OpenOptions | undefined): Promise<boolean> => {
108+
const { scheme } = uri;
109+
if (options?.openExternal || equalsIgnoreCase(scheme, Schemas.mailto) || equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) {
110+
return this._doOpenExternal(uri, options);
111+
}
112+
return false;
113+
}
114+
});
115+
this._openers.push(new CommandOpener(commandService)); // default: run command
116+
this._openers.push(new EditorOpener(editorService)); // default: open editor
42117
}
43118

44119
registerOpener(opener: IOpener): IDisposable {
45-
const remove = this._openers.push(opener);
120+
const remove = this._openers.unshift(opener);
46121

47122
return { dispose: remove };
48123
}
@@ -85,8 +160,7 @@ export class OpenerService extends Disposable implements IOpenerService {
85160
}
86161
}
87162

88-
// use default openers
89-
return this._doOpen(resource, options);
163+
return false;
90164
}
91165

92166
async resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise<IResolvedExternalUri> {
@@ -100,62 +174,6 @@ export class OpenerService extends Disposable implements IOpenerService {
100174
return { resolved: resource, dispose: () => { } };
101175
}
102176

103-
private async _doOpen(resource: URI, options: OpenOptions | undefined): Promise<boolean> {
104-
const { scheme, path, query, fragment } = resource;
105-
106-
if (options?.openExternal || equalsIgnoreCase(scheme, Schemas.mailto) || equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) {
107-
// open externally
108-
return this._doOpenExternal(resource, options);
109-
}
110-
111-
if (equalsIgnoreCase(scheme, Schemas.command)) {
112-
// run command or bail out if command isn't known
113-
if (!CommandsRegistry.getCommand(path)) {
114-
throw new Error(`command '${path}' NOT known`);
115-
}
116-
// execute as command
117-
let args: any = [];
118-
try {
119-
args = parse(query);
120-
if (!Array.isArray(args)) {
121-
args = [args];
122-
}
123-
} catch (e) {
124-
// ignore error
125-
}
126-
127-
await this._commandService.executeCommand(path, ...args);
128-
129-
return true;
130-
}
131-
132-
// finally open in editor
133-
let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined;
134-
const match = /^L?(\d+)(?:,(\d+))?/.exec(fragment);
135-
if (match) {
136-
// support file:///some/file.js#73,84
137-
// support file:///some/file.js#L73
138-
selection = {
139-
startLineNumber: parseInt(match[1]),
140-
startColumn: match[2] ? parseInt(match[2]) : 1
141-
};
142-
// remove fragment
143-
resource = resource.with({ fragment: '' });
144-
}
145-
146-
if (resource.scheme === Schemas.file) {
147-
resource = resources.normalizePath(resource); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954)
148-
}
149-
150-
await this._editorService.openCodeEditor(
151-
{ resource, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } },
152-
this._editorService.getFocusedCodeEditor(),
153-
options?.openToSide
154-
);
155-
156-
return true;
157-
}
158-
159177
private async _doOpenExternal(resource: URI, options: OpenOptions | undefined): Promise<boolean> {
160178
const { resolved } = await this.resolveExternalUri(resource, options);
161179

0 commit comments

Comments
 (0)