mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-02-12 11:08:34 -05:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4694fbc1ae | ||
|
|
b56d101f76 | ||
|
|
ce9394297d | ||
|
|
4d18bf708d | ||
|
|
c44e4c6968 | ||
|
|
c5f57172e4 | ||
|
|
dd0a636e24 | ||
|
|
393ec351f0 | ||
|
|
99d6da9c90 | ||
|
|
0eb202b8ae | ||
|
|
65736a6ce7 | ||
|
|
ed42eba8b8 | ||
|
|
2245d82319 | ||
|
|
f7df845dfe | ||
|
|
712544fab8 | ||
|
|
a114e2de87 | ||
|
|
70071448d6 | ||
|
|
a10376385a | ||
|
|
41d25803d8 | ||
|
|
3802b43027 | ||
|
|
04ea3b7971 | ||
|
|
6d7f44e091 | ||
|
|
3a1caa2e0d | ||
|
|
3f7058bd48 | ||
|
|
71d17bcc2f | ||
|
|
a69afdb6ef | ||
|
|
26c6346b84 |
39
CHANGELOG.md
39
CHANGELOG.md
@@ -4,6 +4,45 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [5.3.0] - 2017-09-26
|
||||||
|
### Added
|
||||||
|
- Adds new file layouts to the `GitLens` custom view
|
||||||
|
- `auto` - automatically switches between displaying files as a `tree` or `list` based on the `gitlens.gitExplorer.files.threshold` setting and the number of files at each nesting level
|
||||||
|
- `list` - displays files as a list
|
||||||
|
- `tree` - displays files as a tree
|
||||||
|
- Adds `gitlens.gitExplorer.files.layout` setting to specify how the `GitLens` custom view will display files
|
||||||
|
- Adds `gitlens.gitExplorer.files.compact` setting to specify whether or not to compact (flatten) unnecessary file nesting in the `GitLens` custom view
|
||||||
|
- Adds `gitlens.gitExplorer.files.threshold` setting to specify when to switch between displaying files as a `tree` or `list` based on the number of files in a nesting level in the `GitLens` custom view
|
||||||
|
- Adds `${directory}` token to the file formatting settings
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Changes `${path}` token to be the full file path in the file formatting settings
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixes [#153](https://github.com/eamodio/vscode-gitlens/issues/153) - New folders treated as files in "Changed Files" section of the sidebar component
|
||||||
|
|
||||||
|
## [5.2.0] - 2017-09-23
|
||||||
|
### Added
|
||||||
|
- Adds new `Changed Files` node to the `Repository Status` node of the `GitLens` custom view's `Repository View` -- closes [#139](https://github.com/eamodio/vscode-gitlens/issues/139)
|
||||||
|
- Provides a at-a-glance view of all "working" changes
|
||||||
|
- Expands to a file-based view of all changed files in the working tree (enabled via `"gitlens.insiders": true`) and/or all files in all commits ahead of the upstream
|
||||||
|
- Adds optional (on by default) working tree status information to the `Repository Status` node in the `GitLens` custom view
|
||||||
|
- Adds `auto` value to `gitlens.gitExplorer.view` setting - closes [#150](https://github.com/eamodio/vscode-gitlens/issues/150)
|
||||||
|
- Adds `gitlens.gitExplorer.enabled` setting to specify whether or not to show the `GitLens` custom view - closes [#144](https://github.com/eamodio/vscode-gitlens/issues/144)
|
||||||
|
- Adds `gitlens.gitExplorer.includeWorkingTree` setting to specify whether or not to include working tree files inside the `Repository Status` node of the `GitLens` custom view
|
||||||
|
- Adds `gitlens.gitExplorer.statusFileFormat` setting to the format of the status of a working or committed file in the `GitLens` custom view
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Changes the sorting (now alphabetical) of files shown in the `GitLens` custom view
|
||||||
|
- Changes the default of the `gitlens.gitExplorer.view` setting to `auto`
|
||||||
|
- Changes the default of the `gitlens.gitExplorer.commitFormat` setting to add parentheses around the commit id
|
||||||
|
- Removes many menu items from `editor/title` & `editor/title/context` by default -- can be re-enabled via the `gitlens.advanced.menus` setting
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixes [#146](https://github.com/eamodio/vscode-gitlens/issues/146) - Blame gutter annotation issue when commit contains emoji
|
||||||
|
- Fixes an issue when running `Open File in Remote` with a multi-line selection wasn't properly opening the selection in GitLab -- thanks to [PR #145](https://github.com/eamodio/vscode-gitlens/pull/145) by Amanda Cameron ([@AmandaCameron](https://github.com/AmandaCameron))!
|
||||||
|
- Fixes an issue where the `gitlens.advanced.menus` setting wasn't controlling all the menu items properly
|
||||||
|
|
||||||
## [5.1.0] - 2017-09-15
|
## [5.1.0] - 2017-09-15
|
||||||
### Added
|
### Added
|
||||||
- Adds full (multi-line) commit message to the `details` hover annotations -- closes [#116](https://github.com/eamodio/vscode-gitlens/issues/116)
|
- Adds full (multi-line) commit message to the `details` hover annotations -- closes [#116](https://github.com/eamodio/vscode-gitlens/issues/116)
|
||||||
|
|||||||
27
README.md
27
README.md
@@ -1,7 +1,7 @@
|
|||||||
[](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
|
[](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
|
||||||
[](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
|
[](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
|
||||||
[](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
|
[](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
|
||||||
[](https://join.slack.com/t/vscode-gitlens/shared_invite/MjIxOTgxNDE3NzM0LTE1MDE2Nzk1MTgtMjkwMmZjMzcxNQ)
|
[](https://join.slack.com/t/vscode-dev-community/shared_invite/enQtMjIxOTgxNDE3NzM0LWU5M2ZiZDU1YjBlMzdlZjA2YjBjYzRhYTM5NTgzMTAxMjdiNWU0ZmQzYWI3MWU5N2Q1YjBiYmQ4MzY0NDE1MzY)
|
||||||
|
|
||||||
# GitLens
|
# GitLens
|
||||||
|
|
||||||
@@ -126,13 +126,17 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
|
|||||||

|

|
||||||
|
|
||||||
- `Repository Status` node — provides the status of the repository
|
- `Repository Status` node — provides the status of the repository
|
||||||
- Provides the name of the current branch, its upstream tracking branch (if available), and its upstream status (if available)
|
- Provides the name of the current branch, [optionally](#gitlens-custom-view-settings) its working tree status, and its upstream tracking branch and status (if available)
|
||||||
- Provides indicator dots on the repository icon which denote the following:
|
- Provides indicator dots on the repository icon which denote the following:
|
||||||
- `None` - up-to-date with the upstream
|
- `None` - up-to-date with the upstream
|
||||||
- `Green` - ahead of the upstream
|
- `Green` - ahead of the upstream
|
||||||
- `Red` - behind the upstream
|
- `Red` - behind the upstream
|
||||||
- `Yellow` - both ahead of and behind the upstream
|
- `Yellow` - both ahead of and behind the upstream
|
||||||
- Provides additional nodes, if the current branch is not synchronized with the upstream, to quickly see and explore the specific commits ahead and/or behind the upstream
|
- Provides additional upstream status nodes, if the current branch is tracking a remote branch and
|
||||||
|
- is behind the upstream — quickly see and explore the specific commits behind the upstream (i.e. commits that haven't been pulled)
|
||||||
|
- is ahead of the upstream — quickly see and explore the specific commits ahead of the upstream (i.e. commits that haven't been pushed)
|
||||||
|
- `Changed Files` node — provides a at-a-glance view of all "working" changes
|
||||||
|
- Expands to a file-based view of all changed files in the working tree ([optionally](#gitlens-custom-view-settings)) and/or all files in all commits ahead of the upstream
|
||||||
- Provides a context menu with `Open Repository in Remote`, and `Refresh` commands
|
- Provides a context menu with `Open Repository in Remote`, and `Refresh` commands
|
||||||
|
|
||||||
- `Branches` node — provides a list of the local branches
|
- `Branches` node — provides a list of the local branches
|
||||||
@@ -284,7 +288,7 @@ GitLens is highly customizable and provides many configuration settings to allow
|
|||||||
|
|
||||||
|Name | Description
|
|Name | Description
|
||||||
|-----|------------
|
|-----|------------
|
||||||
|`gitlens.defaultDateFormat`|Specifies how all absolute dates will be formatted by default\nSee https://momentjs.com/docs/#/displaying/format/ for valid formats
|
|`gitlens.defaultDateFormat`|Specifies how all absolute dates will be formatted by default<br />See https://momentjs.com/docs/#/displaying/format/ for valid formats
|
||||||
|`gitlens.insiders`|Opts into the insiders channel -- provides access to upcoming features
|
|`gitlens.insiders`|Opts into the insiders channel -- provides access to upcoming features
|
||||||
|`gitlens.outputLevel`|Specifies how much (if any) output will be sent to the GitLens output channel
|
|`gitlens.outputLevel`|Specifies how much (if any) output will be sent to the GitLens output channel
|
||||||
|
|
||||||
@@ -342,7 +346,7 @@ GitLens is highly customizable and provides many configuration settings to allow
|
|||||||
|`gitlens.codeLens.recentChange.command`|Specifies the command to be executed when the `recent change` code lens is clicked<br />`gitlens.toggleFileBlame` - toggles file blame annotations<br />`gitlens.showBlameHistory` - opens the blame history explorer<br />`gitlens.showFileHistory` - opens the file history explorer<br />`gitlens.diffWithPrevious` - compares the current committed file with the previous commit<br />`gitlens.showQuickCommitDetails` - shows a commit details quick pick<br />`gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick<br />`gitlens.showQuickFileHistory` - shows a file history quick pick<br />`gitlens.showQuickRepoHistory` - shows a branch history quick pick
|
|`gitlens.codeLens.recentChange.command`|Specifies the command to be executed when the `recent change` code lens is clicked<br />`gitlens.toggleFileBlame` - toggles file blame annotations<br />`gitlens.showBlameHistory` - opens the blame history explorer<br />`gitlens.showFileHistory` - opens the file history explorer<br />`gitlens.diffWithPrevious` - compares the current committed file with the previous commit<br />`gitlens.showQuickCommitDetails` - shows a commit details quick pick<br />`gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick<br />`gitlens.showQuickFileHistory` - shows a file history quick pick<br />`gitlens.showQuickRepoHistory` - shows a branch history quick pick
|
||||||
|`gitlens.codeLens.authors.enabled`|Specifies whether or not to show an `authors` code lens showing number of authors of the file or code block and the most prominent author (if there is more than one)
|
|`gitlens.codeLens.authors.enabled`|Specifies whether or not to show an `authors` code lens showing number of authors of the file or code block and the most prominent author (if there is more than one)
|
||||||
|`gitlens.codeLens.authors.command`|Specifies the command to be executed when the `authors` code lens is clicked<br />`gitlens.toggleFileBlame` - toggles file blame annotations<br />`gitlens.showBlameHistory` - opens the blame history explorer<br />`gitlens.showFileHistory` - opens the file history explorer<br />`gitlens.diffWithPrevious` - compares the current committed file with the previous commit<br />`gitlens.showQuickCommitDetails` - shows a commit details quick pick<br />`gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick<br />`gitlens.showQuickFileHistory` - shows a file history quick pick<br />`gitlens.showQuickRepoHistory` - shows a branch history quick pick
|
|`gitlens.codeLens.authors.command`|Specifies the command to be executed when the `authors` code lens is clicked<br />`gitlens.toggleFileBlame` - toggles file blame annotations<br />`gitlens.showBlameHistory` - opens the blame history explorer<br />`gitlens.showFileHistory` - opens the file history explorer<br />`gitlens.diffWithPrevious` - compares the current committed file with the previous commit<br />`gitlens.showQuickCommitDetails` - shows a commit details quick pick<br />`gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick<br />`gitlens.showQuickFileHistory` - shows a file history quick pick<br />`gitlens.showQuickRepoHistory` - shows a branch history quick pick
|
||||||
|`gitlens.codeLens.locations`|Specifies where Git code lens will be shown in the document<br />`document` - adds code lens at the top of the document<br />`containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)<br />`blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines<br />`custom` - adds code lens at the start of symbols contained in `gitlens.codeLens.locationCustomSymbols`
|
|`gitlens.codeLens.locations`|Specifies where Git code lens will be shown in the document<br />`document` - adds code lens at the top of the document<br />`containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)<br />`blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines<br />`custom` - adds code lens at the start of symbols contained in `gitlens.codeLens.locationCustomSymbols`
|
||||||
|`gitlens.codeLens.customLocationSymbols`|Specifies the set of document symbols where Git code lens will be shown in the document
|
|`gitlens.codeLens.customLocationSymbols`|Specifies the set of document symbols where Git code lens will be shown in the document
|
||||||
|`gitlens.codeLens.perLanguageLocations`|Specifies where Git code lens will be shown in the document for the specified languages
|
|`gitlens.codeLens.perLanguageLocations`|Specifies where Git code lens will be shown in the document for the specified languages
|
||||||
|
|
||||||
@@ -350,12 +354,18 @@ GitLens is highly customizable and provides many configuration settings to allow
|
|||||||
|
|
||||||
|Name | Description
|
|Name | Description
|
||||||
|-----|------------
|
|-----|------------
|
||||||
|`gitlens.gitExplorer.view`|Specifies the starting view (mode) of the `GitLens` custom view<br />`history` - shows the commit history of the active file<br />`repository` - shows a repository explorer"
|
|`gitlens.gitExplorer.enabled`|Specifies whether or not to show the `GitLens` custom view"
|
||||||
|
|`gitlens.gitExplorer.view`|Specifies the starting view (mode) of the `GitLens` custom view<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.files.layout`|Specifies how the `GitLens` custom view will display files<br /> `auto` - automatically switches between displaying files as a `tree` or `list` based on the `gitlens.gitExplorer.files.threshold` setting and the number of files at each nesting level<br /> `list` - displays files as a list<br /> `tree` - displays files as a tree
|
||||||
|
|`gitlens.gitExplorer.files.compact`|Specifies whether or not to compact (flatten) unnecessary file nesting in the `GitLens` custom view<br />Only applies when displaying files as a `tree` or `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` custom view<br />Only applies when displaying files as `auto`
|
||||||
|
|`gitlens.gitExplorer.includeWorkingTree`|Specifies whether or not to include working tree files inside the `Repository Status` node of the `GitLens` custom view
|
||||||
|`gitlens.gitExplorer.showTrackingBranch`|Specifies whether or not to show the tracking branch when displaying local branches in the `GitLens` custom view"
|
|`gitlens.gitExplorer.showTrackingBranch`|Specifies whether or not to show the tracking branch when displaying local branches in the `GitLens` custom view"
|
||||||
|`gitlens.gitExplorer.commitFormat`|Specifies the format of committed changes in the `GitLens` custom view<br />Available tokens<br /> ${id} - commit id<br /> ${author} - commit author<br /> ${message} - commit message<br /> ${ago} - relative commit date (e.g. 1 day ago)<br /> ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)<br /> ${authorAgo} - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
|
|`gitlens.gitExplorer.commitFormat`|Specifies the format of committed changes in the `GitLens` custom view<br />Available tokens<br /> ${id} - commit id<br /> ${author} - commit author<br /> ${message} - commit message<br /> ${ago} - relative commit date (e.g. 1 day ago)<br /> ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)<br /> ${authorAgo} - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
|
||||||
|`gitlens.gitExplorer.commitFileFormat`|Specifies the format of a committed file in the `GitLens` custom view<br />Available tokens<br /> ${file} - file name<br /> ${filePath} - file name and path<br /> ${path} - file path
|
|`gitlens.gitExplorer.commitFileFormat`|Specifies the format of a committed file in the `GitLens` custom view<br />Available tokens<br /> ${directory} - directory name<br /> ${file} - file name<br /> ${filePath} - formatted file name and path<br /> ${path} - full file path
|
||||||
|`gitlens.gitExplorer.stashFormat`|Specifies the format of stashed changes in the `GitLens` custom view<br />Available tokens<br /> ${id} - commit id<br /> ${author} - commit author<br /> ${message} - commit message<br /> ${ago} - relative commit date (e.g. 1 day ago)<br /> ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)<br /> ${authorAgo} - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
|
|`gitlens.gitExplorer.stashFormat`|Specifies the format of stashed changes in the `GitLens` custom view<br />Available tokens<br /> ${id} - commit id<br /> ${author} - commit author<br /> ${message} - commit message<br /> ${ago} - relative commit date (e.g. 1 day ago)<br /> ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)<br /> ${authorAgo} - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
|
||||||
|`gitlens.gitExplorer.stashFileFormat`|Specifies the format of a stashed file in the `GitLens` custom view<br />Available tokens<br /> ${file} - file name<br /> ${filePath} - file name and path<br /> ${path} - file path
|
|`gitlens.gitExplorer.stashFileFormat`|Specifies the format of a stashed file in the `GitLens` custom view<br />Available tokens<br /> ${directory} - directory name<br /> ${file} - file name<br /> ${filePath} - formatted file name and path<br /> ${path} - full file path
|
||||||
|
|`gitlens.gitExplorer.statusFileFormat`|Specifies the format of the status of a working or committed file in the `GitLens` custom view<br />Available tokens<br /> ${directory} - directory name<br /> ${file} - file name<br /> ${filePath} - formatted file name and path<br /> ${path} - full file path<br />${working} - optional indicator if the file is uncommitted
|
||||||
|
|
||||||
### Custom Remotes Settings
|
### Custom Remotes Settings
|
||||||
|
|
||||||
@@ -422,6 +432,7 @@ GitLens is highly customizable and provides many configuration settings to allow
|
|||||||
|
|
||||||
A big thanks to the people that have contributed to this project:
|
A big thanks to the people that have contributed to this project:
|
||||||
|
|
||||||
|
- Amanda Cameron ([@AmandaCameron](https://github.com/AmandaCameron)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=AmandaCameron))
|
||||||
- Peng Lyu ([@rebornix](https://github.com/rebornix)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=rebornix))
|
- Peng Lyu ([@rebornix](https://github.com/rebornix)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=rebornix))
|
||||||
- Aurelio Ogliari ([@nobitagit](https://github.com/nobitagit)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=nobitagit)
|
- Aurelio Ogliari ([@nobitagit](https://github.com/nobitagit)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=nobitagit)
|
||||||
- Johannes Rieken ([@jrieken](https://github.com/jrieken)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=jrieken))
|
- Johannes Rieken ([@jrieken](https://github.com/jrieken)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=jrieken))
|
||||||
|
|||||||
4
images/dark/icon-diff.svg
Normal file
4
images/dark/icon-diff.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m7.5,10l2,0l0,1l-2,0l0,2l-1,0l0,-2l-2,0l0,-1l2,0l0,-2l1,0l0,2l0,0zm-3,6l5,0l0,-1l-5,0l0,1l0,0zm4.5,-11l3.5,3.5l0,9.5c0,0.55 -0.45,1 -1,1l-9,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l6.5,0l0,0zm2.5,4l-3,-3l-6,0l0,12l9,0l0,-9l0,0zm-1.5,-6l-5.5,0l0,1l5,0l4,4l0,8l1,0l0,-8.5l-4.5,-4.5l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 437 B |
4
images/light/icon-diff.svg
Normal file
4
images/light/icon-diff.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m7.5,10l2,0l0,1l-2,0l0,2l-1,0l0,-2l-2,0l0,-1l2,0l0,-2l1,0l0,2l0,0zm-3,6l5,0l0,-1l-5,0l0,1l0,0zm4.5,-11l3.5,3.5l0,9.5c0,0.55 -0.45,1 -1,1l-9,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l6.5,0l0,0zm2.5,4l-3,-3l-6,0l0,12l9,0l0,-9l0,0zm-1.5,-6l-5.5,0l0,1l5,0l4,4l0,8l1,0l0,-8.5l-4.5,-4.5l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 437 B |
292
package-lock.json
generated
292
package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gitlens",
|
"name": "gitlens",
|
||||||
"version": "5.1.0",
|
"version": "5.3.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"integrity": "sha1-qjuL2ivlErGuCgV7lC6GnDcKVWk=",
|
"integrity": "sha1-qjuL2ivlErGuCgV7lC6GnDcKVWk=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "8.0.28"
|
"@types/node": "8.0.31"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/mocha": {
|
"@types/mocha": {
|
||||||
@@ -26,9 +26,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "8.0.28",
|
"version": "8.0.31",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.31.tgz",
|
||||||
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ==",
|
"integrity": "sha512-R+LdMJHJQwRd/Ca0Nr5KnwbSWHxTD3DWz4ivqoPeNH+YPcuirMWK+Ti9Mx32jOecmPhHOCd+6CefU5e1eVq2Ew==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/tmp": {
|
"@types/tmp": {
|
||||||
@@ -38,19 +38,22 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ajv": {
|
"ajv": {
|
||||||
"version": "4.11.8",
|
"version": "5.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz",
|
||||||
"integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
|
"integrity": "sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"co": "4.6.0",
|
"co": "4.6.0",
|
||||||
|
"fast-deep-equal": "1.0.0",
|
||||||
|
"json-schema-traverse": "0.3.1",
|
||||||
"json-stable-stringify": "1.0.1"
|
"json-stable-stringify": "1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "3.0.0",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||||
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
|
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"ansi-styles": {
|
"ansi-styles": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
@@ -249,21 +252,6 @@
|
|||||||
"supports-color": "2.0.0"
|
"supports-color": "2.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
|
||||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"strip-ansi": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
|
||||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"ansi-regex": "2.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||||
@@ -385,15 +373,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dateformat": {
|
"dateformat": {
|
||||||
"version": "2.0.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz",
|
||||||
"integrity": "sha1-J0Pjq7XD/CRi5SfcpEXgTp9N7hc=",
|
"integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.6.8",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
"integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
@@ -597,6 +585,12 @@
|
|||||||
"time-stamp": "1.1.0"
|
"time-stamp": "1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fast-deep-equal": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"fd-slicer": {
|
"fd-slicer": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
|
||||||
@@ -684,7 +678,7 @@
|
|||||||
"graceful-fs": "4.1.11",
|
"graceful-fs": "4.1.11",
|
||||||
"inherits": "2.0.3",
|
"inherits": "2.0.3",
|
||||||
"mkdirp": "0.5.1",
|
"mkdirp": "0.5.1",
|
||||||
"rimraf": "2.6.1"
|
"rimraf": "2.6.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"generate-function": {
|
"generate-function": {
|
||||||
@@ -1003,7 +997,7 @@
|
|||||||
"oauth-sign": "0.8.2",
|
"oauth-sign": "0.8.2",
|
||||||
"qs": "6.3.2",
|
"qs": "6.3.2",
|
||||||
"stringstream": "0.0.5",
|
"stringstream": "0.0.5",
|
||||||
"tough-cookie": "2.3.2",
|
"tough-cookie": "2.3.3",
|
||||||
"tunnel-agent": "0.4.3",
|
"tunnel-agent": "0.4.3",
|
||||||
"uuid": "3.1.0"
|
"uuid": "3.1.0"
|
||||||
}
|
}
|
||||||
@@ -1086,7 +1080,7 @@
|
|||||||
"array-uniq": "1.0.3",
|
"array-uniq": "1.0.3",
|
||||||
"beeper": "1.1.1",
|
"beeper": "1.1.1",
|
||||||
"chalk": "1.1.3",
|
"chalk": "1.1.3",
|
||||||
"dateformat": "2.0.0",
|
"dateformat": "2.2.0",
|
||||||
"fancy-log": "1.3.0",
|
"fancy-log": "1.3.0",
|
||||||
"gulplog": "1.0.0",
|
"gulplog": "1.0.0",
|
||||||
"has-gulplog": "0.1.0",
|
"has-gulplog": "0.1.0",
|
||||||
@@ -1187,9 +1181,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"har-schema": {
|
"har-schema": {
|
||||||
"version": "1.0.5",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||||
"integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=",
|
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"har-validator": {
|
"har-validator": {
|
||||||
@@ -1211,14 +1205,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "2.1.1"
|
"ansi-regex": "2.1.1"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
|
||||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"has-flag": {
|
"has-flag": {
|
||||||
@@ -1336,11 +1322,6 @@
|
|||||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"is-fullwidth-code-point": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
|
|
||||||
},
|
|
||||||
"is-glob": {
|
"is-glob": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
|
||||||
@@ -1459,6 +1440,12 @@
|
|||||||
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
|
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"json-schema-traverse": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
|
||||||
|
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"json-stable-stringify": {
|
"json-stable-stringify": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
|
||||||
@@ -1794,9 +1781,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"version": "3.5.2",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz",
|
||||||
"integrity": "sha512-iH5zl7afRZl1GvD0pnrRlazgc9Z/o34pXWmTFi8xNIMFKXgNL1SoBTDDb9sJfbV/sJV/j8X+0gvwY1QS1He7Nw==",
|
"integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"browser-stdout": "1.3.0",
|
"browser-stdout": "1.3.0",
|
||||||
@@ -1811,6 +1798,17 @@
|
|||||||
"lodash.create": "3.1.1",
|
"lodash.create": "3.1.1",
|
||||||
"mkdirp": "0.5.1",
|
"mkdirp": "0.5.1",
|
||||||
"supports-color": "3.1.2"
|
"supports-color": "3.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
|
||||||
|
"integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"moment": {
|
"moment": {
|
||||||
@@ -1971,9 +1969,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"performance-now": {
|
"performance-now": {
|
||||||
"version": "0.2.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||||
"integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=",
|
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"pinkie": {
|
"pinkie": {
|
||||||
@@ -2120,57 +2118,147 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"request": {
|
"request": {
|
||||||
"version": "2.81.0",
|
"version": "2.82.0",
|
||||||
"resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
|
"resolved": "https://registry.npmjs.org/request/-/request-2.82.0.tgz",
|
||||||
"integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=",
|
"integrity": "sha512-/QWqfmyTfQ4OYs6EhB1h2wQsX9ZxbuNePCvCm0Mdz/mxw73mjdg0D4QdIl0TQBFs35CZmMXLjk0iCGK395CUDg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"aws-sign2": "0.6.0",
|
"aws-sign2": "0.7.0",
|
||||||
"aws4": "1.6.0",
|
"aws4": "1.6.0",
|
||||||
"caseless": "0.12.0",
|
"caseless": "0.12.0",
|
||||||
"combined-stream": "1.0.5",
|
"combined-stream": "1.0.5",
|
||||||
"extend": "3.0.1",
|
"extend": "3.0.1",
|
||||||
"forever-agent": "0.6.1",
|
"forever-agent": "0.6.1",
|
||||||
"form-data": "2.1.4",
|
"form-data": "2.3.1",
|
||||||
"har-validator": "4.2.1",
|
"har-validator": "5.0.3",
|
||||||
"hawk": "3.1.3",
|
"hawk": "6.0.2",
|
||||||
"http-signature": "1.1.1",
|
"http-signature": "1.2.0",
|
||||||
"is-typedarray": "1.0.0",
|
"is-typedarray": "1.0.0",
|
||||||
"isstream": "0.1.2",
|
"isstream": "0.1.2",
|
||||||
"json-stringify-safe": "5.0.1",
|
"json-stringify-safe": "5.0.1",
|
||||||
"mime-types": "2.1.17",
|
"mime-types": "2.1.17",
|
||||||
"oauth-sign": "0.8.2",
|
"oauth-sign": "0.8.2",
|
||||||
"performance-now": "0.2.0",
|
"performance-now": "2.1.0",
|
||||||
"qs": "6.4.0",
|
"qs": "6.5.1",
|
||||||
"safe-buffer": "5.1.1",
|
"safe-buffer": "5.1.1",
|
||||||
"stringstream": "0.0.5",
|
"stringstream": "0.0.5",
|
||||||
"tough-cookie": "2.3.2",
|
"tough-cookie": "2.3.3",
|
||||||
"tunnel-agent": "0.6.0",
|
"tunnel-agent": "0.6.0",
|
||||||
"uuid": "3.1.0"
|
"uuid": "3.1.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"assert-plus": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"aws-sign2": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||||
|
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"boom": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
|
||||||
|
"integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"hoek": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"caseless": {
|
"caseless": {
|
||||||
"version": "0.12.0",
|
"version": "0.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
|
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"har-validator": {
|
"cryptiles": {
|
||||||
"version": "4.2.1",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
|
||||||
"integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
|
"integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "4.11.8",
|
"boom": "5.2.0"
|
||||||
"har-schema": "1.0.5"
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"boom": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"hoek": "4.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"form-data": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz",
|
||||||
|
"integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"asynckit": "0.4.0",
|
||||||
|
"combined-stream": "1.0.5",
|
||||||
|
"mime-types": "2.1.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"har-validator": {
|
||||||
|
"version": "5.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
|
||||||
|
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ajv": "5.2.3",
|
||||||
|
"har-schema": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hawk": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"boom": "4.3.1",
|
||||||
|
"cryptiles": "3.1.2",
|
||||||
|
"hoek": "4.2.0",
|
||||||
|
"sntp": "2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hoek": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"http-signature": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"assert-plus": "1.0.0",
|
||||||
|
"jsprim": "1.4.1",
|
||||||
|
"sshpk": "1.13.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"qs": {
|
"qs": {
|
||||||
"version": "6.4.0",
|
"version": "6.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
|
||||||
"integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=",
|
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"sntp": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz",
|
||||||
|
"integrity": "sha1-UGQRDwr4X3z9t9a2ekACjOUrSys=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"hoek": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tunnel-agent": {
|
"tunnel-agent": {
|
||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||||
@@ -2198,9 +2286,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rimraf": {
|
"rimraf": {
|
||||||
"version": "2.6.1",
|
"version": "2.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
|
||||||
"integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=",
|
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"glob": "7.1.1"
|
"glob": "7.1.1"
|
||||||
@@ -2260,7 +2348,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-2.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-2.0.11.tgz",
|
||||||
"integrity": "sha1-ZUUa1lZigB2up1VJgyp4LeAEjb8=",
|
"integrity": "sha1-ZUUa1lZigB2up1VJgyp4LeAEjb8=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "2.6.8",
|
"debug": "2.6.9",
|
||||||
"lodash.assign": "4.2.0",
|
"lodash.assign": "4.2.0",
|
||||||
"rxjs": "5.4.3"
|
"rxjs": "5.4.3"
|
||||||
}
|
}
|
||||||
@@ -2334,15 +2422,6 @@
|
|||||||
"integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8=",
|
"integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"string-width": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
|
||||||
"requires": {
|
|
||||||
"is-fullwidth-code-point": "2.0.0",
|
|
||||||
"strip-ansi": "4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"string_decoder": {
|
"string_decoder": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||||
@@ -2359,11 +2438,12 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"strip-ansi": {
|
"strip-ansi": {
|
||||||
"version": "4.0.0",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||||
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "3.0.0"
|
"ansi-regex": "2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"strip-bom": {
|
"strip-bom": {
|
||||||
@@ -2466,9 +2546,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tough-cookie": {
|
"tough-cookie": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
|
||||||
"integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=",
|
"integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"punycode": "1.4.1"
|
"punycode": "1.4.1"
|
||||||
@@ -2495,13 +2575,13 @@
|
|||||||
"resolve": "1.4.0",
|
"resolve": "1.4.0",
|
||||||
"semver": "5.4.1",
|
"semver": "5.4.1",
|
||||||
"tslib": "1.7.1",
|
"tslib": "1.7.1",
|
||||||
"tsutils": "2.8.2"
|
"tsutils": "2.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tsutils": {
|
"tsutils": {
|
||||||
"version": "2.8.2",
|
"version": "2.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.9.0.tgz",
|
||||||
"integrity": "sha1-LBSGukMSYIRbCsb5Aq/Z1wio6mo=",
|
"integrity": "sha1-fhU3tVa6tocvp+ZIXf9FsHbVUz0=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "1.7.1"
|
"tslib": "1.7.1"
|
||||||
@@ -2521,9 +2601,9 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "2.5.2",
|
"version": "2.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz",
|
||||||
"integrity": "sha1-A4qV99m7tCCxvzW6MdTFwd0//jQ=",
|
"integrity": "sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"unique-stream": {
|
"unique-stream": {
|
||||||
@@ -2714,8 +2794,8 @@
|
|||||||
"gulp-symdest": "1.1.0",
|
"gulp-symdest": "1.1.0",
|
||||||
"gulp-untar": "0.0.6",
|
"gulp-untar": "0.0.6",
|
||||||
"gulp-vinyl-zip": "1.4.0",
|
"gulp-vinyl-zip": "1.4.0",
|
||||||
"mocha": "3.5.2",
|
"mocha": "3.5.3",
|
||||||
"request": "2.81.0",
|
"request": "2.82.0",
|
||||||
"semver": "5.4.1",
|
"semver": "5.4.1",
|
||||||
"source-map-support": "0.4.18",
|
"source-map-support": "0.4.18",
|
||||||
"url-parse": "1.1.9",
|
"url-parse": "1.1.9",
|
||||||
|
|||||||
132
package.json
132
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gitlens",
|
"name": "gitlens",
|
||||||
"version": "5.1.0",
|
"version": "5.3.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Eric Amodio",
|
"name": "Eric Amodio",
|
||||||
"email": "eamodio@gmail.com"
|
"email": "eamodio@gmail.com"
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
"badges": [
|
"badges": [
|
||||||
{
|
{
|
||||||
"url": "https://img.shields.io/badge/chat-on%20slack-brightgreen.svg",
|
"url": "https://img.shields.io/badge/chat-on%20slack-brightgreen.svg",
|
||||||
"href": "https://join.slack.com/t/vscode-gitlens/shared_invite/MjIxOTgxNDE3NzM0LTE1MDE2Nzk1MTgtMjkwMmZjMzcxNQ",
|
"href": "https://join.slack.com/t/vscode-dev-community/shared_invite/enQtMjIxOTgxNDE3NzM0LWU5M2ZiZDU1YjBlMzdlZjA2YjBjYzRhYTM5NTgzMTAxMjdiNWU0ZmQzYWI3MWU5N2Q1YjBiYmQ4MzY0NDE1MzY",
|
||||||
"description": "Chat at https://vscode-gitlens.slack.com/"
|
"description": "Chat at https://vscode-dev-community.slack.com/"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"categories": [
|
"categories": [
|
||||||
@@ -303,7 +303,7 @@
|
|||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
"maxItems": 4,
|
"maxItems": 4,
|
||||||
"uniqueItems": true,
|
"uniqueItems": true,
|
||||||
"description": "Specifies where Git code lens will be shown in the document\n `document` - adds code lens at the top of the document\n `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)\n `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines\n `custom` - adds code lens at the start of symbols contained in `gitlens.codeLens.locationCustomSymbols`"
|
"description": "Specifies where Git code lens will be shown in the document\n `document` - adds code lens at the top of the document\n `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)\n `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines\n `custom` - adds code lens at the start of symbols contained in `gitlens.codeLens.locationCustomSymbols`"
|
||||||
},
|
},
|
||||||
"gitlens.codeLens.customLocationSymbols": {
|
"gitlens.codeLens.customLocationSymbols": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@@ -388,7 +388,7 @@
|
|||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
"maxItems": 4,
|
"maxItems": 4,
|
||||||
"uniqueItems": true,
|
"uniqueItems": true,
|
||||||
"description": "Specifies where Git code lens will be shown in the document for the specified language\n `document` - adds code lens at the top of the document\n `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)\n `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines\n `custom` - adds code lens at the start of symbols contained in `customSymbols`"
|
"description": "Specifies where Git code lens will be shown in the document for the specified language\n `document` - adds code lens at the top of the document\n `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)\n `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines\n `custom` - adds code lens at the start of symbols contained in `customSymbols`"
|
||||||
},
|
},
|
||||||
"customSymbols": {
|
"customSymbols": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@@ -415,13 +415,43 @@
|
|||||||
},
|
},
|
||||||
"gitlens.gitExplorer.commitFormat": {
|
"gitlens.gitExplorer.commitFormat": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "${message} \u00a0\u2022\u00a0 ${authorAgo} \u00a0\u2022\u00a0 ${id}",
|
"default": "${message} \u00a0\u2022\u00a0 ${authorAgo} \u00a0 (${id})",
|
||||||
"description": "Specifies the format of committed changes in the `GitLens` custom view\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
|
"description": "Specifies the format of committed changes in the `GitLens` custom view\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
|
||||||
},
|
},
|
||||||
"gitlens.gitExplorer.commitFileFormat": {
|
"gitlens.gitExplorer.commitFileFormat": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "${filePath}",
|
"default": "${filePath}",
|
||||||
"description": "Specifies the format of a committed file in the `GitLens` custom view\nAvailable tokens\n ${file} - file name\n ${filePath} - file name and path\n ${path} - file path"
|
"description": "Specifies the format of a committed file in the `GitLens` custom view\nAvailable tokens\n ${directory} - directory name\n ${file} - file name\n ${filePath} - formatted file name and path\n ${path} - full file path"
|
||||||
|
},
|
||||||
|
"gitlens.gitExplorer.enabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Specifies whether or not to show the `GitLens` custom view"
|
||||||
|
},
|
||||||
|
"gitlens.gitExplorer.files.layout": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "auto",
|
||||||
|
"enum": [
|
||||||
|
"auto",
|
||||||
|
"list",
|
||||||
|
"tree"
|
||||||
|
],
|
||||||
|
"description": "Specifies how the `GitLens` custom view will display files\n `auto` - automatically switches between displaying files as a `tree` or `list` based on the `gitlens.gitExplorer.files.threshold` setting and the number of files at each nesting level\n `list` - displays files as a list\n `tree` - displays files as a tree"
|
||||||
|
},
|
||||||
|
"gitlens.gitExplorer.files.compact": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Specifies whether or not to compact (flatten) unnecessary file nesting in the `GitLens` custom view\nOnly applies when displaying files as a `tree` or `auto`"
|
||||||
|
},
|
||||||
|
"gitlens.gitExplorer.files.threshold": {
|
||||||
|
"type": "number",
|
||||||
|
"default": 5,
|
||||||
|
"description": "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` custom view\nOnly applies when displaying files as `auto`"
|
||||||
|
},
|
||||||
|
"gitlens.gitExplorer.includeWorkingTree": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Specifies whether or not to include working tree files inside the `Repository Status` node of the `GitLens` custom view"
|
||||||
},
|
},
|
||||||
"gitlens.gitExplorer.showTrackingBranch": {
|
"gitlens.gitExplorer.showTrackingBranch": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -436,16 +466,22 @@
|
|||||||
"gitlens.gitExplorer.stashFileFormat": {
|
"gitlens.gitExplorer.stashFileFormat": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "${filePath}",
|
"default": "${filePath}",
|
||||||
"description": "Specifies the format of a stashed file in the `GitLens` custom view\nAvailable tokens\n ${file} - file name\n ${filePath} - file name and path\n ${path} - file path"
|
"description": "Specifies the format of a stashed file in the `GitLens` custom view\nAvailable tokens\n ${directory} - directory name\n ${file} - file name\n ${filePath} - formatted file name and path\n ${path} - full file path"
|
||||||
|
},
|
||||||
|
"gitlens.gitExplorer.statusFileFormat": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "${working}${filePath}",
|
||||||
|
"description": "Specifies the format of the status of a working or committed file in the `GitLens` custom view\nAvailable tokens\n ${directory} - directory name\n ${file} - file name\n ${filePath} - formatted file name and path\n ${path} - full file path\n ${working} - optional indicator if the file is uncommitted"
|
||||||
},
|
},
|
||||||
"gitlens.gitExplorer.view": {
|
"gitlens.gitExplorer.view": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "repository",
|
"default": "auto",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
"auto",
|
||||||
"history",
|
"history",
|
||||||
"repository"
|
"repository"
|
||||||
],
|
],
|
||||||
"description": "Specifies the starting view (mode) of the `GitLens` custom view\n `history` - shows the commit history of the active file\n `repository` - shows a repository explorer"
|
"description": "Specifies the starting view (mode) of the `GitLens` custom view\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"
|
||||||
},
|
},
|
||||||
"gitlens.remotes": {
|
"gitlens.remotes": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@@ -646,16 +682,16 @@
|
|||||||
},
|
},
|
||||||
"editorTitle": {
|
"editorTitle": {
|
||||||
"blame": true,
|
"blame": true,
|
||||||
"fileDiff": true,
|
"fileDiff": false,
|
||||||
"history": true,
|
"history": false,
|
||||||
"remote": true,
|
"remote": false,
|
||||||
"status": true
|
"status": false
|
||||||
},
|
},
|
||||||
"editorTitleContext": {
|
"editorTitleContext": {
|
||||||
"blame": true,
|
"blame": false,
|
||||||
"fileDiff": true,
|
"fileDiff": false,
|
||||||
"history": true,
|
"history": false,
|
||||||
"remote": true
|
"remote": false
|
||||||
},
|
},
|
||||||
"explorerContext": {
|
"explorerContext": {
|
||||||
"fileDiff": true,
|
"fileDiff": true,
|
||||||
@@ -1397,12 +1433,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.openFileInRemote",
|
"command": "gitlens.openFileInRemote",
|
||||||
"when": "gitlens:enabled && gitlens:hasRemotes && config.gitlens.advanced.menus.editorTitleContext.remote",
|
"when": "gitlens:enabled && gitlens:hasRemotes && config.gitlens.advanced.menus.editorTitle.remote",
|
||||||
"group": "1_gitlens"
|
"group": "1_gitlens"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.openRepoInRemote",
|
"command": "gitlens.openRepoInRemote",
|
||||||
"when": "gitlens:enabled && gitlens:hasRemotes && config.gitlens.advanced.menus.editorTitleContext.remote",
|
"when": "gitlens:enabled && gitlens:hasRemotes && config.gitlens.advanced.menus.editorTitle.remote",
|
||||||
"group": "1_gitlens"
|
"group": "1_gitlens"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1737,9 +1773,54 @@
|
|||||||
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:status",
|
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:status",
|
||||||
"group": "1_gitlens@1"
|
"group": "1_gitlens@1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.gitExplorer.openChanges",
|
||||||
|
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
|
||||||
|
"group": "1_gitlens@1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.gitExplorer.openChangesWithWorking",
|
||||||
|
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
|
||||||
|
"group": "1_gitlens@2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.gitExplorer.openFile",
|
||||||
|
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
|
||||||
|
"group": "2_gitlens@1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.gitExplorer.openFileRevision",
|
||||||
|
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
|
||||||
|
"group": "2_gitlens@2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.openFileInRemote",
|
||||||
|
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:status-file",
|
||||||
|
"group": "3_gitlens@1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.showQuickFileHistory",
|
||||||
|
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file && gitlens:gitExplorer:view == repository",
|
||||||
|
"group": "5_gitlens@1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.showQuickCommitFileDetails",
|
||||||
|
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file",
|
||||||
|
"group": "5_gitlens@2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.gitExplorer.openFile",
|
||||||
|
"when": "view == gitlens.gitExplorer && viewItem == gitlens:status-file-commits",
|
||||||
|
"group": "1_gitlens@1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.openFileInRemote",
|
||||||
|
"when": "gitlens:hasRemotes && view == gitlens.gitExplorer && viewItem == gitlens:status-file-commits",
|
||||||
|
"group": "1_gitlens@2"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.gitExplorer.refresh",
|
"command": "gitlens.gitExplorer.refresh",
|
||||||
"when": "view == gitlens.gitExplorer && viewItem != gitlens:commit-file && viewItem != gitlens:stash-file",
|
"when": "view == gitlens.gitExplorer && viewItem != gitlens:commit-file && viewItem != gitlens:stash-file && viewItem != gitlens:status-file",
|
||||||
"group": "9_gitlens@1"
|
"group": "9_gitlens@1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1841,7 +1922,7 @@
|
|||||||
{
|
{
|
||||||
"id": "gitlens.gitExplorer",
|
"id": "gitlens.gitExplorer",
|
||||||
"name": "GitLens",
|
"name": "GitLens",
|
||||||
"when": "gitlens:enabled"
|
"when": "gitlens:enabled && config.gitlens.gitExplorer.enabled"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1871,18 +1952,17 @@
|
|||||||
"lodash.once": "4.1.1",
|
"lodash.once": "4.1.1",
|
||||||
"moment": "2.18.1",
|
"moment": "2.18.1",
|
||||||
"spawn-rx": "2.0.11",
|
"spawn-rx": "2.0.11",
|
||||||
"string-width": "2.1.1",
|
|
||||||
"tmp": "0.0.33"
|
"tmp": "0.0.33"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/copy-paste": "1.1.30",
|
"@types/copy-paste": "1.1.30",
|
||||||
"@types/iconv-lite": "0.0.1",
|
"@types/iconv-lite": "0.0.1",
|
||||||
"@types/mocha": "2.2.43",
|
"@types/mocha": "2.2.43",
|
||||||
"@types/node": "8.0.28",
|
"@types/node": "8.0.31",
|
||||||
"@types/tmp": "0.0.33",
|
"@types/tmp": "0.0.33",
|
||||||
"mocha": "3.5.2",
|
"mocha": "3.5.3",
|
||||||
"tslint": "5.7.0",
|
"tslint": "5.7.0",
|
||||||
"typescript": "2.5.2",
|
"typescript": "2.5.3",
|
||||||
"vscode": "1.1.5"
|
"vscode": "1.1.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Dates, Strings } from '../system';
|
import { Dates, Objects, Strings } from '../system';
|
||||||
import { DecorationInstanceRenderOptions, DecorationOptions, MarkdownString, ThemableDecorationRenderOptions } from 'vscode';
|
import { DecorationInstanceRenderOptions, DecorationOptions, MarkdownString, ThemableDecorationRenderOptions } from 'vscode';
|
||||||
import { DiffWithCommand, OpenCommitInRemoteCommand, ShowQuickCommitDetailsCommand } from '../commands';
|
import { DiffWithCommand, OpenCommitInRemoteCommand, ShowQuickCommitDetailsCommand } from '../commands';
|
||||||
import { IThemeConfig, themeDefaults } from '../configuration';
|
import { IThemeConfig, themeDefaults } from '../configuration';
|
||||||
@@ -109,14 +109,14 @@ export class Annotations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gutter(commit: GitCommit, format: string, dateFormatOrFormatOptions: string | null | ICommitFormatOptions, renderOptions: IRenderOptions): DecorationOptions {
|
static gutter(commit: GitCommit, format: string, dateFormatOrFormatOptions: string | null | ICommitFormatOptions, renderOptions: IRenderOptions): DecorationOptions {
|
||||||
const content = Strings.pad(CommitFormatter.fromTemplate(format, commit, dateFormatOrFormatOptions), 1, 1);
|
const message = CommitFormatter.fromTemplate(format, commit, dateFormatOrFormatOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
renderOptions: {
|
renderOptions: {
|
||||||
before: {
|
before: {
|
||||||
...renderOptions.before,
|
...renderOptions.before,
|
||||||
...{
|
...{
|
||||||
contentText: content
|
contentText: Strings.pad(message.replace(/ /g, GlyphChars.Space), 1, 1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dark: {
|
dark: {
|
||||||
@@ -133,9 +133,23 @@ export class Annotations {
|
|||||||
} as DecorationOptions;
|
} as DecorationOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gutterRenderOptions(cfgTheme: IThemeConfig, heatmap: IHeatmapConfig): IRenderOptions {
|
static gutterRenderOptions(cfgTheme: IThemeConfig, heatmap: IHeatmapConfig, options: ICommitFormatOptions): IRenderOptions {
|
||||||
const cfgFileTheme = cfgTheme.annotations.file.gutter;
|
const cfgFileTheme = cfgTheme.annotations.file.gutter;
|
||||||
|
|
||||||
|
// Try to get the width of the string, if there is a cap
|
||||||
|
let width = 4; // Start with a padding
|
||||||
|
for (const token of Objects.values(options.tokenOptions!)) {
|
||||||
|
if (token === undefined) continue;
|
||||||
|
|
||||||
|
// If any token is uncapped, kick out and set no max
|
||||||
|
if (token.truncateTo == null) {
|
||||||
|
width = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
width += token.truncateTo;
|
||||||
|
}
|
||||||
|
|
||||||
let borderStyle = undefined;
|
let borderStyle = undefined;
|
||||||
let borderWidth = undefined;
|
let borderWidth = undefined;
|
||||||
if (heatmap.enabled) {
|
if (heatmap.enabled) {
|
||||||
@@ -152,7 +166,8 @@ export class Annotations {
|
|||||||
borderStyle: borderStyle,
|
borderStyle: borderStyle,
|
||||||
borderWidth: borderWidth,
|
borderWidth: borderWidth,
|
||||||
height: '100%',
|
height: '100%',
|
||||||
margin: '0 26px -1px 0'
|
margin: '0 26px -1px 0',
|
||||||
|
width: (width > 4) ? `${width}ch` : undefined
|
||||||
},
|
},
|
||||||
dark: {
|
dark: {
|
||||||
backgroundColor: cfgFileTheme.dark.backgroundColor || undefined,
|
backgroundColor: cfgFileTheme.dark.backgroundColor || undefined,
|
||||||
@@ -195,10 +210,11 @@ export class Annotations {
|
|||||||
truncateMessageAtNewLine: true,
|
truncateMessageAtNewLine: true,
|
||||||
dateFormat: dateFormat
|
dateFormat: dateFormat
|
||||||
} as ICommitFormatOptions);
|
} as ICommitFormatOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
renderOptions: {
|
renderOptions: {
|
||||||
after: {
|
after: {
|
||||||
contentText: Strings.pad(message, 1, 1)
|
contentText: Strings.pad(message.replace(/ /g, GlyphChars.Space), 1, 1)
|
||||||
},
|
},
|
||||||
dark: {
|
dark: {
|
||||||
after: {
|
after: {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
|||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const offset = this.uri.offset;
|
const offset = this.uri.offset;
|
||||||
const renderOptions = Annotations.gutterRenderOptions(this._config.theme, cfg.heatmap);
|
const renderOptions = Annotations.gutterRenderOptions(this._config.theme, cfg.heatmap, options);
|
||||||
const separateLines = this._config.theme.annotations.file.gutter.separateLines;
|
const separateLines = this._config.theme.annotations.file.gutter.separateLines;
|
||||||
|
|
||||||
const decorations: DecorationOptions[] = [];
|
const decorations: DecorationOptions[] = [];
|
||||||
@@ -58,7 +58,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
|||||||
...gutter.renderOptions,
|
...gutter.renderOptions,
|
||||||
before: {
|
before: {
|
||||||
...gutter.renderOptions!.before,
|
...gutter.renderOptions!.before,
|
||||||
contentText: GlyphChars.Space.repeat(Strings.getWidth(gutter.renderOptions!.before!.contentText!))
|
contentText: GlyphChars.Space.repeat(Strings.width(gutter.renderOptions!.before!.contentText!))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class ResetSuppressedWarningsCommand extends Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async execute() {
|
async execute() {
|
||||||
for (const key of Objects.values<string>(SuppressedKeys)) {
|
for (const key of Objects.values(SuppressedKeys)) {
|
||||||
await this.context.globalState.update(key, false);
|
await this.context.globalState.update(key, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,16 @@ export const CustomRemoteType = {
|
|||||||
GitLab: 'GitLab' as CustomRemoteType
|
GitLab: 'GitLab' as CustomRemoteType
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type GitExplorerFilesLayout =
|
||||||
|
'auto' |
|
||||||
|
'list' |
|
||||||
|
'tree';
|
||||||
|
export const GitExplorerFilesLayout = {
|
||||||
|
Auto: 'auto' as GitExplorerFilesLayout,
|
||||||
|
List: 'list' as GitExplorerFilesLayout,
|
||||||
|
Tree: 'tree' as GitExplorerFilesLayout
|
||||||
|
};
|
||||||
|
|
||||||
export type StatusBarCommand =
|
export type StatusBarCommand =
|
||||||
'gitlens.toggleFileBlame' |
|
'gitlens.toggleFileBlame' |
|
||||||
'gitlens.showBlameHistory' |
|
'gitlens.showBlameHistory' |
|
||||||
@@ -132,6 +142,24 @@ export interface ICodeLensLanguageLocation {
|
|||||||
customSymbols?: string[];
|
customSymbols?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IGitExplorerConfig {
|
||||||
|
enabled: boolean;
|
||||||
|
view: GitExplorerView;
|
||||||
|
files: {
|
||||||
|
layout: GitExplorerFilesLayout;
|
||||||
|
compact: boolean;
|
||||||
|
threshold: number;
|
||||||
|
};
|
||||||
|
includeWorkingTree: boolean;
|
||||||
|
showTrackingBranch: boolean;
|
||||||
|
commitFormat: string;
|
||||||
|
commitFileFormat: string;
|
||||||
|
stashFormat: string;
|
||||||
|
stashFileFormat: string;
|
||||||
|
statusFileFormat: string;
|
||||||
|
// dateFormat: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IRemotesConfig {
|
export interface IRemotesConfig {
|
||||||
type: CustomRemoteType;
|
type: CustomRemoteType;
|
||||||
domain: string;
|
domain: string;
|
||||||
@@ -316,15 +344,7 @@ export interface IConfig {
|
|||||||
|
|
||||||
defaultDateFormat: string | null;
|
defaultDateFormat: string | null;
|
||||||
|
|
||||||
gitExplorer: {
|
gitExplorer: IGitExplorerConfig;
|
||||||
view: GitExplorerView;
|
|
||||||
showTrackingBranch: boolean;
|
|
||||||
commitFormat: string;
|
|
||||||
commitFileFormat: string;
|
|
||||||
stashFormat: string;
|
|
||||||
stashFileFormat: string;
|
|
||||||
// dateFormat: string | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
remotes: IRemotesConfig[];
|
remotes: IRemotesConfig[];
|
||||||
|
|
||||||
|
|||||||
@@ -81,10 +81,12 @@ export type GlyphChars = '\u21a9' |
|
|||||||
'\u21e8' |
|
'\u21e8' |
|
||||||
'\u2191' |
|
'\u2191' |
|
||||||
'\u2197' |
|
'\u2197' |
|
||||||
|
'\u2217' |
|
||||||
'\u2713' |
|
'\u2713' |
|
||||||
'\u2014' |
|
'\u2014' |
|
||||||
'\u2022' |
|
'\u2022' |
|
||||||
'\u2026' |
|
'\u2026' |
|
||||||
|
'\u270E' |
|
||||||
'\u00a0' |
|
'\u00a0' |
|
||||||
'\u200b';
|
'\u200b';
|
||||||
export const GlyphChars = {
|
export const GlyphChars = {
|
||||||
@@ -97,15 +99,22 @@ export const GlyphChars = {
|
|||||||
ArrowRightHollow: '\u21e8' as GlyphChars,
|
ArrowRightHollow: '\u21e8' as GlyphChars,
|
||||||
ArrowUp: '\u2191' as GlyphChars,
|
ArrowUp: '\u2191' as GlyphChars,
|
||||||
ArrowUpRight: '\u2197' as GlyphChars,
|
ArrowUpRight: '\u2197' as GlyphChars,
|
||||||
|
Asterisk: '\u2217' as GlyphChars,
|
||||||
Check: '\u2713' as GlyphChars,
|
Check: '\u2713' as GlyphChars,
|
||||||
Dash: '\u2014' as GlyphChars,
|
Dash: '\u2014' as GlyphChars,
|
||||||
Dot: '\u2022' as GlyphChars,
|
Dot: '\u2022' as GlyphChars,
|
||||||
Ellipsis: '\u2026' as GlyphChars,
|
Ellipsis: '\u2026' as GlyphChars,
|
||||||
|
Pensil: '\u270E' as GlyphChars,
|
||||||
Space: '\u00a0' as GlyphChars,
|
Space: '\u00a0' as GlyphChars,
|
||||||
ZeroWidthSpace: '\u200b' as GlyphChars
|
ZeroWidthSpace: '\u200b' as GlyphChars
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WorkspaceState = 'gitlensVersion';
|
export type GlobalState = 'gitlensVersion';
|
||||||
export const WorkspaceState = {
|
export const GlobalState = {
|
||||||
GitLensVersion: 'gitlensVersion' as WorkspaceState
|
GitLensVersion: 'gitlensVersion' as GlobalState
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WorkspaceState = 'gitlens:gitExplorer:view';
|
||||||
|
export const WorkspaceState = {
|
||||||
|
GitExplorerView: 'gitlens:gitExplorer:view' as WorkspaceState
|
||||||
};
|
};
|
||||||
@@ -16,7 +16,7 @@ import { ShowQuickRepoStatusCommand, ShowQuickStashListCommand } from './command
|
|||||||
import { StashApplyCommand, StashDeleteCommand, StashSaveCommand } from './commands';
|
import { StashApplyCommand, StashDeleteCommand, StashSaveCommand } from './commands';
|
||||||
import { ToggleCodeLensCommand } from './commands';
|
import { ToggleCodeLensCommand } from './commands';
|
||||||
import { CodeLensLocations, IConfig, LineHighlightLocations } from './configuration';
|
import { CodeLensLocations, IConfig, LineHighlightLocations } from './configuration';
|
||||||
import { ApplicationInsightsKey, CommandContext, ExtensionKey, QualifiedExtensionId, setCommandContext, WorkspaceState } from './constants';
|
import { ApplicationInsightsKey, CommandContext, ExtensionKey, GlobalState, QualifiedExtensionId, setCommandContext } from './constants';
|
||||||
import { CodeLensController } from './codeLensController';
|
import { CodeLensController } from './codeLensController';
|
||||||
import { CurrentLineController, LineAnnotationType } from './currentLineController';
|
import { CurrentLineController, LineAnnotationType } from './currentLineController';
|
||||||
import { RemoteProviderFactory } from './git/remotes/factory';
|
import { RemoteProviderFactory } from './git/remotes/factory';
|
||||||
@@ -71,7 +71,7 @@ export async function activate(context: ExtensionContext) {
|
|||||||
notifyOnUnsupportedGitVersion(context, gitVersion);
|
notifyOnUnsupportedGitVersion(context, gitVersion);
|
||||||
notifyOnNewGitLensVersion(context, gitlensVersion);
|
notifyOnNewGitLensVersion(context, gitlensVersion);
|
||||||
|
|
||||||
await context.globalState.update(WorkspaceState.GitLensVersion, gitlensVersion);
|
await context.globalState.update(GlobalState.GitLensVersion, gitlensVersion);
|
||||||
|
|
||||||
const git = new GitService(repoPath);
|
const git = new GitService(repoPath);
|
||||||
context.subscriptions.push(git);
|
context.subscriptions.push(git);
|
||||||
@@ -148,7 +148,7 @@ export async function activate(context: ExtensionContext) {
|
|||||||
export function deactivate() { }
|
export function deactivate() { }
|
||||||
|
|
||||||
async function migrateSettings(context: ExtensionContext) {
|
async function migrateSettings(context: ExtensionContext) {
|
||||||
const previousVersion = context.globalState.get<string>(WorkspaceState.GitLensVersion);
|
const previousVersion = context.globalState.get<string>(GlobalState.GitLensVersion);
|
||||||
if (previousVersion === undefined) return;
|
if (previousVersion === undefined) return;
|
||||||
|
|
||||||
const [major] = previousVersion.split('.');
|
const [major] = previousVersion.split('.');
|
||||||
@@ -274,7 +274,7 @@ async function migrateSettings(context: ExtensionContext) {
|
|||||||
async function notifyOnNewGitLensVersion(context: ExtensionContext, version: string) {
|
async function notifyOnNewGitLensVersion(context: ExtensionContext, version: string) {
|
||||||
if (context.globalState.get(SuppressedKeys.UpdateNotice, false)) return;
|
if (context.globalState.get(SuppressedKeys.UpdateNotice, false)) return;
|
||||||
|
|
||||||
const previousVersion = context.globalState.get<string>(WorkspaceState.GitLensVersion);
|
const previousVersion = context.globalState.get<string>(GlobalState.GitLensVersion);
|
||||||
|
|
||||||
if (previousVersion === undefined) {
|
if (previousVersion === undefined) {
|
||||||
Logger.log(`GitLens first-time install`);
|
Logger.log(`GitLens first-time install`);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export class CommitFormatter extends Formatter<GitCommit, ICommitFormatOptions>
|
|||||||
}
|
}
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
return this._item.shortSha;
|
return this._item.isUncommitted ? 'index' : this._item.shortSha;
|
||||||
}
|
}
|
||||||
|
|
||||||
get message() {
|
get message() {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export abstract class Formatter<TItem = any, TOptions extends IFormatOptions = I
|
|||||||
|
|
||||||
let max = options.truncateTo;
|
let max = options.truncateTo;
|
||||||
|
|
||||||
const width = Strings.getWidth(s);
|
const width = Strings.width(s);
|
||||||
if (max === undefined) {
|
if (max === undefined) {
|
||||||
if (this.collapsableWhitespace === 0) return s;
|
if (this.collapsableWhitespace === 0) return s;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Strings } from '../../system';
|
import { Strings } from '../../system';
|
||||||
|
import { GlyphChars } from '../../constants';
|
||||||
import { Formatter, IFormatOptions } from './formatter';
|
import { Formatter, IFormatOptions } from './formatter';
|
||||||
import { GitStatusFile, IGitStatusFile } from '../models/status';
|
import { GitStatusFile, IGitStatusFile, IGitStatusFileWithCommit } from '../models/status';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
export interface IStatusFormatOptions extends IFormatOptions {
|
export interface IStatusFormatOptions extends IFormatOptions {
|
||||||
|
relativePath?: string;
|
||||||
|
|
||||||
tokenOptions?: {
|
tokenOptions?: {
|
||||||
|
directory?: Strings.ITokenOptions;
|
||||||
file?: Strings.ITokenOptions;
|
file?: Strings.ITokenOptions;
|
||||||
filePath?: Strings.ITokenOptions;
|
filePath?: Strings.ITokenOptions;
|
||||||
path?: Strings.ITokenOptions;
|
path?: Strings.ITokenOptions;
|
||||||
@@ -14,21 +18,31 @@ export interface IStatusFormatOptions extends IFormatOptions {
|
|||||||
|
|
||||||
export class StatusFileFormatter extends Formatter<IGitStatusFile, IStatusFormatOptions> {
|
export class StatusFileFormatter extends Formatter<IGitStatusFile, IStatusFormatOptions> {
|
||||||
|
|
||||||
|
get directory() {
|
||||||
|
const directory = GitStatusFile.getFormattedDirectory(this._item, false, this._options.relativePath);
|
||||||
|
return this._padOrTruncate(directory, this._options.tokenOptions!.file);
|
||||||
|
}
|
||||||
|
|
||||||
get file() {
|
get file() {
|
||||||
const file = path.basename(this._item.fileName);
|
const file = path.basename(this._item.fileName);
|
||||||
return this._padOrTruncate(file, this._options.tokenOptions!.file);
|
return this._padOrTruncate(file, this._options.tokenOptions!.file);
|
||||||
}
|
}
|
||||||
|
|
||||||
get filePath() {
|
get filePath() {
|
||||||
const filePath = GitStatusFile.getFormattedPath(this._item);
|
const filePath = GitStatusFile.getFormattedPath(this._item, undefined, this._options.relativePath);
|
||||||
return this._padOrTruncate(filePath, this._options.tokenOptions!.filePath);
|
return this._padOrTruncate(filePath, this._options.tokenOptions!.filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
get path() {
|
get path() {
|
||||||
const directory = GitStatusFile.getFormattedDirectory(this._item, false);
|
const directory = GitStatusFile.getRelativePath(this._item, this._options.relativePath);
|
||||||
return this._padOrTruncate(directory, this._options.tokenOptions!.file);
|
return this._padOrTruncate(directory, this._options.tokenOptions!.file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get working() {
|
||||||
|
const commit = (this._item as IGitStatusFileWithCommit).commit;
|
||||||
|
return (commit !== undefined && commit.isUncommitted) ? `${GlyphChars.Pensil} ${GlyphChars.Space}` : '';
|
||||||
|
}
|
||||||
|
|
||||||
static fromTemplate(template: string, status: IGitStatusFile, dateFormat: string | null): string;
|
static fromTemplate(template: string, status: IGitStatusFile, dateFormat: string | null): string;
|
||||||
static fromTemplate(template: string, status: IGitStatusFile, options?: IStatusFormatOptions): string;
|
static fromTemplate(template: string, status: IGitStatusFile, options?: IStatusFormatOptions): string;
|
||||||
static fromTemplate(template: string, status: IGitStatusFile, dateFormatOrOptions?: string | null | IStatusFormatOptions): string;
|
static fromTemplate(template: string, status: IGitStatusFile, dateFormatOrOptions?: string | null | IStatusFormatOptions): string;
|
||||||
|
|||||||
@@ -259,6 +259,14 @@ export class Git {
|
|||||||
return gitCommand({ cwd: repoPath }, ...params);
|
return gitCommand({ cwd: repoPath }, ...params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static diff_shortstat(repoPath: string, sha?: string) {
|
||||||
|
const params = [`diff`, `--shortstat`, `--no-ext-diff`];
|
||||||
|
if (sha) {
|
||||||
|
params.push(sha);
|
||||||
|
}
|
||||||
|
return gitCommand({ cwd: repoPath }, ...params);
|
||||||
|
}
|
||||||
|
|
||||||
static difftool_dirDiff(repoPath: string, sha1: string, sha2?: string) {
|
static difftool_dirDiff(repoPath: string, sha1: string, sha2?: string) {
|
||||||
const params = [`difftool`, `--dir-diff`, sha1];
|
const params = [`difftool`, `--dir-diff`, sha1];
|
||||||
if (sha2) {
|
if (sha2) {
|
||||||
@@ -332,6 +340,14 @@ export class Git {
|
|||||||
return gitCommand({ cwd: repoPath }, ...params, ...search);
|
return gitCommand({ cwd: repoPath }, ...params, ...search);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static log_shortstat(repoPath: string, sha?: string) {
|
||||||
|
const params = [`log`, `--shortstat`, `--oneline`];
|
||||||
|
if (sha) {
|
||||||
|
params.push(sha);
|
||||||
|
}
|
||||||
|
return gitCommand({ cwd: repoPath }, ...params);
|
||||||
|
}
|
||||||
|
|
||||||
static async ls_files(repoPath: string, fileName: string): Promise<string> {
|
static async ls_files(repoPath: string, fileName: string): Promise<string> {
|
||||||
try {
|
try {
|
||||||
return await gitCommand({ cwd: repoPath, overrideErrorHandling: true }, 'ls-files', fileName);
|
return await gitCommand({ cwd: repoPath, overrideErrorHandling: true }, 'ls-files', fileName);
|
||||||
@@ -404,7 +420,7 @@ export class Git {
|
|||||||
|
|
||||||
static status(repoPath: string, porcelainVersion: number = 1): Promise<string> {
|
static status(repoPath: string, porcelainVersion: number = 1): Promise<string> {
|
||||||
const porcelain = porcelainVersion >= 2 ? `--porcelain=v${porcelainVersion}` : '--porcelain';
|
const porcelain = porcelainVersion >= 2 ? `--porcelain=v${porcelainVersion}` : '--porcelain';
|
||||||
return gitCommand({ cwd: repoPath }, 'status', porcelain, '--branch');
|
return gitCommand({ cwd: repoPath }, 'status', porcelain, '--branch', '-u');
|
||||||
}
|
}
|
||||||
|
|
||||||
static status_file(repoPath: string, fileName: string, porcelainVersion: number = 1): Promise<string> {
|
static status_file(repoPath: string, fileName: string, porcelainVersion: number = 1): Promise<string> {
|
||||||
|
|||||||
@@ -63,11 +63,14 @@ export class GitUri extends Uri {
|
|||||||
return Uri.file(this.sha ? this.path : this.fsPath);
|
return Uri.file(this.sha ? this.path : this.fsPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFormattedPath(separator: string = Strings.pad(GlyphChars.Dot, 2, 2)): string {
|
getFormattedPath(separator: string = Strings.pad(GlyphChars.Dot, 2, 2), relativeTo?: string): string {
|
||||||
let directory = path.dirname(this.fsPath);
|
let directory = path.dirname(this.fsPath);
|
||||||
if (this.repoPath) {
|
if (this.repoPath) {
|
||||||
directory = path.relative(this.repoPath, directory);
|
directory = path.relative(this.repoPath, directory);
|
||||||
}
|
}
|
||||||
|
if (relativeTo !== undefined) {
|
||||||
|
directory = path.relative(relativeTo, directory);
|
||||||
|
}
|
||||||
directory = GitService.normalizePath(directory);
|
directory = GitService.normalizePath(directory);
|
||||||
|
|
||||||
return (!directory || directory === '.')
|
return (!directory || directory === '.')
|
||||||
@@ -75,8 +78,12 @@ export class GitUri extends Uri {
|
|||||||
: `${path.basename(this.fsPath)}${separator}${directory}`;
|
: `${path.basename(this.fsPath)}${separator}${directory}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRelativePath(): string {
|
getRelativePath(relativeTo?: string): string {
|
||||||
return GitService.normalizePath(path.relative(this.repoPath || '', this.fsPath));
|
let relativePath = path.relative(this.repoPath || '', this.fsPath);
|
||||||
|
if (relativeTo !== undefined) {
|
||||||
|
relativePath = path.relative(relativeTo, relativePath);
|
||||||
|
}
|
||||||
|
return GitService.normalizePath(relativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fromUri(uri: Uri, git: GitService) {
|
static async fromUri(uri: Uri, git: GitService) {
|
||||||
@@ -104,15 +111,19 @@ export class GitUri extends Uri {
|
|||||||
return new GitUri(uri, repoPathOrCommit);
|
return new GitUri(uri, repoPathOrCommit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDirectory(fileName: string): string {
|
static getDirectory(fileName: string, relativeTo?: string): string {
|
||||||
const directory: string | undefined = GitService.normalizePath(path.dirname(fileName));
|
let directory: string | undefined = path.dirname(fileName);
|
||||||
|
if (relativeTo !== undefined) {
|
||||||
|
directory = path.relative(relativeTo, directory);
|
||||||
|
}
|
||||||
|
directory = GitService.normalizePath(directory);
|
||||||
return (!directory || directory === '.') ? '' : directory;
|
return (!directory || directory === '.') ? '' : directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getFormattedPath(fileNameOrUri: string | Uri, separator: string = Strings.pad(GlyphChars.Dot, 2, 2)): string {
|
static getFormattedPath(fileNameOrUri: string | Uri, separator: string = Strings.pad(GlyphChars.Dot, 2, 2), relativeTo?: string): string {
|
||||||
let fileName: string;
|
let fileName: string;
|
||||||
if (fileNameOrUri instanceof Uri) {
|
if (fileNameOrUri instanceof Uri) {
|
||||||
if (fileNameOrUri instanceof GitUri) return fileNameOrUri.getFormattedPath(separator);
|
if (fileNameOrUri instanceof GitUri) return fileNameOrUri.getFormattedPath(separator, relativeTo);
|
||||||
|
|
||||||
fileName = fileNameOrUri.fsPath;
|
fileName = fileNameOrUri.fsPath;
|
||||||
}
|
}
|
||||||
@@ -120,11 +131,29 @@ export class GitUri extends Uri {
|
|||||||
fileName = fileNameOrUri;
|
fileName = fileNameOrUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
const directory = GitUri.getDirectory(fileName);
|
const directory = GitUri.getDirectory(fileName, relativeTo);
|
||||||
return !directory
|
return !directory
|
||||||
? path.basename(fileName)
|
? path.basename(fileName)
|
||||||
: `${path.basename(fileName)}${separator}${directory}`;
|
: `${path.basename(fileName)}${separator}${directory}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getRelativePath(fileNameOrUri: string | Uri, relativeTo?: string, repoPath?: string): string {
|
||||||
|
let fileName: string;
|
||||||
|
if (fileNameOrUri instanceof Uri) {
|
||||||
|
if (fileNameOrUri instanceof GitUri) return fileNameOrUri.getRelativePath(relativeTo);
|
||||||
|
|
||||||
|
fileName = fileNameOrUri.fsPath;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fileName = fileNameOrUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
let relativePath = path.relative(repoPath || '', fileName);
|
||||||
|
if (relativeTo !== undefined) {
|
||||||
|
relativePath = path.relative(relativeTo, relativePath);
|
||||||
|
}
|
||||||
|
return GitService.normalizePath(relativePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IGitCommitInfo {
|
export interface IGitCommitInfo {
|
||||||
|
|||||||
@@ -34,3 +34,9 @@ export interface GitDiff {
|
|||||||
|
|
||||||
diff?: string;
|
diff?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GitDiffShortStat {
|
||||||
|
files: number;
|
||||||
|
insertions: number;
|
||||||
|
deletions: number;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { Strings } from '../../system';
|
|||||||
import { Uri } from 'vscode';
|
import { Uri } from 'vscode';
|
||||||
import { GlyphChars } from '../../constants';
|
import { GlyphChars } from '../../constants';
|
||||||
import { GitUri } from '../gitUri';
|
import { GitUri } from '../gitUri';
|
||||||
|
import { GitLogCommit } from './logCommit';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
export interface GitStatus {
|
export interface GitStatus {
|
||||||
@@ -27,6 +28,10 @@ export interface IGitStatusFile {
|
|||||||
originalFileName?: string;
|
originalFileName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IGitStatusFileWithCommit extends IGitStatusFile {
|
||||||
|
commit: GitLogCommit;
|
||||||
|
}
|
||||||
|
|
||||||
export class GitStatusFile implements IGitStatusFile {
|
export class GitStatusFile implements IGitStatusFile {
|
||||||
|
|
||||||
originalFileName?: string;
|
originalFileName?: string;
|
||||||
@@ -51,15 +56,19 @@ export class GitStatusFile implements IGitStatusFile {
|
|||||||
return Uri.file(path.resolve(this.repoPath, this.fileName));
|
return Uri.file(path.resolve(this.repoPath, this.fileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
static getFormattedDirectory(status: IGitStatusFile, includeOriginal: boolean = false): string {
|
static getFormattedDirectory(status: IGitStatusFile, includeOriginal: boolean = false, relativeTo?: string): string {
|
||||||
const directory = GitUri.getDirectory(status.fileName);
|
const directory = GitUri.getDirectory(status.fileName, relativeTo);
|
||||||
return (includeOriginal && status.status === 'R' && status.originalFileName)
|
return (includeOriginal && status.status === 'R' && status.originalFileName)
|
||||||
? `${directory} ${Strings.pad(GlyphChars.ArrowLeft, 1, 1)} ${status.originalFileName}`
|
? `${directory} ${Strings.pad(GlyphChars.ArrowLeft, 1, 1)} ${status.originalFileName}`
|
||||||
: directory;
|
: directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getFormattedPath(status: IGitStatusFile, separator: string = Strings.pad(GlyphChars.Dot, 2, 2)): string {
|
static getFormattedPath(status: IGitStatusFile, separator: string = Strings.pad(GlyphChars.Dot, 2, 2), relativeTo?: string): string {
|
||||||
return GitUri.getFormattedPath(status.fileName, separator);
|
return GitUri.getFormattedPath(status.fileName, separator, relativeTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getRelativePath(status: IGitStatusFile, relativeTo?: string): string {
|
||||||
|
return GitUri.getRelativePath(status.fileName, relativeTo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Iterables, Strings } from '../../system';
|
import { Iterables, Strings } from '../../system';
|
||||||
import { GitDiff, GitDiffChunk, GitDiffChunkLine, GitDiffLine } from './../git';
|
import { GitDiff, GitDiffChunk, GitDiffChunkLine, GitDiffLine, GitDiffShortStat } from './../git';
|
||||||
|
|
||||||
const unifiedDiffRegex = /^@@ -([\d]+),([\d]+) [+]([\d]+),([\d]+) @@([\s\S]*?)(?=^@@)/gm;
|
const unifiedDiffRegex = /^@@ -([\d]+),([\d]+) [+]([\d]+),([\d]+) @@([\s\S]*?)(?=^@@)/gm;
|
||||||
|
const shortStatDiffRegex = /^\s*(\d+)\sfiles? changed(?:,\s+(\d+)\s+insertions?\(\+\))?(?:,\s+(\d+)\s+deletions?\(-\))?/;
|
||||||
|
|
||||||
export class GitDiffParser {
|
export class GitDiffParser {
|
||||||
|
|
||||||
@@ -116,4 +117,20 @@ export class GitDiffParser {
|
|||||||
|
|
||||||
return chunkLines;
|
return chunkLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static parseShortStat(data: string): GitDiffShortStat | undefined {
|
||||||
|
if (!data) return undefined;
|
||||||
|
|
||||||
|
const match = shortStatDiffRegex.exec(data);
|
||||||
|
if (match == null) return undefined;
|
||||||
|
|
||||||
|
const files = match[1];
|
||||||
|
const insertions = match[2];
|
||||||
|
const deletions = match[3];
|
||||||
|
return {
|
||||||
|
files: files == null ? 0 : parseInt(files, 10),
|
||||||
|
insertions: insertions == null ? 0 : parseInt(insertions, 10),
|
||||||
|
deletions: deletions == null ? 0 : parseInt(deletions, 10)
|
||||||
|
} as GitDiffShortStat;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { GitHubService } from './github';
|
import { Range } from 'vscode';
|
||||||
|
import { RemoteProvider } from './provider';
|
||||||
|
|
||||||
export class GitLabService extends GitHubService {
|
export class GitLabService extends RemoteProvider {
|
||||||
|
|
||||||
constructor(public domain: string, public path: string, public custom: boolean = false) {
|
constructor(public domain: string, public path: string, public custom: boolean = false) {
|
||||||
super(domain, path);
|
super(domain, path);
|
||||||
@@ -10,4 +11,32 @@ export class GitLabService extends GitHubService {
|
|||||||
get name() {
|
get name() {
|
||||||
return this.formatName('GitLab');
|
return this.formatName('GitLab');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getUrlForBranches(): string {
|
||||||
|
return `${this.baseUrl}/branches`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getUrlForBranch(branch: string): string {
|
||||||
|
return `${this.baseUrl}/commits/${branch}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getUrlForCommit(sha: string): string {
|
||||||
|
return `${this.baseUrl}/commit/${sha}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getUrlForFile(fileName: string, branch?: string, sha?: string, range?: Range): string {
|
||||||
|
let line = '';
|
||||||
|
if (range) {
|
||||||
|
if (range.start.line === range.end.line) {
|
||||||
|
line = `#L${range.start.line}`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
line = `#L${range.start.line}-${range.end.line}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sha) return `${this.baseUrl}/blob/${sha}/${fileName}${line}`;
|
||||||
|
if (branch) return `${this.baseUrl}/blob/${branch}/${fileName}${line}`;
|
||||||
|
return `${this.baseUrl}?path=${fileName}${line}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ import { Disposable, Event, EventEmitter, FileSystemWatcher, Location, Position,
|
|||||||
import { IConfig } from './configuration';
|
import { IConfig } from './configuration';
|
||||||
import { DocumentSchemes, ExtensionKey, GlyphChars } from './constants';
|
import { DocumentSchemes, ExtensionKey, GlyphChars } from './constants';
|
||||||
import { RemoteProviderFactory } from './git/remotes/factory';
|
import { RemoteProviderFactory } from './git/remotes/factory';
|
||||||
import { Git, GitAuthor, GitBlame, GitBlameCommit, GitBlameLine, GitBlameLines, GitBlameParser, GitBranch, GitBranchParser, GitCommit, GitDiff, GitDiffChunkLine, GitDiffParser, GitLog, GitLogCommit, GitLogParser, GitRemote, GitRemoteParser, GitStash, GitStashParser, GitStatus, GitStatusFile, GitStatusParser, IGit, setDefaultEncoding } from './git/git';
|
import { Git, GitAuthor, GitBlame, GitBlameCommit, GitBlameLine, GitBlameLines, GitBlameParser, GitBranch, GitBranchParser, GitCommit, GitDiff, GitDiffChunkLine, GitDiffParser, GitDiffShortStat, GitLog, GitLogCommit, GitLogParser, GitRemote, GitRemoteParser, GitStash, GitStashParser, GitStatus, GitStatusFile, GitStatusParser, IGit, setDefaultEncoding } from './git/git';
|
||||||
import { GitUri, IGitCommitInfo, IGitUriData } from './git/gitUri';
|
import { GitUri, IGitCommitInfo, IGitUriData } from './git/gitUri';
|
||||||
import { Logger } from './logger';
|
import { Logger } from './logger';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
@@ -74,6 +74,7 @@ export const RepoChangedReasons = {
|
|||||||
export class GitService extends Disposable {
|
export class GitService extends Disposable {
|
||||||
|
|
||||||
static fakeSha = 'ffffffffffffffffffffffffffffffffffffffff';
|
static fakeSha = 'ffffffffffffffffffffffffffffffffffffffff';
|
||||||
|
static uncommittedSha = '0000000000000000000000000000000000000000';
|
||||||
|
|
||||||
private _onDidBlameFail = new EventEmitter<string>();
|
private _onDidBlameFail = new EventEmitter<string>();
|
||||||
get onDidBlameFail(): Event<string> {
|
get onDidBlameFail(): Event<string> {
|
||||||
@@ -85,6 +86,11 @@ export class GitService extends Disposable {
|
|||||||
return this._onDidChangeGitCache.event;
|
return this._onDidChangeGitCache.event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _onDidChangeFileSystem = new EventEmitter<Uri>();
|
||||||
|
get onDidChangeFileSystem(): Event<Uri> {
|
||||||
|
return this._onDidChangeFileSystem.event;
|
||||||
|
}
|
||||||
|
|
||||||
private _onDidChangeRepo = new EventEmitter<RepoChangedReasons[]>();
|
private _onDidChangeRepo = new EventEmitter<RepoChangedReasons[]>();
|
||||||
get onDidChangeRepo(): Event<RepoChangedReasons[]> {
|
get onDidChangeRepo(): Event<RepoChangedReasons[]> {
|
||||||
return this._onDidChangeRepo.event;
|
return this._onDidChangeRepo.event;
|
||||||
@@ -120,14 +126,16 @@ export class GitService extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
|
this.stopWatchingFileSystem();
|
||||||
|
|
||||||
|
this._repoWatcher && this._repoWatcher.dispose();
|
||||||
|
this._repoWatcher = undefined;
|
||||||
|
|
||||||
this._disposable && this._disposable.dispose();
|
this._disposable && this._disposable.dispose();
|
||||||
|
|
||||||
this._cacheDisposable && this._cacheDisposable.dispose();
|
this._cacheDisposable && this._cacheDisposable.dispose();
|
||||||
this._cacheDisposable = undefined;
|
this._cacheDisposable = undefined;
|
||||||
|
|
||||||
this._repoWatcher && this._repoWatcher.dispose();
|
|
||||||
this._repoWatcher = undefined;
|
|
||||||
|
|
||||||
this._gitCache.clear();
|
this._gitCache.clear();
|
||||||
this._remotesCache.clear();
|
this._remotesCache.clear();
|
||||||
this._uriCache.clear();
|
this._uriCache.clear();
|
||||||
@@ -155,6 +163,8 @@ export class GitService extends Disposable {
|
|||||||
disposables.push(workspace.onDidChangeTextDocument(this._onTextDocumentChanged, this));
|
disposables.push(workspace.onDidChangeTextDocument(this._onTextDocumentChanged, this));
|
||||||
disposables.push(workspace.onDidSaveTextDocument(d => this._removeCachedEntry(d, RemoveCacheReason.DocumentSaved)));
|
disposables.push(workspace.onDidSaveTextDocument(d => this._removeCachedEntry(d, RemoveCacheReason.DocumentSaved)));
|
||||||
disposables.push(this._repoWatcher.onDidChange(this._onRepoChanged, this));
|
disposables.push(this._repoWatcher.onDidChange(this._onRepoChanged, this));
|
||||||
|
disposables.push(this._repoWatcher.onDidCreate(this._onRepoChanged, this));
|
||||||
|
disposables.push(this._repoWatcher.onDidDelete(this._onRepoChanged, this));
|
||||||
|
|
||||||
this._cacheDisposable = Disposable.from(...disposables);
|
this._cacheDisposable = Disposable.from(...disposables);
|
||||||
}
|
}
|
||||||
@@ -598,6 +608,11 @@ export class GitService extends Disposable {
|
|||||||
return Git.normalizePath(typeof fileNameOrUri === 'string' ? fileNameOrUri : fileNameOrUri.fsPath).toLowerCase();
|
return Git.normalizePath(typeof fileNameOrUri === 'string' ? fileNameOrUri : fileNameOrUri.fsPath).toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getChangedFilesCount(repoPath: string, sha?: string): Promise<GitDiffShortStat | undefined> {
|
||||||
|
const data = await Git.diff_shortstat(repoPath, sha);
|
||||||
|
return GitDiffParser.parseShortStat(data);
|
||||||
|
}
|
||||||
|
|
||||||
async getConfig(key: string, repoPath?: string): Promise<string> {
|
async getConfig(key: string, repoPath?: string): Promise<string> {
|
||||||
Logger.log(`getConfig('${key}', '${repoPath}')`);
|
Logger.log(`getConfig('${key}', '${repoPath}')`);
|
||||||
|
|
||||||
@@ -1027,6 +1042,33 @@ export class GitService extends Disposable {
|
|||||||
return Git.difftool_dirDiff(repoPath, sha1, sha2);
|
return Git.difftool_dirDiff(repoPath, sha1, sha2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _fsWatcherDisposable: Disposable | undefined;
|
||||||
|
|
||||||
|
startWatchingFileSystem() {
|
||||||
|
if (this._fsWatcherDisposable !== undefined) return;
|
||||||
|
|
||||||
|
const debouncedFn = Functions.debounce((uri: Uri) => this._onDidChangeFileSystem.fire(uri), 2500);
|
||||||
|
const fn = (uri: Uri) => {
|
||||||
|
// Ignore .git changes
|
||||||
|
if (/\.git/.test(uri.fsPath)) return;
|
||||||
|
|
||||||
|
debouncedFn(uri);
|
||||||
|
};
|
||||||
|
|
||||||
|
const watcher = workspace.createFileSystemWatcher(`**`);
|
||||||
|
this._fsWatcherDisposable = Disposable.from(
|
||||||
|
watcher,
|
||||||
|
watcher.onDidChange(fn),
|
||||||
|
watcher.onDidCreate(fn),
|
||||||
|
watcher.onDidDelete(fn)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopWatchingFileSystem() {
|
||||||
|
this._fsWatcherDisposable && this._fsWatcherDisposable.dispose();
|
||||||
|
this._fsWatcherDisposable = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
stashApply(repoPath: string, stashName: string, deleteAfter: boolean = false) {
|
stashApply(repoPath: string, stashName: string, deleteAfter: boolean = false) {
|
||||||
Logger.log(`stashApply('${repoPath}', ${stashName}, ${deleteAfter})`);
|
Logger.log(`stashApply('${repoPath}', ${stashName}, ${deleteAfter})`);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
import { Objects } from './object';
|
||||||
|
|
||||||
export namespace Arrays {
|
export namespace Arrays {
|
||||||
export function groupBy<T>(array: T[], accessor: (item: T) => any): T[] {
|
export function countUniques<T>(array: T[], accessor: (item: T) => string): { [key: string]: number } {
|
||||||
|
const uniqueCounts = Object.create(null);
|
||||||
|
for (const item of array) {
|
||||||
|
const value = accessor(item);
|
||||||
|
uniqueCounts[value] = (uniqueCounts[value] || 0) + 1;
|
||||||
|
}
|
||||||
|
return uniqueCounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function groupBy<T>(array: T[], accessor: (item: T) => string): { [key: string]: T[] } {
|
||||||
return array.reduce((previous, current) => {
|
return array.reduce((previous, current) => {
|
||||||
const value = accessor(current);
|
const value = accessor(current);
|
||||||
previous[value] = previous[value] || [];
|
previous[value] = previous[value] || [];
|
||||||
@@ -10,6 +20,96 @@ export namespace Arrays {
|
|||||||
}, Object.create(null));
|
}, Object.create(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IHierarchicalItem<T> {
|
||||||
|
name: string;
|
||||||
|
relativePath: string;
|
||||||
|
value?: T;
|
||||||
|
|
||||||
|
// parent?: IHierarchicalItem<T>;
|
||||||
|
children: { [key: string]: IHierarchicalItem<T> } | undefined;
|
||||||
|
descendants: T[] | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeHierarchical<T>(values: T[], splitPath: (i: T) => string[], joinPath: (...paths: string[]) => string, compact: boolean = false): IHierarchicalItem<T> {
|
||||||
|
const seed = {
|
||||||
|
name: '',
|
||||||
|
relativePath: '',
|
||||||
|
children: Object.create(null),
|
||||||
|
descendants: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const hierarchy = values.reduce((root: IHierarchicalItem<T>, value) => {
|
||||||
|
let folder = root;
|
||||||
|
|
||||||
|
let relativePath = '';
|
||||||
|
for (const folderName of splitPath(value)) {
|
||||||
|
relativePath = joinPath(relativePath, folderName);
|
||||||
|
|
||||||
|
if (folder.children === undefined) {
|
||||||
|
folder.children = Object.create(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
let f = folder.children![folderName];
|
||||||
|
if (f === undefined) {
|
||||||
|
folder.children![folderName] = f = {
|
||||||
|
name: folderName,
|
||||||
|
relativePath: relativePath,
|
||||||
|
// parent: folder,
|
||||||
|
children: undefined,
|
||||||
|
descendants: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folder.descendants === undefined) {
|
||||||
|
folder.descendants = [];
|
||||||
|
}
|
||||||
|
folder.descendants.push(value);
|
||||||
|
folder = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
folder.value = value;
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}, seed);
|
||||||
|
|
||||||
|
if (compact) return compactHierarchy(hierarchy, joinPath, true);
|
||||||
|
return hierarchy;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compactHierarchy<T>(root: IHierarchicalItem<T>, joinPath: (...paths: string[]) => string, isRoot: boolean = true): IHierarchicalItem<T> {
|
||||||
|
if (root.children === undefined) return root;
|
||||||
|
|
||||||
|
const children = [...Objects.values(root.children)];
|
||||||
|
|
||||||
|
// // Attempts less nesting but duplicate roots
|
||||||
|
// if (!isRoot && children.every(c => c.value === undefined)) {
|
||||||
|
// const parentSiblings = root.parent!.children!;
|
||||||
|
// if (parentSiblings[root.name] !== undefined) {
|
||||||
|
// delete parentSiblings[root.name];
|
||||||
|
|
||||||
|
// for (const child of children) {
|
||||||
|
// child.name = joinPath(root.name, child.name);
|
||||||
|
// parentSiblings[child.name] = child;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (const child of children) {
|
||||||
|
compactHierarchy(child, joinPath, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isRoot && children.length === 1) {
|
||||||
|
const child = children[0];
|
||||||
|
if (child.value === undefined) {
|
||||||
|
root.name = joinPath(root.name, child.name);
|
||||||
|
root.relativePath = child.relativePath;
|
||||||
|
root.children = child.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
export function uniqueBy<T>(array: T[], accessor: (item: T) => any, predicate?: (item: T) => boolean): T[] {
|
export function uniqueBy<T>(array: T[], accessor: (item: T) => any, predicate?: (item: T) => boolean): T[] {
|
||||||
const uniqueValues = Object.create(null);
|
const uniqueValues = Object.create(null);
|
||||||
return array.filter(_ => {
|
return array.filter(_ => {
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ export namespace Objects {
|
|||||||
return _isEqual(first, second);
|
return _isEqual(first, second);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* entries(o: any): IterableIterator<[string, any]> {
|
export function entries<T>(o: { [key: string]: T }): IterableIterator<[string, T]>;
|
||||||
|
export function entries<T>(o: { [key: number]: T }): IterableIterator<[string, T]>;
|
||||||
|
export function* entries<T>(o: any): IterableIterator<[string, T]> {
|
||||||
for (const key in o) {
|
for (const key in o) {
|
||||||
yield [key, o[key]];
|
yield [key, o[key]];
|
||||||
}
|
}
|
||||||
@@ -56,6 +58,8 @@ export namespace Objects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function values<T>(o: { [key: string]: T }): IterableIterator<T>;
|
||||||
|
export function values<T>(o: { [key: number]: T }): IterableIterator<T>;
|
||||||
export function* values<T>(o: any): IterableIterator<T> {
|
export function* values<T>(o: any): IterableIterator<T> {
|
||||||
for (const key in o) {
|
for (const key in o) {
|
||||||
yield o[key];
|
yield o[key];
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const _escapeRegExp = require('lodash.escaperegexp');
|
const _escapeRegExp = require('lodash.escaperegexp');
|
||||||
const stringWidth = require('string-width');
|
|
||||||
|
|
||||||
export namespace Strings {
|
export namespace Strings {
|
||||||
export function escapeRegExp(s: string): string {
|
export function escapeRegExp(s: string): string {
|
||||||
return _escapeRegExp(s);
|
return _escapeRegExp(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWidth(s: string): number {
|
|
||||||
return stringWidth(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
const TokenRegex = /\$\{([^|]*?)(?:\|(\d+)(\-|\?)?)?\}/g;
|
const TokenRegex = /\$\{([^|]*?)(?:\|(\d+)(\-|\?)?)?\}/g;
|
||||||
const TokenSanitizeRegex = /\$\{(\w*?)(?:\W|\d)*?\}/g;
|
const TokenSanitizeRegex = /\$\{(\w*?)(?:\W|\d)*?\}/g;
|
||||||
|
|
||||||
@@ -68,19 +63,19 @@ export namespace Strings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function padLeft(s: string, padTo: number, padding: string = '\u00a0') {
|
export function padLeft(s: string, padTo: number, padding: string = '\u00a0') {
|
||||||
const diff = padTo - getWidth(s);
|
const diff = padTo - width(s);
|
||||||
return (diff <= 0) ? s : '\u00a0'.repeat(diff) + s;
|
return (diff <= 0) ? s : '\u00a0'.repeat(diff) + s;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function padLeftOrTruncate(s: string, max: number, padding?: string) {
|
export function padLeftOrTruncate(s: string, max: number, padding?: string) {
|
||||||
const len = getWidth(s);
|
const len = width(s);
|
||||||
if (len < max) return padLeft(s, max, padding);
|
if (len < max) return padLeft(s, max, padding);
|
||||||
if (len > max) return truncate(s, max);
|
if (len > max) return truncate(s, max);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function padRight(s: string, padTo: number, padding: string = '\u00a0') {
|
export function padRight(s: string, padTo: number, padding: string = '\u00a0') {
|
||||||
const diff = padTo - getWidth(s);
|
const diff = padTo - width(s);
|
||||||
return (diff <= 0) ? s : s + '\u00a0'.repeat(diff);
|
return (diff <= 0) ? s : s + '\u00a0'.repeat(diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,14 +83,14 @@ export namespace Strings {
|
|||||||
const left = max < 0;
|
const left = max < 0;
|
||||||
max = Math.abs(max);
|
max = Math.abs(max);
|
||||||
|
|
||||||
const len = getWidth(s);
|
const len = width(s);
|
||||||
if (len < max) return left ? padLeft(s, max, padding) : padRight(s, max, padding);
|
if (len < max) return left ? padLeft(s, max, padding) : padRight(s, max, padding);
|
||||||
if (len > max) return truncate(s, max);
|
if (len > max) return truncate(s, max);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function padRightOrTruncate(s: string, max: number, padding?: string) {
|
export function padRightOrTruncate(s: string, max: number, padding?: string) {
|
||||||
const len = getWidth(s);
|
const len = width(s);
|
||||||
if (len < max) return padRight(s, max, padding);
|
if (len < max) return padRight(s, max, padding);
|
||||||
if (len > max) return truncate(s, max);
|
if (len > max) return truncate(s, max);
|
||||||
return s;
|
return s;
|
||||||
@@ -112,15 +107,15 @@ export namespace Strings {
|
|||||||
export function truncate(s: string, truncateTo: number, ellipsis: string = '\u2026') {
|
export function truncate(s: string, truncateTo: number, ellipsis: string = '\u2026') {
|
||||||
if (!s) return s;
|
if (!s) return s;
|
||||||
|
|
||||||
const len = getWidth(s);
|
const len = width(s);
|
||||||
if (len <= truncateTo) return s;
|
if (len <= truncateTo) return s;
|
||||||
if (len === s.length) return `${s.substring(0, truncateTo - 1)}${ellipsis}`;
|
if (len === s.length) return `${s.substring(0, truncateTo - 1)}${ellipsis}`;
|
||||||
|
|
||||||
// Skip ahead to start as far as we can by assuming all the double-width characters won't be truncated
|
// Skip ahead to start as far as we can by assuming all the double-width characters won't be truncated
|
||||||
let chars = Math.floor(truncateTo / (len / s.length));
|
let chars = Math.floor(truncateTo / (len / s.length));
|
||||||
let count = getWidth(s.substring(0, chars));
|
let count = width(s.substring(0, chars));
|
||||||
while (count < truncateTo) {
|
while (count < truncateTo) {
|
||||||
count += getWidth(s[chars++]);
|
count += width(s[chars++]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count >= truncateTo) {
|
if (count >= truncateTo) {
|
||||||
@@ -129,4 +124,107 @@ export namespace Strings {
|
|||||||
|
|
||||||
return `${s.substring(0, chars)}${ellipsis}`;
|
return `${s.substring(0, chars)}${ellipsis}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ansiRegex = /[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))/g;
|
||||||
|
|
||||||
|
export function width(s: string): number {
|
||||||
|
if (!s || s.length === 0) return 0;
|
||||||
|
|
||||||
|
s = s.replace(ansiRegex, '');
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
let emoji = 0;
|
||||||
|
let joiners = 0;
|
||||||
|
|
||||||
|
const graphemes = [...s];
|
||||||
|
for (let i = 0; i < graphemes.length; i++) {
|
||||||
|
const code = graphemes[i].codePointAt(0)!;
|
||||||
|
|
||||||
|
// Ignore control characters
|
||||||
|
if (code <= 0x1F || (code >= 0x7F && code <= 0x9F)) continue;
|
||||||
|
|
||||||
|
// Ignore combining characters
|
||||||
|
if (code >= 0x300 && code <= 0x36F) continue;
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/30757193/find-out-if-character-in-string-is-emoji
|
||||||
|
if (
|
||||||
|
(code >= 0x1F600 && code <= 0x1F64F) || // Emoticons
|
||||||
|
(code >= 0x1F300 && code <= 0x1F5FF) || // Misc Symbols and Pictographs
|
||||||
|
(code >= 0x1F680 && code <= 0x1F6FF) || // Transport and Map
|
||||||
|
(code >= 0x2600 && code <= 0x26FF) || // Misc symbols
|
||||||
|
(code >= 0x2700 && code <= 0x27BF) || // Dingbats
|
||||||
|
(code >= 0xFE00 && code <= 0xFE0F) || // Variation Selectors
|
||||||
|
(code >= 0x1F900 && code <= 0x1F9FF) || // Supplemental Symbols and Pictographs
|
||||||
|
(code >= 65024 && code <= 65039) || // Variation selector
|
||||||
|
(code >= 8400 && code <= 8447) // Combining Diacritical Marks for Symbols
|
||||||
|
) {
|
||||||
|
if (code >= 0x1F3FB && code <= 0x1F3FF) continue; // emoji modifier fitzpatrick type
|
||||||
|
|
||||||
|
emoji++;
|
||||||
|
count += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore zero-width joiners '\u200d'
|
||||||
|
if (code === 8205) {
|
||||||
|
joiners++;
|
||||||
|
count -= 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Surrogates
|
||||||
|
if (code > 0xFFFF) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
count += isFullwidthCodePoint(code) ? 2 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offset = emoji - joiners;
|
||||||
|
if (offset > 1) {
|
||||||
|
count += offset - 1;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFullwidthCodePoint(cp: number) {
|
||||||
|
// code points are derived from:
|
||||||
|
// http://www.unix.org/Public/UNIDATA/EastAsianWidth.txt
|
||||||
|
if (
|
||||||
|
cp >= 0x1100 && (
|
||||||
|
cp <= 0x115f || // Hangul Jamo
|
||||||
|
cp === 0x2329 || // LEFT-POINTING ANGLE BRACKET
|
||||||
|
cp === 0x232a || // RIGHT-POINTING ANGLE BRACKET
|
||||||
|
// CJK Radicals Supplement .. Enclosed CJK Letters and Months
|
||||||
|
(0x2e80 <= cp && cp <= 0x3247 && cp !== 0x303f) ||
|
||||||
|
// Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A
|
||||||
|
(0x3250 <= cp && cp <= 0x4dbf) ||
|
||||||
|
// CJK Unified Ideographs .. Yi Radicals
|
||||||
|
(0x4e00 <= cp && cp <= 0xa4c6) ||
|
||||||
|
// Hangul Jamo Extended-A
|
||||||
|
(0xa960 <= cp && cp <= 0xa97c) ||
|
||||||
|
// Hangul Syllables
|
||||||
|
(0xac00 <= cp && cp <= 0xd7a3) ||
|
||||||
|
// CJK Compatibility Ideographs
|
||||||
|
(0xf900 <= cp && cp <= 0xfaff) ||
|
||||||
|
// Vertical Forms
|
||||||
|
(0xfe10 <= cp && cp <= 0xfe19) ||
|
||||||
|
// CJK Compatibility Forms .. Small Form Variants
|
||||||
|
(0xfe30 <= cp && cp <= 0xfe6b) ||
|
||||||
|
// Halfwidth and Fullwidth Forms
|
||||||
|
(0xff01 <= cp && cp <= 0xff60) ||
|
||||||
|
(0xffe0 <= cp && cp <= 0xffe6) ||
|
||||||
|
// Kana Supplement
|
||||||
|
(0x1b000 <= cp && cp <= 0x1b001) ||
|
||||||
|
// Enclosed Ideographic Supplement
|
||||||
|
(0x1f200 <= cp && cp <= 0x1f251) ||
|
||||||
|
// CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
|
||||||
|
(0x20000 <= cp && cp <= 0x3fffd)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ import { Iterables } from '../system';
|
|||||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||||
import { CommitNode } from './commitNode';
|
import { CommitNode } from './commitNode';
|
||||||
import { GlyphChars } from '../constants';
|
import { GlyphChars } from '../constants';
|
||||||
import { ExplorerNode, ResourceType, ShowAllCommitsNode } from './explorerNode';
|
import { ExplorerNode, ResourceType, ShowAllNode } from './explorerNode';
|
||||||
import { GitBranch, GitService, GitUri } from '../gitService';
|
import { GitBranch, GitService, GitUri } from '../gitService';
|
||||||
|
|
||||||
export class BranchHistoryNode extends ExplorerNode {
|
export class BranchHistoryNode extends ExplorerNode {
|
||||||
@@ -12,7 +12,12 @@ export class BranchHistoryNode extends ExplorerNode {
|
|||||||
|
|
||||||
maxCount: number | undefined = undefined;
|
maxCount: number | undefined = undefined;
|
||||||
|
|
||||||
constructor(public readonly branch: GitBranch, uri: GitUri, private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
constructor(
|
||||||
|
public readonly branch: GitBranch,
|
||||||
|
uri: GitUri,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService
|
||||||
|
) {
|
||||||
super(uri);
|
super(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,10 +25,11 @@ export class BranchHistoryNode extends ExplorerNode {
|
|||||||
const log = await this.git.getLogForRepo(this.uri.repoPath!, this.branch.name, this.maxCount);
|
const log = await this.git.getLogForRepo(this.uri.repoPath!, this.branch.name, this.maxCount);
|
||||||
if (log === undefined) return [];
|
if (log === undefined) return [];
|
||||||
|
|
||||||
const children = Iterables.map(log.commits.values(), c => new CommitNode(c, this.template, this.context, this.git, this.branch));
|
const children: (CommitNode | ShowAllNode)[] = [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.context, this.git, this.branch))];
|
||||||
if (!log.truncated) return [...children];
|
if (log.truncated) {
|
||||||
|
children.push(new ShowAllNode('Show All Commits', this, this.context));
|
||||||
return [...children, new ShowAllCommitsNode(this, this.context)];
|
}
|
||||||
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTreeItem(): Promise<TreeItem> {
|
async getTreeItem(): Promise<TreeItem> {
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ export class BranchesNode extends ExplorerNode {
|
|||||||
|
|
||||||
readonly resourceType: ResourceType = 'gitlens:branches';
|
readonly resourceType: ResourceType = 'gitlens:branches';
|
||||||
|
|
||||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
constructor(
|
||||||
|
uri: GitUri,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService
|
||||||
|
) {
|
||||||
super(uri);
|
super(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,7 +22,7 @@ export class BranchesNode extends ExplorerNode {
|
|||||||
if (branches === undefined) return [];
|
if (branches === undefined) return [];
|
||||||
|
|
||||||
branches.sort((a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || a.name.localeCompare(b.name));
|
branches.sort((a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || a.name.localeCompare(b.name));
|
||||||
return [...Iterables.filterMap(branches, b => b.remote ? undefined : new BranchHistoryNode(b, this.uri, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
|
return [...Iterables.filterMap(branches, b => b.remote ? undefined : new BranchHistoryNode(b, this.uri, this.context, this.git))];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTreeItem(): Promise<TreeItem> {
|
async getTreeItem(): Promise<TreeItem> {
|
||||||
|
|||||||
@@ -2,33 +2,56 @@
|
|||||||
import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
|
import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
|
||||||
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
|
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
|
||||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||||
import { getGitStatusIcon, GitBranch, GitCommit, GitService, GitUri, IGitStatusFile, StatusFileFormatter } from '../gitService';
|
import { CommitFormatter, getGitStatusIcon, GitBranch, GitCommit, GitService, GitUri, ICommitFormatOptions, IGitStatusFile, IStatusFormatOptions, StatusFileFormatter } from '../gitService';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
|
export enum CommitFileNodeDisplayAs {
|
||||||
|
CommitLabel = 1 << 0,
|
||||||
|
CommitIcon = 1 << 1,
|
||||||
|
FileLabel = 1 << 2,
|
||||||
|
StatusIcon = 1 << 3,
|
||||||
|
|
||||||
|
Commit = CommitLabel | CommitIcon,
|
||||||
|
File = FileLabel | StatusIcon
|
||||||
|
}
|
||||||
|
|
||||||
export class CommitFileNode extends ExplorerNode {
|
export class CommitFileNode extends ExplorerNode {
|
||||||
|
|
||||||
|
readonly priority: boolean = false;
|
||||||
|
readonly repoPath: string;
|
||||||
readonly resourceType: ResourceType = 'gitlens:commit-file';
|
readonly resourceType: ResourceType = 'gitlens:commit-file';
|
||||||
|
|
||||||
constructor(public readonly status: IGitStatusFile, public commit: GitCommit, protected readonly context: ExtensionContext, protected readonly git: GitService, public readonly branch?: GitBranch) {
|
constructor(
|
||||||
|
public readonly status: IGitStatusFile,
|
||||||
|
public commit: GitCommit,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService,
|
||||||
|
private displayAs: CommitFileNodeDisplayAs = CommitFileNodeDisplayAs.Commit,
|
||||||
|
public readonly branch?: GitBranch
|
||||||
|
) {
|
||||||
super(new GitUri(Uri.file(path.resolve(commit.repoPath, status.fileName)), { repoPath: commit.repoPath, fileName: status.fileName, sha: commit.sha }));
|
super(new GitUri(Uri.file(path.resolve(commit.repoPath, status.fileName)), { repoPath: commit.repoPath, fileName: status.fileName, sha: commit.sha }));
|
||||||
|
this.repoPath = commit.repoPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildren(): Promise<ExplorerNode[]> {
|
async getChildren(): Promise<ExplorerNode[]> {
|
||||||
return Promise.resolve([]);
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTreeItem(): Promise<TreeItem> {
|
async getTreeItem(): Promise<TreeItem> {
|
||||||
if (this.commit.type !== 'file') {
|
if (this.commit.type !== 'file') {
|
||||||
const log = await this.git.getLogForFile(this.commit.repoPath, this.status.fileName, this.commit.sha, { maxCount: 2 });
|
const log = await this.git.getLogForFile(this.repoPath, this.status.fileName, this.commit.sha, { maxCount: 2 });
|
||||||
if (log !== undefined) {
|
if (log !== undefined) {
|
||||||
this.commit = log.commits.get(this.commit.sha) || this.commit;
|
this.commit = log.commits.get(this.commit.sha) || this.commit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = new TreeItem(StatusFileFormatter.fromTemplate(this.git.config.gitExplorer.commitFileFormat, this.status), TreeItemCollapsibleState.None);
|
const item = new TreeItem(this.label, TreeItemCollapsibleState.None);
|
||||||
item.contextValue = this.resourceType;
|
item.contextValue = this.resourceType;
|
||||||
|
|
||||||
const icon = getGitStatusIcon(this.status.status);
|
const icon = (this.displayAs & CommitFileNodeDisplayAs.CommitIcon)
|
||||||
|
? 'icon-commit.svg'
|
||||||
|
: getGitStatusIcon(this.status.status);
|
||||||
|
|
||||||
item.iconPath = {
|
item.iconPath = {
|
||||||
dark: this.context.asAbsolutePath(path.join('images', 'dark', icon)),
|
dark: this.context.asAbsolutePath(path.join('images', 'dark', icon)),
|
||||||
light: this.context.asAbsolutePath(path.join('images', 'light', icon))
|
light: this.context.asAbsolutePath(path.join('images', 'light', icon))
|
||||||
@@ -36,9 +59,52 @@ export class CommitFileNode extends ExplorerNode {
|
|||||||
|
|
||||||
item.command = this.getCommand();
|
item.command = this.getCommand();
|
||||||
|
|
||||||
|
// Only cache the label for a single refresh
|
||||||
|
this._label = undefined;
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _folderName: string | undefined;
|
||||||
|
get folderName() {
|
||||||
|
if (this._folderName === undefined) {
|
||||||
|
this._folderName = path.dirname(this.uri.getRelativePath());
|
||||||
|
}
|
||||||
|
return this._folderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _label: string | undefined;
|
||||||
|
get label() {
|
||||||
|
if (this._label === undefined) {
|
||||||
|
this._label = (this.displayAs & CommitFileNodeDisplayAs.CommitLabel)
|
||||||
|
? CommitFormatter.fromTemplate(this.getCommitTemplate(), this.commit, {
|
||||||
|
truncateMessageAtNewLine: true,
|
||||||
|
dataFormat: this.git.config.defaultDateFormat
|
||||||
|
} as ICommitFormatOptions)
|
||||||
|
: StatusFileFormatter.fromTemplate(this.getCommitFileTemplate(),
|
||||||
|
this.status,
|
||||||
|
{ relativePath: this.relativePath } as IStatusFormatOptions);
|
||||||
|
}
|
||||||
|
return this._label;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _relativePath: string | undefined;
|
||||||
|
get relativePath(): string | undefined {
|
||||||
|
return this._relativePath;
|
||||||
|
}
|
||||||
|
set relativePath(value: string | undefined) {
|
||||||
|
this._relativePath = value;
|
||||||
|
this._label = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getCommitTemplate() {
|
||||||
|
return this.git.config.gitExplorer.commitFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getCommitFileTemplate() {
|
||||||
|
return this.git.config.gitExplorer.commitFileFormat;
|
||||||
|
}
|
||||||
|
|
||||||
getCommand(): Command | undefined {
|
getCommand(): Command | undefined {
|
||||||
return {
|
return {
|
||||||
title: 'Compare File with Previous Revision',
|
title: 'Compare File with Previous Revision',
|
||||||
|
|||||||
@@ -1,59 +1,66 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Iterables } from '../system';
|
import { Arrays, Iterables } from '../system';
|
||||||
import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||||
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
|
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
|
||||||
import { CommitFileNode } from './commitFileNode';
|
import { CommitFileNode, CommitFileNodeDisplayAs } from './commitFileNode';
|
||||||
|
import { GitExplorerFilesLayout } from '../configuration';
|
||||||
|
import { FolderNode, IFileExplorerNode } from './folderNode';
|
||||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||||
import { CommitFormatter, getGitStatusIcon, GitBranch, GitLogCommit, GitService, GitUri, ICommitFormatOptions } from '../gitService';
|
import { CommitFormatter, GitBranch, GitLogCommit, GitService, GitUri, ICommitFormatOptions } from '../gitService';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
export class CommitNode extends ExplorerNode {
|
export class CommitNode extends ExplorerNode {
|
||||||
|
|
||||||
|
readonly repoPath: string;
|
||||||
readonly resourceType: ResourceType = 'gitlens:commit';
|
readonly resourceType: ResourceType = 'gitlens:commit';
|
||||||
|
|
||||||
constructor(public readonly commit: GitLogCommit, private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService, public readonly branch?: GitBranch) {
|
constructor(
|
||||||
|
public readonly commit: GitLogCommit,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService,
|
||||||
|
public readonly branch?: GitBranch
|
||||||
|
) {
|
||||||
super(new GitUri(commit.uri, commit));
|
super(new GitUri(commit.uri, commit));
|
||||||
|
this.repoPath = commit.repoPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChildren(): Promise<ExplorerNode[]> {
|
async getChildren(): Promise<ExplorerNode[]> {
|
||||||
if (this.commit.type === 'file') Promise.resolve([]);
|
const repoPath = this.repoPath;
|
||||||
|
|
||||||
const log = await this.git.getLogForRepo(this.commit.repoPath, this.commit.sha, 1);
|
const log = await this.git.getLogForRepo(repoPath, this.commit.sha, 1);
|
||||||
if (log === undefined) return [];
|
if (log === undefined) return [];
|
||||||
|
|
||||||
const commit = Iterables.first(log.commits.values());
|
const commit = Iterables.first(log.commits.values());
|
||||||
if (commit === undefined) return [];
|
if (commit === undefined) return [];
|
||||||
|
|
||||||
return [...Iterables.map(commit.fileStatuses, s => new CommitFileNode(s, commit, this.context, this.git, this.branch))];
|
let children: IFileExplorerNode[] = [
|
||||||
|
...Iterables.map(commit.fileStatuses, s => new CommitFileNode(s, commit, this.context, this.git, CommitFileNodeDisplayAs.File, this.branch))
|
||||||
|
];
|
||||||
|
|
||||||
|
if (this.git.config.gitExplorer.files.layout !== GitExplorerFilesLayout.List) {
|
||||||
|
const hierarchy = Arrays.makeHierarchical(children, n => n.uri.getRelativePath().split('/'),
|
||||||
|
(...paths: string[]) => GitService.normalizePath(path.join(...paths)), this.git.config.gitExplorer.files.compact);
|
||||||
|
|
||||||
|
const root = new FolderNode(repoPath, '', undefined, hierarchy, this.git.config.gitExplorer);
|
||||||
|
children = await root.getChildren() as IFileExplorerNode[];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
children.sort((a, b) => a.label!.localeCompare(b.label!));
|
||||||
|
}
|
||||||
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTreeItem(): TreeItem {
|
getTreeItem(): TreeItem {
|
||||||
const item = new TreeItem(CommitFormatter.fromTemplate(this.template, this.commit, {
|
const item = new TreeItem(CommitFormatter.fromTemplate(this.git.config.gitExplorer.commitFormat, this.commit, {
|
||||||
truncateMessageAtNewLine: true,
|
truncateMessageAtNewLine: true,
|
||||||
dataFormat: this.git.config.defaultDateFormat
|
dataFormat: this.git.config.defaultDateFormat
|
||||||
} as ICommitFormatOptions));
|
} as ICommitFormatOptions), TreeItemCollapsibleState.Collapsed);
|
||||||
|
|
||||||
if (this.commit.type === 'file') {
|
item.contextValue = this.resourceType;
|
||||||
item.collapsibleState = TreeItemCollapsibleState.None;
|
item.iconPath = {
|
||||||
item.command = this.getCommand();
|
dark: this.context.asAbsolutePath('images/dark/icon-commit.svg'),
|
||||||
const resourceType: ResourceType = 'gitlens:commit-file';
|
light: this.context.asAbsolutePath('images/light/icon-commit.svg')
|
||||||
item.contextValue = resourceType;
|
};
|
||||||
|
|
||||||
const icon = getGitStatusIcon(this.commit.status!);
|
|
||||||
item.iconPath = {
|
|
||||||
dark: this.context.asAbsolutePath(path.join('images', 'dark', icon)),
|
|
||||||
light: this.context.asAbsolutePath(path.join('images', 'light', icon))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
item.collapsibleState = TreeItemCollapsibleState.Collapsed;
|
|
||||||
item.contextValue = this.resourceType;
|
|
||||||
|
|
||||||
item.iconPath = {
|
|
||||||
dark: this.context.asAbsolutePath('images/dark/icon-commit.svg'),
|
|
||||||
light: this.context.asAbsolutePath('images/light/icon-commit.svg')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export declare type ResourceType =
|
|||||||
'gitlens:commit' |
|
'gitlens:commit' |
|
||||||
'gitlens:commit-file' |
|
'gitlens:commit-file' |
|
||||||
'gitlens:file-history' |
|
'gitlens:file-history' |
|
||||||
|
'gitlens:folder' |
|
||||||
'gitlens:history' |
|
'gitlens:history' |
|
||||||
'gitlens:message' |
|
'gitlens:message' |
|
||||||
'gitlens:pager' |
|
'gitlens:pager' |
|
||||||
@@ -20,6 +21,9 @@ export declare type ResourceType =
|
|||||||
'gitlens:stash-file' |
|
'gitlens:stash-file' |
|
||||||
'gitlens:stashes' |
|
'gitlens:stashes' |
|
||||||
'gitlens:status' |
|
'gitlens:status' |
|
||||||
|
'gitlens:status-file' |
|
||||||
|
'gitlens:status-files' |
|
||||||
|
'gitlens:status-file-commits' |
|
||||||
'gitlens:status-upstream';
|
'gitlens:status-upstream';
|
||||||
|
|
||||||
export abstract class ExplorerNode {
|
export abstract class ExplorerNode {
|
||||||
@@ -88,11 +92,11 @@ export class PagerNode extends ExplorerNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ShowAllCommitsNode extends PagerNode {
|
export class ShowAllNode extends PagerNode {
|
||||||
|
|
||||||
args: RefreshNodeCommandArgs = { maxCount: 0 };
|
args: RefreshNodeCommandArgs = { maxCount: 0 };
|
||||||
|
|
||||||
constructor(node: ExplorerNode, context: ExtensionContext) {
|
constructor(message: string, node: ExplorerNode, context: ExtensionContext) {
|
||||||
super(`Show All Commits ${GlyphChars.Space}${GlyphChars.Dash}${GlyphChars.Space} this may take a while`, node, context);
|
super(`${message} ${GlyphChars.Space}${GlyphChars.Dash}${GlyphChars.Space} this may take a while`, node, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,8 +10,10 @@ export * from './historyNode';
|
|||||||
export * from './remoteNode';
|
export * from './remoteNode';
|
||||||
export * from './remotesNode';
|
export * from './remotesNode';
|
||||||
export * from './repositoryNode';
|
export * from './repositoryNode';
|
||||||
|
export * from './stashesNode';
|
||||||
export * from './stashFileNode';
|
export * from './stashFileNode';
|
||||||
export * from './stashNode';
|
export * from './stashNode';
|
||||||
export * from './stashesNode';
|
export * from './statusFileCommitsNode';
|
||||||
|
export * from './statusFilesNode';
|
||||||
export * from './statusNode';
|
export * from './statusNode';
|
||||||
export * from './statusUpstreamNode';
|
export * from './statusUpstreamNode';
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Iterables } from '../system';
|
import { Iterables } from '../system';
|
||||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||||
import { CommitNode } from './commitNode';
|
import { CommitFileNode, CommitFileNodeDisplayAs } from './commitFileNode';
|
||||||
import { ExplorerNode, MessageNode, ResourceType } from './explorerNode';
|
import { ExplorerNode, MessageNode, ResourceType } from './explorerNode';
|
||||||
import { GitService, GitUri } from '../gitService';
|
import { GitService, GitUri } from '../gitService';
|
||||||
|
|
||||||
@@ -9,15 +9,19 @@ export class FileHistoryNode extends ExplorerNode {
|
|||||||
|
|
||||||
readonly resourceType: ResourceType = 'gitlens:file-history';
|
readonly resourceType: ResourceType = 'gitlens:file-history';
|
||||||
|
|
||||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
constructor(
|
||||||
|
uri: GitUri,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService
|
||||||
|
) {
|
||||||
super(uri);
|
super(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChildren(): Promise<ExplorerNode[]> {
|
async getChildren(): Promise<ExplorerNode[]> {
|
||||||
const log = await this.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, this.uri.sha);
|
const log = await this.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, this.uri.sha);
|
||||||
if (log === undefined) return [new MessageNode('No file history')];
|
if (log === undefined) return [new MessageNode('No file history')];
|
||||||
|
|
||||||
return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
|
return [...Iterables.map(log.commits.values(), c => new CommitFileNode(c.fileStatuses[0], c, this.context, this.git, CommitFileNodeDisplayAs.CommitLabel | CommitFileNodeDisplayAs.StatusIcon))];
|
||||||
}
|
}
|
||||||
|
|
||||||
getTreeItem(): TreeItem {
|
getTreeItem(): TreeItem {
|
||||||
|
|||||||
85
src/views/folderNode.ts
Normal file
85
src/views/folderNode.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
'use strict';
|
||||||
|
import { Arrays, Objects } from '../system';
|
||||||
|
import { TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
|
||||||
|
import { GitExplorerFilesLayout, IGitExplorerConfig } from '../configuration';
|
||||||
|
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||||
|
import { GitUri } from '../gitService';
|
||||||
|
|
||||||
|
export interface IFileExplorerNode extends ExplorerNode {
|
||||||
|
folderName: string;
|
||||||
|
label?: string;
|
||||||
|
priority: boolean;
|
||||||
|
relativePath?: string;
|
||||||
|
root?: Arrays.IHierarchicalItem<IFileExplorerNode>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FolderNode extends ExplorerNode {
|
||||||
|
|
||||||
|
readonly priority: boolean = true;
|
||||||
|
readonly resourceType: ResourceType = 'gitlens:folder';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly repoPath: string,
|
||||||
|
public folderName: string,
|
||||||
|
public relativePath: string | undefined,
|
||||||
|
public readonly root: Arrays.IHierarchicalItem<IFileExplorerNode>,
|
||||||
|
private readonly config: IGitExplorerConfig
|
||||||
|
) {
|
||||||
|
super(new GitUri(Uri.file(repoPath), { repoPath: repoPath, fileName: repoPath }));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getChildren(): Promise<(FolderNode | IFileExplorerNode)[]> {
|
||||||
|
if (this.root.descendants === undefined || this.root.children === undefined) return [];
|
||||||
|
|
||||||
|
let children: (FolderNode | IFileExplorerNode)[];
|
||||||
|
|
||||||
|
const nesting = FolderNode.getFileNesting(this.config, this.root.descendants, this.relativePath === undefined);
|
||||||
|
if (nesting !== GitExplorerFilesLayout.List) {
|
||||||
|
children = [];
|
||||||
|
for (const folder of Objects.values(this.root.children)) {
|
||||||
|
if (folder.value === undefined) {
|
||||||
|
children.push(new FolderNode(this.repoPath, folder.name, folder.relativePath, folder, this.config));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
folder.value.relativePath = this.root.relativePath;
|
||||||
|
children.push(folder.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.root.descendants.forEach(n => n.relativePath = this.root.relativePath);
|
||||||
|
children = this.root.descendants;
|
||||||
|
}
|
||||||
|
|
||||||
|
children.sort((a, b) => {
|
||||||
|
return ((a instanceof FolderNode) ? -1 : 1) - ((b instanceof FolderNode) ? -1 : 1) ||
|
||||||
|
(a.priority ? -1 : 1) - (b.priority ? -1 : 1) ||
|
||||||
|
a.label!.localeCompare(b.label!);
|
||||||
|
});
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTreeItem(): Promise<TreeItem> {
|
||||||
|
// TODO: Change this to expanded once https://github.com/Microsoft/vscode/issues/30918 is fixed
|
||||||
|
const item = new TreeItem(this.label, TreeItemCollapsibleState.Collapsed);
|
||||||
|
item.contextValue = this.resourceType;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
get label(): string {
|
||||||
|
return this.folderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getFileNesting<T extends IFileExplorerNode>(config: IGitExplorerConfig, children: T[], isRoot: boolean): GitExplorerFilesLayout {
|
||||||
|
const nesting = config.files.layout || GitExplorerFilesLayout.Auto;
|
||||||
|
if (nesting === GitExplorerFilesLayout.Auto) {
|
||||||
|
if (isRoot || config.files.compact) {
|
||||||
|
const nestingThreshold = config.files.threshold || 5;
|
||||||
|
if (children.length <= nestingThreshold) return GitExplorerFilesLayout.List;
|
||||||
|
}
|
||||||
|
return GitExplorerFilesLayout.Tree;
|
||||||
|
}
|
||||||
|
return nesting;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,16 +4,18 @@ import { commands, Event, EventEmitter, ExtensionContext, TextDocumentShowOption
|
|||||||
import { Commands, DiffWithCommandArgs, DiffWithCommandArgsRevision, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
|
import { Commands, DiffWithCommandArgs, DiffWithCommandArgsRevision, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
|
||||||
import { UriComparer } from '../comparers';
|
import { UriComparer } from '../comparers';
|
||||||
import { ExtensionKey, IConfig } from '../configuration';
|
import { ExtensionKey, IConfig } from '../configuration';
|
||||||
import { CommandContext, setCommandContext } from '../constants';
|
import { CommandContext, setCommandContext, WorkspaceState } from '../constants';
|
||||||
import { BranchHistoryNode, CommitFileNode, CommitNode, ExplorerNode, HistoryNode, MessageNode, RepositoryNode, StashNode } from './explorerNodes';
|
import { BranchHistoryNode, CommitFileNode, CommitNode, ExplorerNode, HistoryNode, MessageNode, RepositoryNode, StashNode } from './explorerNodes';
|
||||||
import { GitService, GitUri, RepoChangedReasons } from '../gitService';
|
import { GitService, GitUri, RepoChangedReasons } from '../gitService';
|
||||||
|
|
||||||
export * from './explorerNodes';
|
export * from './explorerNodes';
|
||||||
|
|
||||||
export type GitExplorerView =
|
export type GitExplorerView =
|
||||||
|
'auto' |
|
||||||
'history' |
|
'history' |
|
||||||
'repository';
|
'repository';
|
||||||
export const GitExplorerView = {
|
export const GitExplorerView = {
|
||||||
|
Auto: 'auto' as GitExplorerView,
|
||||||
History: 'history' as GitExplorerView,
|
History: 'history' as GitExplorerView,
|
||||||
Repository: 'repository' as GitExplorerView
|
Repository: 'repository' as GitExplorerView
|
||||||
};
|
};
|
||||||
@@ -31,7 +33,7 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
|
|||||||
|
|
||||||
private _config: IConfig;
|
private _config: IConfig;
|
||||||
private _root?: ExplorerNode;
|
private _root?: ExplorerNode;
|
||||||
private _view: GitExplorerView = GitExplorerView.Repository;
|
private _view: GitExplorerView | undefined;
|
||||||
|
|
||||||
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
|
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
|
||||||
public get onDidChangeTreeData(): Event<ExplorerNode> {
|
public get onDidChangeTreeData(): Event<ExplorerNode> {
|
||||||
@@ -62,10 +64,6 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
|
|||||||
context.subscriptions.push(workspace.onDidChangeConfiguration(this.onConfigurationChanged, this));
|
context.subscriptions.push(workspace.onDidChangeConfiguration(this.onConfigurationChanged, this));
|
||||||
|
|
||||||
this.onConfigurationChanged();
|
this.onConfigurationChanged();
|
||||||
|
|
||||||
this._view = this._config.gitExplorer.view;
|
|
||||||
setCommandContext(CommandContext.GitExplorerView, this._view);
|
|
||||||
this._root = this.getRootNode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTreeItem(node: ExplorerNode): Promise<TreeItem> {
|
async getTreeItem(node: ExplorerNode): Promise<TreeItem> {
|
||||||
@@ -83,14 +81,14 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getRootNode(editor?: TextEditor): ExplorerNode | undefined {
|
private getRootNode(editor?: TextEditor): ExplorerNode | undefined {
|
||||||
const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
|
|
||||||
|
|
||||||
switch (this._view) {
|
switch (this._view) {
|
||||||
case GitExplorerView.History: return this.getHistoryNode(editor || window.activeTextEditor);
|
case GitExplorerView.History:
|
||||||
case GitExplorerView.Repository: return new RepositoryNode(uri, this.context, this.git);
|
return this.getHistoryNode(editor || window.activeTextEditor);
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
default:
|
||||||
|
const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
|
||||||
|
return new RepositoryNode(uri, this.context, this.git);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getHistoryNode(editor: TextEditor | undefined): ExplorerNode | undefined {
|
private getHistoryNode(editor: TextEditor | undefined): ExplorerNode | undefined {
|
||||||
@@ -116,15 +114,21 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
|
|||||||
private onConfigurationChanged() {
|
private onConfigurationChanged() {
|
||||||
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
|
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
|
||||||
|
|
||||||
if (!Objects.areEquivalent(cfg.gitExplorer, this._config && this._config.gitExplorer)) {
|
const changed = !Objects.areEquivalent(cfg.gitExplorer, this._config && this._config.gitExplorer);
|
||||||
setTimeout(() => {
|
|
||||||
this._root = this.getRootNode(window.activeTextEditor);
|
|
||||||
this.refresh();
|
|
||||||
}, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._config = cfg;
|
this._config = cfg;
|
||||||
}
|
|
||||||
|
if (changed) {
|
||||||
|
let view = cfg.gitExplorer.view;
|
||||||
|
if (view === GitExplorerView.Auto) {
|
||||||
|
view = this.context.workspaceState.get<GitExplorerView>(WorkspaceState.GitExplorerView, GitExplorerView.Repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setView(view);
|
||||||
|
this._root = this.getRootNode(window.activeTextEditor);
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private onRepoChanged(reasons: RepoChangedReasons[]) {
|
private onRepoChanged(reasons: RepoChangedReasons[]) {
|
||||||
if (this._view !== GitExplorerView.Repository) return;
|
if (this._view !== GitExplorerView.Repository) return;
|
||||||
@@ -148,12 +152,26 @@ export class GitExplorer implements TreeDataProvider<ExplorerNode> {
|
|||||||
this.refresh(node);
|
this.refresh(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
switchTo(view: GitExplorerView) {
|
setView(view: GitExplorerView) {
|
||||||
if (this._view === view) return;
|
if (this._view === view) return;
|
||||||
|
|
||||||
|
if (this._config.gitExplorer.view === GitExplorerView.Auto) {
|
||||||
|
this.context.workspaceState.update(WorkspaceState.GitExplorerView, view);
|
||||||
|
}
|
||||||
|
|
||||||
this._view = view;
|
this._view = view;
|
||||||
setCommandContext(CommandContext.GitExplorerView, this._view);
|
setCommandContext(CommandContext.GitExplorerView, this._view);
|
||||||
|
|
||||||
|
if (view !== GitExplorerView.Repository) {
|
||||||
|
this.git.stopWatchingFileSystem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switchTo(view: GitExplorerView) {
|
||||||
|
if (this._view === view) return;
|
||||||
|
|
||||||
|
this.setView(view);
|
||||||
|
|
||||||
this._root = this.getRootNode(window.activeTextEditor);
|
this._root = this.getRootNode(window.activeTextEditor);
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ export class HistoryNode extends ExplorerNode {
|
|||||||
|
|
||||||
readonly resourceType: ResourceType = 'gitlens:history';
|
readonly resourceType: ResourceType = 'gitlens:history';
|
||||||
|
|
||||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
constructor(
|
||||||
|
uri: GitUri,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService
|
||||||
|
) {
|
||||||
super(uri);
|
super(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChildren(): Promise<ExplorerNode[]> {
|
async getChildren(): Promise<ExplorerNode[]> {
|
||||||
return [new FileHistoryNode(this.uri, this.context, this.git)];
|
return [new FileHistoryNode(this.uri, this.context, this.git)];
|
||||||
|
|||||||
@@ -10,7 +10,12 @@ export class RemoteNode extends ExplorerNode {
|
|||||||
|
|
||||||
readonly resourceType: ResourceType = 'gitlens:remote';
|
readonly resourceType: ResourceType = 'gitlens:remote';
|
||||||
|
|
||||||
constructor(public readonly remote: GitRemote, uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
constructor(
|
||||||
|
public readonly remote: GitRemote,
|
||||||
|
uri: GitUri,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService
|
||||||
|
) {
|
||||||
super(uri);
|
super(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,7 +24,7 @@ export class RemoteNode extends ExplorerNode {
|
|||||||
if (branches === undefined) return [];
|
if (branches === undefined) return [];
|
||||||
|
|
||||||
branches.sort((a, b) => a.name.localeCompare(b.name));
|
branches.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
return [...Iterables.filterMap(branches, b => !b.remote || !b.name.startsWith(this.remote.name) ? undefined : new BranchHistoryNode(b, this.uri, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
|
return [...Iterables.filterMap(branches, b => !b.remote || !b.name.startsWith(this.remote.name) ? undefined : new BranchHistoryNode(b, this.uri, this.context, this.git))];
|
||||||
}
|
}
|
||||||
|
|
||||||
getTreeItem(): TreeItem {
|
getTreeItem(): TreeItem {
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ export class RemotesNode extends ExplorerNode {
|
|||||||
|
|
||||||
readonly resourceType: ResourceType = 'gitlens:remotes';
|
readonly resourceType: ResourceType = 'gitlens:remotes';
|
||||||
|
|
||||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
constructor(
|
||||||
|
uri: GitUri,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService
|
||||||
|
) {
|
||||||
super(uri);
|
super(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,13 @@ export class RepositoryNode extends ExplorerNode {
|
|||||||
|
|
||||||
readonly resourceType: ResourceType = 'gitlens:repository';
|
readonly resourceType: ResourceType = 'gitlens:repository';
|
||||||
|
|
||||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
constructor(
|
||||||
|
uri: GitUri,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService
|
||||||
|
) {
|
||||||
super(uri);
|
super(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChildren(): Promise<ExplorerNode[]> {
|
async getChildren(): Promise<ExplorerNode[]> {
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -2,13 +2,26 @@
|
|||||||
import { ExtensionContext } from 'vscode';
|
import { ExtensionContext } from 'vscode';
|
||||||
import { ResourceType } from './explorerNode';
|
import { ResourceType } from './explorerNode';
|
||||||
import { GitService, GitStashCommit, IGitStatusFile } from '../gitService';
|
import { GitService, GitStashCommit, IGitStatusFile } from '../gitService';
|
||||||
import { CommitFileNode } from './commitFileNode';
|
import { CommitFileNode, CommitFileNodeDisplayAs } from './commitFileNode';
|
||||||
|
|
||||||
export class StashFileNode extends CommitFileNode {
|
export class StashFileNode extends CommitFileNode {
|
||||||
|
|
||||||
readonly resourceType: ResourceType = 'gitlens:stash-file';
|
readonly resourceType: ResourceType = 'gitlens:stash-file';
|
||||||
|
|
||||||
constructor(readonly status: IGitStatusFile, readonly commit: GitStashCommit, readonly context: ExtensionContext, readonly git: GitService) {
|
constructor(
|
||||||
super(status, commit, context, git);
|
readonly status: IGitStatusFile,
|
||||||
|
readonly commit: GitStashCommit,
|
||||||
|
readonly context: ExtensionContext,
|
||||||
|
readonly git: GitService
|
||||||
|
) {
|
||||||
|
super(status, commit, context, git, CommitFileNodeDisplayAs.File);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getCommitTemplate() {
|
||||||
|
return this.git.config.gitExplorer.stashFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getCommitFileTemplate() {
|
||||||
|
return this.git.config.gitExplorer.stashFileFormat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,11 @@ export class StashNode extends ExplorerNode {
|
|||||||
|
|
||||||
readonly resourceType: ResourceType = 'gitlens:stash';
|
readonly resourceType: ResourceType = 'gitlens:stash';
|
||||||
|
|
||||||
constructor(public readonly commit: GitStashCommit, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
constructor(
|
||||||
|
public readonly commit: GitStashCommit,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService
|
||||||
|
) {
|
||||||
super(new GitUri(commit.uri, commit));
|
super(new GitUri(commit.uri, commit));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +31,9 @@ export class StashNode extends ExplorerNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(statuses.map(s => new StashFileNode(s, this.commit, this.context, this.git)));
|
const children = statuses.map(s => new StashFileNode(s, this.commit, this.context, this.git));
|
||||||
|
children.sort((a, b) => a.label!.localeCompare(b.label!));
|
||||||
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTreeItem(): TreeItem {
|
getTreeItem(): TreeItem {
|
||||||
|
|||||||
@@ -9,9 +9,13 @@ export class StashesNode extends ExplorerNode {
|
|||||||
|
|
||||||
readonly resourceType: ResourceType = 'gitlens:stashes';
|
readonly resourceType: ResourceType = 'gitlens:stashes';
|
||||||
|
|
||||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
constructor(
|
||||||
|
uri: GitUri,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService
|
||||||
|
) {
|
||||||
super(uri);
|
super(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChildren(): Promise<ExplorerNode[]> {
|
async getChildren(): Promise<ExplorerNode[]> {
|
||||||
const stash = await this.git.getStashList(this.uri.repoPath!);
|
const stash = await this.git.getStashList(this.uri.repoPath!);
|
||||||
|
|||||||
102
src/views/statusFileCommitsNode.ts
Normal file
102
src/views/statusFileCommitsNode.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
'use strict';
|
||||||
|
import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
|
||||||
|
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
|
||||||
|
import { CommitFileNode, CommitFileNodeDisplayAs } from './commitFileNode';
|
||||||
|
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||||
|
import { getGitStatusIcon, GitBranch, GitLogCommit, GitService, GitUri, IGitStatusFile, IGitStatusFileWithCommit, IStatusFormatOptions, StatusFileFormatter } from '../gitService';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
export class StatusFileCommitsNode extends ExplorerNode {
|
||||||
|
|
||||||
|
readonly resourceType: ResourceType = 'gitlens:status-file-commits';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly repoPath: string,
|
||||||
|
public readonly status: IGitStatusFile,
|
||||||
|
public commits: GitLogCommit[],
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService,
|
||||||
|
public readonly branch?: GitBranch
|
||||||
|
) {
|
||||||
|
super(new GitUri(Uri.file(path.resolve(repoPath, status.fileName)), { repoPath: repoPath, fileName: status.fileName, sha: 'HEAD' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getChildren(): Promise<ExplorerNode[]> {
|
||||||
|
return this.commits.map(c => new CommitFileNode(this.status, c, this.context, this.git, CommitFileNodeDisplayAs.Commit, this.branch));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTreeItem(): Promise<TreeItem> {
|
||||||
|
const item = new TreeItem(this.label, TreeItemCollapsibleState.Collapsed);
|
||||||
|
item.contextValue = this.resourceType;
|
||||||
|
|
||||||
|
const icon = getGitStatusIcon(this.status.status);
|
||||||
|
item.iconPath = {
|
||||||
|
dark: this.context.asAbsolutePath(path.join('images', 'dark', icon)),
|
||||||
|
light: this.context.asAbsolutePath(path.join('images', 'light', icon))
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.commits.length === 1 && this.commits[0].isUncommitted) {
|
||||||
|
item.collapsibleState = TreeItemCollapsibleState.None;
|
||||||
|
item.contextValue = 'gitlens:status-file' as ResourceType;
|
||||||
|
item.command = this.getCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only cache the label for a single refresh
|
||||||
|
this._label = undefined;
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _folderName: string | undefined;
|
||||||
|
get folderName() {
|
||||||
|
if (this._folderName === undefined) {
|
||||||
|
this._folderName = path.dirname(this.uri.getRelativePath());
|
||||||
|
}
|
||||||
|
return this._folderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _label: string | undefined;
|
||||||
|
get label() {
|
||||||
|
if (this._label === undefined) {
|
||||||
|
this._label = StatusFileFormatter.fromTemplate(this.git.config.gitExplorer.statusFileFormat,
|
||||||
|
{ ...this.status, commit: this.commit } as IGitStatusFileWithCommit,
|
||||||
|
{ relativePath: this.relativePath } as IStatusFormatOptions);
|
||||||
|
}
|
||||||
|
return this._label;
|
||||||
|
}
|
||||||
|
|
||||||
|
get commit() {
|
||||||
|
return this.commits[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
get priority(): boolean {
|
||||||
|
return this.commit.isUncommitted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _relativePath: string | undefined;
|
||||||
|
get relativePath(): string | undefined {
|
||||||
|
return this._relativePath;
|
||||||
|
}
|
||||||
|
set relativePath(value: string | undefined) {
|
||||||
|
this._relativePath = value;
|
||||||
|
this._label = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCommand(): Command | undefined {
|
||||||
|
return {
|
||||||
|
title: 'Compare File with Previous Revision',
|
||||||
|
command: Commands.DiffWithPrevious,
|
||||||
|
arguments: [
|
||||||
|
GitUri.fromFileStatus(this.status, this.repoPath),
|
||||||
|
{
|
||||||
|
commit: this.commit,
|
||||||
|
line: 0,
|
||||||
|
showOptions: {
|
||||||
|
preserveFocus: true,
|
||||||
|
preview: true
|
||||||
|
}
|
||||||
|
} as DiffWithPreviousCommandArgs
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
103
src/views/statusFilesNode.ts
Normal file
103
src/views/statusFilesNode.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
'use strict';
|
||||||
|
import { Arrays, Iterables, Objects } from '../system';
|
||||||
|
import { ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
|
||||||
|
import { GitExplorerFilesLayout } from '../configuration';
|
||||||
|
import { ExplorerNode, ResourceType, ShowAllNode } from './explorerNode';
|
||||||
|
import { FolderNode, IFileExplorerNode } from './folderNode';
|
||||||
|
import { GitBranch, GitLog, GitLogCommit, GitService, GitStatus, GitUri, IGitStatusFileWithCommit } from '../gitService';
|
||||||
|
import { StatusFileCommitsNode } from './statusFileCommitsNode';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
export class StatusFilesNode extends ExplorerNode {
|
||||||
|
|
||||||
|
readonly repoPath: string;
|
||||||
|
readonly resourceType: ResourceType = 'gitlens:status-files';
|
||||||
|
|
||||||
|
maxCount: number | undefined = undefined;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly status: GitStatus,
|
||||||
|
public readonly range: string | undefined,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService,
|
||||||
|
public readonly branch?: GitBranch
|
||||||
|
) {
|
||||||
|
super(new GitUri(Uri.file(status.repoPath), { repoPath: status.repoPath, fileName: status.repoPath }));
|
||||||
|
this.repoPath = status.repoPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getChildren(): Promise<ExplorerNode[]> {
|
||||||
|
let statuses: IGitStatusFileWithCommit[] = [];
|
||||||
|
|
||||||
|
const repoPath = this.repoPath;
|
||||||
|
|
||||||
|
let log: GitLog | undefined;
|
||||||
|
if (this.range !== undefined) {
|
||||||
|
log = await this.git.getLogForRepo(repoPath, this.range, this.maxCount);
|
||||||
|
if (log !== undefined) {
|
||||||
|
statuses = Array.from(Iterables.flatMap(log.commits.values(), c => {
|
||||||
|
return c.fileStatuses.map(s => {
|
||||||
|
return { ...s, commit: c } as IGitStatusFileWithCommit;
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.status.files.length !== 0 && this.includeWorkingTree) {
|
||||||
|
statuses.splice(0, 0, ...this.status.files.map(s => {
|
||||||
|
return {
|
||||||
|
...s,
|
||||||
|
commit: new GitLogCommit('file', repoPath, GitService.uncommittedSha, s.fileName, 'You', new Date(), '', s.status, [s], s.originalFileName, 'HEAD', s.fileName)
|
||||||
|
} as IGitStatusFileWithCommit;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
statuses.sort((a, b) => b.commit.date.getTime() - a.commit.date.getTime());
|
||||||
|
|
||||||
|
const groups = Arrays.groupBy(statuses, s => s.fileName);
|
||||||
|
|
||||||
|
let children: IFileExplorerNode[] = [
|
||||||
|
...Iterables.map(Objects.values(groups), statuses => new StatusFileCommitsNode(repoPath, statuses[statuses.length - 1], statuses.map(s => s.commit), this.context, this.git, this.branch))
|
||||||
|
];
|
||||||
|
|
||||||
|
if (this.git.config.gitExplorer.files.layout !== GitExplorerFilesLayout.List) {
|
||||||
|
const hierarchy = Arrays.makeHierarchical(children, n => n.uri.getRelativePath().split('/'),
|
||||||
|
(...paths: string[]) => GitService.normalizePath(path.join(...paths)), this.git.config.gitExplorer.files.compact);
|
||||||
|
|
||||||
|
const root = new FolderNode(repoPath, '', undefined, hierarchy, this.git.config.gitExplorer);
|
||||||
|
children = await root.getChildren() as IFileExplorerNode[];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
children.sort((a, b) => (a.priority ? -1 : 1) - (b.priority ? -1 : 1) || a.label!.localeCompare(b.label!));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log !== undefined && log.truncated) {
|
||||||
|
(children as (IFileExplorerNode | ShowAllNode)[]).push(new ShowAllNode('Show All Changes', this, this.context));
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTreeItem(): Promise<TreeItem> {
|
||||||
|
let files = (this.status.files !== undefined && this.includeWorkingTree) ? this.status.files.length : 0;
|
||||||
|
|
||||||
|
if (this.status.upstream !== undefined) {
|
||||||
|
const stats = await this.git.getChangedFilesCount(this.repoPath, `${this.status.upstream}...`);
|
||||||
|
if (stats !== undefined) {
|
||||||
|
files += stats.files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const label = `${files} file${files > 1 ? 's' : ''} changed`; // ${this.status.upstream === undefined ? '' : ` (ahead of ${this.status.upstream})`}`;
|
||||||
|
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
|
||||||
|
item.contextValue = this.resourceType;
|
||||||
|
item.iconPath = {
|
||||||
|
dark: this.context.asAbsolutePath(`images/dark/icon-diff.svg`),
|
||||||
|
light: this.context.asAbsolutePath(`images/light/icon-diff.svg`)
|
||||||
|
};
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get includeWorkingTree(): boolean {
|
||||||
|
return this.git.config.gitExplorer.includeWorkingTree;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,46 +1,76 @@
|
|||||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
import { commands, Disposable, ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
|
||||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||||
import { GitService, GitUri } from '../gitService';
|
import { GitService, GitStatus, GitUri } from '../gitService';
|
||||||
|
import { StatusFilesNode } from './statusFilesNode';
|
||||||
import { StatusUpstreamNode } from './statusUpstreamNode';
|
import { StatusUpstreamNode } from './statusUpstreamNode';
|
||||||
|
|
||||||
|
let _eventDisposable: Disposable | undefined;
|
||||||
|
|
||||||
export class StatusNode extends ExplorerNode {
|
export class StatusNode extends ExplorerNode {
|
||||||
|
|
||||||
readonly resourceType: ResourceType = 'gitlens:status';
|
readonly resourceType: ResourceType = 'gitlens:status';
|
||||||
|
|
||||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
constructor(
|
||||||
|
uri: GitUri,
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService
|
||||||
|
) {
|
||||||
super(uri);
|
super(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChildren(): Promise<ExplorerNode[]> {
|
async getChildren(): Promise<ExplorerNode[]> {
|
||||||
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
|
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
|
||||||
if (status === undefined) return [];
|
if (status === undefined) return [];
|
||||||
|
|
||||||
const children = [];
|
const children: ExplorerNode[] = [];
|
||||||
|
|
||||||
if (status.state.behind) {
|
if (status.state.behind) {
|
||||||
children.push(new StatusUpstreamNode(status, 'behind', this.git.config.gitExplorer.commitFormat, this.context, this.git));
|
children.push(new StatusUpstreamNode(status, 'behind', this.context, this.git));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.state.ahead) {
|
if (status.state.ahead) {
|
||||||
children.push(new StatusUpstreamNode(status, 'ahead', this.git.config.gitExplorer.commitFormat, this.context, this.git));
|
children.push(new StatusUpstreamNode(status, 'ahead', this.context, this.git));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.state.ahead || (status.files.length !== 0 && this.includeWorkingTree)) {
|
||||||
|
const range = status.upstream
|
||||||
|
? `${status.upstream}..${status.branch}`
|
||||||
|
: undefined;
|
||||||
|
children.push(new StatusFilesNode(status, range, this.context, this.git));
|
||||||
}
|
}
|
||||||
|
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTreeItem(): Promise<TreeItem> {
|
private _status: GitStatus | undefined;
|
||||||
|
|
||||||
|
async getTreeItem(): Promise < TreeItem > {
|
||||||
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
|
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
|
||||||
if (status === undefined) return new TreeItem('No repo status');
|
if (status === undefined) return new TreeItem('No repo status');
|
||||||
|
|
||||||
|
if (_eventDisposable !== undefined) {
|
||||||
|
_eventDisposable.dispose();
|
||||||
|
_eventDisposable = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.includeWorkingTree) {
|
||||||
|
this._status = status;
|
||||||
|
|
||||||
|
_eventDisposable = this.git.onDidChangeFileSystem(this.onFileSystemChanged, this);
|
||||||
|
this.git.startWatchingFileSystem();
|
||||||
|
}
|
||||||
|
|
||||||
let hasChildren = false;
|
let hasChildren = false;
|
||||||
|
const hasWorkingChanges = status.files.length !== 0 && this.includeWorkingTree;
|
||||||
let label = '';
|
let label = '';
|
||||||
let iconSuffix = '';
|
let iconSuffix = '';
|
||||||
if (status.upstream) {
|
if (status.upstream) {
|
||||||
if (!status.state.ahead && !status.state.behind) {
|
if (!status.state.ahead && !status.state.behind) {
|
||||||
label = `${status.branch} is up-to-date with ${status.upstream}`;
|
label = `${status.branch}${hasWorkingChanges ? ' has uncommitted changes and' : ''} is up-to-date with ${status.upstream}`;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
label = `${status.branch} is not up-to-date with ${status.upstream}`;
|
label = `${status.branch}${hasWorkingChanges ? ' has uncommitted changes and' : ''} is not up-to-date with ${status.upstream}`;
|
||||||
|
|
||||||
hasChildren = true;
|
hasChildren = true;
|
||||||
if (status.state.ahead && status.state.behind) {
|
if (status.state.ahead && status.state.behind) {
|
||||||
iconSuffix = '-yellow';
|
iconSuffix = '-yellow';
|
||||||
@@ -54,10 +84,10 @@ export class StatusNode extends ExplorerNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
label = `${status.branch} is up-to-date`;
|
label = `${status.branch} ${hasWorkingChanges ? 'has uncommitted changes' : this.includeWorkingTree ? 'has no changes' : 'has nothing to commit'}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = new TreeItem(label, hasChildren ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.None);
|
const item = new TreeItem(label, (hasChildren || hasWorkingChanges) ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.None);
|
||||||
item.contextValue = this.resourceType;
|
item.contextValue = this.resourceType;
|
||||||
|
|
||||||
item.iconPath = {
|
item.iconPath = {
|
||||||
@@ -67,4 +97,21 @@ export class StatusNode extends ExplorerNode {
|
|||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get includeWorkingTree(): boolean {
|
||||||
|
return this.git.config.gitExplorer.includeWorkingTree;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async onFileSystemChanged(uri: Uri) {
|
||||||
|
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
|
||||||
|
|
||||||
|
// If we haven't changed from having some working changes to none or vice versa then just refresh the node
|
||||||
|
// This is because of https://github.com/Microsoft/vscode/issues/34789
|
||||||
|
if (this._status !== undefined && status !== undefined &&
|
||||||
|
((this._status.files.length === status.files.length) || (this._status.files.length > 0 && status.files.length > 0))) {
|
||||||
|
commands.executeCommand('gitlens.gitExplorer.refreshNode', this);
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.executeCommand('gitlens.gitExplorer.refresh');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,20 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Iterables } from '../system';
|
import { Iterables } from '../system';
|
||||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
|
import { ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
|
||||||
|
import { CommitNode } from './commitNode';
|
||||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||||
import { GitService, GitStatus, GitUri } from '../gitService';
|
import { GitService, GitStatus, GitUri } from '../gitService';
|
||||||
import { CommitNode } from './commitNode';
|
|
||||||
|
|
||||||
export class StatusUpstreamNode extends ExplorerNode {
|
export class StatusUpstreamNode extends ExplorerNode {
|
||||||
|
|
||||||
readonly resourceType: ResourceType = 'gitlens:status-upstream';
|
readonly resourceType: ResourceType = 'gitlens:status-upstream';
|
||||||
|
|
||||||
constructor(public readonly status: GitStatus, public readonly direction: 'ahead' | 'behind', private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
constructor(
|
||||||
|
public readonly status: GitStatus,
|
||||||
|
public readonly direction: 'ahead' | 'behind',
|
||||||
|
protected readonly context: ExtensionContext,
|
||||||
|
protected readonly git: GitService
|
||||||
|
) {
|
||||||
super(new GitUri(Uri.file(status.repoPath), { repoPath: status.repoPath, fileName: status.repoPath }));
|
super(new GitUri(Uri.file(status.repoPath), { repoPath: status.repoPath, fileName: status.repoPath }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,10 +22,11 @@ export class StatusUpstreamNode extends ExplorerNode {
|
|||||||
const range = this.direction === 'ahead'
|
const range = this.direction === 'ahead'
|
||||||
? `${this.status.upstream}..${this.status.branch}`
|
? `${this.status.upstream}..${this.status.branch}`
|
||||||
: `${this.status.branch}..${this.status.upstream}`;
|
: `${this.status.branch}..${this.status.upstream}`;
|
||||||
|
|
||||||
let log = await this.git.getLogForRepo(this.uri.repoPath!, range, 0);
|
let log = await this.git.getLogForRepo(this.uri.repoPath!, range, 0);
|
||||||
if (log === undefined) return [];
|
if (log === undefined) return [];
|
||||||
|
|
||||||
if (this.direction !== 'ahead') return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.template, this.context, this.git))];
|
if (this.direction !== 'ahead') return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.context, this.git))];
|
||||||
|
|
||||||
// Since the last commit when we are looking 'ahead' can have no previous (because of the range given) -- look it up
|
// Since the last commit when we are looking 'ahead' can have no previous (because of the range given) -- look it up
|
||||||
const commits = Array.from(log.commits.values());
|
const commits = Array.from(log.commits.values());
|
||||||
@@ -32,13 +38,13 @@ export class StatusUpstreamNode extends ExplorerNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [...Iterables.map(commits, c => new CommitNode(c, this.template, this.context, this.git))];
|
return [...Iterables.map(commits, c => new CommitNode(c, this.context, this.git))];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTreeItem(): Promise<TreeItem> {
|
async getTreeItem(): Promise<TreeItem> {
|
||||||
const label = this.direction === 'ahead'
|
const label = this.direction === 'ahead'
|
||||||
? `${this.status.state.ahead} commit${this.status.state.ahead > 1 ? 's' : ''} ahead (local changes)` // of ${this.status.upstream}`
|
? `${this.status.state.ahead} commit${this.status.state.ahead > 1 ? 's' : ''} (ahead of ${this.status.upstream})`
|
||||||
: `${this.status.state.behind} commit${this.status.state.behind > 1 ? 's' : ''} behind (remote changes)`; // ${this.status.upstream}`;
|
: `${this.status.state.behind} commit${this.status.state.behind > 1 ? 's' : ''} (behind ${this.status.upstream})`;
|
||||||
|
|
||||||
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
|
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
|
||||||
item.contextValue = this.resourceType;
|
item.contextValue = this.resourceType;
|
||||||
|
|||||||
Reference in New Issue
Block a user