Skip to content

Commit 67b30c0

Browse files
authored
Merge pull request microsoft#67828 from hhu94/git-diff-links-simple
Add git diff terminal link handler
2 parents e6d4139 + fb4d028 commit 67b30c0

2 files changed

Lines changed: 77 additions & 24 deletions

File tree

src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ export class TerminalLinkHandler {
6363
private _widgetManager: TerminalWidgetManager;
6464
private _processCwd: string;
6565
private _localLinkPattern: RegExp;
66+
private _gitDiffPreImagePattern: RegExp;
67+
private _gitDiffPostImagePattern: RegExp;
68+
private readonly _tooltipCallback: (event: MouseEvent, uri: string) => boolean | void;
6669

6770
constructor(
6871
private _xterm: any,
@@ -75,8 +78,23 @@ export class TerminalLinkHandler {
7578
const baseLocalLinkClause = _platform === platform.Platform.Windows ? winLocalLinkClause : unixLocalLinkClause;
7679
// Append line and column number regex
7780
this._localLinkPattern = new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`);
81+
// Matches '--- a/src/file1', capturing 'src/file1' in group 1
82+
this._gitDiffPreImagePattern = /^--- a\/(\S*)/;
83+
// Matches '+++ b/src/file1', capturing 'src/file1' in group 1
84+
this._gitDiffPostImagePattern = /^\+\+\+ b\/(\S*)/;
85+
86+
this._tooltipCallback = (e: MouseEvent) => {
87+
if (this._terminalService && this._terminalService.configHelper.config.rendererType === 'dom') {
88+
const target = (e.target as HTMLElement);
89+
this._widgetManager.showMessage(target.offsetLeft, target.offsetTop, this._getLinkHoverString());
90+
} else {
91+
this._widgetManager.showMessage(e.offsetX, e.offsetY, this._getLinkHoverString());
92+
}
93+
};
94+
7895
this.registerWebLinkHandler();
7996
this.registerLocalLinkHandler();
97+
this.registerGitDiffLinkHandlers();
8098
}
8199

82100
public setWidgetManager(widgetManager: TerminalWidgetManager): void {
@@ -90,14 +108,7 @@ export class TerminalLinkHandler {
90108
public registerCustomLinkHandler(regex: RegExp, handler: (uri: string) => void, matchIndex?: number, validationCallback?: XtermLinkMatcherValidationCallback): number {
91109
const options: ILinkMatcherOptions = {
92110
matchIndex,
93-
tooltipCallback: (e: MouseEvent) => {
94-
if (this._terminalService && this._terminalService.configHelper.config.rendererType === 'dom') {
95-
const target = (e.target as HTMLElement);
96-
this._widgetManager.showMessage(target.offsetLeft, target.offsetTop, this._getLinkHoverString());
97-
} else {
98-
this._widgetManager.showMessage(e.offsetX, e.offsetY, this._getLinkHoverString());
99-
}
100-
},
111+
tooltipCallback: this._tooltipCallback,
101112
leaveCallback: () => this._widgetManager.closeMessage(),
102113
willLinkActivate: (e: MouseEvent) => this._isLinkActivationModifierDown(e),
103114
priority: CUSTOM_LINK_PRIORITY
@@ -114,14 +125,7 @@ export class TerminalLinkHandler {
114125
});
115126
this._xterm.webLinksInit(wrappedHandler, {
116127
validationCallback: (uri: string, callback: (isValid: boolean) => void) => this._validateWebLink(uri, callback),
117-
tooltipCallback: (e: MouseEvent) => {
118-
if (this._terminalService && this._terminalService.configHelper.config.rendererType === 'dom') {
119-
const target = (e.target as HTMLElement);
120-
this._widgetManager.showMessage(target.offsetLeft, target.offsetTop, this._getLinkHoverString());
121-
} else {
122-
this._widgetManager.showMessage(e.offsetX, e.offsetY, this._getLinkHoverString());
123-
}
124-
},
128+
tooltipCallback: this._tooltipCallback,
125129
leaveCallback: () => this._widgetManager.closeMessage(),
126130
willLinkActivate: (e: MouseEvent) => this._isLinkActivationModifierDown(e)
127131
});
@@ -133,20 +137,29 @@ export class TerminalLinkHandler {
133137
});
134138
this._xterm.registerLinkMatcher(this._localLinkRegex, wrappedHandler, {
135139
validationCallback: (uri: string, callback: (isValid: boolean) => void) => this._validateLocalLink(uri, callback),
136-
tooltipCallback: (e: MouseEvent) => {
137-
if (this._terminalService && this._terminalService.configHelper.config.rendererType === 'dom') {
138-
const target = (e.target as HTMLElement);
139-
this._widgetManager.showMessage(target.offsetLeft, target.offsetTop, this._getLinkHoverString());
140-
} else {
141-
this._widgetManager.showMessage(e.offsetX, e.offsetY, this._getLinkHoverString());
142-
}
143-
},
140+
tooltipCallback: this._tooltipCallback,
144141
leaveCallback: () => this._widgetManager.closeMessage(),
145142
willLinkActivate: (e: MouseEvent) => this._isLinkActivationModifierDown(e),
146143
priority: LOCAL_LINK_PRIORITY
147144
});
148145
}
149146

147+
public registerGitDiffLinkHandlers(): void {
148+
const wrappedHandler = this._wrapLinkHandler(url => {
149+
this._handleLocalLink(url);
150+
});
151+
const options = {
152+
matchIndex: 1,
153+
validationCallback: (uri: string, callback: (isValid: boolean) => void) => this._validateLocalLink(uri, callback),
154+
tooltipCallback: this._tooltipCallback,
155+
leaveCallback: () => this._widgetManager.closeMessage(),
156+
willLinkActivate: (e: MouseEvent) => this._isLinkActivationModifierDown(e),
157+
priority: LOCAL_LINK_PRIORITY
158+
};
159+
this._xterm.registerLinkMatcher(this._gitDiffPreImagePattern, wrappedHandler, options);
160+
this._xterm.registerLinkMatcher(this._gitDiffPostImagePattern, wrappedHandler, options);
161+
}
162+
150163
public dispose(): void {
151164
this._xterm = null;
152165
this._hoverDisposables = dispose(this._hoverDisposables);
@@ -172,6 +185,14 @@ export class TerminalLinkHandler {
172185
return this._localLinkPattern;
173186
}
174187

188+
protected get _gitDiffPreImageRegex(): RegExp {
189+
return this._gitDiffPreImagePattern;
190+
}
191+
192+
protected get _gitDiffPostImageRegex(): RegExp {
193+
return this._gitDiffPostImagePattern;
194+
}
195+
175196
private _handleLocalLink(link: string): PromiseLike<any> {
176197
return this._resolvePath(link).then(resolvedLink => {
177198
if (!resolvedLink) {

src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ class TestTerminalLinkHandler extends TerminalLinkHandler {
1414
public get localLinkRegex(): RegExp {
1515
return this._localLinkRegex;
1616
}
17+
public get gitDiffLinkPreImageRegex(): RegExp {
18+
return this._gitDiffPreImageRegex;
19+
}
20+
public get gitDiffLinkPostImageRegex(): RegExp {
21+
return this._gitDiffPostImageRegex;
22+
}
1723
public preprocessPath(link: string): string | null {
1824
return this._preprocessPath(link);
1925
}
@@ -217,4 +223,30 @@ suite('Workbench - TerminalLinkHandler', () => {
217223
assert.equal(linkHandler.preprocessPath('/absolute/path/file3'), '/absolute/path/file3');
218224
});
219225
});
226+
227+
test('gitDiffLinkRegex', () => {
228+
// The platform is irrelevant because the links generated by Git are the same format regardless of platform
229+
const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!);
230+
231+
function assertAreGoodMatches(matches: RegExpMatchArray | null) {
232+
if (matches) {
233+
assert.equal(matches.length, 2);
234+
assert.equal(matches[1], 'src/file1');
235+
} else {
236+
assert.fail();
237+
}
238+
}
239+
240+
// Happy cases
241+
assertAreGoodMatches('--- a/src/file1'.match(linkHandler.gitDiffLinkPreImageRegex));
242+
assertAreGoodMatches('--- a/src/file1 '.match(linkHandler.gitDiffLinkPreImageRegex));
243+
assertAreGoodMatches('+++ b/src/file1'.match(linkHandler.gitDiffLinkPostImageRegex));
244+
assertAreGoodMatches('+++ b/src/file1 '.match(linkHandler.gitDiffLinkPostImageRegex));
245+
246+
// Make sure /dev/null isn't a match
247+
assert.equal(linkHandler.gitDiffLinkPreImageRegex.test('--- /dev/null'), false);
248+
assert.equal(linkHandler.gitDiffLinkPreImageRegex.test('--- /dev/null '), false);
249+
assert.equal(linkHandler.gitDiffLinkPostImageRegex.test('+++ /dev/null'), false);
250+
assert.equal(linkHandler.gitDiffLinkPostImageRegex.test('+++ /dev/null '), false);
251+
});
220252
});

0 commit comments

Comments
 (0)