Parcourir la source

Allows the Graph in a view & a tab simultaneously

main
Eric Amodio il y a 1 an
Parent
révision
3254bf3c49
14 fichiers modifiés avec 399 ajouts et 196 suppressions
  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 Voir le fichier

@ -8,8 +8,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### 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))
### Changed
- Changes the `gitlens.graph.layout` setting to be a default preference rather than a mode change
### Fixed
- Fixes [#2885](https://github.com/gitkraken/vscode-gitlens/issues/2885) - Folder History not show changed files of commit

+ 59
- 44
package.json Voir le fichier

@ -2402,10 +2402,10 @@
"panel"
],
"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",
"order": 99
},
@ -4711,7 +4711,7 @@
},
{
"command": "gitlens.showGraphPage",
"title": "Show Commit Graph",
"title": "Show Commit Graph in Editor Area",
"category": "GitLens",
"icon": "$(gitlens-graph)"
},
@ -4750,6 +4750,12 @@
"icon": "$(gitlens-graph)"
},
{
"command": "gitlens.showInCommitGraphView",
"title": "Open in Commit Graph",
"category": "GitLens",
"icon": "$(gitlens-graph)"
},
{
"command": "gitlens.showLineHistoryView",
"title": "Show Line History View",
"category": "GitLens"
@ -6556,6 +6562,18 @@
"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",
"title": "Refresh",
"category": "GitLens",
@ -7211,13 +7229,15 @@
},
{
"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",
"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",
@ -8065,23 +8085,23 @@
},
{
"command": "gitlens.showGraph",
"when": "false"
"when": "gitlens:enabled"
},
{
"command": "gitlens.showGraphPage",
"when": "gitlens:enabled && config.gitlens.graph.layout == editor"
"when": "gitlens:enabled"
},
{
"command": "gitlens.showGraphView",
"when": "gitlens:enabled && config.gitlens.graph.layout == panel"
"when": "gitlens:enabled"
},
{
"command": "gitlens.toggleGraph",
"when": "gitlens:enabled && config.gitlens.graph.layout == panel"
"when": "gitlens:enabled"
},
{
"command": "gitlens.toggleMaximizedGraph",
"when": "gitlens:enabled && config.gitlens.graph.layout == panel"
"when": "gitlens:enabled"
},
{
"command": "gitlens.showHomeView",
@ -8096,6 +8116,10 @@
"when": "false"
},
{
"command": "gitlens.showInCommitGraphView",
"when": "false"
},
{
"command": "gitlens.showLineHistoryView",
"when": "gitlens:enabled && !gitlens:hasVirtualFolders"
},
@ -9304,6 +9328,14 @@
"when": "false"
},
{
"command": "gitlens.views.graph.openInTab",
"when": "false"
},
{
"command": "gitlens.views.graph.refresh",
"when": "false"
},
{
"command": "gitlens.views.graphDetails.refresh",
"when": "false"
},
@ -10337,26 +10369,6 @@
"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",
"when": "gitlens:webview:graph:active",
"group": "navigation@-99"
@ -10875,6 +10887,16 @@
"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",
"when": "view =~ /^gitlens\\.views\\.graphDetails/",
"group": "navigation@99"
@ -11255,11 +11277,6 @@
"group": "5_gitlens@3"
},
{
"command": "gitlens.graph.refresh",
"when": "view =~ /^gitlens\\.views\\.graph\\b/",
"group": "navigation@-99"
},
{
"submenu": "gitlens/graph/configuration",
"when": "view =~ /^gitlens\\.views\\.graph\\b/",
"group": "navigation@-98"
@ -13684,13 +13701,11 @@
"gitlens/graph/configuration": [
{
"command": "gitlens.graph.switchToEditorLayout",
"group": "1_gitlens@1",
"when": "config.gitlens.graph.layout != editor"
"group": "1_gitlens@1"
},
{
"command": "gitlens.graph.switchToPanelLayout",
"group": "1_gitlens@1",
"when": "config.gitlens.graph.layout != panel"
"group": "1_gitlens@1"
},
{
"command": "gitlens.showSettingsPage#commit-graph",
@ -14559,7 +14574,7 @@
"type": "webview",
"id": "gitlens.views.graph",
"name": "Graph",
"when": "!gitlens:disabled && gitlens:plus:enabled && config.gitlens.graph.layout == panel",
"when": "!gitlens:disabled && gitlens:plus:enabled",
"contextualTitle": "GL",
"icon": "$(gitlens-graph)",
"initialSize": 4,
@ -14569,7 +14584,7 @@
"type": "webview",
"id": "gitlens.views.graphDetails",
"name": "Graph Details",
"when": "!gitlens:disabled && gitlens:plus:enabled && config.gitlens.graph.layout == panel",
"when": "!gitlens:disabled && gitlens:plus:enabled",
"contextualTitle": "GL",
"icon": "$(gitlens-commit-view)",
"initialSize": 1,

+ 1
- 0
src/constants.ts Voir le fichier

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

+ 2
- 18
src/container.ts Voir le fichier

@ -226,9 +226,7 @@ export class Container {
this._disposables.push((this._graphPanel = registerGraphWebviewPanel(this._webviews)));
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));
const settingsWebviewPanel = registerSettingsWebviewPanel(this._webviews);
@ -324,16 +322,6 @@ export class Container {
if (configuration.changed(e, 'mode')) {
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;
@ -482,12 +470,8 @@ export class Container {
}
private readonly _graphPanel: WebviewPanelProxy;
private _graphView: WebviewViewProxy | undefined;
private readonly _graphView: WebviewViewProxy;
get graphView() {
if (this._graphView == null) {
this._disposables.push((this._graphView = registerGraphWebviewView(this._webviews)));
}
return this._graphView;
}

+ 147
- 111
src/plus/webviews/graph/graphWebview.ts Voir le fichier

@ -347,122 +347,161 @@ export class GraphWebviewProvider implements WebviewProvider {
registerCommands(): Disposable[] {
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),
),
registerCommand('gitlens.graph.scrollMarkerLocalBranchOff', () =>
this.host.registerWebviewCommand('gitlens.graph.scrollMarkerLocalBranchOff', () =>
this.toggleScrollMarker('localBranches', false),
),
registerCommand('gitlens.graph.scrollMarkerRemoteBranchOn', () =>
this.host.registerWebviewCommand('gitlens.graph.scrollMarkerRemoteBranchOn', () =>
this.toggleScrollMarker('remoteBranches', true),
),
registerCommand('gitlens.graph.scrollMarkerRemoteBranchOff', () =>
this.host.registerWebviewCommand('gitlens.graph.scrollMarkerRemoteBranchOff', () =>
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) {
void details.show({ preserveFocus: e.preserveFocus }, {
commit: commit,

+ 16
- 2
src/plus/webviews/graph/registration.ts Voir le fichier

@ -34,7 +34,6 @@ export function registerGraphWebviewPanel(controller: WebviewsController) {
const { GraphWebviewProvider } = await import(/* webpackChunkName: "graph" */ './graphWebview');
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');
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 Voir le fichier

@ -5,6 +5,9 @@ import type { Command } from '../commands/base';
import type { CoreCommands, CoreGitCommands, TreeViewCommands } from '../constants';
import { Commands } from '../constants';
import { Container } from '../container';
import { isWebviewContext } from './webview';
export type CommandCallback = Parameters<typeof commands.registerCommand>[1];
type CommandConstructor = new (container: Container) => Command;
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(
command,
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[] {
return registrableCommands.map(c => new c(container));
}

+ 23
- 8
src/system/webview.ts Voir le fichier

@ -1,31 +1,46 @@
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;
webviewItemValue: TValue;
}
export function isWebviewItemContext<TValue = unknown>(
item: object | null | undefined,
): item is WebviewItemContext<TValue> {
): item is WebviewItemContext<TValue> & WebviewContext {
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;
webviewItemGroupValue: TValue;
}
export function isWebviewItemGroupContext<TValue = unknown>(
item: object | null | undefined,
): item is WebviewItemGroupContext<TValue> {
): item is WebviewItemGroupContext<TValue> & WebviewContext {
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 {

+ 12
- 6
src/webviews/apps/plus/graph/GraphWrapper.tsx Voir le fichier

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

+ 12
- 0
src/webviews/apps/settings/partials/commit-graph.html Voir le fichier

@ -26,6 +26,18 @@
<div class="settings settings--fixed ml-1">
<div class="setting">
<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>
<input
id="graph.defaultItemLimit"

+ 8
- 3
src/webviews/commitDetails/commitDetailsWebview.ts Voir le fichier

@ -369,9 +369,14 @@ export class CommitDetailsWebviewProvider implements WebviewProvider
case 'graph':
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;
case 'more':
this.showCommitActions();

+ 70
- 0
src/webviews/webviewCommandRegistrar.ts Voir le fichier

@ -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 Voir le fichier

@ -8,8 +8,10 @@ import { setContext } from '../system/context';
import { debug, logName } from '../system/decorators/log';
import { serialize } from '../system/decorators/serialize';
import { isPromise } from '../system/promise';
import type { WebviewContext } from '../system/webview';
import type { IpcMessage, IpcMessageParams, IpcNotificationType, WebviewFocusChangedParams } from './protocol';
import { ExecuteCommandType, onIpc, WebviewFocusChangedCommandType, WebviewReadyCommandType } from './protocol';
import type { WebviewCommandCallback, WebviewCommandRegistrar } from './webviewCommandRegistrar';
import type { WebviewPanelDescriptor, WebviewViewDescriptor } from './webviewsController';
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>(
container: Container,
commandRegistrar: WebviewCommandRegistrar,
descriptor: WebviewPanelDescriptor,
parent: WebviewPanel,
resolveProvider: (
@ -84,6 +87,7 @@ export class WebviewController<
): Promise<WebviewController<State, SerializedState, WebviewPanelDescriptor>>;
static async create<State, SerializedState = State>(
container: Container,
commandRegistrar: WebviewCommandRegistrar,
descriptor: WebviewViewDescriptor,
parent: WebviewView,
resolveProvider: (
@ -93,6 +97,7 @@ export class WebviewController<
): Promise<WebviewController<State, SerializedState, WebviewViewDescriptor>>;
static async create<State, SerializedState = State>(
container: Container,
commandRegistrar: WebviewCommandRegistrar,
descriptor: WebviewPanelDescriptor | WebviewViewDescriptor,
parent: WebviewPanel | WebviewView,
resolveProvider: (
@ -102,6 +107,7 @@ export class WebviewController<
): Promise<WebviewController<State, SerializedState>> {
const controller = new WebviewController<State, SerializedState>(
container,
commandRegistrar,
descriptor,
parent,
resolveProvider,
@ -128,6 +134,7 @@ export class WebviewController<
private constructor(
private readonly container: Container,
private readonly _commandRegistrar: WebviewCommandRegistrar,
private readonly descriptor: Descriptor,
public readonly parent: GetParentType<Descriptor>,
resolveProvider: (
@ -179,6 +186,10 @@ export class WebviewController<
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 async initialize() {
if (this._initializing == null) return;

+ 14
- 3
src/webviews/webviewsController.ts Voir le fichier

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

Chargement…
Annuler
Enregistrer