Browse Source

Allows the Graph in a view & a tab simultaneously

main
Eric Amodio 1 year ago
parent
commit
3254bf3c49
14 changed files with 399 additions and 196 deletions
  1. +6
    -0
      CHANGELOG.md
  2. +59
    -44
      package.json
  3. +1
    -0
      src/constants.ts
  4. +2
    -18
      src/container.ts
  5. +147
    -111
      src/plus/webviews/graph/graphWebview.ts
  6. +16
    -2
      src/plus/webviews/graph/registration.ts
  7. +18
    -1
      src/system/command.ts
  8. +23
    -8
      src/system/webview.ts
  9. +12
    -6
      src/webviews/apps/plus/graph/GraphWrapper.tsx
  10. +12
    -0
      src/webviews/apps/settings/partials/commit-graph.html
  11. +8
    -3
      src/webviews/commitDetails/commitDetailsWebview.ts
  12. +70
    -0
      src/webviews/webviewCommandRegistrar.ts
  13. +11
    -0
      src/webviews/webviewController.ts
  14. +14
    -3
      src/webviews/webviewsController.ts

+ 6
- 0
CHANGELOG.md View File

@ -8,8 +8,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Added ### Added
- Adds checkboxes to files in the _Search & Compare_ view to allow for tracking review progress — closes [#836](https://github.com/gitkraken/vscode-gitlens/issues/836)
- Allows the _Commit Graph_ to be open in the panel and in the editor area simultaneously
- Adds an _Open Changes_ button to commits in the file history quick pick menu — closes [#2641](https://github.com/gitkraken/vscode-gitlens/issues/2641) thanks to [PR #2800](https://github.com/gitkraken/vscode-gitlens/pull/2800) by Omar Ghazi ([@omarfesal](https://github.com/omarfesal)) - Adds an _Open Changes_ button to commits in the file history quick pick menu — closes [#2641](https://github.com/gitkraken/vscode-gitlens/issues/2641) thanks to [PR #2800](https://github.com/gitkraken/vscode-gitlens/pull/2800) by Omar Ghazi ([@omarfesal](https://github.com/omarfesal))
### Changed
- Changes the `gitlens.graph.layout` setting to be a default preference rather than a mode change
### Fixed ### Fixed
- Fixes [#2885](https://github.com/gitkraken/vscode-gitlens/issues/2885) - Folder History not show changed files of commit - Fixes [#2885](https://github.com/gitkraken/vscode-gitlens/issues/2885) - Folder History not show changed files of commit

+ 59
- 44
package.json View File

@ -2402,10 +2402,10 @@
"panel" "panel"
], ],
"enumDescriptions": [ "enumDescriptions": [
"Shows the Commit Graph in an editor tab",
"Shows the Commit Graph in the bottom panel"
"Prefer showing the Commit Graph in the editor area",
"Prefer showing the Commit Graph in the bottom panel"
], ],
"markdownDescription": "Specifies the layout of the _Commit Graph_",
"markdownDescription": "Specifies the preferred layout of the _Commit Graph_",
"scope": "window", "scope": "window",
"order": 99 "order": 99
}, },
@ -4711,7 +4711,7 @@
}, },
{ {
"command": "gitlens.showGraphPage", "command": "gitlens.showGraphPage",
"title": "Show Commit Graph",
"title": "Show Commit Graph in Editor Area",
"category": "GitLens", "category": "GitLens",
"icon": "$(gitlens-graph)" "icon": "$(gitlens-graph)"
}, },
@ -4750,6 +4750,12 @@
"icon": "$(gitlens-graph)" "icon": "$(gitlens-graph)"
}, },
{ {
"command": "gitlens.showInCommitGraphView",
"title": "Open in Commit Graph",
"category": "GitLens",
"icon": "$(gitlens-graph)"
},
{
"command": "gitlens.showLineHistoryView", "command": "gitlens.showLineHistoryView",
"title": "Show Line History View", "title": "Show Line History View",
"category": "GitLens" "category": "GitLens"
@ -6556,6 +6562,18 @@
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.views.graph.openInTab",
"title": "Open in Editor Area",
"category": "GitLens",
"icon": "$(link-external)"
},
{
"command": "gitlens.views.graph.refresh",
"title": "Refresh",
"category": "GitLens",
"icon": "$(refresh)"
},
{
"command": "gitlens.views.graphDetails.refresh", "command": "gitlens.views.graphDetails.refresh",
"title": "Refresh", "title": "Refresh",
"category": "GitLens", "category": "GitLens",
@ -7211,13 +7229,15 @@
}, },
{ {
"command": "gitlens.graph.switchToEditorLayout", "command": "gitlens.graph.switchToEditorLayout",
"title": "Switch Commit Graph to Editor Layout",
"category": "GitLens"
"title": "Prefer Commit Graph in Editor Area",
"category": "GitLens",
"enablement": "config.gitlens.graph.layout != editor"
}, },
{ {
"command": "gitlens.graph.switchToPanelLayout", "command": "gitlens.graph.switchToPanelLayout",
"title": "Switch Commit Graph to Panel Layout",
"category": "GitLens"
"title": "Prefer Commit Graph in Panel",
"category": "GitLens",
"enablement": "config.gitlens.graph.layout != panel"
}, },
{ {
"command": "gitlens.graph.push", "command": "gitlens.graph.push",
@ -8065,23 +8085,23 @@
}, },
{ {
"command": "gitlens.showGraph", "command": "gitlens.showGraph",
"when": "false"
"when": "gitlens:enabled"
}, },
{ {
"command": "gitlens.showGraphPage", "command": "gitlens.showGraphPage",
"when": "gitlens:enabled && config.gitlens.graph.layout == editor"
"when": "gitlens:enabled"
}, },
{ {
"command": "gitlens.showGraphView", "command": "gitlens.showGraphView",
"when": "gitlens:enabled && config.gitlens.graph.layout == panel"
"when": "gitlens:enabled"
}, },
{ {
"command": "gitlens.toggleGraph", "command": "gitlens.toggleGraph",
"when": "gitlens:enabled && config.gitlens.graph.layout == panel"
"when": "gitlens:enabled"
}, },
{ {
"command": "gitlens.toggleMaximizedGraph", "command": "gitlens.toggleMaximizedGraph",
"when": "gitlens:enabled && config.gitlens.graph.layout == panel"
"when": "gitlens:enabled"
}, },
{ {
"command": "gitlens.showHomeView", "command": "gitlens.showHomeView",
@ -8096,6 +8116,10 @@
"when": "false" "when": "false"
}, },
{ {
"command": "gitlens.showInCommitGraphView",
"when": "false"
},
{
"command": "gitlens.showLineHistoryView", "command": "gitlens.showLineHistoryView",
"when": "gitlens:enabled && !gitlens:hasVirtualFolders" "when": "gitlens:enabled && !gitlens:hasVirtualFolders"
}, },
@ -9304,6 +9328,14 @@
"when": "false" "when": "false"
}, },
{ {
"command": "gitlens.views.graph.openInTab",
"when": "false"
},
{
"command": "gitlens.views.graph.refresh",
"when": "false"
},
{
"command": "gitlens.views.graphDetails.refresh", "command": "gitlens.views.graphDetails.refresh",
"when": "false" "when": "false"
}, },
@ -10337,26 +10369,6 @@
"group": "navigation@-99" "group": "navigation@-99"
}, },
{ {
"command": "gitlens.graph.push",
"when": "gitlens:webview:graph:active && gitlens:hasRemotes && !gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders",
"group": "navigation@-103"
},
{
"command": "gitlens.graph.pull",
"when": "gitlens:webview:graph:active && gitlens:hasRemotes && !gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders",
"group": "navigation@-102"
},
{
"command": "gitlens.graph.fetch",
"when": "gitlens:webview:graph:active && gitlens:hasRemotes && !gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders",
"group": "navigation@-101"
},
{
"command": "gitlens.graph.switchToAnotherBranch",
"when": "gitlens:webview:graph:active && !gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders",
"group": "navigation@-100"
},
{
"command": "gitlens.graph.refresh", "command": "gitlens.graph.refresh",
"when": "gitlens:webview:graph:active", "when": "gitlens:webview:graph:active",
"group": "navigation@-99" "group": "navigation@-99"
@ -10875,6 +10887,16 @@
"group": "5_gitlens@0" "group": "5_gitlens@0"
}, },
{ {
"command": "gitlens.views.graph.openInTab",
"when": "view =~ /^gitlens\\.views\\.graph\\b/",
"group": "navigation@-100"
},
{
"command": "gitlens.views.graph.refresh",
"when": "view =~ /^gitlens\\.views\\.graph\\b/",
"group": "navigation@-99"
},
{
"command": "gitlens.views.graphDetails.refresh", "command": "gitlens.views.graphDetails.refresh",
"when": "view =~ /^gitlens\\.views\\.graphDetails/", "when": "view =~ /^gitlens\\.views\\.graphDetails/",
"group": "navigation@99" "group": "navigation@99"
@ -11255,11 +11277,6 @@
"group": "5_gitlens@3" "group": "5_gitlens@3"
}, },
{ {
"command": "gitlens.graph.refresh",
"when": "view =~ /^gitlens\\.views\\.graph\\b/",
"group": "navigation@-99"
},
{
"submenu": "gitlens/graph/configuration", "submenu": "gitlens/graph/configuration",
"when": "view =~ /^gitlens\\.views\\.graph\\b/", "when": "view =~ /^gitlens\\.views\\.graph\\b/",
"group": "navigation@-98" "group": "navigation@-98"
@ -13684,13 +13701,11 @@
"gitlens/graph/configuration": [ "gitlens/graph/configuration": [
{ {
"command": "gitlens.graph.switchToEditorLayout", "command": "gitlens.graph.switchToEditorLayout",
"group": "1_gitlens@1",
"when": "config.gitlens.graph.layout != editor"
"group": "1_gitlens@1"
}, },
{ {
"command": "gitlens.graph.switchToPanelLayout", "command": "gitlens.graph.switchToPanelLayout",
"group": "1_gitlens@1",
"when": "config.gitlens.graph.layout != panel"
"group": "1_gitlens@1"
}, },
{ {
"command": "gitlens.showSettingsPage#commit-graph", "command": "gitlens.showSettingsPage#commit-graph",
@ -14559,7 +14574,7 @@
"type": "webview", "type": "webview",
"id": "gitlens.views.graph", "id": "gitlens.views.graph",
"name": "Graph", "name": "Graph",
"when": "!gitlens:disabled && gitlens:plus:enabled && config.gitlens.graph.layout == panel",
"when": "!gitlens:disabled && gitlens:plus:enabled",
"contextualTitle": "GL", "contextualTitle": "GL",
"icon": "$(gitlens-graph)", "icon": "$(gitlens-graph)",
"initialSize": 4, "initialSize": 4,
@ -14569,7 +14584,7 @@
"type": "webview", "type": "webview",
"id": "gitlens.views.graphDetails", "id": "gitlens.views.graphDetails",
"name": "Graph Details", "name": "Graph Details",
"when": "!gitlens:disabled && gitlens:plus:enabled && config.gitlens.graph.layout == panel",
"when": "!gitlens:disabled && gitlens:plus:enabled",
"contextualTitle": "GL", "contextualTitle": "GL",
"icon": "$(gitlens-commit-view)", "icon": "$(gitlens-commit-view)",
"initialSize": 1, "initialSize": 1,

+ 1
- 0
src/constants.ts View File

@ -243,6 +243,7 @@ export const enum Commands {
ShowHomeView = 'gitlens.showHomeView', ShowHomeView = 'gitlens.showHomeView',
ShowAccountView = 'gitlens.showAccountView', ShowAccountView = 'gitlens.showAccountView',
ShowInCommitGraph = 'gitlens.showInCommitGraph', ShowInCommitGraph = 'gitlens.showInCommitGraph',
ShowInCommitGraphView = 'gitlens.showInCommitGraphView',
ShowInDetailsView = 'gitlens.showInDetailsView', ShowInDetailsView = 'gitlens.showInDetailsView',
ShowLastQuickPick = 'gitlens.showLastQuickPick', ShowLastQuickPick = 'gitlens.showLastQuickPick',
ShowLineCommitInView = 'gitlens.showLineCommitInView', ShowLineCommitInView = 'gitlens.showLineCommitInView',

+ 2
- 18
src/container.ts View File

@ -226,9 +226,7 @@ export class Container {
this._disposables.push((this._graphPanel = registerGraphWebviewPanel(this._webviews))); this._disposables.push((this._graphPanel = registerGraphWebviewPanel(this._webviews)));
this._disposables.push(registerGraphWebviewCommands(this, this._graphPanel)); this._disposables.push(registerGraphWebviewCommands(this, this._graphPanel));
if (configuration.get('graph.layout') === 'panel') {
this._disposables.push((this._graphView = registerGraphWebviewView(this._webviews)));
}
this._disposables.push((this._graphView = registerGraphWebviewView(this._webviews)));
this._disposables.push(new GraphStatusBarController(this)); this._disposables.push(new GraphStatusBarController(this));
const settingsWebviewPanel = registerSettingsWebviewPanel(this._webviews); const settingsWebviewPanel = registerSettingsWebviewPanel(this._webviews);
@ -324,16 +322,6 @@ export class Container {
if (configuration.changed(e, 'mode')) { if (configuration.changed(e, 'mode')) {
this.ensureModeApplied(); this.ensureModeApplied();
} }
if (configuration.changed(e, 'graph.layout')) {
if (configuration.get('graph.layout') === 'panel') {
this._graphPanel?.close();
this._graphView = registerGraphWebviewView(this._webviews);
} else {
this._graphView?.dispose();
this._graphView = undefined;
}
}
} }
private _accountAuthentication: AccountAuthenticationProvider; private _accountAuthentication: AccountAuthenticationProvider;
@ -482,12 +470,8 @@ export class Container {
} }
private readonly _graphPanel: WebviewPanelProxy; private readonly _graphPanel: WebviewPanelProxy;
private _graphView: WebviewViewProxy | undefined;
private readonly _graphView: WebviewViewProxy;
get graphView() { get graphView() {
if (this._graphView == null) {
this._disposables.push((this._graphView = registerGraphWebviewView(this._webviews)));
}
return this._graphView; return this._graphView;
} }

+ 147
- 111
src/plus/webviews/graph/graphWebview.ts View File

@ -347,122 +347,161 @@ export class GraphWebviewProvider implements WebviewProvider {
registerCommands(): Disposable[] { registerCommands(): Disposable[] {
return [ return [
registerCommand(Commands.RefreshGraph, () => this.host.refresh(true)),
registerCommand('gitlens.graph.push', this.push, this),
registerCommand('gitlens.graph.pull', this.pull, this),
registerCommand('gitlens.graph.fetch', this.fetch, this),
registerCommand('gitlens.graph.publishBranch', this.publishBranch, this),
registerCommand('gitlens.graph.switchToAnotherBranch', this.switchToAnother, this),
registerCommand('gitlens.graph.createBranch', this.createBranch, this),
registerCommand('gitlens.graph.deleteBranch', this.deleteBranch, this),
registerCommand('gitlens.graph.copyRemoteBranchUrl', item => this.openBranchOnRemote(item, true), this),
registerCommand('gitlens.graph.openBranchOnRemote', this.openBranchOnRemote, this),
registerCommand('gitlens.graph.mergeBranchInto', this.mergeBranchInto, this),
registerCommand('gitlens.graph.rebaseOntoBranch', this.rebase, this),
registerCommand('gitlens.graph.rebaseOntoUpstream', this.rebaseToRemote, this),
registerCommand('gitlens.graph.renameBranch', this.renameBranch, this),
registerCommand('gitlens.graph.switchToBranch', this.switchTo, this),
registerCommand('gitlens.graph.hideLocalBranch', this.hideRef, this),
registerCommand('gitlens.graph.hideRemoteBranch', this.hideRef, this),
registerCommand('gitlens.graph.hideRemote', item => this.hideRef(item, { remote: true }), this),
registerCommand('gitlens.graph.hideRefGroup', item => this.hideRef(item, { group: true }), this),
registerCommand('gitlens.graph.hideTag', this.hideRef, this),
registerCommand('gitlens.graph.cherryPick', this.cherryPick, this),
registerCommand('gitlens.graph.copyRemoteCommitUrl', item => this.openCommitOnRemote(item, true), this),
registerCommand('gitlens.graph.showInDetailsView', this.openInDetailsView, this),
registerCommand('gitlens.graph.openCommitOnRemote', this.openCommitOnRemote, this),
registerCommand('gitlens.graph.openSCM', this.openSCM, this),
registerCommand('gitlens.graph.rebaseOntoCommit', this.rebase, this),
registerCommand('gitlens.graph.resetCommit', this.resetCommit, this),
registerCommand('gitlens.graph.resetToCommit', this.resetToCommit, this),
registerCommand('gitlens.graph.resetToTip', this.resetToTip, this),
registerCommand('gitlens.graph.revert', this.revertCommit, this),
registerCommand('gitlens.graph.switchToCommit', this.switchTo, this),
registerCommand('gitlens.graph.undoCommit', this.undoCommit, this),
registerCommand('gitlens.graph.saveStash', this.saveStash, this),
registerCommand('gitlens.graph.applyStash', this.applyStash, this),
registerCommand('gitlens.graph.stash.delete', this.deleteStash, this),
registerCommand('gitlens.graph.stash.rename', this.renameStash, this),
registerCommand('gitlens.graph.createTag', this.createTag, this),
registerCommand('gitlens.graph.deleteTag', this.deleteTag, this),
registerCommand('gitlens.graph.switchToTag', this.switchTo, this),
registerCommand('gitlens.graph.createWorktree', this.createWorktree, this),
registerCommand('gitlens.graph.createPullRequest', this.createPullRequest, this),
registerCommand('gitlens.graph.openPullRequestOnRemote', this.openPullRequestOnRemote, this),
registerCommand('gitlens.graph.compareWithUpstream', this.compareWithUpstream, this),
registerCommand('gitlens.graph.compareWithHead', this.compareHeadWith, this),
registerCommand('gitlens.graph.compareWithWorking', this.compareWorkingWith, this),
registerCommand('gitlens.graph.compareAncestryWithWorking', this.compareAncestryWithWorking, this),
registerCommand('gitlens.graph.copy', this.copy, this),
registerCommand('gitlens.graph.copyMessage', this.copyMessage, this),
registerCommand('gitlens.graph.copySha', this.copySha, this),
registerCommand('gitlens.graph.addAuthor', this.addAuthor, this),
registerCommand('gitlens.graph.columnAuthorOn', () => this.toggleColumn('author', true)),
registerCommand('gitlens.graph.columnAuthorOff', () => this.toggleColumn('author', false)),
registerCommand('gitlens.graph.columnDateTimeOn', () => this.toggleColumn('datetime', true)),
registerCommand('gitlens.graph.columnDateTimeOff', () => this.toggleColumn('datetime', false)),
registerCommand('gitlens.graph.columnShaOn', () => this.toggleColumn('sha', true)),
registerCommand('gitlens.graph.columnShaOff', () => this.toggleColumn('sha', false)),
registerCommand('gitlens.graph.columnChangesOn', () => this.toggleColumn('changes', true)),
registerCommand('gitlens.graph.columnChangesOff', () => this.toggleColumn('changes', false)),
registerCommand('gitlens.graph.columnGraphOn', () => this.toggleColumn('graph', true)),
registerCommand('gitlens.graph.columnGraphOff', () => this.toggleColumn('graph', false)),
registerCommand('gitlens.graph.columnMessageOn', () => this.toggleColumn('message', true)),
registerCommand('gitlens.graph.columnMessageOff', () => this.toggleColumn('message', false)),
registerCommand('gitlens.graph.columnRefOn', () => this.toggleColumn('ref', true)),
registerCommand('gitlens.graph.columnRefOff', () => this.toggleColumn('ref', false)),
registerCommand('gitlens.graph.columnGraphCompact', () => this.setColumnMode('graph', 'compact')),
registerCommand('gitlens.graph.columnGraphDefault', () => this.setColumnMode('graph', undefined)),
registerCommand('gitlens.graph.scrollMarkerLocalBranchOn', () =>
registerCommand(`${this.host.id}.refresh`, () => this.host.refresh(true)),
...(this.host.isView()
? [
registerCommand(
`${this.host.id}.openInTab`,
() => void executeCommand(Commands.ShowGraphPage, this.repository),
),
]
: []),
this.host.registerWebviewCommand('gitlens.graph.push', this.push),
this.host.registerWebviewCommand('gitlens.graph.pull', this.pull),
this.host.registerWebviewCommand('gitlens.graph.fetch', this.fetch),
this.host.registerWebviewCommand('gitlens.graph.publishBranch', this.publishBranch),
this.host.registerWebviewCommand('gitlens.graph.switchToAnotherBranch', this.switchToAnother),
this.host.registerWebviewCommand('gitlens.graph.createBranch', this.createBranch),
this.host.registerWebviewCommand('gitlens.graph.deleteBranch', this.deleteBranch),
this.host.registerWebviewCommand<GraphItemContext>('gitlens.graph.copyRemoteBranchUrl', item =>
this.openBranchOnRemote(item, true),
),
this.host.registerWebviewCommand('gitlens.graph.openBranchOnRemote', this.openBranchOnRemote),
this.host.registerWebviewCommand('gitlens.graph.mergeBranchInto', this.mergeBranchInto),
this.host.registerWebviewCommand('gitlens.graph.rebaseOntoBranch', this.rebase),
this.host.registerWebviewCommand('gitlens.graph.rebaseOntoUpstream', this.rebaseToRemote),
this.host.registerWebviewCommand('gitlens.graph.renameBranch', this.renameBranch),
this.host.registerWebviewCommand('gitlens.graph.switchToBranch', this.switchTo),
this.host.registerWebviewCommand('gitlens.graph.hideLocalBranch', this.hideRef),
this.host.registerWebviewCommand('gitlens.graph.hideRemoteBranch', this.hideRef),
this.host.registerWebviewCommand<GraphItemContext>('gitlens.graph.hideRemote', item =>
this.hideRef(item, { remote: true }),
),
this.host.registerWebviewCommand<GraphItemContext>('gitlens.graph.hideRefGroup', item =>
this.hideRef(item, { group: true }),
),
this.host.registerWebviewCommand('gitlens.graph.hideTag', this.hideRef),
this.host.registerWebviewCommand('gitlens.graph.cherryPick', this.cherryPick),
this.host.registerWebviewCommand<GraphItemContext>('gitlens.graph.copyRemoteCommitUrl', item =>
this.openCommitOnRemote(item, true),
),
this.host.registerWebviewCommand('gitlens.graph.showInDetailsView', this.openInDetailsView),
this.host.registerWebviewCommand('gitlens.graph.openCommitOnRemote', this.openCommitOnRemote),
this.host.registerWebviewCommand('gitlens.graph.openSCM', this.openSCM),
this.host.registerWebviewCommand('gitlens.graph.rebaseOntoCommit', this.rebase),
this.host.registerWebviewCommand('gitlens.graph.resetCommit', this.resetCommit),
this.host.registerWebviewCommand('gitlens.graph.resetToCommit', this.resetToCommit),
this.host.registerWebviewCommand('gitlens.graph.resetToTip', this.resetToTip),
this.host.registerWebviewCommand('gitlens.graph.revert', this.revertCommit),
this.host.registerWebviewCommand('gitlens.graph.switchToCommit', this.switchTo),
this.host.registerWebviewCommand('gitlens.graph.undoCommit', this.undoCommit),
this.host.registerWebviewCommand('gitlens.graph.saveStash', this.saveStash),
this.host.registerWebviewCommand('gitlens.graph.applyStash', this.applyStash),
this.host.registerWebviewCommand('gitlens.graph.stash.delete', this.deleteStash),
this.host.registerWebviewCommand('gitlens.graph.stash.rename', this.renameStash),
this.host.registerWebviewCommand('gitlens.graph.createTag', this.createTag),
this.host.registerWebviewCommand('gitlens.graph.deleteTag', this.deleteTag),
this.host.registerWebviewCommand('gitlens.graph.switchToTag', this.switchTo),
this.host.registerWebviewCommand('gitlens.graph.createWorktree', this.createWorktree),
this.host.registerWebviewCommand('gitlens.graph.createPullRequest', this.createPullRequest),
this.host.registerWebviewCommand('gitlens.graph.openPullRequestOnRemote', this.openPullRequestOnRemote),
this.host.registerWebviewCommand('gitlens.graph.compareWithUpstream', this.compareWithUpstream),
this.host.registerWebviewCommand('gitlens.graph.compareWithHead', this.compareHeadWith),
this.host.registerWebviewCommand('gitlens.graph.compareWithWorking', this.compareWorkingWith),
this.host.registerWebviewCommand(
'gitlens.graph.compareAncestryWithWorking',
this.compareAncestryWithWorking,
),
this.host.registerWebviewCommand('gitlens.graph.copy', this.copy),
this.host.registerWebviewCommand('gitlens.graph.copyMessage', this.copyMessage),
this.host.registerWebviewCommand('gitlens.graph.copySha', this.copySha),
this.host.registerWebviewCommand('gitlens.graph.addAuthor', this.addAuthor),
this.host.registerWebviewCommand('gitlens.graph.columnAuthorOn', () => this.toggleColumn('author', true)),
this.host.registerWebviewCommand('gitlens.graph.columnAuthorOff', () => this.toggleColumn('author', false)),
this.host.registerWebviewCommand('gitlens.graph.columnDateTimeOn', () =>
this.toggleColumn('datetime', true),
),
this.host.registerWebviewCommand('gitlens.graph.columnDateTimeOff', () =>
this.toggleColumn('datetime', false),
),
this.host.registerWebviewCommand('gitlens.graph.columnShaOn', () => this.toggleColumn('sha', true)),
this.host.registerWebviewCommand('gitlens.graph.columnShaOff', () => this.toggleColumn('sha', false)),
this.host.registerWebviewCommand('gitlens.graph.columnChangesOn', () => this.toggleColumn('changes', true)),
this.host.registerWebviewCommand('gitlens.graph.columnChangesOff', () =>
this.toggleColumn('changes', false),
),
this.host.registerWebviewCommand('gitlens.graph.columnGraphOn', () => this.toggleColumn('graph', true)),
this.host.registerWebviewCommand('gitlens.graph.columnGraphOff', () => this.toggleColumn('graph', false)),
this.host.registerWebviewCommand('gitlens.graph.columnMessageOn', () => this.toggleColumn('message', true)),
this.host.registerWebviewCommand('gitlens.graph.columnMessageOff', () =>
this.toggleColumn('message', false),
),
this.host.registerWebviewCommand('gitlens.graph.columnRefOn', () => this.toggleColumn('ref', true)),
this.host.registerWebviewCommand('gitlens.graph.columnRefOff', () => this.toggleColumn('ref', false)),
this.host.registerWebviewCommand('gitlens.graph.columnGraphCompact', () =>
this.setColumnMode('graph', 'compact'),
),
this.host.registerWebviewCommand('gitlens.graph.columnGraphDefault', () =>
this.setColumnMode('graph', undefined),
),
this.host.registerWebviewCommand('gitlens.graph.scrollMarkerLocalBranchOn', () =>
this.toggleScrollMarker('localBranches', true), this.toggleScrollMarker('localBranches', true),
), ),
registerCommand('gitlens.graph.scrollMarkerLocalBranchOff', () =>
this.host.registerWebviewCommand('gitlens.graph.scrollMarkerLocalBranchOff', () =>
this.toggleScrollMarker('localBranches', false), this.toggleScrollMarker('localBranches', false),
), ),
registerCommand('gitlens.graph.scrollMarkerRemoteBranchOn', () =>
this.host.registerWebviewCommand('gitlens.graph.scrollMarkerRemoteBranchOn', () =>
this.toggleScrollMarker('remoteBranches', true), this.toggleScrollMarker('remoteBranches', true),
), ),
registerCommand('gitlens.graph.scrollMarkerRemoteBranchOff', () =>
this.host.registerWebviewCommand('gitlens.graph.scrollMarkerRemoteBranchOff', () =>
this.toggleScrollMarker('remoteBranches', false), this.toggleScrollMarker('remoteBranches', false),
), ),
registerCommand('gitlens.graph.scrollMarkerStashOn', () => this.toggleScrollMarker('stashes', true)),
registerCommand('gitlens.graph.scrollMarkerStashOff', () => this.toggleScrollMarker('stashes', false)),
registerCommand('gitlens.graph.scrollMarkerTagOn', () => this.toggleScrollMarker('tags', true)),
registerCommand('gitlens.graph.scrollMarkerTagOff', () => this.toggleScrollMarker('tags', false)),
registerCommand('gitlens.graph.copyDeepLinkToBranch', this.copyDeepLinkToBranch, this),
registerCommand('gitlens.graph.copyDeepLinkToCommit', this.copyDeepLinkToCommit, this),
registerCommand('gitlens.graph.copyDeepLinkToRepo', this.copyDeepLinkToRepo, this),
registerCommand('gitlens.graph.copyDeepLinkToTag', this.copyDeepLinkToTag, this),
registerCommand('gitlens.graph.openChangedFiles', this.openFiles, this),
registerCommand('gitlens.graph.openOnlyChangedFiles', this.openOnlyChangedFiles, this),
registerCommand('gitlens.graph.openChangedFileDiffs', this.openAllChanges, this),
registerCommand('gitlens.graph.openChangedFileDiffsWithWorking', this.openAllChangesWithWorking, this),
registerCommand('gitlens.graph.openChangedFileRevisions', this.openRevisions, this),
registerCommand(
'gitlens.graph.resetColumnsDefault',
() => this.updateColumns(defaultGraphColumnsSettings),
this,
this.host.registerWebviewCommand('gitlens.graph.scrollMarkerStashOn', () =>
this.toggleScrollMarker('stashes', true),
), ),
registerCommand(
'gitlens.graph.resetColumnsCompact',
() => this.updateColumns(compactGraphColumnsSettings),
this,
this.host.registerWebviewCommand('gitlens.graph.scrollMarkerStashOff', () =>
this.toggleScrollMarker('stashes', false),
),
this.host.registerWebviewCommand('gitlens.graph.scrollMarkerTagOn', () =>
this.toggleScrollMarker('tags', true),
),
this.host.registerWebviewCommand('gitlens.graph.scrollMarkerTagOff', () =>
this.toggleScrollMarker('tags', false),
),
this.host.registerWebviewCommand('gitlens.graph.copyDeepLinkToBranch', this.copyDeepLinkToBranch),
this.host.registerWebviewCommand('gitlens.graph.copyDeepLinkToCommit', this.copyDeepLinkToCommit),
this.host.registerWebviewCommand('gitlens.graph.copyDeepLinkToRepo', this.copyDeepLinkToRepo),
this.host.registerWebviewCommand('gitlens.graph.copyDeepLinkToTag', this.copyDeepLinkToTag),
this.host.registerWebviewCommand('gitlens.graph.openChangedFiles', this.openFiles),
this.host.registerWebviewCommand('gitlens.graph.openOnlyChangedFiles', this.openOnlyChangedFiles),
this.host.registerWebviewCommand('gitlens.graph.openChangedFileDiffs', this.openAllChanges),
this.host.registerWebviewCommand(
'gitlens.graph.openChangedFileDiffsWithWorking',
this.openAllChangesWithWorking,
),
this.host.registerWebviewCommand('gitlens.graph.openChangedFileRevisions', this.openRevisions),
this.host.registerWebviewCommand('gitlens.graph.resetColumnsDefault', () =>
this.updateColumns(defaultGraphColumnsSettings),
),
this.host.registerWebviewCommand('gitlens.graph.resetColumnsCompact', () =>
this.updateColumns(compactGraphColumnsSettings),
), ),
]; ];
} }
@ -792,10 +831,7 @@ export class GraphWebviewProvider implements WebviewProvider {
}, },
); );
const details =
configuration.get('graph.layout') === 'panel'
? this.container.graphDetailsView
: this.container.commitDetailsView;
const details = this.host.isView() ? this.container.graphDetailsView : this.container.commitDetailsView;
if (!details.ready) { if (!details.ready) {
void details.show({ preserveFocus: e.preserveFocus }, { void details.show({ preserveFocus: e.preserveFocus }, {
commit: commit, commit: commit,

+ 16
- 2
src/plus/webviews/graph/registration.ts View File

@ -34,7 +34,6 @@ export function registerGraphWebviewPanel(controller: WebviewsController) {
const { GraphWebviewProvider } = await import(/* webpackChunkName: "graph" */ './graphWebview'); const { GraphWebviewProvider } = await import(/* webpackChunkName: "graph" */ './graphWebview');
return new GraphWebviewProvider(container, host); return new GraphWebviewProvider(container, host);
}, },
() => configuration.get('graph.layout') === 'editor',
); );
} }
@ -55,7 +54,6 @@ export function registerGraphWebviewView(controller: WebviewsController) {
const { GraphWebviewProvider } = await import(/* webpackChunkName: "graph" */ './graphWebview'); const { GraphWebviewProvider } = await import(/* webpackChunkName: "graph" */ './graphWebview');
return new GraphWebviewProvider(container, host); return new GraphWebviewProvider(container, host);
}, },
() => configuration.get('graph.layout') === 'panel',
); );
} }
@ -113,5 +111,21 @@ export function registerGraphWebviewCommands(container: Container, webview: Webv
} }
}, },
), ),
registerCommand(
Commands.ShowInCommitGraphView,
(
args:
| ShowInCommitGraphCommandArgs
| Repository
| BranchNode
| CommitNode
| CommitFileNode
| StashNode
| TagNode,
) => {
const preserveFocus = 'preserveFocus' in args ? args.preserveFocus ?? false : false;
void container.graphView.show({ preserveFocus: preserveFocus }, args);
},
),
); );
} }

