Skip to content

Commit 1df7ec7

Browse files
committed
Support git diff @@ ... to-file-range @@ links
Fixes microsoft#182878
1 parent 2bd04a1 commit 1df7ec7

File tree

1 file changed

+96
-5
lines changed

1 file changed

+96
-5
lines changed

src/vs/workbench/contrib/terminalContrib/links/browser/terminalMultiLineLinkDetector.ts

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,22 @@ const enum Constants {
2525
MaxResolvedLinkLength = 1024,
2626
}
2727

28-
const candidateMatchers = [
28+
const lineNumberPrefixMatchers = [
2929
// Ripgrep:
30+
// /some/file
3031
// 16:searchresult
3132
// 16: searchresult
3233
// Eslint:
33-
// 16:5 error ...
34-
/\s*(?<link>(?<line>\d+):(?<col>\d+)?)/
34+
// /some/file
35+
// 16:5 error ...
36+
/ *(?<link>(?<line>\d+):(?<col>\d+)?)/
37+
];
38+
39+
const gitDiffMatchers = [
40+
// --- a/some/file
41+
// +++ b/some/file
42+
// @@ -8,11 +8,11 @@ file content...
43+
/^(?<link>@@ .+ \+(?<toFileLine>\d+),(?<toFileCount>\d+) @@)/
3544
];
3645

3746
export class TerminalMultiLineLinkDetector implements ITerminalLinkDetector {
@@ -66,7 +75,7 @@ export class TerminalMultiLineLinkDetector implements ITerminalLinkDetector {
6675

6776
// Match against the fallback matchers which are mainly designed to catch paths with spaces
6877
// that aren't possible using the regular mechanism.
69-
for (const matcher of candidateMatchers) {
78+
for (const matcher of lineNumberPrefixMatchers) {
7079
const match = text.match(matcher);
7180
const group = match?.groups;
7281
if (!group) {
@@ -130,7 +139,7 @@ export class TerminalMultiLineLinkDetector implements ITerminalLinkDetector {
130139
uri: linkStat.uri,
131140
selection: {
132141
startLineNumber: parseInt(line),
133-
startColumn: col ? parseInt(col) : 0
142+
startColumn: col ? parseInt(col) : 1
134143
},
135144
disableTrimColon: true,
136145
bufferRange: bufferRange,
@@ -144,6 +153,88 @@ export class TerminalMultiLineLinkDetector implements ITerminalLinkDetector {
144153
}
145154
}
146155

156+
if (links.length === 0) {
157+
for (const matcher of gitDiffMatchers) {
158+
const match = text.match(matcher);
159+
const group = match?.groups;
160+
if (!group) {
161+
continue;
162+
}
163+
const link = group?.link;
164+
const toFileLine = group?.toFileLine;
165+
const toFileCount = group?.toFileCount;
166+
if (!link || toFileLine === undefined) {
167+
continue;
168+
}
169+
170+
// Don't try resolve any links of excessive length
171+
if (link.length > Constants.MaxResolvedLinkLength) {
172+
continue;
173+
}
174+
175+
this._logService.trace('terminalMultiLineLinkDetector#detect candidate', link);
176+
177+
178+
// Scan up looking for the first line that could be a path
179+
let possiblePath: string | undefined;
180+
for (let index = startLine - 1; index >= 0; index--) {
181+
// Ignore lines that aren't at the beginning of a wrapped line
182+
if (this.xterm.buffer.active.getLine(index)!.isWrapped) {
183+
continue;
184+
}
185+
const text = getXtermLineContent(this.xterm.buffer.active, index, index, this.xterm.cols);
186+
const match = text.match(/\+\+\+ b\/(?<path>.+)/);
187+
if (match) {
188+
possiblePath = match.groups?.path;
189+
break;
190+
}
191+
}
192+
if (!possiblePath) {
193+
continue;
194+
}
195+
196+
// Check if the first non-matching line is an absolute or relative link
197+
const linkStat = await this._linkResolver.resolveLink(this._processManager, possiblePath);
198+
if (linkStat) {
199+
let type: TerminalBuiltinLinkType;
200+
if (linkStat.isDirectory) {
201+
if (this._isDirectoryInsideWorkspace(linkStat.uri)) {
202+
type = TerminalBuiltinLinkType.LocalFolderInWorkspace;
203+
} else {
204+
type = TerminalBuiltinLinkType.LocalFolderOutsideWorkspace;
205+
}
206+
} else {
207+
type = TerminalBuiltinLinkType.LocalFile;
208+
}
209+
210+
// Convert the link to the buffer range
211+
const bufferRange = convertLinkRangeToBuffer(lines, this.xterm.cols, {
212+
startColumn: 1,
213+
startLineNumber: 1,
214+
endColumn: 1 + link.length,
215+
endLineNumber: 1
216+
}, startLine);
217+
218+
const simpleLink: ITerminalSimpleLink = {
219+
text: link,
220+
uri: linkStat.uri,
221+
selection: {
222+
startLineNumber: parseInt(toFileLine),
223+
startColumn: 1,
224+
endLineNumber: parseInt(toFileLine) + parseInt(toFileCount)
225+
},
226+
bufferRange: bufferRange,
227+
type
228+
};
229+
this._logService.trace('terminalMultiLineLinkDetector#detect verified link', simpleLink);
230+
links.push(simpleLink);
231+
232+
// Break on the first match
233+
break;
234+
}
235+
}
236+
}
237+
147238
return links;
148239
}
149240

0 commit comments

Comments
 (0)