Browse Source

Adds history explorer

main
Eric Amodio 6 years ago
parent
commit
c34ed2d13f
33 changed files with 629 additions and 206 deletions
  1. +4
    -0
      CHANGELOG.md
  2. +48
    -22
      README.md
  3. BIN
      images/cl-history-explorer.png
  4. +4
    -0
      images/dark/icon-dock.svg
  5. +4
    -0
      images/dark/icon-undock.svg
  6. +4
    -0
      images/light/icon-dock.svg
  7. +4
    -0
      images/light/icon-undock.svg
  8. BIN
      images/ss-gitlens-explorer-history.png
  9. BIN
      images/ss-gitlens-history-explorer.png
  10. +134
    -9
      package.json
  11. +3
    -0
      src/configuration.ts
  12. +1
    -0
      src/constants.ts
  13. +23
    -0
      src/container.ts
  14. +6
    -0
      src/ui/config.ts
  15. BIN
      src/ui/images/settings/gitlens-explorer-history.png
  16. BIN
      src/ui/images/settings/gitlens-explorer-repository-history-docked.png
  17. BIN
      src/ui/images/settings/gitlens-explorer-repository-history-undocked.png
  18. BIN
      src/ui/images/settings/gitlens-explorer-repository-tree-compact.png
  19. BIN
      src/ui/images/settings/gitlens-explorer-repository-tree.png
  20. BIN
      src/ui/images/settings/gitlens-explorer-repository.png
  21. BIN
      src/ui/images/settings/gitlens-history-explorer-close.png
  22. BIN
      src/ui/images/settings/gitlens-history-explorer.png
  23. +5
    -9
      src/ui/scss/main.scss
  24. +58
    -11
      src/ui/settings/index.html
  25. +6
    -0
      src/ui/welcome/index.html
  26. +2
    -2
      src/views/branchFolderNode.ts
  27. +2
    -1
      src/views/explorerNode.ts
  28. +5
    -29
      src/views/fileHistoryNode.ts
  29. +2
    -2
      src/views/folderNode.ts
  30. +132
    -109
      src/views/gitExplorer.ts
  31. +178
    -0
      src/views/historyExplorer.ts
  32. +2
    -3
      src/views/historyNode.ts
  33. +2
    -9
      src/views/resultsExplorer.ts

+ 4
- 0
CHANGELOG.md View File

@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
## [8.2.0-beta] - 2018-03-26 ## [8.2.0-beta] - 2018-03-26
### Added ### Added
- Adds new *GitLens History* explorer to explore the history of the current file — same as the history view in the *GitLens* explorer when undocked
![GitLens History explorer](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/ss-gitlens-history-explorer.png)
- Adds rich tooltip details to the *GitLens* explorer and *GitLens Results* view - Adds rich tooltip details to the *GitLens* explorer and *GitLens Results* view
- Adds richer working tree and upstream status information to branches in the *GitLens* explorer - Adds richer working tree and upstream status information to branches in the *GitLens* explorer
- Adds an indicator to the *GitLens* explorer's branch history to mark the synchronization point between the local and remote branch (if available) - Adds an indicator to the *GitLens* explorer's branch history to mark the synchronization point between the local and remote branch (if available)

+ 48
- 22
README.md View File

