diff --git a/CHANGELOG.md b/CHANGELOG.md index 326185d..8e538ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## 3.1.0 - 2024-06-02 + +### Added + +- Selections in VSCode will now highlight the same span of characters on GitHub. (#63) + ## 3.0.0 - 2023-04-19 ### Changed diff --git a/package.json b/package.json index f05462e..105cf9b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "githubinator", "displayName": "Githubinator", "description": "Quickly open files on Github and other providers. View blame information, copy permalinks and more. See the \"commands\" section of the README for more details.", - "version": "3.0.0", + "version": "3.1.0", "publisher": "chdsbd", "license": "SEE LICENSE IN LICENSE", "icon": "images/logo256.png", diff --git a/src/extension.ts b/src/extension.ts index 26d9d4e..87e28e4 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -196,15 +196,16 @@ async function githubinator({ const editor = vscode.window.activeTextEditor let urls: IUrlInfo | null = null for (const provider of providers) { + const selection = editor?.selection + if (selection == null) { + return err("Could not find editor") + } const parsedUrl = await new provider( providersConfig, globalDefaultRemote, remote => git.origin(gitDir, remote), ).getUrls({ - selection: [ - editor ? editor.selection.start.line : null, - editor ? editor.selection.end.line : null, - ], + selection, // priority: permalink > branch > branch from HEAD // If branchName could not be found (null) then we generate a permalink // using the SHA. diff --git a/src/providers.ts b/src/providers.ts index 415508a..1fd1342 100644 --- a/src/providers.ts +++ b/src/providers.ts @@ -5,8 +5,13 @@ import { cleanHostname } from "./utils" import { flatten } from "lodash" import gitUrlParse from "git-url-parse" +interface ISelection { + start: { line: number; character: number } + end: { line: number; character: number } +} + interface IBaseGetUrls { - readonly selection: [number | null, number | null] + readonly selection: ISelection readonly head: Head readonly relativeFilePath: string | null } @@ -116,10 +121,10 @@ export class Github extends BaseProvider { return null } const rootUrl = `https://${repoInfo.hostname}/` - const [start, end] = selection + const { start, end } = selection // Github uses 1-based indexing - const lines = - start != null && end != null ? `L${start + 1}-L${end + 1}` : null + const lines = `L${start.line + 1}C${start.character + 1}-L${end.line + + 1}C${end.character + 1}` const repoUrl = new url.URL( path.join(repoInfo.org, repoInfo.repo), rootUrl, @@ -138,7 +143,7 @@ export class Github extends BaseProvider { ), rootUrl, ) - if (hash && lines) { + if (hash) { u.hash = lines } return u.toString() @@ -178,10 +183,9 @@ export class Gitlab extends BaseProvider { return null } const rootUrl = `https://${repoInfo.hostname}/` - const [start, end] = selection + const { start, end } = selection // The format is L34-56 (this is one character off from Github) - const lines = - start != null && end != null ? `L${start + 1}-${end + 1}` : null + const lines = `L${start.line + 1}-${end.line + 1}` const repoUrl = new url.URL( pathJoin(repoInfo.org, repoInfo.repo), rootUrl, @@ -243,9 +247,8 @@ export class Bitbucket extends BaseProvider { } // https://bitbucket.org/recipeyak/recipeyak/src/master/app/main.py#lines-12:15 const rootUrl = `https://${repoInfo.hostname}/` - const [start, end] = selection - const lines = - start != null && end != null ? `lines-${start + 1}:${end + 1}` : null + const { start, end } = selection + const lines = `lines-${start.line + 1}:${end.line + 1}` const repoUrl = new url.URL( pathJoin(repoInfo.org, repoInfo.repo), rootUrl, diff --git a/src/test/suite/providers.test.ts b/src/test/suite/providers.test.ts index 8ced11c..df0bbb2 100644 --- a/src/test/suite/providers.test.ts +++ b/src/test/suite/providers.test.ts @@ -45,15 +45,18 @@ suite("Github", async () => { } const gh = new Github({}, "origin", findRemote) const result = await gh.getUrls({ - selection: [17, 24], + selection: { + start: { line: 17, character: 0 }, + end: { line: 24, character: 0 }, + }, head: createSha("db99a912f5c4bffe11d91e163cd78ed96589611b"), relativeFilePath: "frontend/src/components/App.tsx", }) const expected = { blobUrl: - "https://github.com/recipeyak/recipeyak/blob/db99a912f5c4bffe11d91e163cd78ed96589611b/frontend/src/components/App.tsx#L18-L25", + "https://github.com/recipeyak/recipeyak/blob/db99a912f5c4bffe11d91e163cd78ed96589611b/frontend/src/components/App.tsx#L18C1-L25C1", blameUrl: - "https://github.com/recipeyak/recipeyak/blame/db99a912f5c4bffe11d91e163cd78ed96589611b/frontend/src/components/App.tsx#L18-L25", + "https://github.com/recipeyak/recipeyak/blame/db99a912f5c4bffe11d91e163cd78ed96589611b/frontend/src/components/App.tsx#L18C1-L25C1", compareUrl: "https://github.com/recipeyak/recipeyak/compare/db99a912f5c4bffe11d91e163cd78ed96589611b", historyUrl: @@ -83,15 +86,18 @@ suite("Github", async () => { findRemote, ) const result = await gh.getUrls({ - selection: [17, 24], + selection: { + start: { line: 17, character: 0 }, + end: { line: 24, character: 0 }, + }, head: createBranch("master"), relativeFilePath: "frontend/src/components/App.tsx", }) const expected = { blobUrl: - "https://github.mycompany.com/recipeyak/recipeyak/blob/master/frontend/src/components/App.tsx#L18-L25", + "https://github.mycompany.com/recipeyak/recipeyak/blob/master/frontend/src/components/App.tsx#L18C1-L25C1", blameUrl: - "https://github.mycompany.com/recipeyak/recipeyak/blame/master/frontend/src/components/App.tsx#L18-L25", + "https://github.mycompany.com/recipeyak/recipeyak/blame/master/frontend/src/components/App.tsx#L18C1-L25C1", compareUrl: "https://github.mycompany.com/recipeyak/recipeyak/compare/master", historyUrl: @@ -113,7 +119,10 @@ suite("Gitlab", async () => { async _ => "git@gitlab.com:recipeyak/recipeyak.git", ) const result = await gl.getUrls({ - selection: [17, 24], + selection: { + start: { line: 17, character: 0 }, + end: { line: 24, character: 0 }, + }, head: createSha("db99a912f5c4bffe11d91e163cd78ed96589611b"), relativeFilePath: "frontend/src/components/App.tsx", }) @@ -141,7 +150,10 @@ suite("Gitlab", async () => { async _ => "https://gitlab.mycompany.com/recipeyak/recipeyak.git", ) const result = await gl.getUrls({ - selection: [17, 24], + selection: { + start: { line: 17, character: 0 }, + end: { line: 24, character: 0 }, + }, head: createBranch("master"), relativeFilePath: "frontend/src/components/App.tsx", }) @@ -171,7 +183,10 @@ suite("Bitbucket", async () => { async _ => "git@bitbucket.org:recipeyak/recipeyak.git", ) const result = await bb.getUrls({ - selection: [17, 24], + selection: { + start: { line: 17, character: 0 }, + end: { line: 24, character: 0 }, + }, head: createSha("db99a912f5c4bffe11d91e163cd78ed96589611b"), relativeFilePath: "frontend/src/components/App.tsx", }) @@ -204,7 +219,10 @@ suite("Bitbucket", async () => { getOrigin, ) const result = await bb.getUrls({ - selection: [17, 24], + selection: { + start: { line: 17, character: 0 }, + end: { line: 24, character: 0 }, + }, head: createBranch("master"), relativeFilePath: "frontend/src/components/App.tsx", })