Skip to content

Commit 3632c53

Browse files
Copilotalexr00
andauthored
Fix unpredictable markdown link text when copying permalink from gutter (#8252)
* Initial plan * Initial analysis of permalink markdown text issue Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> * Fix unpredictable markdown link text when copying permalink from gutter - Add range field to PermalinkInfo to track the range used for the permalink - Update getMarkdownLinkText to accept and use the permalink range - When copying markdown permalink, extract text from the correct line/range - Fixes issue where right-clicking gutter would use previous selection text Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> * Add comment explaining NotebookRange exclusion in getMarkdownLinkText Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> * Full line length --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
1 parent c594492 commit 3632c53

2 files changed

Lines changed: 26 additions & 12 deletions

File tree

src/issues/issueFeatureRegistrar.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,10 +1621,21 @@ ${options?.body ?? ''}\n
16211621
}
16221622
}
16231623

1624-
private getMarkdownLinkText(): string | undefined {
1624+
private getMarkdownLinkText(range?: vscode.Range | vscode.NotebookRange): string | undefined {
16251625
if (!vscode.window.activeTextEditor) {
16261626
return undefined;
16271627
}
1628+
1629+
// If a specific range is provided (e.g., from a gutter click), use that
1630+
// Note: NotebookRange is excluded because getText() only accepts vscode.Range
1631+
if (range && !(range instanceof vscode.NotebookRange)) {
1632+
const text = vscode.window.activeTextEditor.document.getText(range);
1633+
if (text) {
1634+
return text;
1635+
}
1636+
}
1637+
1638+
// Otherwise fall back to the current selection
16281639
let editorSelection: vscode.Range | undefined = vscode.window.activeTextEditor.selection;
16291640
if (editorSelection.start.line !== editorSelection.end.line) {
16301641
editorSelection = new vscode.Range(
@@ -1648,7 +1659,7 @@ ${options?.body ?? ''}\n
16481659
const withPermalinks: (PermalinkInfo & { permalink: string })[] = links.filter((link): link is PermalinkInfo & { permalink: string } => !!link.permalink);
16491660

16501661
if (withPermalinks.length === 1) {
1651-
const selection = this.getMarkdownLinkText();
1662+
const selection = this.getMarkdownLinkText(withPermalinks[0].range);
16521663
if (selection) {
16531664
return vscode.env.clipboard.writeText(`[${selection.trim()}](${withPermalinks[0].permalink})`);
16541665
}

src/issues/util.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ function getFileAndPosition(context: LinkContext, positionInfo?: NewIssue): { ur
225225
uri = fileUri;
226226
if (vscode.window.activeTextEditor?.document.uri.fsPath === uri.fsPath && !vscode.window.activeNotebookEditor) {
227227
if (lineNumber !== undefined && (vscode.window.activeTextEditor.selection.isEmpty || !vscode.window.activeTextEditor.selection.contains(new vscode.Position(lineNumber - 1, 0)))) {
228-
range = new vscode.Range(new vscode.Position(lineNumber - 1, 0), new vscode.Position(lineNumber - 1, 1));
228+
range = new vscode.Range(new vscode.Position(lineNumber - 1, 0), new vscode.Position(lineNumber - 1, vscode.window.activeTextEditor.document.lineAt(lineNumber - 1).text.length));
229229
} else {
230230
range = vscode.window.activeTextEditor.selection;
231231
}
@@ -252,6 +252,7 @@ export interface PermalinkInfo {
252252
permalink: string | undefined;
253253
error: string | undefined;
254254
originalFile: vscode.Uri | undefined;
255+
range: vscode.Range | vscode.NotebookRange | undefined;
255256
}
256257

257258
export function getSimpleUpstream(repository: Repository) {
@@ -311,12 +312,12 @@ export async function createSinglePermalink(
311312
): Promise<PermalinkInfo> {
312313
const { uri, range } = getFileAndPosition(context, positionInfo);
313314
if (!uri) {
314-
return { permalink: undefined, error: vscode.l10n.t('No active text editor position to create permalink from.'), originalFile: undefined };
315+
return { permalink: undefined, error: vscode.l10n.t('No active text editor position to create permalink from.'), originalFile: undefined, range: undefined };
315316
}
316317

317318
const repository = getRepositoryForFile(gitAPI, uri);
318319
if (!repository) {
319-
return { permalink: undefined, error: vscode.l10n.t('The current file isn\'t part of repository.'), originalFile: uri };
320+
return { permalink: undefined, error: vscode.l10n.t('The current file isn\'t part of repository.'), originalFile: uri, range };
320321
}
321322

322323
let commitHash: string | undefined;
@@ -328,7 +329,7 @@ export async function createSinglePermalink(
328329
try {
329330
const log = await repository.log({ maxEntries: 1, path: uri.fsPath });
330331
if (log.length === 0) {
331-
return { permalink: undefined, error: vscode.l10n.t('No branch on a remote contains the most recent commit for the file.'), originalFile: uri };
332+
return { permalink: undefined, error: vscode.l10n.t('No branch on a remote contains the most recent commit for the file.'), originalFile: uri, range };
332333
}
333334
// Now that we know that the file existed at some point in the repo, use the head commit to construct the URI.
334335
if (repository.state.HEAD?.commit && (log[0].hash !== repository.state.HEAD?.commit)) {
@@ -345,7 +346,7 @@ export async function createSinglePermalink(
345346

346347
const rawUpstream = await getBestPossibleUpstream(repositoriesManager, repository, commitHash);
347348
if (!rawUpstream || !rawUpstream.fetchUrl) {
348-
return { permalink: undefined, error: vscode.l10n.t('The selection may not exist on any remote.'), originalFile: uri };
349+
return { permalink: undefined, error: vscode.l10n.t('The selection may not exist on any remote.'), originalFile: uri, range };
349350
}
350351
const upstream: Remote & { fetchUrl: string } = rawUpstream as Remote & { fetchUrl: string };
351352

@@ -357,7 +358,8 @@ export async function createSinglePermalink(
357358
permalink: (`${originOfFetchUrl}/${getOwnerAndRepo(repositoriesManager, repository, upstream)}/blob/${commitHash
358359
}${includeFile ? `${encodedPathSegment}${includeRange ? rangeString(range) : ''}` : ''}`),
359360
error: undefined,
360-
originalFile: uri
361+
originalFile: uri,
362+
range
361363
};
362364
Logger.debug(`permalink generated: ${result.permalink}`, PERMALINK_COMPONENT);
363365
return result;
@@ -431,11 +433,11 @@ export async function createSingleGitHubLink(
431433
): Promise<PermalinkInfo> {
432434
const { uri, range } = getFileAndPosition(context);
433435
if (!uri) {
434-
return { permalink: undefined, error: vscode.l10n.t('No active text editor position to create permalink from.'), originalFile: undefined };
436+
return { permalink: undefined, error: vscode.l10n.t('No active text editor position to create permalink from.'), originalFile: undefined, range: undefined };
435437
}
436438
const folderManager = managers.getManagerForFile(uri);
437439
if (!folderManager) {
438-
return { permalink: undefined, error: vscode.l10n.t('Current file does not belong to an open repository.'), originalFile: undefined };
440+
return { permalink: undefined, error: vscode.l10n.t('Current file does not belong to an open repository.'), originalFile: undefined, range: undefined };
439441
}
440442
let branchName = folderManager.repository.state.HEAD?.name;
441443
if (!branchName) {
@@ -446,7 +448,7 @@ export async function createSingleGitHubLink(
446448
}
447449
const upstream = getSimpleUpstream(folderManager.repository);
448450
if (!upstream?.fetchUrl) {
449-
return { permalink: undefined, error: vscode.l10n.t('Repository does not have any remotes.'), originalFile: undefined };
451+
return { permalink: undefined, error: vscode.l10n.t('Repository does not have any remotes.'), originalFile: undefined, range: undefined };
450452
}
451453
const pathSegment = uri.path.substring(folderManager.repository.rootUri.path.length);
452454
const originOfFetchUrl = getUpstreamOrigin(upstream).replace(/\/$/, '');
@@ -455,7 +457,8 @@ export async function createSingleGitHubLink(
455457
permalink: (`${originOfFetchUrl}/${new Protocol(upstream.fetchUrl).nameWithOwner}/blob/${encodedBranchAndFilePath
456458
}${includeRange ? rangeString(range) : ''}`),
457459
error: undefined,
458-
originalFile: uri
460+
originalFile: uri,
461+
range
459462
};
460463
}
461464

0 commit comments

Comments
 (0)