Skip to content

Commit 57cbb22

Browse files
authored
support calling Githubinator without open files (#34)
This change allows Githubinator to function when there is no active text editor open. We fall back to the workspace information to find `.git/`.
1 parent c24a62a commit 57cbb22

File tree

6 files changed

+101
-39
lines changed

6 files changed

+101
-39
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## 0.3.0 - 2020-08-15
11+
12+
### Added
13+
14+
- Support calling Githubinator without an open file.
15+
16+
### Changed
17+
18+
- Don't copy URL when using "Open PR".
19+
1020
## 0.2.3 - 2019-04-04
1121

1222
### Fixed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ With `vsce` installed from NPM (`yarn global add vsce`), clone [this repo](https
4040
| `Githubinator: Blame Permalink` ||| blame | current sha |
4141
| `Githubinator: Repository` ||| open repo | N/A |
4242
| `Githubinator: History` ||| open history | N/A |
43-
| `Githubinator: Open PR` | || open PR | N/A |
43+
| `Githubinator: Open PR` | || open PR | N/A |
4444
| `Githubinator: Compare` ||| compare branch | N/A |
4545

4646
## Requirements
@@ -65,6 +65,11 @@ With `vsce` installed from NPM (`yarn global add vsce`), clone [this repo](https
6565

6666
## Release Notes
6767

68+
## 0.3.0
69+
70+
- Support calling Githubinator without an open file.
71+
- Don't copy URL when using "Open PR".
72+
6873
## 0.2.3
6974

7075
- Fix ref lookup to ensure most recent ref is always used.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "githubinator",
33
"displayName": "Githubinator",
44
"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.",
5-
"version": "0.2.3",
5+
"version": "0.3.0",
66
"publisher": "chdsbd",
77
"license": "SEE LICENSE IN LICENSE",
88
"icon": "images/logo256.png",

src/extension.ts

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const COMMANDS: [string, IGithubinator][] = [
5454
],
5555
[
5656
"extension.githubinatorOpenPR", //
57-
{ copyToClipboard: true, openUrl: true, openPR: true },
57+
{ copyToClipboard: false, openUrl: true, openPR: true },
5858
],
5959
[
6060
"extension.githubinatorCompare", //
@@ -96,6 +96,25 @@ function err(message: string) {
9696
vscode.window.showErrorMessage(message)
9797
}
9898

99+
function getEditorInfo(): { uri: vscode.Uri | null; fileName: string | null } {
100+
const workspaceUri =
101+
vscode.workspace.workspaceFolders != null &&
102+
vscode.workspace.workspaceFolders.length > 0
103+
? vscode.workspace.workspaceFolders[0].uri
104+
: null
105+
const editor = vscode.window.activeTextEditor
106+
// if we cannot find editor information fall back to the workspace path.
107+
if (!editor) {
108+
return { uri: workspaceUri, fileName: null }
109+
}
110+
111+
const { fileName, isUntitled, uri } = editor.document
112+
if (isUntitled) {
113+
return { uri: workspaceUri, fileName: null }
114+
}
115+
return { uri, fileName }
116+
}
117+
99118
interface IGithubinator {
100119
openUrl?: boolean
101120
copyToClipboard?: boolean
@@ -119,20 +138,12 @@ async function githubinator({
119138
compare,
120139
}: IGithubinator) {
121140
console.log("githubinator.call")
122-
const editor = vscode.window.activeTextEditor
123-
if (!editor) {
124-
return err("There is no active file to look up.")
125-
}
126-
127-
const { fileName, isUntitled, uri } = editor.document
128-
if (isUntitled) {
129-
return err("Cannot lookup unsaved file.")
130-
}
131-
if (uri.scheme !== "file") {
132-
return err("Only native files can be used.")
141+
const editorConfig = getEditorInfo()
142+
if (!editorConfig.uri) {
143+
return err("could not find file")
133144
}
134145

135-
const gitDir = git.dir(uri.fsPath)
146+
const gitDir = git.dir(editorConfig.uri.fsPath)
136147
if (gitDir == null) {
137148
return err("Could not find .git directory.")
138149
}
@@ -158,22 +169,28 @@ async function githubinator({
158169
.getConfiguration("githubinator")
159170
.get<IGithubinatorConfig["providers"]>("providers", {})
160171

172+
const editor = vscode.window.activeTextEditor
161173
let urls: IUrlInfo | null = null
162174
for (const provider of providers) {
163175
const parsedUrl = await new provider(
164176
providersConfig,
165177
globalDefaultRemote,
166178
remote => git.origin(gitDir, remote),
167179
).getUrls({
168-
selection: [editor.selection.start.line, editor.selection.end.line],
180+
selection: [
181+
editor ? editor.selection.start.line : null,
182+
editor ? editor.selection.end.line : null,
183+
],
169184
// priority: permalink > branch > branch from HEAD
170185
// If branchName could not be found (null) then we generate a permalink
171186
// using the SHA.
172187
head:
173188
!!permalink || branchName == null
174189
? createSha(head)
175190
: createBranch(branchName),
176-
relativeFilePath: getRelativeFilePath(gitDir, fileName),
191+
relativeFilePath: editorConfig.fileName
192+
? getRelativeFilePath(gitDir, editorConfig.fileName)
193+
: null,
177194
})
178195
if (parsedUrl != null) {
179196
console.log("Found provider", provider.name)
@@ -187,7 +204,7 @@ async function githubinator({
187204
return err("Could not find provider for repo.")
188205
}
189206

190-
const url = compare
207+
let url = compare
191208
? urls.compareUrl
192209
: openPR
193210
? urls.prUrl
@@ -199,6 +216,16 @@ async function githubinator({
199216
? urls.blameUrl
200217
: urls.blobUrl
201218

219+
// file-specific urls will be null when we are using the workspace path. Use
220+
// the compare url or repo url if available instead.
221+
const fallbackUrl = [urls.compareUrl, urls.repoUrl].find(x => x != null)
222+
if (!url && fallbackUrl) {
223+
url = fallbackUrl
224+
}
225+
226+
if (url == null) {
227+
return err("could not find url")
228+
}
202229
if (openUrl) {
203230
vscode.env.openExternal(vscode.Uri.parse(url))
204231
}

src/git.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ function walkUpDirectories(
101101
file_path: string,
102102
file_or_folder: string,
103103
): string | null {
104-
let directory = path.dirname(file_path)
104+
let directory = file_path
105105
while (true) {
106106
const newPath = path.resolve(directory, file_or_folder)
107107
if (fs.existsSync(newPath)) {

src/providers.ts

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@ import { IProviderConfig } from "./extension"
44
import { cleanHostname } from "./utils"
55

66
interface IBaseGetUrls {
7-
readonly selection: [number, number]
7+
readonly selection: [number | null, number | null]
88
readonly head: Head
9-
readonly relativeFilePath: string
9+
readonly relativeFilePath: string | null
1010
}
1111

1212
export interface IUrlInfo {
13-
readonly blobUrl: string
14-
readonly repoUrl: string
15-
readonly blameUrl: string
16-
readonly historyUrl: string
17-
readonly prUrl: string
18-
readonly compareUrl: string
13+
readonly blobUrl: string | null
14+
readonly repoUrl: string | null
15+
readonly blameUrl: string | null
16+
readonly historyUrl: string | null
17+
readonly prUrl: string | null
18+
readonly compareUrl: string | null
1919
}
2020

2121
interface IOrgInfo {
@@ -120,12 +120,16 @@ export class Github extends BaseProvider {
120120
const rootUrl = `https://${repoInfo.hostname}/`
121121
const [start, end] = selection
122122
// Github uses 1-based indexing
123-
const lines = `L${start + 1}-L${end + 1}`
123+
const lines =
124+
start != null && end != null ? `L${start + 1}-L${end + 1}` : null
124125
const repoUrl = new url.URL(
125126
path.join(repoInfo.org, repoInfo.repo),
126127
rootUrl,
127128
).toString()
128129
const createUrl = (mode: string, hash = true) => {
130+
if (relativeFilePath == null) {
131+
return null
132+
}
129133
const u = new url.URL(
130134
path.join(
131135
repoInfo.org,
@@ -136,7 +140,7 @@ export class Github extends BaseProvider {
136140
),
137141
rootUrl,
138142
)
139-
if (hash) {
143+
if (hash && lines) {
140144
u.hash = lines
141145
}
142146
return u.toString()
@@ -182,12 +186,16 @@ export class Gitlab extends BaseProvider {
182186
const rootUrl = `https://${repoInfo.hostname}/`
183187
const [start, end] = selection
184188
// The format is L34-56 (this is one character off from Github)
185-
const lines = `L${start + 1}-${end + 1}`
189+
const lines =
190+
start != null && end != null ? `L${start + 1}-${end + 1}` : null
186191
const repoUrl = new url.URL(
187192
path.join(repoInfo.org, repoInfo.repo),
188193
rootUrl,
189194
).toString()
190195
const createUrl = (mode: string, hash = true) => {
196+
if (relativeFilePath == null) {
197+
return null
198+
}
191199
const u = new url.URL(
192200
path.join(
193201
repoInfo.org,
@@ -198,7 +206,7 @@ export class Gitlab extends BaseProvider {
198206
),
199207
rootUrl,
200208
)
201-
if (hash) {
209+
if (hash && lines) {
202210
u.hash = lines
203211
}
204212
return u.toString()
@@ -246,12 +254,16 @@ export class Bitbucket extends BaseProvider {
246254
// https://bitbucket.org/recipeyak/recipeyak/src/master/app/main.py#lines-12:15
247255
const rootUrl = `https://${repoInfo.hostname}/`
248256
const [start, end] = selection
249-
const lines = `lines-${start + 1}:${end + 1}`
257+
const lines =
258+
start != null && end != null ? `lines-${start + 1}:${end + 1}` : null
250259
const repoUrl = new url.URL(
251260
path.join(repoInfo.org, repoInfo.repo),
252261
rootUrl,
253262
).toString()
254263
const createUrl = (mode: string, hash = true) => {
264+
if (relativeFilePath == null) {
265+
return null
266+
}
255267
const u = new url.URL(
256268
path.join(
257269
repoInfo.org,
@@ -262,7 +274,7 @@ export class Bitbucket extends BaseProvider {
262274
),
263275
rootUrl,
264276
)
265-
if (hash) {
277+
if (hash && lines) {
266278
u.hash = lines
267279
}
268280
return u.toString()
@@ -319,24 +331,32 @@ export class VisualStudio extends BaseProvider {
319331
// https://bitbucket.org/recipeyak/recipeyak/src/master/app/main.py#lines-12:15
320332
const rootUrl = `https://${repoInfo.hostname}/`
321333
const [start, end] = selection
322-
const lines = `&line=${start + 1}&lineEnd=${end + 1}`
334+
const lines =
335+
start != null && end != null
336+
? `&line=${start + 1}&lineEnd=${end + 1}`
337+
: null
323338
const repoUrl = new url.URL(
324339
path.join(repoInfo.org, "_git", repoInfo.repo),
325340
rootUrl,
326341
)
327342
let filePath = relativeFilePath
328-
if (!filePath.startsWith("/")) {
343+
if (filePath != null && !filePath.startsWith("/")) {
329344
filePath = "/" + filePath
330345
}
331346
const version =
332347
head.kind === "branch" ? `GB${head.value}` : `GC${head.value}`
333-
const baseSearch = `path=${encodeURIComponent(filePath)}&version=${version}`
348+
const baseSearch =
349+
filePath != null
350+
? `path=${encodeURIComponent(filePath)}&version=${version}`
351+
: null
334352
const blobUrl = new url.URL(repoUrl.toString())
335353
const blameUrl = new url.URL(repoUrl.toString())
336354
const historyUrl = new url.URL(repoUrl.toString())
337-
blobUrl.search = baseSearch + lines
338-
blameUrl.search = baseSearch + lines + "&_a=annotate"
339-
historyUrl.search = baseSearch + "&_a=history"
355+
if (baseSearch != null) {
356+
blobUrl.search = baseSearch + lines
357+
blameUrl.search = baseSearch + lines + "&_a=annotate"
358+
historyUrl.search = baseSearch + "&_a=history"
359+
}
340360
const compareUrl = new url.URL(
341361
path.join(repoInfo.org, "_git", repoInfo.repo, "branches"),
342362
rootUrl,

0 commit comments

Comments
 (0)