Skip to content
This repository was archived by the owner on Mar 10, 2022. It is now read-only.

Commit f631b92

Browse files
committed
fix: clean up how to handle file/directory URIs
1 parent 2a1cbd1 commit f631b92

12 files changed

Lines changed: 107 additions & 98 deletions

src/commands/SourcegraphQuickPick.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,9 @@ export class SourcegraphQuickPick {
6969

7070
private async resolveFileUri(selection: BrowseQuickPickItem): Promise<SourcegraphUri> {
7171
let uriString = selection.uri
72-
if (selection.unresolvedRepositoryName) {
73-
// Update the missing file path if it's missing
74-
if (!uriString || !SourcegraphUri.parse(uriString).path) {
75-
uriString = (await this.fs.defaultFileUri(selection.unresolvedRepositoryName)).uri
76-
}
77-
78-
// Update the missing revision if it's missing
79-
const uri = SourcegraphUri.parse(uriString)
80-
if (!uri.revision) {
81-
const metadata = await this.fs.repositoryMetadata(uri.repositoryName)
82-
const revision = metadata?.defaultBranch || 'HEAD'
83-
uriString = uri.withRevision(revision).uri
84-
}
72+
if (selection.unresolvedRepositoryName && (!uriString || !SourcegraphUri.parse(uriString).path)) {
73+
// Update the missing file path
74+
uriString = (await this.fs.defaultFileUri(selection.unresolvedRepositoryName)).uri
8575
}
8676
return SourcegraphUri.parse(uriString)
8777
}

src/commands/goToFileCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,5 @@ export async function goToFileCommand(fs: SourcegraphFileSystemProvider): Promis
3535
)
3636
const uri = await quick.showQuickPickAndGetUserInput()
3737
await recentlyOpenFilesSetting.update(uri.uri)
38-
await openSourcegraphUriCommand(uri)
38+
await openSourcegraphUriCommand(fs, uri)
3939
}

src/commands/goToRepositoryCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,5 @@ export async function goToRepositoryCommand(fs: SourcegraphFileSystemProvider):
5050
})
5151
const uri = await quick.showQuickPickAndGetUserInput()
5252
await recentlyOpenRepositoriesSetting.update({ label: uri.repositoryName, uri: uri.uri })
53-
await openSourcegraphUriCommand(uri)
53+
await openSourcegraphUriCommand(fs, uri)
5454
}

src/commands/openSourcegraphUriCommand.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import * as vscode from 'vscode'
2+
import { SourcegraphFileSystemProvider } from '../file-system/SourcegraphFileSystemProvider'
23
import { SourcegraphUri } from '../file-system/SourcegraphUri'
34