@ -14,13 +14,18 @@
# What's new in GitLens 8 # What's new in GitLens 8
## 8.2 — April 2018 ## 8.2 — April 2018
- Adds rich tooltip details to the *GitLens* explorer and *GitLens Results* view
- Adds richer working tree and upstream status information to branches in the *GitLens* explorer
- Adds new *GitLens History* explorer to explore the history of the current file — same as the history view in the *GitLens* explorer when undocked
- Adds richer tooltips to the *GitLens* explorer and *GitLens Results* view, and richer working tree and upstream status to the *GitLens* explorer
- Adds an indicator to the *GitLens* explorer's branch history to mark the synchronization point between the local and remote branch (if available) - Adds an indicator to the *GitLens* explorer's branch history to mark the synchronization point between the local and remote branch (if available)
- Adds ability to easily switch between relative and absolute dates via the `gitlens.defaultDateStyle` settings — closes [#312](https://github.com/eamodio/vscode-gitlens/issues/312) - Adds ability to easily switch between relative and absolute dates via the `gitlens.defaultDateStyle` settings — closes [#312](https://github.com/eamodio/vscode-gitlens/issues/312)
- Adds `${agoOrDate}` and `${authorAgoOrDate}` tokens to `gitlens.blame.format`, `gitlens.currentLine.format`, `gitlens.explorers.commitFormat`, `gitlens.explorers.stashFormat`, and `gitlens.statusBar.format` settings which will honor the `gitlens.defaultDateStyle` setting
- Adds annotation format settings (`gitlens.*.format`) to the interactive settings editor
- Adds `gitlens.currentLine.scrollable` setting to specify whether the current line blame annotation can be scrolled into view when it is outside the viewport — closes [#149](https://github.com/eamodio/vscode-gitlens/issues/149), [#290](https://github.com/eamodio/vscode-gitlens/issues/290), [#265](https://github.com/eamodio/vscode-gitlens/issues/265) - Adds `gitlens.currentLine.scrollable` setting to specify whether the current line blame annotation can be scrolled into view when it is outside the viewport — closes [#149](https://github.com/eamodio/vscode-gitlens/issues/149), [#290](https://github.com/eamodio/vscode-gitlens/issues/290), [#265](https://github.com/eamodio/vscode-gitlens/issues/265)
- Adds `gitlens.statusBar.reduceFlicker` setting to the interactive settings editor
- Adds a one-time notification on startup if the `alt-based` keyboard shortcuts are in use, with options to easily switch to another set
- Adds *Copy Commit ID to Clipboard* (`gitlens.copyShaToClipboard`) command to changed file nodes in the *GitLens* explorer and *GitLens Results* view - Adds *Copy Commit ID to Clipboard* (`gitlens.copyShaToClipboard`) command to changed file nodes in the *GitLens* explorer and *GitLens Results* view
- Adds *Copy Commit Message to Clipboard* (`gitlens.copyMessageToClipboard`) command to changed file nodes in the *GitLens* explorer and *GitLens Results* view - Adds *Copy Commit Message to Clipboard* (`gitlens.copyMessageToClipboard`) command to changed file nodes in the *GitLens* explorer and *GitLens Results* view
- Moves *Keyboard Settings* to the *General* section of the interactive settings editor
- Renames *Compare with Index (HEAD)* (`gitlens.explorers.compareWithHead`) command to *Compare with HEAD* — closes [#309](https://github.com/eamodio/vscode-gitlens/issues/309) - Renames *Compare with Index (HEAD)* (`gitlens.explorers.compareWithHead`) command to *Compare with HEAD* — closes [#309](https://github.com/eamodio/vscode-gitlens/issues/309)
- Renames *Compare Index (HEAD) with Branch or Tag...* (`gitlens.diffHeadWithBranch`) command to *Compare HEAD with Branch or Tag...* — closes [#309](https://github.com/eamodio/vscode-gitlens/issues/309) - Renames *Compare Index (HEAD) with Branch or Tag...* (`gitlens.diffHeadWithBranch`) command to *Compare HEAD with Branch or Tag...* — closes [#309](https://github.com/eamodio/vscode-gitlens/issues/309)
- Removes the unnecessary *Show File Blame Annotations* (`gitlens.showFileBlame`) command — *Toggle File Blame Annotations* (`gitlens.toggleFileBlame`) provides similar functionality - Removes the unnecessary *Show File Blame Annotations* (`gitlens.showFileBlame`) command — *Toggle File Blame Annotations* (`gitlens.toggleFileBlame`) provides similar functionality
@ -95,7 +100,7 @@ For more advanced customizations, refer to the [settings documentation](#gitlens
## Features ## Features
### GitLens Explorer ### GitLens Explorer
A [customizable](#gitlens-explorer-settings "Jump to the GitLens Explorer settings") explorer to navigate and explore repositories or file histories. The GitLens explorer provides two views (modes) — a Repository view and a History view.
A [customizable](#gitlens-explorer-settings "Jump to the GitLens Explorer settings") explorer to navigate and explore repositories or file histories. The *GitLens* explorer provides two views (modes) — a Repository view and a History view.
- A toolbar provides *Search Commits*, *Switch to Repository View* or *Switch to History View*, and *Refresh* commands - A toolbar provides *Search Commits*, *Switch to Repository View* or *Switch to History View*, and *Refresh* commands
- Quickly switch between views using the *Switch to Repository View* or *Switch to History View* commands - Quickly switch between views using the *Switch to Repository View* or *Switch to History View* commands
- A context menu provides *Automatic Layout*, *List Layout*, *Tree Layout*, *Enable Automatic Refresh* or *Disable Automatic Refresh*, and *Follow Renames* or *Don't Follow Renames* commands - A context menu provides *Automatic Layout*, *List Layout*, *Tree Layout*, *Enable Automatic Refresh* or *Disable Automatic Refresh*, and *Follow Renames* or *Don't Follow Renames* commands
@ -169,8 +174,21 @@ The repository view provides a full Git repository explorer, which has the follo
<img src="https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/ss-gitlens-explorer-history.png" alt="GitLens Explorer History view" /> <img src="https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/ss-gitlens-explorer-history.png" alt="GitLens Explorer History view" />
</p> </p>
The history view provides the revision history of the active file, which has the following features,
- Automatically updates to track the active editor
The history view provides the revision history of the current file, which has the following features,
- Automatically updates to track the current editor
- A context menu provides *Open File*, *Open File in Remote* (if available), and *Refresh* commands
- An inline toolbar provides an *Open File* command
- Context menus for each revision (commit) provides
- *Open Changes*, *Open Changes with Working File*, *Open File*, *Open Revision*, *Open File in Remote* (if available), *Open Revision in Remote* (if available), *Apply Changes*, and *Show Commit File Details* commands
---
### GitLens History Explorer
<p align="center">
<img src="https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/ss-gitlens-history-explorer.png" alt="GitLens History Explorer" />
</p>
A [customizable](#gitlens-history-explorer-settings "Jump to the GitLens History Explorer settings") explorer to explore the history of the current file. This is same as the history view in the *GitLens* Explorer when undocked
- Automatically updates to track the current editor
- A context menu provides *Open File*, *Open File in Remote* (if available), and *Refresh* commands - A context menu provides *Open File*, *Open File in Remote* (if available), and *Refresh* commands
- An inline toolbar provides an *Open File* command - An inline toolbar provides an *Open File* command
- Context menus for each revision (commit) provides - Context menus for each revision (commit) provides
@ -383,8 +401,8 @@ An on-demand, [customizable](#gitlens-results-view-settings "Jump to the GitLens
- Also supports [remote services with custom domains](#custom-remotes-settings "Jump to Custom Remotes settings"), such as **Bitbucket, Bitbucket Server (previously called Stash), GitHub, GitHub Enterprise, GitLab** - Also supports [remote services with custom domains](#custom-remotes-settings "Jump to Custom Remotes settings"), such as **Bitbucket, Bitbucket Server (previously called Stash), GitHub, GitHub Enterprise, GitLab**
- *Open Branches in Remote* command (`gitlens.openBranchesInRemote`) &mdash; opens the branches in the supported remote service - *Open Branches in Remote* command (`gitlens.openBranchesInRemote`) &mdash; opens the branches in the supported remote service
- *Open Branch in Remote* command (`gitlens.openBranchInRemote`) &mdash; opens the current branch commits in the supported remote service - *Open Branch in Remote* command (`gitlens.openBranchInRemote`) &mdash; opens the current branch commits in the supported remote service
- *Open Commit in Remote* command (`gitlens.openCommitInRemote`) &mdash; opens the commit revision of the active line in the supported remote service
- *Open File in Remote* command (`gitlens.openFileInRemote`) &mdash; opens the active file/revision in the supported remote service
- *Open Commit in Remote* command (`gitlens.openCommitInRemote`) &mdash; opens the commit revision of the current line in the supported remote service
- *Open File in Remote* command (`gitlens.openFileInRemote`) &mdash; opens the current file/revision in the supported remote service
- *Open Repository in Remote* command (`gitlens.openRepoInRemote`) &mdash; opens the repository in the supported remote service - *Open Repository in Remote* command (`gitlens.openRepoInRemote`) &mdash; opens the repository in the supported remote service
#### Branch History #### Branch History
@ -405,7 +423,7 @@ An on-demand, [customizable](#gitlens-results-view-settings "Jump to the GitLens
<img src="https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/ss-menu-file-history.png" alt="File History Quick Pick Menu" /> <img src="https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/ss-menu-file-history.png" alt="File History Quick Pick Menu" />
</p> </p>
- Adds a *Show File History* command (`gitlens.showQuickFileHistory`) to show a paged **file history quick pick menu** of the active file for exploring its commit history
- Adds a *Show File History* command (`gitlens.showQuickFileHistory`) to show a paged **file history quick pick menu** of the current file for exploring its commit history
- Provides additional entries to *Show in Results*, *Show Branch History*, and *Open File in \<remote-service\>* when available - Provides additional entries to *Show in Results*, *Show Branch History*, and *Open File in \<remote-service\>* when available
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available - Navigate back to the previous quick pick menu via `alt+left arrow`, if available
- Navigate pages via `alt+,` and `alt+.` to go backward and forward respectively - Navigate pages via `alt+,` and `alt+.` to go backward and forward respectively
@ -415,7 +433,7 @@ An on-demand, [customizable](#gitlens-results-view-settings "Jump to the GitLens
<img src="https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/ss-menu-commit-details.png" alt="Commit Details Quick Pick Menu" /> <img src="https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/ss-menu-commit-details.png" alt="Commit Details Quick Pick Menu" />
</p> </p>
- Adds a *Show Commit Details* command (`gitlens.showQuickCommitDetails`) to show a **commit details quick pick menu** of the most recent commit of the active file
- Adds a *Show Commit Details* command (`gitlens.showQuickCommitDetails`) to show a **commit details quick pick menu** of the most recent commit of the current file
- Quickly see the set of files changed in the commit, complete with status indicators for adds, changes, renames, and deletes - Quickly see the set of files changed in the commit, complete with status indicators for adds, changes, renames, and deletes
- Provides additional entries to *Show in Results*, *Open Commit in \<remote-service\>* when available, *Open Files*, *Open Revisions*, *Open Directory Compare with Previous Revision*, *Open Directory Compare with Working Tree*, *Copy Commit ID to Clipboard*, *Copy Commit Message to Clipboard* - Provides additional entries to *Show in Results*, *Open Commit in \<remote-service\>* when available, *Open Files*, *Open Revisions*, *Open Directory Compare with Previous Revision*, *Open Directory Compare with Working Tree*, *Copy Commit ID to Clipboard*, *Copy Commit Message to Clipboard*
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available - Navigate back to the previous quick pick menu via `alt+left arrow`, if available
@ -426,7 +444,7 @@ An on-demand, [customizable](#gitlens-results-view-settings "Jump to the GitLens
<img src="https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/ss-menu-commit-file-details.png" alt="Commit File Details Quick Pick Menu" /> <img src="https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/ss-menu-commit-file-details.png" alt="Commit File Details Quick Pick Menu" />
</p> </p>
- Adds a *Show Commit File Details* command (`gitlens.showQuickCommitFileDetails`) with a shortcut of `alt+c` to show a **file commit details quick pick menu** of the most recent commit of the active file
- Adds a *Show Commit File Details* command (`gitlens.showQuickCommitFileDetails`) with a shortcut of `alt+c` to show a **file commit details quick pick menu** of the most recent commit of the current file
- Provides entries to *Open Changes*, *Open Changes with Working File*, *Open File*, *Open Revision*, *Open File in \<remote-service\>* when available, *Open Revision in \<remote-service\>* when available, *Copy Commit ID to Clipboard*, *Copy Commit Message to Clipboard*, *Show Commit Details*, *Show File History*, and *Show Previous File History* - Provides entries to *Open Changes*, *Open Changes with Working File*, *Open File*, *Open Revision*, *Open File in \<remote-service\>* when available, *Open Revision in \<remote-service\>* when available, *Copy Commit ID to Clipboard*, *Copy Commit Message to Clipboard*, *Show Commit Details*, *Show File History*, and *Show Previous File History*
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available - Navigate back to the previous quick pick menu via `alt+left arrow`, if available
- Use the `alt+right arrow` shortcut on an entry to execute it without closing the quick pick menu, if possible &mdash; commands that open windows outside of VS Code will still close the quick pick menu unless [`"gitlens.advanced.quickPick.closeOnFocusOut": false`](#advanced-settings "Jump to Advanced settings") is set - Use the `alt+right arrow` shortcut on an entry to execute it without closing the quick pick menu, if possible &mdash; commands that open windows outside of VS Code will still close the quick pick menu unless [`"gitlens.advanced.quickPick.closeOnFocusOut": false`](#advanced-settings "Jump to Advanced settings") is set
@ -484,30 +502,30 @@ An on-demand, [customizable](#gitlens-results-view-settings "Jump to the GitLens
- Adds a *Compare Working Tree with Branch or Tag...* command (`gitlens.diffWorkingWithBranch`) to compare the working tree with the selected branch or tag - Adds a *Compare Working Tree with Branch or Tag...* command (`gitlens.diffWorkingWithBranch`) to compare the working tree with the selected branch or tag
- Adds a *Compare File with Branch or Tag...* command (`gitlens.diffWithBranch`) to compare the active file with the same file on the selected branch or tag
- Adds a *Compare File with Branch or Tag...* command (`gitlens.diffWithBranch`) to compare the current file with the same file on the selected branch or tag
- Adds a *Compare File with Next Revision* command (`gitlens.diffWithNext`) with a shortcut of `alt+.` to compare the active file/diff with the next commit revision
- Adds a *Compare File with Next Revision* command (`gitlens.diffWithNext`) with a shortcut of `alt+.` to compare the current file/diff with the next commit revision
- Adds a *Compare File with Previous Revision* command (`gitlens.diffWithPrevious`) with a shortcut of `alt+,` to compare the active file/diff with the previous commit revision
- Adds a *Compare File with Previous Revision* command (`gitlens.diffWithPrevious`) with a shortcut of `alt+,` to compare the current file/diff with the previous commit revision
- Adds a *Compare Line Revision with Previous* command (`gitlens.diffLineWithPrevious`) with a shortcut of `shift+alt+,` to compare the active file/diff with the previous line commit revision
- Adds a *Compare Line Revision with Previous* command (`gitlens.diffLineWithPrevious`) with a shortcut of `shift+alt+,` to compare the current file/diff with the previous line commit revision
- Adds a *Compare File with Revision...* command (`gitlens.diffWithRevision`) to compare the active file with the selected revision of the same file
- Adds a *Compare File with Revision...* command (`gitlens.diffWithRevision`) to compare the current file with the selected revision of the same file
- Adds a *Compare File with Working Revision* command (`gitlens.diffWithWorking`) with a shortcut of `shift+alt+w` to compare the most recent commit revision of the active file/diff with the working tree
- Adds a *Compare File with Working Revision* command (`gitlens.diffWithWorking`) with a shortcut of `shift+alt+w` to compare the most recent commit revision of the current file/diff with the working tree
- Adds a *Compare Line Revision with Working File* command (`gitlens.diffLineWithWorking`) with a shortcut of `alt+w` to compare the commit revision of the active line with the working tree
- Adds a *Compare Line Revision with Working File* command (`gitlens.diffLineWithWorking`) with a shortcut of `alt+w` to compare the commit revision of the current line with the working tree
--- ---
### And More ### And More
- Adds a *Copy Commit ID to Clipboard* command (`gitlens.copyShaToClipboard`) to copy the commit id (sha) of the active line to the clipboard or from the most recent commit to the current branch, if there is no active editor
- Adds a *Copy Commit ID to Clipboard* command (`gitlens.copyShaToClipboard`) to copy the commit id (sha) of the current line to the clipboard or from the most recent commit to the current branch, if there is no current editor
- Adds a *Copy Commit Message to Clipboard* command (`gitlens.copyMessageToClipboard`) to copy the commit message of the active line to the clipboard or from the most recent commit to the current branch, if there is no active editor
- Adds a *Copy Commit Message to Clipboard* command (`gitlens.copyMessageToClipboard`) to copy the commit message of the current line to the clipboard or from the most recent commit to the current branch, if there is no current editor
- Adds a *Open Working File"* command (`gitlens.openWorkingFile`) to open the working file for the active file revision
- Adds a *Open Working File"* command (`gitlens.openWorkingFile`) to open the working file for the current file revision
- Adds a *Open Revision...* command (`gitlens.openFileRevision`) to open the selected revision for the active file
- Adds a *Open Revision...* command (`gitlens.openFileRevision`) to open the selected revision for the current file
- Adds a *Open Changes (with difftool)* command (`gitlens.externalDiff`) to the source control group and source control resource context menus to open the changes of a file or set of files with the configured git difftool - Adds a *Open Changes (with difftool)* command (`gitlens.externalDiff`) to the source control group and source control resource context menus to open the changes of a file or set of files with the configured git difftool
@ -549,7 +567,15 @@ See also [Explorer Settings](#explorer-settings "Jump to the Explorer settings")
|`gitlens.gitExplorer.files.threshold`|Specifies when to switch between displaying files as a `tree` or `list` based on the number of files in a nesting level in the *GitLens* explorer<br />Only applies when displaying files as `auto` |`gitlens.gitExplorer.files.threshold`|Specifies when to switch between displaying files as a `tree` or `list` based on the number of files in a nesting level in the *GitLens* explorer<br />Only applies when displaying files as `auto`
|`gitlens.gitExplorer.includeWorkingTree`|Specifies whether to include working tree files inside the `Repository Status` node of the *GitLens* explorer |`gitlens.gitExplorer.includeWorkingTree`|Specifies whether to include working tree files inside the `Repository Status` node of the *GitLens* explorer
|`gitlens.gitExplorer.showTrackingBranch`|Specifies whether to show the tracking branch when displaying local branches in the *GitLens* explorer" |`gitlens.gitExplorer.showTrackingBranch`|Specifies whether to show the tracking branch when displaying local branches in the *GitLens* explorer"
|`gitlens.gitExplorer.view`|Specifies the starting view (mode) of the *GitLens* explorer<br /> `auto` - shows the last selected view, defaults to `repository`<br />`history` - shows the commit history of the active file<br />`repository` - shows a repository explorer"
|`gitlens.gitExplorer.view`|Specifies the starting view of the *GitLens* explorer<br /> `auto` - shows the last selected view, defaults to `repository`<br />`history` - shows the commit history of the current file<br />`repository` - shows a repository explorer"
### GitLens History Explorer Settings
See also [Explorer Settings](#explorer-settings "Jump to the Explorer settings")
|Name | Description
|-----|------------
|`gitlens.historyExplorer.enabled`|Specifies whether to show the current file history undocked in a *GitLens History* explorer
### GitLens Results View Settings ### GitLens Results View Settings

BIN
images/cl-history-explorer.png View File

Before After
Width: 610  |  Height: 370  |  Size: 62 KiB

+ 4
- 0
images/dark/icon-dock.svg View File

@ -0,0 +1,4 @@
<svg width="16" height="22" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="M12.7 8.12L8 12.82 3.26 8.1l.8-.8 3.35 3.35V3.32h1.15v7.35l3.34-3.35"/>
<rect fill="#C5C5C5" rx="1" height="6.07" width="12.38" y="13.53" x="1.81"/>
</svg>

+ 4
- 0
images/dark/icon-undock.svg View File

@ -0,0 +1,4 @@
<svg width="16" height="22" xmlns="http://www.w3.org/2000/svg">
<path fill="#C5C5C5" d="M3.28 7.46l4.7-4.71 4.74 4.74-.8.8-3.35-3.35v7.32H7.43V4.9L4.08 8.26"/>
<rect fill="#C5C5C5" rx="1" height="6.07" width="12.38" y="13.53" x="1.81"/>
</svg>

+ 4
- 0
images/light/icon-dock.svg View File

@ -0,0 +1,4 @@
<svg width="16" height="22" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="M12.7 8.12L8 12.82 3.26 8.1l.8-.8 3.35 3.35V3.32h1.15v7.35l3.34-3.35"/>
<rect fill="#424242" rx="1" height="6.07" width="12.38" y="13.53" x="1.81"/>
</svg>

+ 4
- 0
images/light/icon-undock.svg View File

@ -0,0 +1,4 @@
<svg width="16" height="22" xmlns="http://www.w3.org/2000/svg">
<path fill="#424242" d="M3.28 7.46l4.7-4.71 4.74 4.74-.8.8-3.35-3.35v7.32H7.43V4.9L4.08 8.26"/>
<rect fill="#424242" rx="1" height="6.07" width="12.38" y="13.53" x="1.81"/>
</svg>

BIN
images/ss-gitlens-explorer-history.png View File

Before After
Width: 610  |  Height: 370  |  Size: 62 KiB Width: 610  |  Height: 370  |  Size: 62 KiB

BIN
images/ss-gitlens-history-explorer.png View File

Before After
Width: 610  |  Height: 370  |  Size: 62 KiB

+ 134
- 9
package.json View File

@ -472,7 +472,7 @@
"history", "history",
"repository" "repository"
], ],
"description": "Specifies the starting view (mode) of the `GitLens` explorer\n `auto` - shows the last selected view, defaults to `repository`\n `history` - shows the commit history of the active file\n `repository` - shows a repository explorer",
"description": "Specifies the starting view of the `GitLens` explorer\n `auto` - shows the last selected view, defaults to `repository`\n `history` - shows the commit history of the current file\n `repository` - shows a repository explorer",
"scope": "window" "scope": "window"
}, },
"gitlens.heatmap.toggleMode": { "gitlens.heatmap.toggleMode": {
@ -485,6 +485,12 @@
"description": "Specifies how the gutter heatmap annotations will be toggled\n `file` - toggle each file individually\n `window` - toggle the window, i.e. all files at once", "description": "Specifies how the gutter heatmap annotations will be toggled\n `file` - toggle each file individually\n `window` - toggle the window, i.e. all files at once",
"scope": "window" "scope": "window"
}, },
"gitlens.historyExplorer.enabled": {
"type": "boolean",
"default": false,
"description": "Specifies whether to show the current file history undocked in a `GitLens History` explorer",
"scope": "window"
},
"gitlens.hovers.annotations.changes": { "gitlens.hovers.annotations.changes": {
"type": "boolean", "type": "boolean",
"default": true, "default": true,
@ -1555,6 +1561,15 @@
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.gitExplorer.undockHistory",
"title": "Undock History from GitLens Explorer",
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-undock.svg",
"light": "images/light/icon-undock.svg"
}
},
{
"command": "gitlens.gitExplorer.refresh", "command": "gitlens.gitExplorer.refresh",
"title": "Refresh", "title": "Refresh",
"category": "GitLens", "category": "GitLens",
@ -1622,6 +1637,48 @@
} }
}, },
{ {
"command": "gitlens.historyExplorer.close",
"title": "Close",
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-close.svg",
"light": "images/light/icon-close.svg"
}
},
{
"command": "gitlens.historyExplorer.dock",
"title": "Dock History to GitLens Explorer",
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-dock.svg",
"light": "images/light/icon-dock.svg"
}
},
{
"command": "gitlens.historyExplorer.refresh",
"title": "Refresh",
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-refresh.svg",
"light": "images/light/icon-refresh.svg"
}
},
{
"command": "gitlens.historyExplorer.refreshNode",
"title": "Refresh",
"category": "GitLens"
},
{
"command": "gitlens.historyExplorer.setRenameFollowingOn",
"title": "Follow Renames",
"category": "GitLens"
},
{
"command": "gitlens.historyExplorer.setRenameFollowingOff",
"title": "Don't Follow Renames",
"category": "GitLens"
},
{
"command": "gitlens.resultsExplorer.clearResultsNode", "command": "gitlens.resultsExplorer.clearResultsNode",
"title": "Clear Results", "title": "Clear Results",
"category": "GitLens", "category": "GitLens",
@ -2044,11 +2101,39 @@
}, },
{ {
"command": "gitlens.gitExplorer.switchToHistoryView", "command": "gitlens.gitExplorer.switchToHistoryView",
"when": "gitlens:enabled && gitlens:gitExplorer:view == repository"
"when": "gitlens:enabled && !gitlens:historyExplorer && gitlens:gitExplorer:view == repository"
}, },
{ {
"command": "gitlens.gitExplorer.switchToRepositoryView", "command": "gitlens.gitExplorer.switchToRepositoryView",
"when": "gitlens:enabled && gitlens:gitExplorer:view == history"
"when": "gitlens:enabled && !gitlens:historyExplorer && gitlens:gitExplorer:view == history"
},
{
"command": "gitlens.gitExplorer.undockHistory",
"when": "gitlens:enabled && !gitlens:historyExplorer"
},
{
"command": "gitlens.historyExplorer.close",
"when": "false"
},
{
"command": "gitlens.historyExplorer.dock",
"when": "gitlens:enabled && gitlens:historyExplorer"
},
{
"command": "gitlens.historyExplorer.refresh",
"when": "false"
},
{
"command": "gitlens.historyExplorer.refreshNode",
"when": "false"
},
{
"command": "gitlens.historyExplorer.setRenameFollowingOn",
"when": "false"
},
{
"command": "gitlens.historyExplorer.setRenameFollowingOff",
"when": "false"
}, },
{ {
"command": "gitlens.resultsExplorer.clearResultsNode", "command": "gitlens.resultsExplorer.clearResultsNode",
@ -2321,19 +2406,24 @@
"group": "navigation@1" "group": "navigation@1"
}, },
{ {
"command": "gitlens.gitExplorer.switchToHistoryView",
"when": "view == gitlens.gitExplorer && gitlens:gitExplorer:view == repository",
"command": "gitlens.gitExplorer.undockHistory",
"when": "view == gitlens.gitExplorer && gitlens:gitExplorer:view == history && !gitlens:historyExplorer",
"group": "navigation@2" "group": "navigation@2"
}, },
{ {
"command": "gitlens.gitExplorer.switchToHistoryView",
"when": "view == gitlens.gitExplorer && !gitlens:historyExplorer && gitlens:gitExplorer:view == repository",
"group": "navigation@3"
},
{
"command": "gitlens.gitExplorer.switchToRepositoryView", "command": "gitlens.gitExplorer.switchToRepositoryView",
"when": "view == gitlens.gitExplorer && gitlens:gitExplorer:view == history",
"when": "view == gitlens.gitExplorer && !gitlens:historyExplorer && gitlens:gitExplorer:view == history",
"group": "navigation@3" "group": "navigation@3"
}, },
{ {
"command": "gitlens.gitExplorer.refresh", "command": "gitlens.gitExplorer.refresh",
"when": "view == gitlens.gitExplorer", "when": "view == gitlens.gitExplorer",
"group": "navigation@4"
"group": "navigation@8"
}, },
{ {
"command": "gitlens.gitExplorer.setFilesLayoutToAuto", "command": "gitlens.gitExplorer.setFilesLayoutToAuto",
@ -2371,6 +2461,31 @@
"group": "2_gitlens_1" "group": "2_gitlens_1"
}, },
{ {
"command": "gitlens.historyExplorer.refresh",
"when": "view == gitlens.historyExplorer",
"group": "navigation@1"
},
{
"command": "gitlens.historyExplorer.dock",
"when": "view == gitlens.historyExplorer && gitlens:gitExplorer",
"group": "navigation@9"
},
{
"command": "gitlens.historyExplorer.close",
"when": "view == gitlens.historyExplorer && !gitlens:gitExplorer",
"group": "navigation@9"
},
{
"command": "gitlens.historyExplorer.setRenameFollowingOn",
"when": "view == gitlens.historyExplorer && !config.gitlens.advanced.fileHistoryFollowsRenames",
"group": "1_gitlens"
},
{
"command": "gitlens.historyExplorer.setRenameFollowingOff",
"when": "view == gitlens.historyExplorer && config.gitlens.advanced.fileHistoryFollowsRenames",
"group": "1_gitlens"
},
{
"command": "gitlens.showCommitSearch", "command": "gitlens.showCommitSearch",
"when": "view == gitlens.resultsExplorer", "when": "view == gitlens.resultsExplorer",
"group": "navigation@1" "group": "navigation@1"
@ -2694,12 +2809,17 @@
}, },
{ {
"command": "gitlens.gitExplorer.refreshNode", "command": "gitlens.gitExplorer.refreshNode",
"when": "view =~ /gitlens\\.gitExplorer/ && viewItem =~ /gitlens:(?!file\\b)/",
"when": "view == gitlens.gitExplorer && viewItem =~ /gitlens:(?!file\\b)/",
"group": "9_gitlens@1" "group": "9_gitlens@1"
}, },
{ {
"command": "gitlens.resultsExplorer.refreshNode", "command": "gitlens.resultsExplorer.refreshNode",
"when": "view =~ /gitlens\\.resultsExplorer/ && viewItem =~ /gitlens:(?!file\\b)/",
"when": "view == gitlens.resultsExplorer && viewItem =~ /gitlens:(?!file\\b)/",
"group": "9_gitlens@1"
},
{
"command": "gitlens.historyExplorer.refreshNode",
"when": "view == gitlens.historyExplorer && viewItem =~ /gitlens:(?!file\\b)/",
"group": "9_gitlens@1" "group": "9_gitlens@1"
} }
] ]
@ -2887,6 +3007,11 @@
"when": "gitlens:enabled && gitlens:gitExplorer" "when": "gitlens:enabled && gitlens:gitExplorer"
}, },
{ {
"id": "gitlens.historyExplorer",
"name": "GitLens History",
"when": "gitlens:enabled && gitlens:historyExplorer"
},
{
"id": "gitlens.resultsExplorer", "id": "gitlens.resultsExplorer",
"name": "GitLens Results", "name": "GitLens Results",
"when": "gitlens:enabled && gitlens:resultsExplorer" "when": "gitlens:enabled && gitlens:resultsExplorer"

+ 3
- 0
src/configuration.ts View File

@ -175,12 +175,15 @@ export class Configuration {
async updateEffective(section: string, value: any, resource: Uri | null = null) { async updateEffective(section: string, value: any, resource: Uri | null = null) {
const inspect = await configuration.inspect(section, resource)!; const inspect = await configuration.inspect(section, resource)!;
if (inspect.workspaceFolderValue !== undefined) { if (inspect.workspaceFolderValue !== undefined) {
if (inspect.workspaceFolderValue === value) return;
await configuration.update(section, value, ConfigurationTarget.WorkspaceFolder, resource); await configuration.update(section, value, ConfigurationTarget.WorkspaceFolder, resource);
} }
else if (inspect.workspaceValue !== undefined) { else if (inspect.workspaceValue !== undefined) {
if (inspect.workspaceValue === value) return;
await configuration.update(section, value, ConfigurationTarget.Workspace); await configuration.update(section, value, ConfigurationTarget.Workspace);
} }
else { else {
if (inspect.globalValue === value) return;
await configuration.update(section, value, ConfigurationTarget.Global); await configuration.update(section, value, ConfigurationTarget.Global);
} }
} }

+ 1
- 0
src/constants.ts View File

@ -40,6 +40,7 @@ export enum CommandContext {
GitExplorerAutoRefresh = 'gitlens:gitExplorer:autoRefresh', GitExplorerAutoRefresh = 'gitlens:gitExplorer:autoRefresh',
GitExplorerView = 'gitlens:gitExplorer:view', GitExplorerView = 'gitlens:gitExplorer:view',
HasRemotes = 'gitlens:hasRemotes', HasRemotes = 'gitlens:hasRemotes',
HistoryExplorer = 'gitlens:historyExplorer',
Key = 'gitlens:key', Key = 'gitlens:key',
KeyMap = 'gitlens:keymap', KeyMap = 'gitlens:keymap',
ResultsExplorer = 'gitlens:resultsExplorer', ResultsExplorer = 'gitlens:resultsExplorer',

+ 23
- 0
src/container.ts View File

@ -12,6 +12,7 @@ import { GitExplorer } from './views/gitExplorer';
import { GitLineTracker } from './trackers/gitLineTracker'; import { GitLineTracker } from './trackers/gitLineTracker';
import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider'; import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider';
import { GitService } from './gitService'; import { GitService } from './gitService';
import { HistoryExplorer } from './views/historyExplorer';
import { Keyboard } from './keyboard'; import { Keyboard } from './keyboard';
import { PageProvider } from './pageProvider'; import { PageProvider } from './pageProvider';
import { ResultsExplorer } from './views/resultsExplorer'; import { ResultsExplorer } from './views/resultsExplorer';
@ -51,6 +52,19 @@ export class Container {
}); });
} }
if (config.historyExplorer.enabled) {
context.subscriptions.push(this._historyExplorer = new HistoryExplorer());
}
else {
let disposable: Disposable;
disposable = configuration.onDidChange(e => {
if (configuration.changed(e, configuration.name('historyExplorer')('enabled').value)) {
disposable.dispose();
context.subscriptions.push(this._historyExplorer = new HistoryExplorer());
}
});
}
context.subscriptions.push(workspace.registerTextDocumentContentProvider(GitContentProvider.scheme, new GitContentProvider())); context.subscriptions.push(workspace.registerTextDocumentContentProvider(GitContentProvider.scheme, new GitContentProvider()));
context.subscriptions.push(languages.registerCodeLensProvider(GitRevisionCodeLensProvider.selector, new GitRevisionCodeLensProvider())); context.subscriptions.push(languages.registerCodeLensProvider(GitRevisionCodeLensProvider.selector, new GitRevisionCodeLensProvider()));
} }
@ -96,6 +110,15 @@ export class Container {
return this._gitExplorer!; return this._gitExplorer!;
} }
private static _historyExplorer: HistoryExplorer | undefined;
static get historyExplorer() {
if (this._historyExplorer === undefined) {
this._context.subscriptions.push(this._historyExplorer = new HistoryExplorer());
}
return this._historyExplorer;
}
private static _keyboard: Keyboard; private static _keyboard: Keyboard;
static get keyboard() { static get keyboard() {
return this._keyboard; return this._keyboard;

+ 6
- 0
src/ui/config.ts View File

@ -222,6 +222,10 @@ export interface IGitExplorerConfig {
view: GitExplorerView; view: GitExplorerView;
} }
export interface IHistoryExplorerConfig {
enabled: boolean;
}
export interface IResultsExplorerConfig { export interface IResultsExplorerConfig {
files: IExplorersFilesConfig; files: IExplorersFilesConfig;
} }
@ -287,6 +291,8 @@ export interface IConfig {
toggleMode: AnnotationsToggleMode; toggleMode: AnnotationsToggleMode;
}; };
historyExplorer: IHistoryExplorerConfig;
hovers: { hovers: {
annotations: { annotations: {
changes: boolean; changes: boolean;

BIN
src/ui/images/settings/gitlens-explorer-history.png View File

Before After
Width: 600  |  Height: 360  |  Size: 49 KiB Width: 600  |  Height: 360  |  Size: 49 KiB

BIN
src/ui/images/settings/gitlens-explorer-repository-history-docked.png View File

Before After
Width: 600  |  Height: 360  |  Size: 2.5 KiB

BIN
src/ui/images/settings/gitlens-explorer-repository-history-undocked.png View File

Before After
Width: 600  |  Height: 360  |  Size: 2.4 KiB

BIN
src/ui/images/settings/gitlens-explorer-repository-tree-compact.png View File

Before After
Width: 600  |  Height: 360  |  Size: 23 KiB Width: 600  |  Height: 360  |  Size: 22 KiB

BIN
src/ui/images/settings/gitlens-explorer-repository-tree.png View File

Before After
Width: 600  |  Height: 360  |  Size: 22 KiB Width: 600  |  Height: 360  |  Size: 21 KiB

BIN
src/ui/images/settings/gitlens-explorer-repository.png View File

Before After
Width: 600  |  Height: 360  |  Size: 26 KiB Width: 600  |  Height: 360  |  Size: 25 KiB

BIN
src/ui/images/settings/gitlens-history-explorer-close.png View File

Before After
Width: 600  |  Height: 360  |  Size: 2.4 KiB

BIN
src/ui/images/settings/gitlens-history-explorer.png View File

Before After
Width: 600  |  Height: 360  |  Size: 49 KiB

+ 5
- 9
src/ui/scss/main.scss View File

@ -853,15 +853,7 @@ ul {
.setting__hint { .setting__hint {
color: var(--color--50); color: var(--color--50);
font-size: 0.9em; font-size: 0.9em;
margin: -1em 0 1em 3em;
}
.setting__hint--indent-2 {
margin-left: 5em;
}
.setting__hint--indent-4 {
margin-left: 7em;
margin: -1em 0 1em 1em;
} }
.settings-scope { .settings-scope {
@ -908,6 +900,10 @@ ul {
margin-left: 2em; margin-left: 2em;
} }
.ml-3 {
margin-left: 3em;
}
.ml-4 { .ml-4 {
margin-left: 4em; margin-left: 4em;
} }

+ 58
- 11
src/ui/settings/index.html View File

@ -128,13 +128,18 @@
<label for="gitExplorer.enabled">Show the GitLens explorer</label> <label for="gitExplorer.enabled">Show the GitLens explorer</label>
</div> </div>
<div class="settings-group__setting ml-2" data-enablement="gitExplorer.enabled" disabled>
<div class="settings-group__setting ml-2" data-enablement="gitExplorer.enabled &amp; historyExplorer.enabled =false" disabled>
<label for="gitExplorer.view">When opened, show the</label> <label for="gitExplorer.view">When opened, show the</label>
<select class="setting" id="gitExplorer.view" name="gitExplorer.view" disabled>
<select class="setting hidden" id="gitExplorer.view" name="gitExplorer.view" data-visibility="historyExplorer.enabled =false" disabled>
<option value="auto">last selected view</option> <option value="auto">last selected view</option>
<option value="repository">Repository view</option> <option value="repository">Repository view</option>
<option value="history">History view</option> <option value="history">History view</option>
</select> </select>
<select class="setting hidden" id="gitExplorer.view-undocked" name="!gitExplorer.view" data-visibility="historyExplorer.enabled" disabled>
<option value="auto">last selected view</option>
<option value="repository" selected>Repository view</option>
<option value="history">History view</option>
</select>
</div> </div>
<div class="settings-group__setting ml-2" data-enablement="gitExplorer.enabled" disabled> <div class="settings-group__setting ml-2" data-enablement="gitExplorer.enabled" disabled>
@ -153,13 +158,13 @@
<option value="tree">as a tree</option> <option value="tree">as a tree</option>
</select> </select>
</div> </div>
<p class="setting__hint hidden" data-visibility="gitExplorer.files.layout =auto">Chooses the best layout based on the number of files at each nesting level</p>
<p class="setting__hint ml-3 hidden" data-visibility="gitExplorer.files.layout =auto">Chooses the best layout based on the number of files at each nesting level</p>
<div class="settings-group__setting nowrap ml-2" data-enablement="gitExplorer.enabled" disabled> <div class="settings-group__setting nowrap ml-2" data-enablement="gitExplorer.enabled" disabled>
<input class="setting" id="gitExplorer.files.compact" name="gitExplorer.files.compact" type="checkbox" disabled/> <input class="setting" id="gitExplorer.files.compact" name="gitExplorer.files.compact" type="checkbox" disabled/>
<label for="gitExplorer.files.compact">Use compact layout</label> <label for="gitExplorer.files.compact">Use compact layout</label>
</div> </div>
<p class="setting__hint setting__hint--indent-1">Compacts (flattens) unnecessary nesting when using a tree layouts</p>
<p class="setting__hint ml-3">Compacts (flattens) unnecessary nesting when using a tree layouts</p>
<div class="settings-group__setting nowrap ml-2" data-enablement="gitExplorer.enabled" disabled> <div class="settings-group__setting nowrap ml-2" data-enablement="gitExplorer.enabled" disabled>
<input class="setting" id="gitExplorer.avatars" name="explorers.avatars" type="checkbox" disabled/> <input class="setting" id="gitExplorer.avatars" name="explorers.avatars" type="checkbox" disabled/>
@ -175,10 +180,18 @@
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-explorer-repository.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view !history &amp; gitExplorer.files.layout !tree"/> <img class="image__preview hidden" src="{{root}}/images/settings/gitlens-explorer-repository.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view !history &amp; gitExplorer.files.layout !tree"/>
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-explorer-repository-tree-compact.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view !history &amp; gitExplorer.files.layout =tree &amp; gitExplorer.files.compact"/> <img class="image__preview hidden" src="{{root}}/images/settings/gitlens-explorer-repository-tree-compact.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view !history &amp; gitExplorer.files.layout =tree &amp; gitExplorer.files.compact"/>
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-explorer-repository-tree.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view !history &amp; gitExplorer.files.layout =tree &amp; gitExplorer.files.compact =false"/> <img class="image__preview hidden" src="{{root}}/images/settings/gitlens-explorer-repository-tree.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view !history &amp; gitExplorer.files.layout =tree &amp; gitExplorer.files.compact =false"/>
<img class="image__preview--overlay hidden" src="{{root}}/images/settings/gitlens-explorer-repository-avatars.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view !history &amp; explorers.avatars =true"/>
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-explorer-history.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view =history"/>
<img class="image__preview--overlay hidden" src="{{root}}/images/settings/gitlens-explorer-history-avatars.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view =history &amp; explorers.avatars =true"/>
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-explorer-repository.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view =history &amp; historyExplorer.enabled &amp; gitExplorer.files.layout !tree"/>
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-explorer-repository-tree-compact.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view =history &amp; historyExplorer.enabled &amp; gitExplorer.files.layout =tree &amp; gitExplorer.files.compact"/>
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-explorer-repository-tree.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view =history &amp; historyExplorer.enabled &amp; gitExplorer.files.layout =tree &amp; gitExplorer.files.compact =false"/>
<img class="image__preview--overlay hidden" src="{{root}}/images/settings/gitlens-explorer-repository-avatars.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view !history &amp; explorers.avatars"/>
<img class="image__preview--overlay hidden" src="{{root}}/images/settings/gitlens-explorer-repository-avatars.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view =history &amp; historyExplorer.enabled &amp; explorers.avatars"/>
<img class="image__preview--overlay hidden" src="{{root}}/images/settings/gitlens-explorer-repository-history-docked.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view !history &amp; historyExplorer.enabled =false"/>
<img class="image__preview--overlay hidden" src="{{root}}/images/settings/gitlens-explorer-repository-history-undocked.png" data-visibility="gitExplorer.enabled &amp; historyExplorer.enabled"/>
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-explorer-history.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view =history &amp; historyExplorer.enabled =false"/>
<img class="image__preview--overlay hidden" src="{{root}}/images/settings/gitlens-explorer-history-avatars.png" data-visibility="gitExplorer.enabled &amp; gitExplorer.view =history &amp; historyExplorer.enabled =false &amp; explorers.avatars"/>
</div> </div>
<p class="settings-group__hint"> <p class="settings-group__hint">
<i class="icon icon--lg icon__info"></i> <i class="icon icon--lg icon__info"></i>
@ -187,6 +200,39 @@
</div> </div>
</section> </section>
<section id="gitlens-history-explorer">
<div class="section__header">
<h2 class="section__title">GitLens History Explorer
<a class="link__learn-more" title="Learn more" href="https://github.com/eamodio/vscode-gitlens/#gitlens-history-explorer">
<i class="icon icon__info"></i>
</a>
</h2>
<p class="section__title-hint">Adds a GitLens History explorer to explore the history of the current file</p>
</div>
<div class="section__settings">
<div class="settings-group">
<div class="settings-group__setting nowrap">
<input class="setting" id="historyExplorer.enabled" name="historyExplorer.enabled" type="checkbox"/>
<label for="historyExplorer.enabled">Show the GitLens History explorer</label>
</div>
<p class="setting__hint hidden" data-visibility="gitExplorer.enabled &amp; historyExplorer.enabled =false">The current file history will be shown docked within the GitLens explorer</p>
<div class="settings-group__setting nowrap ml-2" data-enablement="historyExplorer.enabled" disabled>
<input class="setting" id="historyExplorer.avatars" name="explorers.avatars" type="checkbox" disabled/>
<label for="historyExplorer.avatars">Use author avatars icons</label>
</div>
</div>
<div class="section__preview">
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-history-explorer.png" data-visibility="historyExplorer.enabled"/>
<img class="image__preview--overlay hidden" src="{{root}}/images/settings/gitlens-history-explorer-close.png" data-visibility="historyExplorer.enabled &amp; gitExplorer.enabled =false"/>
<img class="image__preview--overlay hidden" src="{{root}}/images/settings/gitlens-explorer-history-avatars.png" data-visibility="historyExplorer.enabled &amp; explorers.avatars"/>
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-explorer-history.png" data-visibility="gitExplorer.enabled &amp; historyExplorer.enabled =false"/>
<img class="image__preview--overlay hidden" src="{{root}}/images/settings/gitlens-explorer-history-avatars.png" data-visibility="gitExplorer.enabled &amp; historyExplorer.enabled =false &amp; explorers.avatars"/>
</div>
</div>
</section>
<section id="gitlens-results"> <section id="gitlens-results">
<div class="section__header"> <div class="section__header">
<h2 class="section__title">GitLens Results <h2 class="section__title">GitLens Results
@ -211,7 +257,7 @@
<input class="setting" id="resultsExplorer.files.compact" name="resultsExplorer.files.compact" type="checkbox"/> <input class="setting" id="resultsExplorer.files.compact" name="resultsExplorer.files.compact" type="checkbox"/>
<label for="resultsExplorer.files.compact">Use compact layout</label> <label for="resultsExplorer.files.compact">Use compact layout</label>
</div> </div>
<p class="setting__hint setting__hint">Compacts (flattens) unnecessary nesting when using a tree layouts</p>
<p class="setting__hint">Compacts (flattens) unnecessary nesting when using a tree layouts</p>
<div class="settings-group__setting nowrap"> <div class="settings-group__setting nowrap">
<input class="setting" id="resultsExplorer.avatars" name="explorers.avatars" type="checkbox"/> <input class="setting" id="resultsExplorer.avatars" name="explorers.avatars" type="checkbox"/>
@ -223,7 +269,7 @@
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-results.png" data-visibility="resultsExplorer.files.layout !tree"/> <img class="image__preview hidden" src="{{root}}/images/settings/gitlens-results.png" data-visibility="resultsExplorer.files.layout !tree"/>
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-results-tree-compact.png" data-visibility="resultsExplorer.files.layout =tree &amp; resultsExplorer.files.compact"/> <img class="image__preview hidden" src="{{root}}/images/settings/gitlens-results-tree-compact.png" data-visibility="resultsExplorer.files.layout =tree &amp; resultsExplorer.files.compact"/>
<img class="image__preview hidden" src="{{root}}/images/settings/gitlens-results-tree.png" data-visibility="resultsExplorer.files.layout =tree &amp; resultsExplorer.files.compact =false"/> <img class="image__preview hidden" src="{{root}}/images/settings/gitlens-results-tree.png" data-visibility="resultsExplorer.files.layout =tree &amp; resultsExplorer.files.compact =false"/>
<img class="image__preview--overlay hidden" src="{{root}}/images/settings/gitlens-results-avatars.png" data-visibility="explorers.avatars =true"/>
<img class="image__preview--overlay hidden" src="{{root}}/images/settings/gitlens-results-avatars.png" data-visibility="explorers.avatars"/>
</div> </div>
<p class="settings-group__hint"> <p class="settings-group__hint">
<i class="icon icon--lg icon__info"></i> <i class="icon icon--lg icon__info"></i>
@ -370,7 +416,7 @@
<input class="setting" id="currentLine.scrollable" name="currentLine.scrollable" type="checkbox" disabled/> <input class="setting" id="currentLine.scrollable" name="currentLine.scrollable" type="checkbox" disabled/>
<label for="currentLine.scrollable">Include the annotation when scrolling the editor horizontally</label> <label for="currentLine.scrollable">Include the annotation when scrolling the editor horizontally</label>
</div> </div>
<p class="setting__hint">When enabled the annotation can be scrolled into view when it is outside the viewport</p>
<p class="setting__hint ml-3">When enabled the annotation can be scrolled into view when it is outside the viewport</p>
</div> </div>
<div class="section__preview"> <div class="section__preview">
@ -789,7 +835,7 @@
<input class="setting" id="statusBar.reduceFlicker" name="statusBar.reduceFlicker" type="checkbox" disabled/> <input class="setting" id="statusBar.reduceFlicker" name="statusBar.reduceFlicker" type="checkbox" disabled/>
<label for="statusBar.reduceFlicker">Reduce flashing when updating the annotation</label> <label for="statusBar.reduceFlicker">Reduce flashing when updating the annotation</label>
</div> </div>
<p class="setting__hint">Avoids clearing the previous blame information when changing lines to reduce status bar "flashing"</p>
<p class="setting__hint ml-3">Avoids clearing the previous blame information when changing lines to reduce status bar "flashing"</p>
</div> </div>
<div class="section__preview"> <div class="section__preview">
@ -816,6 +862,7 @@
<ul> <ul>
<li><a href="#general">General</a></li> <li><a href="#general">General</a></li>
<li><a href="#gitlens-explorer">GitLens Explorer</a></li> <li><a href="#gitlens-explorer">GitLens Explorer</a></li>
<li><a href="#gitlens-history-explorer">GitLens History Explorer</a></li>
<li><a href="#gitlens-results">GitLens Results</a></li> <li><a href="#gitlens-results">GitLens Results</a></li>
<li><a href="#code-lens">Code Lens</a></li> <li><a href="#code-lens">Code Lens</a></li>
<li><a href="#current-line">Current Line Blame</a></li> <li><a href="#current-line">Current Line Blame</a></li>

+ 6
- 0
src/ui/welcome/index.html View File

@ -46,6 +46,12 @@
<div class="changelog__details"></div> <div class="changelog__details"></div>
</li> </li>
<li><span class="changelog__badge changelog__badge--added">NEW</span> <li><span class="changelog__badge changelog__badge--added">NEW</span>
Adds new <i>GitLens History</i> explorer to explore the history of the current file &mdash; same as the history view in the <i>GitLens</i> explorer when undocked
<div class="changelog__details">
<img class="changelog__image" src="{{root}}/images/cl-history-explorer.png" alt="GitLens History explorer"/>
</div>
</li>
<li><span class="changelog__badge changelog__badge--added">NEW</span>
Adds rich tooltip details to the <i>GitLens</i> explorer and <i>GitLens Results</i> view Adds rich tooltip details to the <i>GitLens</i> explorer and <i>GitLens Results</i> view
<div class="changelog__details"> <div class="changelog__details">
<!-- <img class="changelog__image" src="{{root}}/images/cl-issue-linking.png" alt="Issue linking in commit messages"/> --> <!-- <img class="changelog__image" src="{{root}}/images/cl-issue-linking.png" alt="Issue linking in commit messages"/> -->

+ 2
- 2
src/views/branchFolderNode.ts View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import { Arrays, Objects } from '../system'; import { Arrays, Objects } from '../system';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { BranchNode } from './branchNode'; import { BranchNode } from './branchNode';
// import { Container } from '../container'; // import { Container } from '../container';
import { Explorer, ExplorerNode, ResourceType } from './explorerNode'; import { Explorer, ExplorerNode, ResourceType } from './explorerNode';
@ -37,7 +37,7 @@ export class BranchFolderNode extends ExplorerNode {
async getTreeItem(): Promise<TreeItem> { async getTreeItem(): Promise<TreeItem> {
const item = new TreeItem(this.label, TreeItemCollapsibleState.Collapsed); const item = new TreeItem(this.label, TreeItemCollapsibleState.Collapsed);
item.contextValue = ResourceType.Folder; item.contextValue = ResourceType.Folder;
item.resourceUri = this.explorer.folderResourceUri;
item.iconPath = ThemeIcon.Folder;
item.tooltip = this.label; item.tooltip = this.label;
return item; return item;
} }

+ 2
- 1
src/views/explorerNode.ts View File

@ -5,6 +5,7 @@ import { Container } from '../container';
import { RefreshNodeCommandArgs } from './explorerCommands'; import { RefreshNodeCommandArgs } from './explorerCommands';
import { GitUri } from '../gitService'; import { GitUri } from '../gitService';
import { GitExplorer } from './gitExplorer'; import { GitExplorer } from './gitExplorer';
import { HistoryExplorer } from './historyExplorer';
import { ResultsExplorer } from './resultsExplorer'; import { ResultsExplorer } from './resultsExplorer';
export interface NamedRef { export interface NamedRef {
@ -61,7 +62,7 @@ export enum ResourceType {
Tags = 'gitlens:tags' Tags = 'gitlens:tags'
} }
export type Explorer = GitExplorer | ResultsExplorer;
export type Explorer = GitExplorer | HistoryExplorer | ResultsExplorer;
// let id = 0; // let id = 0;

+ 5
- 29
src/views/fileHistoryNode.ts View File

@ -1,10 +1,9 @@
'use strict'; 'use strict';
import { Iterables } from '../system'; import { Iterables } from '../system';
import { Disposable, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { CommitFileNode, CommitFileNodeDisplayAs } from './commitFileNode'; import { CommitFileNode, CommitFileNodeDisplayAs } from './commitFileNode';
import { Container } from '../container'; import { Container } from '../container';
import { ExplorerNode, MessageNode, ResourceType } from './explorerNode';
import { GitExplorer } from './gitExplorer';
import { Explorer, ExplorerNode, MessageNode, ResourceType } from './explorerNode';
import { GitCommitType, GitLogCommit, GitService, GitUri, Repository, RepositoryChange, RepositoryChangeEvent } from '../gitService'; import { GitCommitType, GitLogCommit, GitService, GitUri, Repository, RepositoryChange, RepositoryChangeEvent } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
@ -13,7 +12,7 @@ export class FileHistoryNode extends ExplorerNode {
constructor( constructor(
uri: GitUri, uri: GitUri,
private readonly repo: Repository, private readonly repo: Repository,
private readonly explorer: GitExplorer
private readonly explorer: Explorer
) { ) {
super(uri); super(uri);
} }
@ -85,36 +84,13 @@ export class FileHistoryNode extends ExplorerNode {
} }
private updateSubscription() { private updateSubscription() {
// We only need to subscribe if auto-refresh is enabled, because if it becomes enabled we will be refreshed
if (this.explorer.autoRefresh) {
this.disposable = this.disposable || Disposable.from(
this.explorer.onDidChangeAutoRefresh(this.onAutoRefreshChanged, this),
this.repo.onDidChange(this.onRepoChanged, this)
// Container.gitContextTracker.onDidChangeBlameability(this.onBlameabilityChanged, this)
);
}
else if (this.disposable !== undefined) {
this.disposable.dispose();
this.disposable = undefined;
}
}
private onAutoRefreshChanged() {
this.updateSubscription();
this.disposable = this.disposable || this.repo.onDidChange(this.onRepoChanged, this);
} }
// private onBlameabilityChanged(e: BlameabilityChangeEvent) {
// if (!e.blameable || e.reason !== BlameabilityChangeReason.DocumentChanged) return;
// // Logger.log(`RepositoryNode.onBlameabilityChanged(${e.reason}); triggering node refresh`);
// this.explorer.refreshNode(this);
// }
private onRepoChanged(e: RepositoryChangeEvent) { private onRepoChanged(e: RepositoryChangeEvent) {
if (!e.changed(RepositoryChange.Repository)) return; if (!e.changed(RepositoryChange.Repository)) return;
Logger.log(`RepositoryNode.onRepoChanged(${e.changes.join()}); triggering node refresh`);
Logger.log(`FileHistoryNode.onRepoChanged(${e.changes.join()}); triggering node refresh`);
this.explorer.refreshNode(this); this.explorer.refreshNode(this);
} }

+ 2
- 2
src/views/folderNode.ts View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import { Arrays, Objects } from '../system'; import { Arrays, Objects } from '../system';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ExplorerFilesLayout, IExplorersFilesConfig } from '../configuration'; import { ExplorerFilesLayout, IExplorersFilesConfig } from '../configuration';
import { Explorer, ExplorerNode, ResourceType } from './explorerNode'; import { Explorer, ExplorerNode, ResourceType } from './explorerNode';
import { GitUri } from '../gitService'; import { GitUri } from '../gitService';
@ -63,7 +63,7 @@ export class FolderNode extends ExplorerNode {
// TODO: Change this to expanded once https://github.com/Microsoft/vscode/issues/30918 is fixed // TODO: Change this to expanded once https://github.com/Microsoft/vscode/issues/30918 is fixed
const item = new TreeItem(this.label, TreeItemCollapsibleState.Collapsed); const item = new TreeItem(this.label, TreeItemCollapsibleState.Collapsed);
item.contextValue = ResourceType.Folder; item.contextValue = ResourceType.Folder;
item.resourceUri = this.explorer.folderResourceUri;
item.iconPath = ThemeIcon.Folder;
item.tooltip = this.label; item.tooltip = this.label;
return item; return item;
} }

+ 132
- 109
src/views/gitExplorer.ts View File

@ -1,12 +1,12 @@
'use strict'; 'use strict';
import { Functions } from '../system'; import { Functions } from '../system';
import { commands, ConfigurationChangeEvent, ConfigurationTarget, Disposable, Event, EventEmitter, TextDocumentShowOptions, TextEditor, TreeDataProvider, TreeItem, Uri, window, workspace } from 'vscode';
import { commands, ConfigurationChangeEvent, ConfigurationTarget, Disposable, Event, EventEmitter, TextDocumentShowOptions, TextEditor, TreeDataProvider, TreeItem, Uri, window } from 'vscode';
import { UriComparer } from '../comparers'; import { UriComparer } from '../comparers';
import { configuration, ExplorerFilesLayout, GitExplorerView, IExplorersConfig, IGitExplorerConfig } from '../configuration'; import { configuration, ExplorerFilesLayout, GitExplorerView, IExplorersConfig, IGitExplorerConfig } from '../configuration';
import { CommandContext, GlyphChars, setCommandContext, WorkspaceState } from '../constants'; import { CommandContext, GlyphChars, setCommandContext, WorkspaceState } from '../constants';
import { Container } from '../container'; import { Container } from '../container';
import { RefreshNodeCommandArgs } from './explorerCommands'; import { RefreshNodeCommandArgs } from './explorerCommands';
import { ExplorerNode, HistoryNode, MessageNode, RefreshReason, RepositoriesNode, RepositoryNode } from './explorerNodes';
import { Explorer, ExplorerNode, HistoryNode, MessageNode, RefreshReason, RepositoriesNode, RepositoryNode } from './explorerNodes';
import { GitUri } from '../gitService'; import { GitUri } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
@ -21,7 +21,6 @@ export class GitExplorer extends Disposable implements TreeDataProvider
private _disposable: Disposable | undefined; private _disposable: Disposable | undefined;
private _root?: ExplorerNode; private _root?: ExplorerNode;
private _view: GitExplorerView | undefined;
private _onDidChangeAutoRefresh = new EventEmitter<void>(); private _onDidChangeAutoRefresh = new EventEmitter<void>();
public get onDidChangeAutoRefresh(): Event<void> { public get onDidChangeAutoRefresh(): Event<void> {
@ -45,11 +44,13 @@ export class GitExplorer extends Disposable implements TreeDataProvider
commands.registerCommand('gitlens.gitExplorer.setAutoRefreshToOn', () => this.setAutoRefresh(Container.config.gitExplorer.autoRefresh, true), this); commands.registerCommand('gitlens.gitExplorer.setAutoRefreshToOn', () => this.setAutoRefresh(Container.config.gitExplorer.autoRefresh, true), this);
commands.registerCommand('gitlens.gitExplorer.setAutoRefreshToOff', () => this.setAutoRefresh(Container.config.gitExplorer.autoRefresh, false), this); commands.registerCommand('gitlens.gitExplorer.setAutoRefreshToOff', () => this.setAutoRefresh(Container.config.gitExplorer.autoRefresh, false), this);
commands.registerCommand('gitlens.gitExplorer.setRenameFollowingOn', () => this.setRenameFollowing(true), this);
commands.registerCommand('gitlens.gitExplorer.setRenameFollowingOff', () => this.setRenameFollowing(false), this);
commands.registerCommand('gitlens.gitExplorer.setRenameFollowingOn', () => GitExplorer.setRenameFollowing(true), this);
commands.registerCommand('gitlens.gitExplorer.setRenameFollowingOff', () => GitExplorer.setRenameFollowing(false), this);
commands.registerCommand('gitlens.gitExplorer.switchToHistoryView', () => this.switchTo(GitExplorerView.History), this); commands.registerCommand('gitlens.gitExplorer.switchToHistoryView', () => this.switchTo(GitExplorerView.History), this);
commands.registerCommand('gitlens.gitExplorer.switchToRepositoryView', () => this.switchTo(GitExplorerView.Repository), this); commands.registerCommand('gitlens.gitExplorer.switchToRepositoryView', () => this.switchTo(GitExplorerView.Repository), this);
commands.registerCommand('gitlens.gitExplorer.undockHistory', this.undockHistory, this);
Container.context.subscriptions.push( Container.context.subscriptions.push(
window.onDidChangeActiveTextEditor(Functions.debounce(this.onActiveEditorChanged, 500), this), window.onDidChangeActiveTextEditor(Functions.debounce(this.onActiveEditorChanged, 500), this),
window.onDidChangeVisibleTextEditors(Functions.debounce(this.onVisibleEditorsChanged, 500), this), window.onDidChangeVisibleTextEditors(Functions.debounce(this.onVisibleEditorsChanged, 500), this),
@ -62,15 +63,6 @@ export class GitExplorer extends Disposable implements TreeDataProvider
this._disposable && this._disposable.dispose(); this._disposable && this._disposable.dispose();
} }
private async onActiveEditorChanged(editor: TextEditor | undefined) {
if (this._view !== GitExplorerView.History) return;
const root = await this.getRootNode(editor);
if (!this.setRoot(root)) return;
this.refresh(RefreshReason.ActiveEditorChanged, root);
}
private async onConfigurationChanged(e: ConfigurationChangeEvent) { private async onConfigurationChanged(e: ConfigurationChangeEvent) {
const initializing = configuration.initializing(e); const initializing = configuration.initializing(e);
@ -88,7 +80,16 @@ export class GitExplorer extends Disposable implements TreeDataProvider
this.setAutoRefresh(Container.config.gitExplorer.autoRefresh); this.setAutoRefresh(Container.config.gitExplorer.autoRefresh);
} }
let view = this._view;
// if (!initializing && configuration.changed(e, configuration.name('gitExplorer')('undockHistory').value)) {
// if (Container.config.historyExplorer.enabled) {
// this.undockHistory(!initializing);
// }
// // else {
// // this.dockHistory(!initializing);
// // }
// }
let view = this.view;
if (initializing || configuration.changed(e, configuration.name('gitExplorer')('view').value)) { if (initializing || configuration.changed(e, configuration.name('gitExplorer')('view').value)) {
view = this.config.view; view = this.config.view;
@ -97,8 +98,8 @@ export class GitExplorer extends Disposable implements TreeDataProvider
} }
if (initializing) { if (initializing) {
this._view = view;
setCommandContext(CommandContext.GitExplorerView, this._view);
this.view = view;
setCommandContext(CommandContext.GitExplorerView, this.view);
this.setRoot(await this.getRootNode(window.activeTextEditor)); this.setRoot(await this.getRootNode(window.activeTextEditor));
@ -111,18 +112,27 @@ export class GitExplorer extends Disposable implements TreeDataProvider
this.reset(view!, configuration.changed(e, configuration.name('advanced')('fileHistoryFollowsRenames').value)); this.reset(view!, configuration.changed(e, configuration.name('advanced')('fileHistoryFollowsRenames').value));
} }
private async onActiveEditorChanged(editor: TextEditor | undefined) {
if (this.view !== GitExplorerView.History) return;
const root = await this.getRootNode(editor);
if (!this.setRoot(root)) return;
this.refresh(RefreshReason.ActiveEditorChanged, root);
}
private onRepositoriesChanged() { private onRepositoriesChanged() {
if (this._view !== GitExplorerView.Repository) return;
if (this.view !== GitExplorerView.Repository) return;
this.clearRoot(); this.clearRoot();
Logger.log(`GitExplorer[view=${this._view}].onRepositoriesChanged`);
Logger.log(`GitExplorer[view=${this.view}].onRepositoriesChanged`);
this.refresh(RefreshReason.RepoChanged); this.refresh(RefreshReason.RepoChanged);
} }
private onVisibleEditorsChanged(editors: TextEditor[]) { private onVisibleEditorsChanged(editors: TextEditor[]) {
if (this._root === undefined || this._view !== GitExplorerView.History) return;
if (this._root === undefined || this.view !== GitExplorerView.History) return;
// If we have no visible editors, or no trackable visible editors reset the view // If we have no visible editors, or no trackable visible editors reset the view
if (editors.length === 0 || !editors.some(e => e.document && Container.git.isTrackable(e.document.uri))) { if (editors.length === 0 || !editors.some(e => e.document && Container.git.isTrackable(e.document.uri))) {
@ -141,11 +151,12 @@ export class GitExplorer extends Disposable implements TreeDataProvider
return { ...Container.config.explorers, ...Container.config.gitExplorer }; return { ...Container.config.explorers, ...Container.config.gitExplorer };
} }
get folderResourceUri(): Uri | undefined {
// Return the uri of any workspace folder -- we just need a folder so that we can use the uri has an icon resourceUri
if (workspace.workspaceFolders === undefined || workspace.workspaceFolders.length === 0) return undefined;
return workspace.workspaceFolders[0].uri;
private _view: GitExplorerView | undefined;
private get view(): GitExplorerView | undefined {
return this._view;
}
private set view(value: GitExplorerView | undefined) {
this._view = Container.config.historyExplorer.enabled ? GitExplorerView.Repository : value;
} }
private _loading: Promise<void> | undefined; private _loading: Promise<void> | undefined;
@ -157,7 +168,7 @@ export class GitExplorer extends Disposable implements TreeDataProvider
} }
if (this._root === undefined) { if (this._root === undefined) {
if (this._view === GitExplorerView.History) return [new MessageNode(`No active file ${GlyphChars.Dash} no history to show`)];
if (this.view === GitExplorerView.History) return [new MessageNode(`No active file ${GlyphChars.Dash} no history to show`)];
return [new MessageNode('No repositories found')]; return [new MessageNode('No repositories found')];
} }
@ -169,46 +180,6 @@ export class GitExplorer extends Disposable implements TreeDataProvider
return node.getTreeItem(); return node.getTreeItem();
} }
private async getRootNode(editor?: TextEditor): Promise<ExplorerNode | undefined> {
switch (this._view) {
case GitExplorerView.History: {
const promise = this.getHistoryNode(editor || window.activeTextEditor);
this._loading = promise.then(_ => Functions.wait(0));
return promise;
}
default: {
const promise = Container.git.getRepositories();
this._loading = promise.then(_ => Functions.wait(0));
const repositories = [...await promise];
if (repositories.length === 0) return undefined; // new MessageNode('No repositories found');
if (repositories.length === 1) {
const repo = repositories[0];
return new RepositoryNode(GitUri.fromRepoPath(repo.path), repo, this, true);
}
return new RepositoriesNode(repositories, this);
}
}
}
private async getHistoryNode(editor: TextEditor | undefined): Promise<ExplorerNode | undefined> {
// If we have no active editor, or no visible editors, or no trackable visible editors reset the view
if (editor === undefined || window.visibleTextEditors.length === 0 || !window.visibleTextEditors.some(e => e.document && Container.git.isTrackable(e.document.uri))) return undefined;
// If we do have a visible trackable editor, don't change from the last state (avoids issues when focus switches to the problems/output/debug console panes)
if (editor.document === undefined || !Container.git.isTrackable(editor.document.uri)) return this._root;
const uri = await GitUri.fromUri(editor.document.uri);
const repo = await Container.git.getRepository(uri);
if (repo === undefined) return undefined;
if (UriComparer.equals(uri, this._root && this._root.uri)) return this._root;
return new HistoryNode(uri, repo, this);
}
getQualifiedCommand(command: string) { getQualifiedCommand(command: string) {
return `gitlens.gitExplorer.${command}`; return `gitlens.gitExplorer.${command}`;
} }
@ -218,9 +189,9 @@ export class GitExplorer extends Disposable implements TreeDataProvider
reason = RefreshReason.Command; reason = RefreshReason.Command;
} }
Logger.log(`GitExplorer[view=${this._view}].refresh`, `reason='${reason}'`);
Logger.log(`GitExplorer[view=${this.view}].refresh`, `reason='${reason}'`);
if (this._root === undefined || (root === undefined && this._view === GitExplorerView.History)) {
if (this._root === undefined || (root === undefined && this.view === GitExplorerView.History)) {
this.clearRoot(); this.clearRoot();
this.setRoot(await this.getRootNode(window.activeTextEditor)); this.setRoot(await this.getRootNode(window.activeTextEditor));
} }
@ -229,7 +200,7 @@ export class GitExplorer extends Disposable implements TreeDataProvider
} }
refreshNode(node: ExplorerNode, args?: RefreshNodeCommandArgs) { refreshNode(node: ExplorerNode, args?: RefreshNodeCommandArgs) {
Logger.log(`GitExplorer[view=${this._view}].refreshNode(${(node as any).id})`);
Logger.log(`GitExplorer[view=${this.view}].refreshNode(${(node as any).id})`);
if (args !== undefined && node.supportsPaging) { if (args !== undefined && node.supportsPaging) {
node.maxCount = args.maxCount; node.maxCount = args.maxCount;
@ -253,37 +224,48 @@ export class GitExplorer extends Disposable implements TreeDataProvider
} }
} }
private clearRoot() {
if (this._root === undefined) return;
private _autoRefreshDisposable: Disposable | undefined;
this._root.dispose();
this._root = undefined;
}
async setAutoRefresh(enabled: boolean, workspaceEnabled?: boolean) {
if (this._autoRefreshDisposable !== undefined) {
this._autoRefreshDisposable.dispose();
this._autoRefreshDisposable = undefined;
}
private async setFilesLayout(layout: ExplorerFilesLayout) {
return configuration.update(configuration.name('gitExplorer')('files')('layout').value, layout, ConfigurationTarget.Global);
}
let toggled = false;
if (enabled) {
if (workspaceEnabled === undefined) {
workspaceEnabled = Container.context.workspaceState.get<boolean>(WorkspaceState.GitExplorerAutoRefresh, true);
}
else {
toggled = workspaceEnabled;
await Container.context.workspaceState.update(WorkspaceState.GitExplorerAutoRefresh, workspaceEnabled);
private setRoot(root: ExplorerNode | undefined): boolean {
if (this._root === root) return false;
this._onDidChangeAutoRefresh.fire();
}
if (this._root !== undefined) {
this._root.dispose();
if (workspaceEnabled) {
this._autoRefreshDisposable = Container.git.onDidChangeRepositories(this.onRepositoriesChanged, this);
Container.context.subscriptions.push(this._autoRefreshDisposable);
}
} }
this._root = root;
return true;
setCommandContext(CommandContext.GitExplorerAutoRefresh, enabled && workspaceEnabled);
if (toggled) {
this.refresh(RefreshReason.AutoRefreshChanged);
}
} }
setView(view: GitExplorerView) { setView(view: GitExplorerView) {
if (this._view === view) return;
if (this.view === view) return;
if (Container.config.gitExplorer.view === GitExplorerView.Auto) { if (Container.config.gitExplorer.view === GitExplorerView.Auto) {
Container.context.workspaceState.update(WorkspaceState.GitExplorerView, view); Container.context.workspaceState.update(WorkspaceState.GitExplorerView, view);
} }
this._view = view;
setCommandContext(CommandContext.GitExplorerView, this._view);
this.view = view;
setCommandContext(CommandContext.GitExplorerView, this.view);
if (view !== GitExplorerView.Repository) { if (view !== GitExplorerView.Repository) {
Container.git.stopWatchingFileSystem(); Container.git.stopWatchingFileSystem();
@ -291,45 +273,86 @@ export class GitExplorer extends Disposable implements TreeDataProvider
} }
async switchTo(view: GitExplorerView) { async switchTo(view: GitExplorerView) {
if (this._view === view) return;
if (this.view === view) return;
this.reset(view, true); this.reset(view, true);
} }
private _autoRefreshDisposable: Disposable | undefined;
// async dockHistory(switchView: boolean = true) {
// Container.historyExplorer.dock(switchView);
// }
async setAutoRefresh(enabled: boolean, workspaceEnabled?: boolean) {
if (this._autoRefreshDisposable !== undefined) {
this._autoRefreshDisposable.dispose();
this._autoRefreshDisposable = undefined;
}
private clearRoot() {
if (this._root === undefined) return;
let toggled = false;
if (enabled) {
if (workspaceEnabled === undefined) {
workspaceEnabled = Container.context.workspaceState.get<boolean>(WorkspaceState.GitExplorerAutoRefresh, true);
}
else {
toggled = workspaceEnabled;
await Container.context.workspaceState.update(WorkspaceState.GitExplorerAutoRefresh, workspaceEnabled);
this._root.dispose();
this._root = undefined;
}
this._onDidChangeAutoRefresh.fire();
private async getRootNode(editor?: TextEditor): Promise<ExplorerNode | undefined> {
switch (this.view) {
case GitExplorerView.History: {
const promise = this.getHistoryNode(editor || window.activeTextEditor);
this._loading = promise.then(_ => Functions.wait(0));
return promise;
} }
default: {
const promise = Container.git.getRepositories();
this._loading = promise.then(_ => Functions.wait(0));
if (workspaceEnabled) {
this._autoRefreshDisposable = Container.git.onDidChangeRepositories(this.onRepositoriesChanged, this);
Container.context.subscriptions.push(this._autoRefreshDisposable);
const repositories = [...await promise];
if (repositories.length === 0) return undefined; // new MessageNode('No repositories found');
if (repositories.length === 1) {
const repo = repositories[0];
return new RepositoryNode(GitUri.fromRepoPath(repo.path), repo, this, true);
}
return new RepositoriesNode(repositories, this);
} }
} }
}
setCommandContext(CommandContext.GitExplorerAutoRefresh, enabled && workspaceEnabled);
private async getHistoryNode(editor: TextEditor | undefined): Promise<ExplorerNode | undefined> {
return GitExplorer.getHistoryNode(this, editor, this._root);
}
if (toggled) {
this.refresh(RefreshReason.AutoRefreshChanged);
private async setFilesLayout(layout: ExplorerFilesLayout) {
return configuration.update(configuration.name('gitExplorer')('files')('layout').value, layout, ConfigurationTarget.Global);
}
private setRoot(root: ExplorerNode | undefined): boolean {
if (this._root === root) return false;
if (this._root !== undefined) {
this._root.dispose();
} }
this._root = root;
return true;
}
private async undockHistory(switchView: boolean = true) {
Container.historyExplorer.undock(switchView);
}
static async getHistoryNode(explorer: Explorer, editor: TextEditor | undefined, root: ExplorerNode | undefined): Promise<ExplorerNode | undefined> {
// If we have no active editor, or no visible editors, or no trackable visible editors reset the view
if (editor === undefined || window.visibleTextEditors.length === 0 || !window.visibleTextEditors.some(e => e.document && Container.git.isTrackable(e.document.uri))) return undefined;
// If we do have a visible trackable editor, don't change from the last state (avoids issues when focus switches to the problems/output/debug console panes)
if (editor.document === undefined || !Container.git.isTrackable(editor.document.uri)) return root;
const uri = await GitUri.fromUri(editor.document.uri);
const repo = await Container.git.getRepository(uri);
if (repo === undefined) return undefined;
if (UriComparer.equals(uri, root && root.uri)) return root;
return new HistoryNode(uri, repo, explorer);
} }
setRenameFollowing(enabled: boolean) {
static setRenameFollowing(enabled: boolean) {
configuration.updateEffective(configuration.name('advanced')('fileHistoryFollowsRenames').value, enabled); configuration.updateEffective(configuration.name('advanced')('fileHistoryFollowsRenames').value, enabled);
} }
} }

+ 178
- 0
src/views/historyExplorer.ts View File

@ -0,0 +1,178 @@
'use strict';
import { Functions } from '../system';
import { commands, ConfigurationChangeEvent, Disposable, Event, EventEmitter, TextEditor, TreeDataProvider, TreeItem, window } from 'vscode';
import { configuration, GitExplorerView, IExplorersConfig } from '../configuration';
import { CommandContext, GlyphChars, setCommandContext } from '../constants';
import { Container } from '../container';
import { RefreshNodeCommandArgs } from './explorerCommands';
import { ExplorerNode, MessageNode, RefreshReason } from './explorerNodes';
import { GitExplorer } from './gitExplorer';
import { Logger } from '../logger';
export * from './explorerNodes';
export class HistoryExplorer extends Disposable implements TreeDataProvider<ExplorerNode> {
private _disposable: Disposable | undefined;
private _root?: ExplorerNode;
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
public get onDidChangeTreeData(): Event<ExplorerNode> {
return this._onDidChangeTreeData.event;
}
constructor() {
super(() => this.dispose());
Container.explorerCommands;
commands.registerCommand('gitlens.historyExplorer.refresh', this.refresh, this);
commands.registerCommand('gitlens.historyExplorer.refreshNode', this.refreshNode, this);
commands.registerCommand('gitlens.historyExplorer.close', () => this.dock(false), this);
commands.registerCommand('gitlens.historyExplorer.dock', this.dock, this);
commands.registerCommand('gitlens.historyExplorer.setRenameFollowingOn', () => GitExplorer.setRenameFollowing(true), this);
commands.registerCommand('gitlens.historyExplorer.setRenameFollowingOff', () => GitExplorer.setRenameFollowing(false), this);
Container.context.subscriptions.push(
window.onDidChangeActiveTextEditor(Functions.debounce(this.onActiveEditorChanged, 500), this),
window.onDidChangeVisibleTextEditors(Functions.debounce(this.onVisibleEditorsChanged, 500), this),
configuration.onDidChange(this.onConfigurationChanged, this)
);
this.onConfigurationChanged(configuration.initializingChangeEvent);
}
dispose() {
this._disposable && this._disposable.dispose();
}
private async onConfigurationChanged(e: ConfigurationChangeEvent) {
const initializing = configuration.initializing(e);
if (!initializing &&
!configuration.changed(e, configuration.name('historyExplorer').value) &&
!configuration.changed(e, configuration.name('explorers').value) &&
!configuration.changed(e, configuration.name('defaultGravatarsStyle').value) &&
!configuration.changed(e, configuration.name('advanced')('fileHistoryFollowsRenames').value)) return;
if (initializing || configuration.changed(e, configuration.name('historyExplorer')('enabled').value)) {
if (Container.config.historyExplorer.enabled) {
this.undock(!initializing);
}
else {
this.dock(!initializing);
}
}
if (!initializing && this._root === undefined) {
this.refresh(RefreshReason.ConfigurationChanged);
}
if (initializing) {
this.setRoot(await this.getRootNode(window.activeTextEditor));
this._disposable = window.registerTreeDataProvider('gitlens.historyExplorer', this);
}
}
private async onActiveEditorChanged(editor: TextEditor | undefined) {
const root = await this.getRootNode(editor);
if (!this.setRoot(root)) return;
this.refresh(RefreshReason.ActiveEditorChanged, root);
}
private onVisibleEditorsChanged(editors: TextEditor[]) {
if (this._root === undefined) return;
// If we have no visible editors, or no trackable visible editors reset the view
if (editors.length === 0 || !editors.some(e => e.document && Container.git.isTrackable(e.document.uri))) {
this.clearRoot();
this.refresh(RefreshReason.VisibleEditorsChanged);
}
}
get config(): IExplorersConfig {
return { ...Container.config.explorers };
}
async getChildren(node?: ExplorerNode): Promise<ExplorerNode[]> {
if (this._root === undefined) return [new MessageNode(`No active file ${GlyphChars.Dash} no history to show`)];
if (node === undefined) return this._root.getChildren();
return node.getChildren();
}
async getTreeItem(node: ExplorerNode): Promise<TreeItem> {
return node.getTreeItem();
}
async dock(switchView: boolean = true) {
if (switchView) {
await Container.gitExplorer.switchTo(GitExplorerView.History);
}
setCommandContext(CommandContext.HistoryExplorer, false);
configuration.updateEffective(configuration.name('historyExplorer')('enabled').value, false);
}
getQualifiedCommand(command: string) {
return `gitlens.historyExplorer.${command}`;
}
async refresh(reason?: RefreshReason, root?: ExplorerNode) {
if (reason === undefined) {
reason = RefreshReason.Command;
}
Logger.log(`HistoryExplorer.refresh`, `reason='${reason}'`);
if (this._root === undefined || root === undefined) {
this.clearRoot();
this.setRoot(await this.getRootNode(window.activeTextEditor));
}
this._onDidChangeTreeData.fire();
}
refreshNode(node: ExplorerNode, args?: RefreshNodeCommandArgs) {
Logger.log(`HistoryExplorer.refreshNode(${(node as any).id})`);
if (args !== undefined && node.supportsPaging) {
node.maxCount = args.maxCount;
}
node.refresh();
// Since a root node won't actually refresh, force everything
this._onDidChangeTreeData.fire(this._root === node ? undefined : node);
}
async undock(switchView: boolean = true) {
if (switchView) {
await Container.gitExplorer.switchTo(GitExplorerView.Repository);
}
setCommandContext(CommandContext.HistoryExplorer, true);
configuration.updateEffective(configuration.name('historyExplorer')('enabled').value, true);
}
private clearRoot() {
if (this._root === undefined) return;
this._root.dispose();
this._root = undefined;
}
private async getRootNode(editor: TextEditor | undefined): Promise<ExplorerNode | undefined> {
return GitExplorer.getHistoryNode(this, editor, this._root);
}
private setRoot(root: ExplorerNode | undefined): boolean {
if (this._root === root) return false;
if (this._root !== undefined) {
this._root.dispose();
}
this._root = root;
return true;
}
}

+ 2
- 3
src/views/historyNode.ts View File

@ -1,9 +1,8 @@
'use strict'; 'use strict';
import { TreeItem, TreeItemCollapsibleState } from 'vscode'; import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Container } from '../container'; import { Container } from '../container';
import { ExplorerNode, ResourceType } from './explorerNode';
import { Explorer, ExplorerNode, ResourceType } from './explorerNode';
import { FileHistoryNode } from './fileHistoryNode'; import { FileHistoryNode } from './fileHistoryNode';
import { GitExplorer } from './gitExplorer';
import { GitUri, Repository } from '../gitService'; import { GitUri, Repository } from '../gitService';
export class HistoryNode extends ExplorerNode { export class HistoryNode extends ExplorerNode {
@ -11,7 +10,7 @@ export class HistoryNode extends ExplorerNode {
constructor( constructor(
uri: GitUri, uri: GitUri,
private readonly repo: Repository, private readonly repo: Repository,
private readonly explorer: GitExplorer
private readonly explorer: Explorer
) { ) {
super(uri); super(uri);
} }

+ 2
- 9
src/views/resultsExplorer.ts View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import { Functions, Strings } from '../system'; import { Functions, Strings } from '../system';
import { commands, ConfigurationChangeEvent, ConfigurationTarget, Disposable, Event, EventEmitter, TreeDataProvider, TreeItem, Uri, window, workspace } from 'vscode';
import { commands, ConfigurationChangeEvent, ConfigurationTarget, Disposable, Event, EventEmitter, TreeDataProvider, TreeItem, window } from 'vscode';
import { configuration, ExplorerFilesLayout, IExplorersConfig, IResultsExplorerConfig } from '../configuration'; import { configuration, ExplorerFilesLayout, IExplorersConfig, IResultsExplorerConfig } from '../configuration';
import { CommandContext, GlyphChars, setCommandContext, WorkspaceState } from '../constants'; import { CommandContext, GlyphChars, setCommandContext, WorkspaceState } from '../constants';
import { Container } from '../container'; import { Container } from '../container';
@ -71,13 +71,6 @@ export class ResultsExplorer extends Disposable implements TreeDataProvider
return { ...Container.config.explorers, ...Container.config.resultsExplorer }; return { ...Container.config.explorers, ...Container.config.resultsExplorer };
} }
get folderResourceUri(): Uri | undefined {
// Return the uri of any workspace folder -- we just need a folder so that we can use the uri has an icon resourceUri
if (workspace.workspaceFolders === undefined || workspace.workspaceFolders.length === 0) return undefined;
return workspace.workspaceFolders[0].uri;
}
get keepResults(): boolean { get keepResults(): boolean {
return Container.context.workspaceState.get<boolean>(WorkspaceState.ResultsExplorerKeepResults, false); return Container.context.workspaceState.get<boolean>(WorkspaceState.ResultsExplorerKeepResults, false);
} }
@ -113,7 +106,7 @@ export class ResultsExplorer extends Disposable implements TreeDataProvider
} }
refreshNode(node: ExplorerNode, args?: RefreshNodeCommandArgs) { refreshNode(node: ExplorerNode, args?: RefreshNodeCommandArgs) {
Logger.log(`ResultsExplorer.refreshNode`);
Logger.log(`ResultsExplorer.refreshNode(${(node as any).id})`);
if (args !== undefined && node.supportsPaging) { if (args !== undefined && node.supportsPaging) {
node.maxCount = args.maxCount; node.maxCount = args.maxCount;

Loading…
Cancel
Save