mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-02-12 02:58:32 -05:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3684629c9b | |||
|
|
65a3b31ca5 | ||
|
|
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)
|
||||||
|
|||||||
46
CODE_OF_CONDUCT.md
Normal file
46
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at eamodio@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||||
|
|
||||||
|
[homepage]: http://contributor-covenant.org
|
||||||
|
[version]: http://contributor-covenant.org/version/1/4/
|
||||||
25
README.md
25
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
|
||||||
|
|
||||||
@@ -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",
|
||||||
|
|||||||
149
package.json
149
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": [
|
||||||
@@ -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,
|
||||||
@@ -1032,6 +1068,11 @@
|
|||||||
"light": "images/light/icon-add.svg"
|
"light": "images/light/icon-add.svg"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.externalDiff",
|
||||||
|
"title": "Open Changes (with difftool)",
|
||||||
|
"category": "GitLens"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.resetSuppressedWarnings",
|
"command": "gitlens.resetSuppressedWarnings",
|
||||||
"title": "Reset Suppressed Warnings",
|
"title": "Reset Suppressed Warnings",
|
||||||
@@ -1397,12 +1438,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"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1484,17 +1525,22 @@
|
|||||||
{
|
{
|
||||||
"command": "gitlens.openChangedFiles",
|
"command": "gitlens.openChangedFiles",
|
||||||
"when": "gitlens:enabled",
|
"when": "gitlens:enabled",
|
||||||
"group": "1_gitlens@1"
|
"group": "2_gitlens@1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.closeUnchangedFiles",
|
"command": "gitlens.closeUnchangedFiles",
|
||||||
"when": "gitlens:enabled",
|
"when": "gitlens:enabled",
|
||||||
"group": "1_gitlens@2"
|
"group": "2_gitlens@2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.externalDiff",
|
||||||
|
"when": "gitlens:enabled",
|
||||||
|
"group": "2_gitlens@3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.stashSave",
|
"command": "gitlens.stashSave",
|
||||||
"when": "gitlens:enabled",
|
"when": "gitlens:enabled",
|
||||||
"group": "2_gitlens@1"
|
"group": "3_gitlens@1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"scm/resourceState/context": [
|
"scm/resourceState/context": [
|
||||||
@@ -1503,6 +1549,11 @@
|
|||||||
"when": "gitlens:enabled && gitlens:hasRemotes",
|
"when": "gitlens:enabled && gitlens:hasRemotes",
|
||||||
"group": "navigation"
|
"group": "navigation"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.externalDiff",
|
||||||
|
"when": "gitlens:enabled",
|
||||||
|
"group": "navigation"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.diffWithRevision",
|
"command": "gitlens.diffWithRevision",
|
||||||
"when": "gitlens:enabled",
|
"when": "gitlens:enabled",
|
||||||
@@ -1737,9 +1788,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 +1937,7 @@
|
|||||||
{
|
{
|
||||||
"id": "gitlens.gitExplorer",
|
"id": "gitlens.gitExplorer",
|
||||||
"name": "GitLens",
|
"name": "GitLens",
|
||||||
"when": "gitlens:enabled"
|
"when": "gitlens:enabled && config.gitlens.gitExplorer.enabled"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1871,18 +1967,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!))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export * from './commands/diffWithNext';
|
|||||||
export * from './commands/diffWithPrevious';
|
export * from './commands/diffWithPrevious';
|
||||||
export * from './commands/diffWithRevision';
|
export * from './commands/diffWithRevision';
|
||||||
export * from './commands/diffWithWorking';
|
export * from './commands/diffWithWorking';
|
||||||
|
export * from './commands/externalDiff';
|
||||||
export * from './commands/openChangedFiles';
|
export * from './commands/openChangedFiles';
|
||||||
export * from './commands/openBranchesInRemote';
|
export * from './commands/openBranchesInRemote';
|
||||||
export * from './commands/openBranchInRemote';
|
export * from './commands/openBranchInRemote';
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export type Commands =
|
|||||||
'gitlens.diffWithRevision' |
|
'gitlens.diffWithRevision' |
|
||||||
'gitlens.diffWithWorking' |
|
'gitlens.diffWithWorking' |
|
||||||
'gitlens.diffLineWithWorking' |
|
'gitlens.diffLineWithWorking' |
|
||||||
|
'gitlens.externalDiff' |
|
||||||
'gitlens.openChangedFiles' |
|
'gitlens.openChangedFiles' |
|
||||||
'gitlens.openBranchesInRemote' |
|
'gitlens.openBranchesInRemote' |
|
||||||
'gitlens.openBranchInRemote' |
|
'gitlens.openBranchInRemote' |
|
||||||
@@ -61,6 +62,7 @@ export const Commands = {
|
|||||||
DiffWithRevision: 'gitlens.diffWithRevision' as Commands,
|
DiffWithRevision: 'gitlens.diffWithRevision' as Commands,
|
||||||
DiffWithWorking: 'gitlens.diffWithWorking' as Commands,
|
DiffWithWorking: 'gitlens.diffWithWorking' as Commands,
|
||||||
DiffLineWithWorking: 'gitlens.diffLineWithWorking' as Commands,
|
DiffLineWithWorking: 'gitlens.diffLineWithWorking' as Commands,
|
||||||
|
ExternalDiff: 'gitlens.externalDiff' as Commands,
|
||||||
OpenChangedFiles: 'gitlens.openChangedFiles' as Commands,
|
OpenChangedFiles: 'gitlens.openChangedFiles' as Commands,
|
||||||
OpenBranchesInRemote: 'gitlens.openBranchesInRemote' as Commands,
|
OpenBranchesInRemote: 'gitlens.openBranchesInRemote' as Commands,
|
||||||
OpenBranchInRemote: 'gitlens.openBranchInRemote' as Commands,
|
OpenBranchInRemote: 'gitlens.openBranchInRemote' as Commands,
|
||||||
|
|||||||
115
src/commands/externalDiff.ts
Normal file
115
src/commands/externalDiff.ts
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
'use strict';
|
||||||
|
import { commands, SourceControlResourceState, Uri, window } from 'vscode';
|
||||||
|
import { Command, Commands } from './common';
|
||||||
|
import { BuiltInCommands } from '../constants';
|
||||||
|
import { CommandContext } from '../commands';
|
||||||
|
import { GitService } from '../gitService';
|
||||||
|
import { Logger } from '../logger';
|
||||||
|
import { Messages } from '../messages';
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
INDEX_MODIFIED,
|
||||||
|
INDEX_ADDED,
|
||||||
|
INDEX_DELETED,
|
||||||
|
INDEX_RENAMED,
|
||||||
|
INDEX_COPIED,
|
||||||
|
|
||||||
|
MODIFIED,
|
||||||
|
DELETED,
|
||||||
|
UNTRACKED,
|
||||||
|
IGNORED,
|
||||||
|
|
||||||
|
ADDED_BY_US,
|
||||||
|
ADDED_BY_THEM,
|
||||||
|
DELETED_BY_US,
|
||||||
|
DELETED_BY_THEM,
|
||||||
|
BOTH_ADDED,
|
||||||
|
BOTH_DELETED,
|
||||||
|
BOTH_MODIFIED
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ResourceGroupType {
|
||||||
|
Merge,
|
||||||
|
Index,
|
||||||
|
WorkingTree
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Resource extends SourceControlResourceState {
|
||||||
|
readonly resourceGroupType: ResourceGroupType;
|
||||||
|
readonly type: Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExternalDiffFile {
|
||||||
|
constructor(public uri: Uri, public staged: boolean) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExternalDiffCommandArgs {
|
||||||
|
files?: ExternalDiffFile[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExternalDiffCommand extends Command {
|
||||||
|
constructor(private git: GitService) {
|
||||||
|
super(Commands.ExternalDiff);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async preExecute(context: CommandContext, args: ExternalDiffCommandArgs = {}): Promise<any> {
|
||||||
|
if (context.type === 'scm-states') {
|
||||||
|
args = { ...args };
|
||||||
|
args.files = context.scmResourceStates.map<ExternalDiffFile>((_: Resource) => new ExternalDiffFile(_.resourceUri, _.resourceGroupType === ResourceGroupType.Index));
|
||||||
|
|
||||||
|
return this.execute(args);
|
||||||
|
} else if (context.type === 'scm-groups') {
|
||||||
|
const isModified = (status: Status): boolean => status === Status.BOTH_MODIFIED || status === Status.INDEX_MODIFIED || status === Status.MODIFIED;
|
||||||
|
|
||||||
|
args = { ...args };
|
||||||
|
args.files = context.scmResourceGroups[0].resourceStates.filter((_: Resource) => isModified(_.type)).map<ExternalDiffFile>((_: Resource) => new ExternalDiffFile(_.resourceUri, _.resourceGroupType === ResourceGroupType.Index));
|
||||||
|
|
||||||
|
return this.execute(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.execute(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(args: ExternalDiffCommandArgs = {}) {
|
||||||
|
try {
|
||||||
|
const diffTool = await this.git.getConfig('diff.tool');
|
||||||
|
if (!diffTool) {
|
||||||
|
const result = await window.showWarningMessage(`Unable to open file compare because there is no Git diff tool configured`, 'View Git Docs');
|
||||||
|
if (!result) return undefined;
|
||||||
|
|
||||||
|
return commands.executeCommand(BuiltInCommands.Open, Uri.parse('https://git-scm.com/docs/git-config#git-config-difftool'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const repoPath = await this.git.getRepoPathFromUri(undefined);
|
||||||
|
if (!repoPath) return Messages.showNoRepositoryWarningMessage(`Unable to open changed files`);
|
||||||
|
|
||||||
|
if (!args.files) {
|
||||||
|
const status = await this.git.getStatusForRepo(repoPath);
|
||||||
|
if (status === undefined) return window.showWarningMessage(`Unable to open changed files`);
|
||||||
|
|
||||||
|
args.files = [];
|
||||||
|
|
||||||
|
for (const file of status.files) {
|
||||||
|
if (file.indexStatus === 'M') {
|
||||||
|
args.files.push(new ExternalDiffFile(file.Uri, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.workTreeStatus === 'M') {
|
||||||
|
args.files.push(new ExternalDiffFile(file.Uri, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const file of args.files) {
|
||||||
|
this.git.openDiffTool(repoPath, file.uri, file.staged);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
Logger.error(ex, 'ExternalDiffCommand');
|
||||||
|
return window.showErrorMessage(`Unable to open external diff. See output channel for more details`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
};
|
};
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
import { commands, ExtensionContext, extensions, languages, window, workspace } from 'vscode';
|
import { commands, ExtensionContext, extensions, languages, window, workspace } from 'vscode';
|
||||||
import { AnnotationController } from './annotations/annotationController';
|
import { AnnotationController } from './annotations/annotationController';
|
||||||
import { CloseUnchangedFilesCommand, OpenChangedFilesCommand } from './commands';
|
import { CloseUnchangedFilesCommand, OpenChangedFilesCommand } from './commands';
|
||||||
|
import { ExternalDiffCommand } from './commands';
|
||||||
import { OpenBranchesInRemoteCommand, OpenBranchInRemoteCommand, OpenCommitInRemoteCommand, OpenFileInRemoteCommand, OpenInRemoteCommand, OpenRepoInRemoteCommand } from './commands';
|
import { OpenBranchesInRemoteCommand, OpenBranchInRemoteCommand, OpenCommitInRemoteCommand, OpenFileInRemoteCommand, OpenInRemoteCommand, OpenRepoInRemoteCommand } from './commands';
|
||||||
import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands';
|
import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands';
|
||||||
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithRevisionCommand, DiffWithWorkingCommand } from './commands';
|
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithRevisionCommand, DiffWithWorkingCommand } from './commands';
|
||||||
@@ -16,7 +17,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 +72,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);
|
||||||
@@ -99,6 +100,7 @@ export async function activate(context: ExtensionContext) {
|
|||||||
context.subscriptions.push(commands.registerTextEditorCommand('gitlens.computingFileAnnotations', () => { }));
|
context.subscriptions.push(commands.registerTextEditorCommand('gitlens.computingFileAnnotations', () => { }));
|
||||||
|
|
||||||
context.subscriptions.push(new CloseUnchangedFilesCommand(git));
|
context.subscriptions.push(new CloseUnchangedFilesCommand(git));
|
||||||
|
context.subscriptions.push(new ExternalDiffCommand(git));
|
||||||
context.subscriptions.push(new OpenChangedFilesCommand(git));
|
context.subscriptions.push(new OpenChangedFilesCommand(git));
|
||||||
context.subscriptions.push(new CopyMessageToClipboardCommand(git));
|
context.subscriptions.push(new CopyMessageToClipboardCommand(git));
|
||||||
context.subscriptions.push(new CopyShaToClipboardCommand(git));
|
context.subscriptions.push(new CopyShaToClipboardCommand(git));
|
||||||
@@ -148,7 +150,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 +276,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) {
|
||||||
@@ -268,6 +276,17 @@ export class Git {
|
|||||||
return gitCommand({ cwd: repoPath }, ...params);
|
return gitCommand({ cwd: repoPath }, ...params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static difftool_fileDiff(repoPath: string, fileName: string, staged: boolean) {
|
||||||
|
const params = [`difftool`, `--no-prompt`];
|
||||||
|
if (staged) {
|
||||||
|
params.push('--staged');
|
||||||
|
}
|
||||||
|
params.push('--');
|
||||||
|
params.push(fileName);
|
||||||
|
|
||||||
|
return gitCommand({ cwd: repoPath }, ...params);
|
||||||
|
}
|
||||||
|
|
||||||
static log(repoPath: string, sha?: string, maxCount?: number, reverse: boolean = false) {
|
static log(repoPath: string, sha?: string, maxCount?: number, reverse: boolean = false) {
|
||||||
const params = [...defaultLogParams, `-m`];
|
const params = [...defaultLogParams, `-m`];
|
||||||
if (maxCount && !reverse) {
|
if (maxCount && !reverse) {
|
||||||
@@ -332,6 +351,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 +431,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 {
|
||||||
@@ -25,13 +26,19 @@ export interface IGitStatusFile {
|
|||||||
status: GitStatusFileStatus;
|
status: GitStatusFileStatus;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
originalFileName?: string;
|
originalFileName?: string;
|
||||||
|
workTreeStatus: GitStatusFileStatus;
|
||||||
|
indexStatus: GitStatusFileStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGitStatusFileWithCommit extends IGitStatusFile {
|
||||||
|
commit: GitLogCommit;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GitStatusFile implements IGitStatusFile {
|
export class GitStatusFile implements IGitStatusFile {
|
||||||
|
|
||||||
originalFileName?: string;
|
originalFileName?: string;
|
||||||
|
|
||||||
constructor(public repoPath: string, public status: GitStatusFileStatus, public fileName: string, public staged: boolean, originalFileName?: string) {
|
constructor(public repoPath: string, public status: GitStatusFileStatus, public workTreeStatus: GitStatusFileStatus, public indexStatus: GitStatusFileStatus, public fileName: string, public staged: boolean, originalFileName?: string) {
|
||||||
this.originalFileName = originalFileName;
|
this.originalFileName = originalFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,15 +58,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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,8 @@ interface FileStatusEntry {
|
|||||||
status: GitStatusFileStatus;
|
status: GitStatusFileStatus;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
originalFileName: string;
|
originalFileName: string;
|
||||||
|
workTreeStatus: GitStatusFileStatus;
|
||||||
|
indexStatus: GitStatusFileStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
const aheadStatusV1Regex = /(?:ahead ([0-9]+))/;
|
const aheadStatusV1Regex = /(?:ahead ([0-9]+))/;
|
||||||
@@ -69,7 +71,7 @@ export class GitStatusParser {
|
|||||||
else {
|
else {
|
||||||
entry = this._parseFileEntry(rawStatus, fileName);
|
entry = this._parseFileEntry(rawStatus, fileName);
|
||||||
}
|
}
|
||||||
status.files.push(new GitStatusFile(repoPath, entry.status, entry.fileName, entry.staged, entry.originalFileName));
|
status.files.push(new GitStatusFile(repoPath, entry.status, entry.workTreeStatus, entry.indexStatus, entry.fileName, entry.staged, entry.originalFileName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,7 +119,7 @@ export class GitStatusParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (entry !== undefined) {
|
if (entry !== undefined) {
|
||||||
status.files.push(new GitStatusFile(repoPath, entry.status, entry.fileName, entry.staged, entry.originalFileName));
|
status.files.push(new GitStatusFile(repoPath, entry.status, entry.workTreeStatus, entry.indexStatus, entry.fileName, entry.staged, entry.originalFileName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,7 +133,9 @@ export class GitStatusParser {
|
|||||||
status: (indexStatus || workTreeStatus || '?') as GitStatusFileStatus,
|
status: (indexStatus || workTreeStatus || '?') as GitStatusFileStatus,
|
||||||
fileName: fileName,
|
fileName: fileName,
|
||||||
originalFileName: originalFileName,
|
originalFileName: originalFileName,
|
||||||
staged: !!indexStatus
|
staged: !!indexStatus,
|
||||||
|
indexStatus: indexStatus,
|
||||||
|
workTreeStatus: workTreeStatus
|
||||||
} as FileStatusEntry;
|
} as FileStatusEntry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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}')`);
|
||||||
|
|
||||||
@@ -1021,12 +1036,45 @@ export class GitService extends Disposable {
|
|||||||
return !!result;
|
return !!result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openDiffTool(repoPath: string, uri: Uri, staged: boolean) {
|
||||||
|
Logger.log(`openDiffTool('${repoPath}', '${uri}', ${staged})`);
|
||||||
|
|
||||||
|
return Git.difftool_fileDiff(repoPath, uri.fsPath, staged);
|
||||||
|
}
|
||||||
|
|
||||||
openDirectoryDiff(repoPath: string, sha1: string, sha2?: string) {
|
openDirectoryDiff(repoPath: string, sha1: string, sha2?: string) {
|
||||||
Logger.log(`openDirectoryDiff('${repoPath}', ${sha1}, ${sha2})`);
|
Logger.log(`openDirectoryDiff('${repoPath}', ${sha1}, ${sha2})`);
|
||||||
|
|
||||||
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.collapsibleState = TreeItemCollapsibleState.None;
|
|
||||||
item.command = this.getCommand();
|
|
||||||
const resourceType: ResourceType = 'gitlens:commit-file';
|
|
||||||
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.contextValue = this.resourceType;
|
||||||
|
|
||||||
item.iconPath = {
|
item.iconPath = {
|
||||||
dark: this.context.asAbsolutePath('images/dark/icon-commit.svg'),
|
dark: this.context.asAbsolutePath('images/dark/icon-commit.svg'),
|
||||||
light: this.context.asAbsolutePath('images/light/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,7 +9,11 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,7 +21,7 @@ export class FileHistoryNode extends 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,14 +114,20 @@ 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[]) {
|
||||||
@@ -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,7 +8,11 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,7 +12,11 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,7 +9,11 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
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,13 +1,20 @@
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,32 +22,55 @@ export class StatusNode extends 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _status: GitStatus | undefined;
|
||||||
|
|
||||||
async getTreeItem(): Promise < TreeItem > {
|
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