4-
export async function openSourcegraphUriCommand(uri: SourcegraphUri): Promise<void> {
5+
export async function openSourcegraphUriCommand(fs: SourcegraphFileSystemProvider, uri: SourcegraphUri): Promise<void> {
6+
if (!uri.revision) {
7+
const metadata = await fs.repositoryMetadata(uri.repositoryName)
8+
uri = uri.withRevision(metadata?.defaultBranch || 'HEAD')
9+
}
510
const textDocument = await vscode.workspace.openTextDocument(vscode.Uri.parse(uri.uri))
611
const selection = getSelection(uri, textDocument)
712
await vscode.window.showTextDocument(textDocument, {

src/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export function activate(context: vscode.ExtensionContext): void {
107107
context.subscriptions.push(
108108
vscode.commands.registerCommand('extension.openFile', async uri => {
109109
if (typeof uri === 'string') {
110-
await openSourcegraphUriCommand(SourcegraphUri.parse(uri))
110+
await openSourcegraphUriCommand(fs, SourcegraphUri.parse(uri))
111111
} else {
112112
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
113113
log.error(`extension.openFile(${uri}) argument is not a string`)

src/file-system/FileTree.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ export class FileTree {
2828
continue
2929
}
3030
if (file.startsWith(directory)) {
31-
const revision = this.uri.revision ? `@${this.uri.revision}` : ''
3231
const fileDepth = this.depth(file)
33-
const isDirect = isRoot ? fileDepth === 0 : fileDepth === depth + 1
34-
const path = isDirect ? file : file.slice(0, file.indexOf('/', directory.length))
35-
const kind = isDirect ? 'blob' : 'tree'
36-
const uri = `sourcegraph://${this.uri.host}/${this.uri.repositoryName}${revision}/-/${kind}/${path}`
37-
if (isDirect) {
32+
const isFile = isRoot ? fileDepth === 0 : fileDepth === depth + 1
33+
const path = isFile ? file : file.slice(0, file.indexOf('/', directory.length))
34+
const uri = SourcegraphUri.fromParts(this.uri.host, this.uri.repositoryName, {
35+
revision: this.uri.revision,
36+
path,
37+
isDirectory: !isFile,
38+
}).uri
39+
if (isFile) {
3840
directFiles.add(uri)
3941
} else {
4042
directDirectories.add(uri)

src/file-system/SourcegraphFileSystemProvider.ts

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -32,26 +32,34 @@ export class SourcegraphFileSystemProvider implements vscode.FileSystemProvider
3232
private fileNamesByRepository: Map<string, Promise<string[]>> = new Map()
3333
private metadata: Map<string, RepositoryMetadata> = new Map()
3434
private didDownloadFilenames = new vscode.EventEmitter<string>()
35-
private readonly cache = new Map<string, Blob>()
3635

3736
// ======================
3837
// FileSystemProvider API
3938
// ======================
4039
private didChangeFile = new vscode.EventEmitter<vscode.FileChangeEvent[]>() // Never used.
4140
public readonly onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]> = this.didChangeFile.event
42-
public async stat(uri: vscode.Uri): Promise<vscode.FileStat> {
43-
const blob = await this.fetchBlob(this.sourcegraphUri(uri))
41+
public async stat(vscodeUri: vscode.Uri): Promise<vscode.FileStat> {
42+
const uri = this.sourcegraphUri(vscodeUri)
43+
const files = await this.downloadedFiles(uri)
44+
const isFile = uri.path && files.includes(uri.path)
45+
const type = isFile ? vscode.FileType.File : vscode.FileType.Directory
46+
const now = Date.now()
4447
return {
45-
mtime: blob.time,
46-
ctime: blob.time,
47-
size: blob.byteSize,
48-
type: blob.type,
48+
// It seems to be OK to return hardcoded values for the timestamps
49+
// and the byte size. If it turns out the byte size needs to be
50+
// correct for some reason, then we can use
51+
// `this.fetchBlob(uri).byteSize` to get the value for files.
52+
mtime: now,
53+
ctime: now,
54+
size: 1337,
55+
type,
4956
}
5057
}
5158

5259
public async readFile(vscodeUri: vscode.Uri): Promise<Uint8Array> {
5360
const uri = this.sourcegraphUri(vscodeUri)
54-
return (await this.fetchBlob(uri)).content
61+
const blob = await this.fetchBlob(uri)
62+
return blob.content
5563
}
5664

5765
public async readDirectory(vscodeUri: vscode.Uri): Promise<[string, vscode.FileType][]> {
@@ -60,13 +68,10 @@ export class SourcegraphFileSystemProvider implements vscode.FileSystemProvider
6068
return []
6169
}
6270
const tree = await this.getFileTree(uri)
63-
if (!tree) {
64-
return []
65-
}
6671
const children = tree.directChildren(uri.path || '')
6772
return children.map(childUri => {
6873
const child = SourcegraphUri.parse(childUri)
69-
const type = child.isDirectory ? vscode.FileType.Directory : vscode.FileType.File
74+
const type = child.isDirectory() ? vscode.FileType.Directory : vscode.FileType.File
7075
return [child.basename(), type]
7176
})
7277
}
@@ -113,7 +118,7 @@ export class SourcegraphFileSystemProvider implements vscode.FileSystemProvider
113118
fileNames,
114119
})
115120
} catch {
116-
log.appendLine(`ERROR: failed to download repo files ${repositoryUri}`)
121+
log.error(`failed to download files for repository '${repositoryUri}'`)
117122
}
118123
}
119124
return promises
@@ -148,12 +153,12 @@ export class SourcegraphFileSystemProvider implements vscode.FileSystemProvider
148153
const token = emptyCancelationToken()
149154
const defaultBranch = (await this.repositoryMetadata(repositoryName, token))?.defaultBranch
150155
if (!defaultBranch) {
151-
const message = `ERROR defaultBranch no repository '${repositoryName}'`
152-
log.appendLine(message)
156+
const message = `repository '${repositoryName}' has no default branch`
157+
log.error(message)
153158
throw new Error(message)
154159
}
155160
const uri = SourcegraphUri.fromParts(endpointHostnameSetting(), repositoryName, { revision: defaultBranch })
156-
const files = await this.downloadFiles(uri, defaultBranch)
161+
const files = await this.downloadFiles(uri)
157162
const readmes = files.filter(name => name.match(/readme/i))
158163
const candidates = readmes.length > 0 ? readmes : files
159164
let readme: string | undefined
@@ -176,21 +181,18 @@ export class SourcegraphFileSystemProvider implements vscode.FileSystemProvider
176181
}
177182

178183
public async fetchBlob(uri: SourcegraphUri): Promise<Blob> {
179-
const result = this.cache.get(uri.uri)
180-
if (result) {
181-
return result
182-
}
183184
await this.repositoryMetadata(uri.repositoryName)
184185
const token = emptyCancelationToken()
185-
const revision = uri.revision || (await this.repositoryMetadata(uri.repositoryName, token))?.defaultBranch
186-
if (!revision) {
187-
throw new Error(`no uri.revision from uri ${uri.uri}`)
186+
if (!uri.revision) {
187+
const error = `missing revision for URI '${uri.uri}'`
188+
log.error(error)
189+
throw new Error(error)
188190
}
189191
const path = uri.path || ''
190192
const content = await contents(
191193
{
192194
repository: uri.repositoryName,
193-
revision,
195+
revision: uri.revision,
194196
path,
195197
},
196198
token
@@ -200,18 +202,17 @@ export class SourcegraphFileSystemProvider implements vscode.FileSystemProvider
200202
const toCacheResult: Blob = {
201203
uri: uri.uri,
202204
repositoryName: uri.repositoryName,
203-
revision,
205+
revision: uri.revision,
204206
content: content.content,
205207
isBinaryFile: content.isBinary,
206208
byteSize: content.byteSize,
207209
path,
208210
time: new Date().getMilliseconds(),
209211
type: vscode.FileType.File,
210212
}
211-
this.cache.set(uri.uri, toCacheResult)
212213

213214
// Start downloading the repository files in the background.
214-
this.downloadFiles(uri, revision).then(
215+
this.downloadFiles(uri).then(
215216
() => {},
216217
() => {}
217218
)
@@ -235,12 +236,19 @@ export class SourcegraphFileSystemProvider implements vscode.FileSystemProvider
235236
return metadata
236237
}
237238

238-
public downloadFiles(uri: SourcegraphUri, revision: string): Promise<string[]> {
239+
public async downloadedFiles(uri: SourcegraphUri): Promise<string[]> {
240+
return this.fileNamesByRepository.get(uri.repositoryUri()) || []
241+
}
242+
243+
public downloadFiles(uri: SourcegraphUri): Promise<string[]> {
239244
const key = uri.repositoryUri()
240245
const fileNamesByRepository = this.fileNamesByRepository
241246
let downloadingFiles = this.fileNamesByRepository.get(key)
242247
if (!downloadingFiles) {
243-
downloadingFiles = filesQuery({ repository: uri.repositoryName, revision }, emptyCancelationToken())
248+
downloadingFiles = filesQuery(
249+
{ repository: uri.repositoryName, revision: uri.revision },
250+
emptyCancelationToken()
251+
)
244252
downloadingFiles.then(
245253
() => this.didDownloadFilenames.fire(key),
246254
() => fileNamesByRepository.delete(key)
@@ -254,22 +262,8 @@ export class SourcegraphFileSystemProvider implements vscode.FileSystemProvider
254262
return SourcegraphUri.parse(uri.toString(true))
255263
}
256264

257-
public async getFileTree(uri: SourcegraphUri): Promise<FileTree | undefined> {
258-
if (!uri.revision) {
259-
uri = uri.withRevision(this.metadata.get(uri.repositoryName)?.defaultBranch)
260-
}
261-
const key = uri.repositoryUri()
262-
const downloading = this.fileNamesByRepository.get(key)
263-
if (!downloading) {
264-
const keys = JSON.stringify([...this.fileNamesByRepository.keys()])
265-
log.error(`getFileTree(${uri.uri}) - empty downloading key=${key} keys=${keys}`)
266-
return Promise.resolve(undefined)
267-
}
268-
const files = await downloading
269-
if (!files) {
270-
log.error('getFileTree - empty files')
271-
return Promise.resolve(undefined)
272-
}
265+
public async getFileTree(uri: SourcegraphUri): Promise<FileTree> {
266+
const files = await this.downloadedFiles(uri)
273267
return new FileTree(uri, files)
274268
}
275269
}

src/file-system/SourcegraphTreeDataProvider.ts

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ export class SourcegraphTreeDataProvider implements vscode.TreeDataProvider<stri
5252
}
5353
const uri = SourcegraphUri.parse(uriString)
5454
const tree = await this.fs.getFileTree(uri)
55-
const result = tree?.directChildren(uri.path || '')
56-
return result
55+
return tree.directChildren(uri.path || '')
5756
} catch (error) {
5857
log.error(`getChildren(${uriString || ''})`, error)
5958
return Promise.resolve(undefined)
@@ -71,19 +70,17 @@ export class SourcegraphTreeDataProvider implements vscode.TreeDataProvider<stri
7170
this.activeUri = vscodeUri
7271
if (vscodeUri && vscodeUri.scheme === 'sourcegraph' && this.treeView && this.isTreeViewVisible) {
7372
const uri = this.fs.sourcegraphUri(vscodeUri)
74-
await this.fs.downloadFiles(uri, uri.revision || '')
73+
await this.fs.downloadFiles(uri)
7574
await this.didFocusString(uri, true, this.didFocusToken.token)
7675
}
7776
}
7877

7978
public async getTreeItem(uriString: string): Promise<vscode.TreeItem> {
80-
const uri = SourcegraphUri.parse(uriString)
8179
try {
80+
const uri = SourcegraphUri.parse(uriString)
8281
const label = await this.treeItemLabel(uri)
83-
const isFile = uri.uri.includes('/-/blob/')
84-
const isDirectory = !isFile
85-
const collapsibleState = await this.getCollapsibleState(uri, isDirectory)
86-
const command = isFile
82+
const collapsibleState = await this.getCollapsibleState(uri)
83+
const command = uri.isFile()
8784
? {
8885
command: 'extension.openFile',
8986
title: 'Open file',
@@ -99,7 +96,7 @@ export class SourcegraphTreeDataProvider implements vscode.TreeDataProvider<stri
9996
resourceUri: vscode.Uri.parse(uri.uri),
10097
}
10198
} catch (error) {
102-
log.error(`getTreeItem(${uri.uri})`, error)
99+
log.error(`getTreeItem(${uriString})`, error)
103100
return Promise.resolve({})
104101
}
105102
}
@@ -141,21 +138,21 @@ export class SourcegraphTreeDataProvider implements vscode.TreeDataProvider<stri
141138
return `${uri.repositoryName}@${revision || ''}`
142139
}
143140

144-
private async getCollapsibleState(
145-
uri: SourcegraphUri,
146-
isDirectory: boolean
147-
): Promise<vscode.TreeItemCollapsibleState> {
148-
const parent = uri.parentUri()
149-
if (isDirectory && parent) {
150-
const parentUri = SourcegraphUri.parse(parent)
151-
if (parentUri.path) {
152-
const tree = await this.fs.getFileTree(parentUri)
153-
const directChildren = tree?.directChildren(parentUri.path)
141+
private async getCollapsibleState(uri: SourcegraphUri): Promise<vscode.TreeItemCollapsibleState> {
142+
if (uri.isFile()) {
143+
return vscode.TreeItemCollapsibleState.None
144+
}
145+
const parentUri = uri.parentUri()
146+
if (parentUri) {
147+
const parent = SourcegraphUri.parse(parentUri)
148+
if (parent.path) {
149+
const tree = await this.fs.getFileTree(parent)
150+
const directChildren = tree.directChildren(parent.path)
154151
if (directChildren && directChildren.length === 1) {
155152
return vscode.TreeItemCollapsibleState.Expanded
156153
}
157154
}
158155
}
159-
return isDirectory ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None
156+
return vscode.TreeItemCollapsibleState.Collapsed
160157
}
161158
}

src/file-system/SourcegraphUri.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ export class SourcegraphUri {
99
public readonly uri: string,
1010
public readonly host: string,
1111
public readonly repositoryName: string,
12-
public readonly revision: string | undefined,
12+
public readonly revision: string,
1313
public readonly path: string | undefined,
14-
public readonly position: Position | undefined,
15-
public readonly isDirectory: boolean
14+
public readonly position: Position | undefined
1615
) {}
1716

1817
public withRevision(newRevision: string | undefined): SourcegraphUri {
@@ -50,8 +49,21 @@ export class SourcegraphUri {
5049
return undefined
5150
}
5251

52+
public withIsDirectory(isDirectory: boolean): SourcegraphUri {
53+
return SourcegraphUri.fromParts(this.host, this.repositoryName, {
54+
isDirectory,
55+
path: this.path,
56+
revision: this.revision,
57+
position: this.position,
58+
})
59+
}
60+
61+
public isDirectory(): boolean {
62+
return !this.isFile()
63+
}
64+
5365
public isFile(): boolean {
54-
return typeof this.path === 'string' && this.uri.includes('/-/blob/')
66+
return this.uri.includes('/-/blob/')
5567
}
5668

5769
public static fromParts(
@@ -65,15 +77,15 @@ export class SourcegraphUri {
6577
}
6678
): SourcegraphUri {
6779
const revisionPart = optional?.revision ? `@${optional.revision}` : ''
68-
const pathPart = optional?.path ? `/-/blob/${optional?.path}` : ''
80+
const directoryPart = optional?.isDirectory ? 'tree' : 'blob'
81+
const pathPart = optional?.path ? `/-/${directoryPart}/${optional?.path}` : ''
6982
return new SourcegraphUri(
7083
`sourcegraph://${host}/${repositoryName}${revisionPart}${pathPart}`,
7184
host,
7285
repositoryName,
73-
optional?.revision,
86+
optional?.revision || '',
7487
optional?.path,
75-
optional?.position,
76-
optional?.isDirectory || false
88+
optional?.position
7789
)
7890
}
7991
public repositoryUri(): string {

src/queries/graphqlQuery.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ export function graphqlQueryWithAccessToken<A, B>(
1515
token: CancellationToken,
1616
accessToken: string
1717
): Promise<B | undefined> {
18-
log.appendLine(`accessToken ${accessToken}`)
1918
return new Promise<B | undefined>((resolve, reject) => {
2019
const data = JSON.stringify({
2120
query,

0 commit comments

Comments
 (0)