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

Commit 02f89b0

Browse files
committed
feat: add support to explore diffs
This commit adds a new "Diffs" tree view that makes it easy to compare git revisions. For example, you can easily compare two git tags, see what files changed and browse the individual commits.
1 parent 7c69503 commit 02f89b0

13 files changed

+870
-96
lines changed

package.json

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@
7676
{
7777
"view": "sourcegraph.files",
7878
"contents": "No open files."
79+
},
80+
{
81+
"view": "sourcegraph.diffs",
82+
"contents": "No open diffs."
7983
}
8084
],
8185
"commands": [
@@ -94,6 +98,13 @@
9498
"category": "Sourcegraph",
9599
"title": "Go to Repository"
96100
},
101+
{
102+
"command": "extension.updateCompareRange",
103+
"category": "Sourcegraph",
104+
"title": "Update Compare Range",
105+
"enablement": "a == b",
106+
"//": "always disabled because we only invoke it programmatically with custom commands"
107+
},
97108
{
98109
"command": "extension.switchGitRevision",
99110
"category": "Sourcegraph",
@@ -106,6 +117,15 @@
106117
"title": "Open File in Web Browser",
107118
"enablement": "resourceScheme == sourcegraph || focusedView == 'sourcegraph.files'"
108119
},
120+
{ "command": "extension.green1", "category": "Sourcegraph", "title": "green1", "icon": "icons/green-square.svg" , "enablement": "a == b"},
121+
{ "command": "extension.green2", "category": "Sourcegraph", "title": "green2", "icon": "icons/green-square.svg" , "enablement": "a == b"},
122+
{ "command": "extension.green3", "category": "Sourcegraph", "title": "green3", "icon": "icons/green-square.svg" , "enablement": "a == b"},
123+
{ "command": "extension.orange1", "category": "Sourcegraph", "title": "orange1", "icon": "icons/orange-square.svg", "enablement": "a == b"},
124+
{ "command": "extension.orange2", "category": "Sourcegraph", "title": "orange2", "icon": "icons/orange-square.svg", "enablement": "a == b"},
125+
{ "command": "extension.orange3", "category": "Sourcegraph", "title": "orange3", "icon": "icons/orange-square.svg", "enablement": "a == b"},
126+
{ "command": "extension.red1", "category": "Sourcegraph", "title": "red1", "icon": "icons/red-square.svg" , "enablement": "a == b"},
127+
{ "command": "extension.red2", "category": "Sourcegraph", "title": "red2", "icon": "icons/red-square.svg" , "enablement": "a == b"},
128+
{ "command": "extension.red3", "category": "Sourcegraph", "title": "red3", "icon": "icons/red-square.svg" , "enablement": "a == b"},
109129
{
110130
"command": "extension.newNotebook",
111131
"category": "Sourcegraph",
@@ -158,6 +178,10 @@
158178
{
159179
"id": "sourcegraph.files",
160180
"name": "Files"
181+
},
182+
{
183+
"id": "sourcegraph.diffs",
184+
"name": "Diffs"
161185
}
162186
]
163187
},
@@ -172,7 +196,16 @@
172196
"view/item/context": [
173197
{"command": "extension.goToFileInFolder", "when": "view == sourcegraph.files && viewItem == directory"},
174198
{"command": "extension.switchGitRevision", "when": "view == sourcegraph.files && viewItem == file"},
175-
{"command": "extension.openFileInBrowser", "when": "view == sourcegraph.files && viewItem == file"}
199+
{"command": "extension.openFileInBrowser", "when": "view == sourcegraph.files && viewItem == file"},
200+
{"command": "extension.green1", "group": "inline", "when": "view == sourcegraph.diffs && viewItem =~ /green1/"},
201+
{"command": "extension.green2", "group": "inline", "when": "view == sourcegraph.diffs && viewItem =~ /green2/"},
202+
{"command": "extension.green3", "group": "inline", "when": "view == sourcegraph.diffs && viewItem =~ /green3/"},
203+
{"command": "extension.orange1", "group": "inline", "when": "view == sourcegraph.diffs && viewItem =~ /orange1/"},
204+
{"command": "extension.orange2", "group": "inline", "when": "view == sourcegraph.diffs && viewItem =~ /orange2/"},
205+
{"command": "extension.orange3", "group": "inline", "when": "view == sourcegraph.diffs && viewItem =~ /orange3/"},
206+
{"command": "extension.red1", "group": "inline", "when": "view == sourcegraph.diffs && viewItem =~ /red1/"},
207+
{"command": "extension.red2", "group": "inline", "when": "view == sourcegraph.diffs && viewItem =~ /red2/"},
208+
{"command": "extension.red3", "group": "inline", "when": "view == sourcegraph.diffs && viewItem =~ /red3/"}
176209
]
177210
},
178211
"keybindings": [

src/commands/goToRepositoryCommand.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export async function goToRepositoryCommand(fs: SourcegraphFileSystemProvider):
2828
uri: uri.uri,
2929
label: recentlyOpenRepositoriesSetting.label(uri.repositoryName),
3030
description: uri.path,
31-
unresolvedRepositoryName: uri.repositoryName,
3231
detail: query.text,
3332
}
3433
quick.pick.items = [item]

