瀏覽代碼

Allows the Graph in a view & a tab simultaneously

main
Eric Amodio 1 年之前
父節點
當前提交
3254bf3c49
共有 14 個檔案被更改,包括 399 行新增196 行删除
  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 查看文件

@ -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 查看文件

@ -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 查看文件

@ -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 查看文件

@ -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 查看文件

@ -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 查看文件

@ -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 查看文件

@ -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 查看文件

@ -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 查看文件

@ -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 查看文件

@ -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 查看文件

@ -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 查看文件

@ -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 查看文件

@ -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 查看文件

@ -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(

Loading…
取消
儲存