+ 18
- 1
src/system/command.ts View File

@ -5,6 +5,9 @@ import type { Command } from '../commands/base';
import type { CoreCommands, CoreGitCommands, TreeViewCommands } from '../constants'; import type { CoreCommands, CoreGitCommands, TreeViewCommands } from '../constants';
import { Commands } from '../constants'; import { Commands } from '../constants';
import { Container } from '../container'; import { Container } from '../container';
import { isWebviewContext } from './webview';
export type CommandCallback = Parameters<typeof commands.registerCommand>[1];
type CommandConstructor = new (container: Container) => Command; type CommandConstructor = new (container: Container) => Command;
const registrableCommands: CommandConstructor[] = []; const registrableCommands: CommandConstructor[] = [];
@ -15,7 +18,7 @@ export function command(): ClassDecorator {
}; };
} }
export function registerCommand(command: string, callback: (...args: any[]) => any, thisArg?: any): Disposable {
export function registerCommand(command: string, callback: CommandCallback, thisArg?: any): Disposable {
return commands.registerCommand( return commands.registerCommand(
command, command,
function (this: any, ...args) { function (this: any, ...args) {
@ -26,6 +29,20 @@ export function registerCommand(command: string, callback: (...args: any[]) => a
); );
} }
export function registerWebviewCommand(command: string, callback: CommandCallback, thisArg?: any): Disposable {
return commands.registerCommand(
command,
function (this: any, ...args) {
Container.instance.telemetry.sendEvent('command', {
command: command,
webview: isWebviewContext(args[0]) ? args[0].webview : '<missing>',
});
callback.call(this, ...args);
},
thisArg,
);
}
export function registerCommands(container: Container): Disposable[] { export function registerCommands(container: Container): Disposable[] {
return registrableCommands.map(c => new c(container)); return registrableCommands.map(c => new c(container));
} }

+ 23
- 8
src/system/webview.ts View File

@ -1,31 +1,46 @@
import type { WebviewIds, WebviewViewIds } from '../constants'; import type { WebviewIds, WebviewViewIds } from '../constants';
export interface WebviewItemContext<TValue = unknown> {
webview?: WebviewIds | WebviewViewIds;
export function createWebviewCommandLink(
command: `${WebviewIds | WebviewViewIds}.${string}`,
webviewId: WebviewIds | WebviewViewIds,
): string {
return `command:${command}?${encodeURIComponent(JSON.stringify({ webview: webviewId } satisfies WebviewContext))}`;
}
export interface WebviewContext {
webview: WebviewIds | WebviewViewIds;
}
export function isWebviewContext(item: object | null | undefined): item is WebviewContext {
if (item == null) return false;
return 'webview' in item && item.webview != null;
}
export interface WebviewItemContext<TValue = unknown> extends Partial<WebviewContext> {
webviewItem: string; webviewItem: string;
webviewItemValue: TValue; webviewItemValue: TValue;
} }
export function isWebviewItemContext<TValue = unknown>( export function isWebviewItemContext<TValue = unknown>(
item: object | null | undefined, item: object | null | undefined,
): item is WebviewItemContext<TValue> {
): item is WebviewItemContext<TValue> & WebviewContext {
if (item == null) return false; if (item == null) return false;
return 'webview' in item && 'webviewItem' in item;
return 'webview' in item && item.webview != null && 'webviewItem' in item;
} }
export interface WebviewItemGroupContext<TValue = unknown> {
webview?: WebviewIds | WebviewViewIds;
export interface WebviewItemGroupContext<TValue = unknown> extends Partial<WebviewContext> {
webviewItemGroup: string; webviewItemGroup: string;
webviewItemGroupValue: TValue; webviewItemGroupValue: TValue;
} }
export function isWebviewItemGroupContext<TValue = unknown>( export function isWebviewItemGroupContext<TValue = unknown>(
item: object | null | undefined, item: object | null | undefined,
): item is WebviewItemGroupContext<TValue> {
): item is WebviewItemGroupContext<TValue> & WebviewContext {
if (item == null) return false; if (item == null) return false;
return 'webview' in item && 'webviewItemGroup' in item;
return 'webview' in item && item.webview != null && 'webviewItemGroup' in item;
} }
export function serializeWebviewItemContext<T = WebviewItemContext | WebviewItemGroupContext>(context: T): string { export function serializeWebviewItemContext<T = WebviewItemContext | WebviewItemGroupContext>(context: T): string {

+ 12
- 6
src/webviews/apps/plus/graph/GraphWrapper.tsx View File

@ -55,6 +55,7 @@ import {
} from '../../../../plus/webviews/graph/protocol'; } from '../../../../plus/webviews/graph/protocol';
import type { Subscription } from '../../../../subscription'; import type { Subscription } from '../../../../subscription';
import { pluralize } from '../../../../system/string'; import { pluralize } from '../../../../system/string';
import { createWebviewCommandLink } from '../../../../system/webview';
import type { IpcNotificationType } from '../../../protocol'; import type { IpcNotificationType } from '../../../protocol';
import { MenuDivider, MenuItem, MenuLabel, MenuList } from '../../shared/components/menu/react'; import { MenuDivider, MenuItem, MenuLabel, MenuList } from '../../shared/components/menu/react';
import { PopMenu } from '../../shared/components/overlays/pop-menu/react'; import { PopMenu } from '../../shared/components/overlays/pop-menu/react';
@ -978,9 +979,10 @@ export function GraphWrapper({
const lastFetchedDate = lastFetched && new Date(lastFetched); const lastFetchedDate = lastFetched && new Date(lastFetched);
const fetchedText = lastFetchedDate && lastFetchedDate.getTime() !== 0 ? fromNow(lastFetchedDate) : undefined; const fetchedText = lastFetchedDate && lastFetchedDate.getTime() !== 0 ? fromNow(lastFetchedDate) : undefined;
let action: 'fetch' | 'pull' | 'push' = 'fetch';
let icon = 'sync'; let icon = 'sync';
let label = 'Fetch'; let label = 'Fetch';
let action = 'command:gitlens.graph.fetch';
let isBehind = false; let isBehind = false;
let isAhead = false; let isAhead = false;
@ -993,12 +995,12 @@ export function GraphWrapper({
const branchPrefix = `Branch ${branchName} is`; const branchPrefix = `Branch ${branchName} is`;
remote = `${branchState.upstream}${branchState.provider?.name ? ` on ${branchState.provider?.name}` : ''}`; remote = `${branchState.upstream}${branchState.provider?.name ? ` on ${branchState.provider?.name}` : ''}`;
if (isBehind) { if (isBehind) {
action = 'command:gitlens.graph.pull';
action = 'pull';
icon = 'arrow-down'; icon = 'arrow-down';
label = 'Pull'; label = 'Pull';
tooltip = `Pull from ${remote}\n${branchPrefix} ${pluralize('commit', branchState.behind)} behind of`; tooltip = `Pull from ${remote}\n${branchPrefix} ${pluralize('commit', branchState.behind)} behind of`;
} else if (isAhead) { } else if (isAhead) {
action = 'command:gitlens.graph.push';
action = 'push';
icon = 'arrow-up'; icon = 'arrow-up';
label = 'Push'; label = 'Push';
tooltip = `Push to ${remote}\n${branchPrefix} ${pluralize('commit', branchState.ahead)} ahead of`; tooltip = `Push to ${remote}\n${branchPrefix} ${pluralize('commit', branchState.ahead)} ahead of`;
@ -1017,7 +1019,7 @@ export function GraphWrapper({
<div className="titlebar__group"> <div className="titlebar__group">
{(isBehind || isAhead) && ( {(isBehind || isAhead) && (
<a <a
href={action}
href={createWebviewCommandLink(`gitlens.graph.${action}`, state.webviewId)}
className={`action-button${isBehind ? ' is-behind' : ''}${isAhead ? ' is-ahead' : ''}`} className={`action-button${isBehind ? ' is-behind' : ''}${isAhead ? ' is-ahead' : ''}`}
title={tooltip} title={tooltip}
> >
@ -1041,7 +1043,11 @@ export function GraphWrapper({
)} )}
</a> </a>
)} )}
<a href="command:gitlens.graph.fetch" className="action-button" title={fetchTooltip}>
<a
href={createWebviewCommandLink('gitlens.graph.fetch', state.webviewId)}
className="action-button"
title={fetchTooltip}
>
<span className="codicon codicon-sync action-button__icon"></span> <span className="codicon codicon-sync action-button__icon"></span>
Fetch Fetch
{fetchedText && <span className="action-button__small">({fetchedText})</span>} {fetchedText && <span className="action-button__small">({fetchedText})</span>}
@ -1099,7 +1105,7 @@ export function GraphWrapper({
<span className="codicon codicon-chevron-right"></span> <span className="codicon codicon-chevron-right"></span>
</span> </span>
<a <a
href="command:gitlens.graph.switchToAnotherBranch"
href={createWebviewCommandLink('gitlens.graph.switchToAnotherBranch', state.webviewId)}
className="action-button" className="action-button"
title="Switch to Another Branch..." title="Switch to Another Branch..."
aria-label="Switch to Another Branch..." aria-label="Switch to Another Branch..."

+ 12
- 0
src/webviews/apps/settings/partials/commit-graph.html View File

@ -26,6 +26,18 @@
<div class="settings settings--fixed ml-1"> <div class="settings settings--fixed ml-1">
<div class="setting"> <div class="setting">
<div class="setting__input"> <div class="setting__input">
<label for="graph.layout">Prefer showing the Commit Graph in the </label>
<div class="select-container">
<select id="graph.layout" name="graph.layout" data-setting>
<option value="editor">editor area</option>
<option value="panel">bottom panel</option>
</select>
</div>
</div>
</div>
<div class="setting">
<div class="setting__input">
<label for="graph.defaultItemLimit">Show </label> <label for="graph.defaultItemLimit">Show </label>
<input <input
id="graph.defaultItemLimit" id="graph.defaultItemLimit"

+ 8
- 3
src/webviews/commitDetails/commitDetailsWebview.ts View File

@ -369,9 +369,14 @@ export class CommitDetailsWebviewProvider implements WebviewProvider
case 'graph': case 'graph':
if (this._context.commit == null) return; if (this._context.commit == null) return;
void executeCommand<ShowInCommitGraphCommandArgs>(Commands.ShowInCommitGraph, {
ref: getReferenceFromRevision(this._context.commit),
});
void executeCommand<ShowInCommitGraphCommandArgs>(
this.options.mode === 'graph'
? Commands.ShowInCommitGraphView
: Commands.ShowInCommitGraph,
{
ref: getReferenceFromRevision(this._context.commit),
},
);
break; break;
case 'more': case 'more':
this.showCommitActions(); this.showCommitActions();

+ 70
- 0
src/webviews/webviewCommandRegistrar.ts View File

@ -0,0 +1,70 @@
import type { Disposable } from 'vscode';
import type { CommandCallback } from '../system/command';
import { registerWebviewCommand } from '../system/command';
import type { WebviewContext } from '../system/webview';
import { isWebviewContext } from '../system/webview';
import type { WebviewProvider } from './webviewController';
export type WebviewCommandCallback<T extends Partial<WebviewContext>> = (arg?: T | undefined) => any;
export class WebviewCommandRegistrar implements Disposable {
private readonly _commandRegistrations = new Map<
string,
{ handlers: Map<string, { callback: CommandCallback; thisArg: any }>; subscription: Disposable }
>();
dispose() {
this._commandRegistrations.forEach(({ subscription }) => void subscription.dispose());
}
registerCommand<T extends WebviewProvider<any>>(
provider: T,
id: string,
command: string,
callback: CommandCallback,
) {
let registration = this._commandRegistrations.get(command);
if (registration == null) {
const handlers = new Map();
registration = {
subscription: registerWebviewCommand(
command,
(...args: any[]) => {
const item = args[0];
if (!isWebviewContext(item)) {
debugger;
return;
}
const handler = handlers.get(item.webview);
if (handler == null) {
throw new Error(
`Unable to find Command '${command}' registration for Webview '${item.webview}'`,
);
}
handler.callback.call(handler.thisArg, item);
},
this,
),
handlers: handlers,
};
this._commandRegistrations.set(command, registration);
}
if (registration.handlers.has(id)) {
throw new Error(`Command '${command}' has already been registered for Webview '${id}'`);
}
registration.handlers.set(id, { callback: callback, thisArg: provider });
return {
dispose: () => {
registration!.handlers.delete(id);
if (registration!.handlers.size === 0) {
this._commandRegistrations.delete(command);
registration!.subscription.dispose();
}
},
};
}
}

+ 11
- 0
src/webviews/webviewController.ts View File

@ -8,8 +8,10 @@ import { setContext } from '../system/context';
import { debug, logName } from '../system/decorators/log'; import { debug, logName } from '../system/decorators/log';
import { serialize } from '../system/decorators/serialize'; import { serialize } from '../system/decorators/serialize';
import { isPromise } from '../system/promise'; import { isPromise } from '../system/promise';
import type { WebviewContext } from '../system/webview';
import type { IpcMessage, IpcMessageParams, IpcNotificationType, WebviewFocusChangedParams } from './protocol'; import type { IpcMessage, IpcMessageParams, IpcNotificationType, WebviewFocusChangedParams } from './protocol';
import { ExecuteCommandType, onIpc, WebviewFocusChangedCommandType, WebviewReadyCommandType } from './protocol'; import { ExecuteCommandType, onIpc, WebviewFocusChangedCommandType, WebviewReadyCommandType } from './protocol';
import type { WebviewCommandCallback, WebviewCommandRegistrar } from './webviewCommandRegistrar';
import type { WebviewPanelDescriptor, WebviewViewDescriptor } from './webviewsController'; import type { WebviewPanelDescriptor, WebviewViewDescriptor } from './webviewsController';
const maxSmallIntegerV8 = 2 ** 30; // Max number that can be stored in V8's smis (small integers) const maxSmallIntegerV8 = 2 ** 30; // Max number that can be stored in V8's smis (small integers)
@ -75,6 +77,7 @@ export class WebviewController<
{ {
static async create<State, SerializedState = State>( static async create<State, SerializedState = State>(
container: Container, container: Container,
commandRegistrar: WebviewCommandRegistrar,
descriptor: WebviewPanelDescriptor, descriptor: WebviewPanelDescriptor,
parent: WebviewPanel, parent: WebviewPanel,
resolveProvider: ( resolveProvider: (
@ -84,6 +87,7 @@ export class WebviewController<
): Promise<WebviewController<State, SerializedState, WebviewPanelDescriptor>>; ): Promise<WebviewController<State, SerializedState, WebviewPanelDescriptor>>;
static async create<State, SerializedState = State>( static async create<State, SerializedState = State>(
container: Container, container: Container,
commandRegistrar: WebviewCommandRegistrar,
descriptor: WebviewViewDescriptor, descriptor: WebviewViewDescriptor,
parent: WebviewView, parent: WebviewView,
resolveProvider: ( resolveProvider: (
@ -93,6 +97,7 @@ export class WebviewController<
): Promise<WebviewController<State, SerializedState, WebviewViewDescriptor>>; ): Promise<WebviewController<State, SerializedState, WebviewViewDescriptor>>;
static async create<State, SerializedState = State>( static async create<State, SerializedState = State>(
container: Container, container: Container,
commandRegistrar: WebviewCommandRegistrar,
descriptor: WebviewPanelDescriptor | WebviewViewDescriptor, descriptor: WebviewPanelDescriptor | WebviewViewDescriptor,
parent: WebviewPanel | WebviewView, parent: WebviewPanel | WebviewView,
resolveProvider: ( resolveProvider: (
@ -102,6 +107,7 @@ export class WebviewController<
): Promise<WebviewController<State, SerializedState>> { ): Promise<WebviewController<State, SerializedState>> {
const controller = new WebviewController<State, SerializedState>( const controller = new WebviewController<State, SerializedState>(
container, container,
commandRegistrar,
descriptor, descriptor,
parent, parent,
resolveProvider, resolveProvider,
@ -128,6 +134,7 @@ export class WebviewController<
private constructor( private constructor(
private readonly container: Container, private readonly container: Container,
private readonly _commandRegistrar: WebviewCommandRegistrar,
private readonly descriptor: Descriptor, private readonly descriptor: Descriptor,
public readonly parent: GetParentType<Descriptor>, public readonly parent: GetParentType<Descriptor>,
resolveProvider: ( resolveProvider: (
@ -179,6 +186,10 @@ export class WebviewController<
this.disposable?.dispose(); this.disposable?.dispose();
} }
registerWebviewCommand<T extends Partial<WebviewContext>>(command: string, callback: WebviewCommandCallback<T>) {
return this._commandRegistrar.registerCommand(this.provider, this.id, command, callback);
}
private _initializing: Promise<void> | undefined; private _initializing: Promise<void> | undefined;
private async initialize() { private async initialize() {
if (this._initializing == null) return; if (this._initializing == null) return;

+ 14
- 3
src/webviews/webviewsController.ts View File

@ -15,6 +15,7 @@ import { debug } from '../system/decorators/log';
import { Logger } from '../system/logger'; import { Logger } from '../system/logger';
import { getLogScope } from '../system/logger.scope'; import { getLogScope } from '../system/logger.scope';
import type { TrackedUsageFeatures } from '../telemetry/usageTracker'; import type { TrackedUsageFeatures } from '../telemetry/usageTracker';
import { WebviewCommandRegistrar } from './webviewCommandRegistrar';
import type { WebviewProvider } from './webviewController'; import type { WebviewProvider } from './webviewController';
import { WebviewController } from './webviewController'; import { WebviewController } from './webviewController';
@ -74,10 +75,13 @@ export interface WebviewViewProxy extends Disposable {
export class WebviewsController implements Disposable { export class WebviewsController implements Disposable {
private readonly disposables: Disposable[] = []; private readonly disposables: Disposable[] = [];
private readonly _commandRegistrar: WebviewCommandRegistrar;
private readonly _panels = new Map<string, WebviewPanelRegistration<any>>(); private readonly _panels = new Map<string, WebviewPanelRegistration<any>>();
private readonly _views = new Map<string, WebviewViewRegistration<any>>(); private readonly _views = new Map<string, WebviewViewRegistration<any>>();
constructor(private readonly container: Container) {}
constructor(private readonly container: Container) {
this.disposables.push((this._commandRegistrar = new WebviewCommandRegistrar()));
}
dispose() { dispose() {
this.disposables.forEach(d => void d.dispose()); this.disposables.forEach(d => void d.dispose());
@ -135,6 +139,7 @@ export class WebviewsController implements Disposable {
const controller = await WebviewController.create( const controller = await WebviewController.create(
this.container, this.container,
this._commandRegistrar,
descriptor, descriptor,
webviewView, webviewView,
resolveProvider, resolveProvider,
@ -219,7 +224,7 @@ export class WebviewsController implements Disposable {
this._panels.set(descriptor.id, registration); this._panels.set(descriptor.id, registration);
const disposables: Disposable[] = []; const disposables: Disposable[] = [];
const { container } = this;
const { container, _commandRegistrar: commandRegistrar } = this;
let serializedPanel: WebviewPanel | undefined; let serializedPanel: WebviewPanel | undefined;
@ -273,7 +278,13 @@ export class WebviewsController implements Disposable {
panel.iconPath = Uri.file(container.context.asAbsolutePath(descriptor.iconPath)); panel.iconPath = Uri.file(container.context.asAbsolutePath(descriptor.iconPath));
controller = await WebviewController.create(container, descriptor, panel, resolveProvider);
controller = await WebviewController.create(
container,
commandRegistrar,
descriptor,
panel,
resolveProvider,
);
registration.controller = controller; registration.controller = controller;
disposables.push( disposables.push(

Loading…
Cancel
Save