src/commands/openFileInBrowserCommand.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import open from 'open'
2-
import { SourcegraphTreeDataProvider } from '../file-system/SourcegraphTreeDataProvider'
2+
import { FilesTreeDataProvider } from '../file-system/FilesTreeDataProvider'
33
import { SourcegraphUri } from '../file-system/SourcegraphUri'
44

55
export async function openFileInBrowserCommand(
6-
tree: SourcegraphTreeDataProvider,
6+
tree: FilesTreeDataProvider,
77
uriString: string | undefined
88
): Promise<void> {
99
const activeTextDocument = uriString ? SourcegraphUri.parse(uriString) : tree.activeTextDocument()

src/commands/openSourcegraphUriCommand.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import * as vscode from 'vscode'
22
import { SourcegraphFileSystemProvider } from '../file-system/SourcegraphFileSystemProvider'
3-
import { SourcegraphUri } from '../file-system/SourcegraphUri'
3+
import { CompareRange, SourcegraphUri } from '../file-system/SourcegraphUri'
4+
import { log } from '../log'
45

56
export async function openSourcegraphUriCommand(fs: SourcegraphFileSystemProvider, uri: SourcegraphUri): Promise<void> {
7+
log.appendLine(`uri=${uri.uri}`)
8+
if (uri.compareRange) {
9+
await openCompareUri(uri, uri.compareRange)
10+
return
11+
}
612
if (!uri.revision) {
713
const metadata = await fs.repositoryMetadata(uri.repositoryName)
814
uri = uri.withRevision(metadata?.defaultBranch || 'HEAD')
@@ -15,6 +21,20 @@ export async function openSourcegraphUriCommand(fs: SourcegraphFileSystemProvide
1521
})
1622
}
1723

24+
async function openCompareUri(uri: SourcegraphUri, compareRange: CompareRange): Promise<void> {
25+
try {
26+
log.appendLine(`openCompareUri uri=${uri.uri} compareRange=${JSON.stringify(compareRange)}`)
27+
await vscode.commands.executeCommand(
28+
'vscode.diff',
29+
vscode.Uri.parse('sourcegraph://sourcegraph.com/github.com/scalameta/metals@v0.10.0/-/blob/build.sbt'),
30+
vscode.Uri.parse('sourcegraph://sourcegraph.com/github.com/scalameta/metals@v0.10.7/-/blob/build.sbt'),
31+
'build.sbt (v0.10.0 <-> v0.10.7)'
32+
)
33+
} catch (error) {
34+
log.error(`openCompareUri(${uri.uri})`, error)
35+
}
36+
}
37+
1838
function getSelection(uri: SourcegraphUri, textDocument: vscode.TextDocument): vscode.Range | undefined {
1939
if (typeof uri?.position?.line !== 'undefined' && typeof uri?.position?.character !== 'undefined') {
2040
return offsetRange(uri.position.line - 1, uri.position.character)

src/commands/switchGitRevisionCommand.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { SourcegraphTreeDataProvider } from '../file-system/SourcegraphTreeDataProvider'
1+
import { FilesTreeDataProvider } from '../file-system/FilesTreeDataProvider'
22
import { SourcegraphUri } from '../file-system/SourcegraphUri'
3-
import { log } from '../log'
43
import { GitReference, gitReferencesQuery } from '../queries/gitReferencesQuery'
54
import { openSourcegraphUriCommand } from './openSourcegraphUriCommand'
65
import { SourcegraphQuickPick } from './SourcegraphQuickPick'
76

87
export async function switchGitRevisionCommand(
9-
tree: SourcegraphTreeDataProvider,
8+
tree: FilesTreeDataProvider,
109
uriString: string | undefined
1110
): Promise<void> {
1211
const quick = new SourcegraphQuickPick(tree.fs)
@@ -19,12 +18,10 @@ export async function switchGitRevisionCommand(
1918
const metadata = await tree.fs.repositoryMetadata(activeTextDocument.repositoryName)
2019
quick.onDidChangeValue(async query => {
2120
quick.pick.busy = true
22-
log.appendLine(`gitReferences: ${query.text}`)
2321
const references = await gitReferencesQuery(
2422
{ query: query.text, repositoryId: metadata?.id || '' },
2523
query.token
2624
)
27-
log.appendLine(`gitReferences: ${query.text} ${JSON.stringify(references)}`)
2825
quick.pick.busy = false
2926
quick.pick.items = references.map(reference => ({
3027
label: gitReferenceTag(reference) + reference.displayName,
@@ -35,7 +32,7 @@ export async function switchGitRevisionCommand(
3532
await openSourcegraphUriCommand(tree.fs, uri)
3633
}
3734

38-
function gitReferenceTag(reference: GitReference): string {
35+
export function gitReferenceTag(reference: GitReference): string {
3936
switch (reference.type) {
4037
case 'GIT_TAG':
4138
return ' $(tag)'
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { DiffsTreeDataProvider } from '../file-system/DiffsTreeDataProvider'
2+
import { log } from '../log'
3+
import { gitReferencesQuery } from '../queries/gitReferencesQuery'
4+
import { endpointHostnameSetting } from '../settings/endpointSetting'
5+
import { SourcegraphQuickPick } from './SourcegraphQuickPick'
6+
import { gitReferenceTag } from './switchGitRevisionCommand'
7+
8+
export async function updateCompareRange(diffs: DiffsTreeDataProvider, commandArguments: any[]): Promise<void> {
9+
const repositoryName: string = commandArguments[0]
10+
if (typeof repositoryName !== 'string') {
11+
log.error(`updateCompareRange(${JSON.stringify(arguments)})`, `first argument is not a string`)
12+
throw new Error(`updateCompareRange(${JSON.stringify(arguments)})`)
13+
}
14+
const kind: 'base' | 'head' = commandArguments[1]
15+
if (kind !== 'base' && kind !== 'head') {
16+
log.error(`updateCompareRange(${JSON.stringify(arguments)})`, `second argument is not 'base' or 'head'`)
17+
throw new Error(`updateCompareRange(${JSON.stringify(arguments)})`)
18+
}
19+
const quick = new SourcegraphQuickPick(diffs.fs)
20+
quick.pick.title = 'Search for a git branch, git tag or a git commit'
21+
const metadata = await diffs.fs.repositoryMetadata(repositoryName)
22+
quick.onDidChangeValue(async query => {
23+
quick.pick.busy = true
24+
const references = await gitReferencesQuery(
25+
{ query: query.text, repositoryId: metadata?.id || '' },
26+
query.token
27+
)
28+
quick.pick.busy = false
29+
quick.pick.items = references.map(reference => ({
30+
label: gitReferenceTag(reference) + reference.displayName,
31+
uri: `sourcegraph://${endpointHostnameSetting()}${reference.url}`,
32+
}))
33+
})
34+
const uri = await quick.showQuickPickAndGetUserInput()
35+
diffs.updateCompareRange(repositoryName, kind, uri.revision)
36+
}

src/extension.ts

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import { searchSelectionCommand } from './commands/searchSelectionCommand'
1515
import { SourcegraphHoverProvider } from './code-intel/SourcegraphHoverProvider'
1616
import { SourcegraphDefinitionProvider } from './code-intel/SourcegraphDefinitionProvider'
1717
import { SourcegraphReferenceProvider } from './code-intel/SourcegraphReferenceProvider'
18-
import { SourcegraphTreeDataProvider } from './file-system/SourcegraphTreeDataProvider'
18+
import { FilesTreeDataProvider } from './file-system/FilesTreeDataProvider'
1919
import { switchGitRevisionCommand } from './commands/switchGitRevisionCommand'
2020
import { openFileInBrowserCommand } from './commands/openFileInBrowserCommand'
21+
import { DiffsTreeDataProvider } from './file-system/DiffsTreeDataProvider'
22+
import { updateCompareRange } from './commands/updateCompareRangeCommand'
2123

2224
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
2325
const { version } = require('../package.json')
@@ -74,14 +76,36 @@ export function activate(context: vscode.ExtensionContext): void {
7476
vscode.languages.registerHoverProvider({ scheme: 'sourcegraph' }, new SourcegraphHoverProvider(fs))
7577
vscode.languages.registerDefinitionProvider({ scheme: 'sourcegraph' }, new SourcegraphDefinitionProvider(fs))
7678
vscode.languages.registerReferenceProvider({ scheme: 'sourcegraph' }, referenceProvider)
77-
const treeDataProvider = new SourcegraphTreeDataProvider(fs)
78-
const treeView = vscode.window.createTreeView<string>('sourcegraph.files', {
79-
treeDataProvider,
79+
80+
const filesTreeProvider = new FilesTreeDataProvider(fs)
81+
const filesTreeView = vscode.window.createTreeView<string>('sourcegraph.files', {
82+
treeDataProvider: filesTreeProvider,
8083
showCollapseAll: true,
8184
})
82-
treeDataProvider.setTreeView(treeView)
85+
filesTreeProvider.setTreeView(filesTreeView)
86+
87+
const diffsTreeProvider = new DiffsTreeDataProvider(fs)
88+
const diffsTreeView = vscode.window.createTreeView('sourcegraph.diffs', {
89+
treeDataProvider: diffsTreeProvider,
90+
showCollapseAll: true,
91+
})
92+
93+
diffsTreeProvider.setTreeView(diffsTreeView)
94+
for (const treeView of [filesTreeView, diffsTreeView]) {
95+
context.subscriptions.push(treeView)
96+
}
97+
8398
const semanticTokens = new SourcegraphSemanticTokenProvider()
84-
context.subscriptions.push(treeView)
99+
for (const color of ['green', 'orange', 'red']) {
100+
for (const index of [1, 2, 3]) {
101+
const name = `extension.${color}${index}`
102+
context.subscriptions.push(
103+
vscode.commands.registerCommand(name, () => {
104+
log.appendLine(`COMMAND ${name}`)
105+
})
106+
)
107+
}
108+
}
85109
context.subscriptions.push(
86110
vscode.commands.registerCommand('extension.goToFileInFolder', async (uri: string | undefined) => {
87111
if (typeof uri === 'string') {
@@ -107,15 +131,20 @@ export function activate(context: vscode.ExtensionContext): void {
107131
vscode.commands.registerCommand(
108132
'extension.switchGitRevision',
109133
handleCommandErrors('extension.switchGitRevision', (uri: string | undefined) =>
110-
switchGitRevisionCommand(treeDataProvider, uri)
134+
switchGitRevisionCommand(filesTreeProvider, uri)
111135
)
112136
)
113137
)
138+
context.subscriptions.push(
139+
vscode.commands.registerCommand('extension.updateCompareRange', (...commandArguments) => {
140+
updateCompareRange(diffsTreeProvider, commandArguments)
141+
})
142+
)
114143
context.subscriptions.push(
115144
vscode.commands.registerCommand(
116145
'extension.openFileInBrowser',
117146
handleCommandErrors('extension.openFileInBrowser', (uri: string | undefined) =>
118-
openFileInBrowserCommand(treeDataProvider, uri)
147+
openFileInBrowserCommand(filesTreeProvider, uri)
119148
)
120149
)
121150
)
@@ -128,7 +157,7 @@ export function activate(context: vscode.ExtensionContext): void {
128157
context.subscriptions.push(
129158
vscode.commands.registerCommand(
130159
'extension.focusActiveFile',
131-
handleCommandErrors('extension.focusActiveFile', () => treeDataProvider.focusActiveFile())
160+
handleCommandErrors('extension.focusActiveFile', () => filesTreeProvider.focusActiveFile())
132161
)
133162
)
134163
context.subscriptions.push(
@@ -149,13 +178,15 @@ export function activate(context: vscode.ExtensionContext): void {
149178
{ language: 'sourcegraph' },
150179
new SourcegraphCompletionItemProvider()
151180
)
152-
context.subscriptions.push(
153-
vscode.window.onDidChangeActiveTextEditor(editor => treeDataProvider.didFocus(editor?.document.uri))
154-
)
155-
treeDataProvider.didFocus(vscode.window.activeTextEditor?.document.uri).then(
156-
() => {},
157-
() => {}
158-
)
181+
for (const treeProvider of [filesTreeProvider, diffsTreeProvider]) {
182+
context.subscriptions.push(
183+
vscode.window.onDidChangeActiveTextEditor(editor => treeProvider.didFocus(editor?.document.uri))
184+
)
185+
treeProvider.didFocus(vscode.window.activeTextEditor?.document.uri).then(
186+
() => {},
187+
() => {}
188+
)
189+
}
159190
vscode.workspace.registerNotebookSerializer('sourcegraph-notebook', new SourcegraphNotebookSerializer(fs), {})
160191
}
161192

0 commit comments

Comments
 (0)