Compare commits
170 Commits
v4.0.0-bet
...
v5.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
821fa1cc3c | ||
|
|
3835193118 | ||
|
|
d161084ccd | ||
|
|
b9c4468cf7 | ||
|
|
4bacb6fbff | ||
|
|
c98755cc87 | ||
|
|
6d759daaad | ||
|
|
5a42ce4ed4 | ||
|
|
a5af318269 | ||
|
|
5a2bd02402 | ||
|
|
2bba14260f | ||
|
|
22378d5f25 | ||
|
|
d1d1db18e2 | ||
|
|
3dab90709b | ||
|
|
f58d085352 | ||
|
|
a77bb36ee3 | ||
|
|
5cc9365fa1 | ||
|
|
a587108cab | ||
|
|
1b4350e476 | ||
|
|
586785cfb8 | ||
|
|
04df931902 | ||
|
|
d31eb25451 | ||
|
|
35b16a78ba | ||
|
|
825b9661fb | ||
|
|
9782a81e46 | ||
|
|
ed58dc3b49 | ||
|
|
480dcb95fb | ||
|
|
ef41176ea7 | ||
|
|
bcd83566a1 | ||
|
|
ca089777db | ||
|
|
a255eea949 | ||
|
|
1ffb42a090 | ||
|
|
bc2f1b192a | ||
|
|
7d99624068 | ||
|
|
c258d04381 | ||
|
|
4c4926c8b5 | ||
|
|
6255b26fd2 | ||
|
|
4db2b116c2 | ||
|
|
b40579f4b2 | ||
|
|
15c981ba06 | ||
|
|
c2f78a7242 | ||
|
|
d3013a03d4 | ||
|
|
323148d9ab | ||
|
|
03fefaad3b | ||
|
|
f11c00ceda | ||
|
|
0e338308c6 | ||
|
|
e39bd5dad3 | ||
|
|
00cdd2e375 | ||
|
|
787e0e6aa1 | ||
|
|
46554e93f3 | ||
|
|
4160234962 | ||
|
|
0bd31099ce | ||
|
|
a272f8f6a4 | ||
|
|
e10d7eb648 | ||
|
|
9d9c3181f7 | ||
|
|
68a42fd0c7 | ||
|
|
28db0bf2f5 | ||
|
|
5f85388db7 | ||
|
|
4d167c3cdd | ||
|
|
4826e99986 | ||
|
|
459a8dce1d | ||
|
|
2650a84105 | ||
|
|
a2cd57cf95 | ||
|
|
62a148c156 | ||
|
|
be682c5def | ||
|
|
12d310bdf1 | ||
|
|
af4f433972 | ||
|
|
52275215fe | ||
|
|
71edb1b99a | ||
|
|
7f3fdb48fd | ||
|
|
81a9092455 | ||
|
|
d288985c26 | ||
|
|
b81d873a34 | ||
|
|
c96a659e9e | ||
|
|
d193676502 | ||
|
|
6410a274d5 | ||
|
|
272bb4db45 | ||
|
|
7fbee675f0 | ||
|
|
bc21272409 | ||
|
|
da450a614d | ||
|
|
0e4664bab1 | ||
|
|
1b4800571d | ||
|
|
c812a56eac | ||
|
|
3081632815 | ||
|
|
3d32d86998 | ||
|
|
0a9559f5a5 | ||
|
|
8693c87523 | ||
|
|
54c077eb8d | ||
|
|
ee124b5325 | ||
|
|
63a0646c8f | ||
|
|
cb360c5a3d | ||
|
|
3bf5f23c66 | ||
|
|
1751987868 | ||
|
|
9179b70875 | ||
|
|
93ea2c7145 | ||
|
|
f9275a8e1a | ||
|
|
ceb9c5e126 | ||
|
|
e42af14b11 | ||
|
|
bcfa2cb4d7 | ||
|
|
16963ee054 | ||
|
|
db1bdaa778 | ||
|
|
ccac13088c | ||
|
|
899a69357c | ||
|
|
c78c3c7525 | ||
|
|
1cbf0b2c73 | ||
|
|
191a158b3b | ||
|
|
f553980146 | ||
|
|
a306ba2b98 | ||
|
|
1cdd94cb46 | ||
|
|
12caa017a9 | ||
|
|
ad790b274b | ||
|
|
55ef004332 | ||
|
|
329d5a496d | ||
|
|
6b4642d8c8 | ||
|
|
45aa9b17ac | ||
|
|
9b908e27ba | ||
|
|
0eb6026a82 | ||
|
|
5823457ea0 | ||
|
|
64b929fd5b | ||
|
|
a618b7efe6 | ||
|
|
a2903ce4a9 | ||
|
|
12debe583c | ||
|
|
0160e064be | ||
|
|
76961d6697 | ||
|
|
cc827f4711 | ||
|
|
6f3441a9e7 | ||
|
|
64ae82075e | ||
|
|
5624567daa | ||
|
|
c1c65127b7 | ||
|
|
ce98f02706 | ||
|
|
7f2ec30164 | ||
|
|
48a1ca704d | ||
|
|
23c7171d7f | ||
|
|
dd0b95498e | ||
|
|
e6316400f0 | ||
|
|
eeff31cf27 | ||
|
|
eb3b9ad6c9 | ||
|
|
10674124c8 | ||
|
|
e0f66247cf | ||
|
|
badd999db1 | ||
|
|
9ae4cc36a1 | ||
|
|
30bb4398a3 | ||
|
|
9c7a971e21 | ||
|
|
ba59fb29ad | ||
|
|
50ba3e1446 | ||
|
|
62e5ef6225 | ||
|
|
ed54d289dd | ||
|
|
2a8dafd9e9 | ||
|
|
016c561ead | ||
|
|
9cf86a41ec | ||
|
|
4eb1c3e36a | ||
|
|
95e0a6c71b | ||
|
|
35ca8106c9 | ||
|
|
948a75de79 | ||
|
|
4b0891b949 | ||
|
|
a9d94868e7 | ||
|
|
3c45c7e049 | ||
|
|
ba0d55d5d4 | ||
|
|
d2dc172042 | ||
|
|
e5e582d300 | ||
|
|
7c9e4b911c | ||
|
|
42fdf9f327 | ||
|
|
547d50fed6 | ||
|
|
6c33686335 | ||
|
|
28355d41b6 | ||
|
|
6e5bb2343e | ||
|
|
d01c592533 | ||
|
|
37e48ded2d | ||
|
|
e3e7605268 | ||
|
|
f16c3857e5 |
8
.vscode/settings.json
vendored
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"editor.insertSpaces": true,
|
"editor.insertSpaces": true,
|
||||||
"files.exclude": {
|
"files.exclude": {
|
||||||
"node_modules": true,
|
"**/node_modules": true,
|
||||||
"out": true
|
"**/out": true
|
||||||
},
|
},
|
||||||
"files.trimTrailingWhitespace": true,
|
"files.trimTrailingWhitespace": true,
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"node_modules": true,
|
"**/node_modules": true,
|
||||||
"out": true
|
"**/out": true
|
||||||
},
|
},
|
||||||
"javascript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": false,
|
"javascript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": false,
|
||||||
"typescript.tsdk": "./node_modules/typescript/lib" // we want to use the TS server from our node_modules folder to control its version
|
"typescript.tsdk": "./node_modules/typescript/lib" // we want to use the TS server from our node_modules folder to control its version
|
||||||
|
|||||||
25
.vscode/tasks.json
vendored
@@ -9,13 +9,18 @@
|
|||||||
// A task runner that calls a custom npm script that compiles the extension.
|
// A task runner that calls a custom npm script that compiles the extension.
|
||||||
{
|
{
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"showOutput": "silent",
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"taskName": "compile",
|
"label": "compile",
|
||||||
"command": "npm run compile --silent",
|
"type": "npm",
|
||||||
"isBuildCommand": true,
|
"script": "compile",
|
||||||
"isShellCommand": true,
|
"group": "build",
|
||||||
"problemMatcher": [
|
"problemMatcher": [
|
||||||
"$tsc",
|
"$tsc",
|
||||||
{
|
{
|
||||||
@@ -25,19 +30,17 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"taskName": "lint",
|
"type": "npm",
|
||||||
"command": "npm run lint --silent",
|
"script": "lint",
|
||||||
"isShellCommand": true,
|
|
||||||
"problemMatcher": {
|
"problemMatcher": {
|
||||||
"base": "$tslint5",
|
"base": "$tslint5",
|
||||||
"fileLocation": "relative"
|
"fileLocation": "relative"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"taskName": "watch",
|
"type": "npm",
|
||||||
"command": "npm run watch --silent",
|
"script": "watch",
|
||||||
"isBackground": true,
|
"isBackground": true,
|
||||||
"isShellCommand": true,
|
|
||||||
"problemMatcher": "$tsc-watch"
|
"problemMatcher": "$tsc-watch"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
!images/*.svg
|
!images/dark/**
|
||||||
|
!images/light/**
|
||||||
|
!images/gitlens-icon.png
|
||||||
images/**
|
images/**
|
||||||
.vscode/**
|
.vscode/**
|
||||||
.vscode-test/**
|
.vscode-test/**
|
||||||
|
|||||||
260
CHANGELOG.md
@@ -4,6 +4,264 @@ 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.0.0-alpha.2] - 2017-09-03
|
||||||
|
### Added
|
||||||
|
- Adds an all-new `GitLens` custom view to the Explorer activity
|
||||||
|
|
||||||
|
- `Repository View` - provides a full repository explorer
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- `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 indicator dots on the repository icon which denote the following:
|
||||||
|
- `None` - up-to-date with the upstream
|
||||||
|
- `Green` - ahead of the upstream
|
||||||
|
- `Red` - 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 a context menu with `Open Repository in Remote`, and `Refresh` commands
|
||||||
|
|
||||||
|
- `Branches` node — provides a list of the local branches
|
||||||
|
- Indicates which branch is the current branch and optionally shows the remote tracking branch
|
||||||
|
- Expand each branch to easily see its revision (commit) history
|
||||||
|
- Expand each revision (commit) to quickly see the set of files changed, complete with status indicators for adds, changes, renames, and deletes
|
||||||
|
- Provides a context menu on each changed file with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Open Revision in Remote`, `Apply Changes`, and `Show Commit File Details` commands
|
||||||
|
- Provides a context menu on each revision (commit) with `Open Commit in Remote`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`, `Open Files`, `Open Revisions`, `Show Commit Details`, and `Refresh` commands
|
||||||
|
- Provides a context menu on each branch with `Open Branch in Remote`, and `Refresh` commands
|
||||||
|
- Provides a context menu with `Open Branches in Remote`, and `Refresh` commands
|
||||||
|
|
||||||
|
- `Remotes` node — provides a list of remotes
|
||||||
|
- Expand each remote to see its list of branches
|
||||||
|
- Expand each branch to easily see its revision (commit) history
|
||||||
|
- Expand each revision (commit) to quickly see the set of files changed, complete with status indicators for adds, changes, renames, and deletes
|
||||||
|
- Provides a context menu on each changed file with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Open Revision in Remote`, `Apply Changes`, and `Show Commit File Details` commands
|
||||||
|
- Provides a context menu on each revision (commit) with `Open Commit in Remote`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`, `Open Files`, `Open Revisions`, `Show Commit Details`, and `Refresh` commands
|
||||||
|
- Provides a context menu on each remote with `Open Branches in Remote`, `Open Repository in Remote`, and `Refresh` commands
|
||||||
|
- Provides a context menu with a `Refresh` command
|
||||||
|
|
||||||
|
- `Stashes` node — provides a list of stashed changes
|
||||||
|
- Expand each stash to quickly see the set of files stashed, complete with status indicators for adds, changes, renames, and deletes
|
||||||
|
- Provides a context menu with `Stash Changes`, and `Refresh` commands
|
||||||
|
- Provides a context menu on each stash with `Apply Stashed Changes` (confirmation required), `Delete Stashed Changes` (confirmation required), `Copy Commit Message to Clipboard`, `Open Files`, `Open Revisions`, and `Refresh` commands
|
||||||
|
- Provides a context menu on each stashed file with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Apply Changes`, and `Show File History` commands
|
||||||
|
|
||||||
|
- `History View` - provides the revision history of the active file
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Automatically updates to track the active editor
|
||||||
|
- Provides a context menu with `Open File`, `Open File in Remote`, and `Refresh` commands
|
||||||
|
- Provides a context menu on each revision (commit) with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Open Revision in Remote`, `Apply Changes`, and `Show Commit File Details` commands
|
||||||
|
|
||||||
|
- Quickly switch between views using the `Switch to Repository View` or `Switch to History View` commands
|
||||||
|
- Provides toolbar commands to `Search Commits`, `Switch to Repository View` or `Switch to History View`, and `Refresh`
|
||||||
|
|
||||||
|
- Adds `Open Branches in Remote` command (`gitlens.openBranchesInRemote`) - opens the branches in the supported remote service
|
||||||
|
- Adds `Stash Changes` command (`gitlens.stashSave`) to the source control group context menu -- can now stash a group of files
|
||||||
|
- Adds `Stash Changes` command (`gitlens.stashSave`) to the source control resource context menu -- can now stash individual files (works with multi-select too!)
|
||||||
|
- Adds `gitlens.gitExplorer.view` setting to specify the starting view (mode) of the `GitLens` custom view
|
||||||
|
- Adds `gitlens.gitExplorer.showTrackingBranch` setting to specify whether or not to show the tracking branch when displaying local branches in the `GitLens` custom view
|
||||||
|
- Adds `gitlens.gitExplorer.commitFormat` setting to specify the format of committed changes in the `GitLens` custom view
|
||||||
|
- Adds `gitlens.gitExplorer.commitFileFormat` setting to specify the format of a committed file in the `GitLens` custom view
|
||||||
|
- Adds `gitlens.gitExplorer.stashFormat` setting to specify the format of stashed changes in the `GitLens` custom view
|
||||||
|
- Adds `gitlens.gitExplorer.stashFileFormat` setting to specify the format of a stashed file in the `GitLens` custom view
|
||||||
|
- Adds `${filePath}` token to file formatting settings
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Changes `Show Stashed Changes` option icon in repository status quick pick menu to match the `GitLens` custom view
|
||||||
|
- Changes `Stash Changes` option icon in stashed changes quick pick menu to a plus (+)
|
||||||
|
- Renames `Compare File with Previous` command (`gitlens.diffWithPrevious`) to `Compare File with Previous Revision`
|
||||||
|
- Renames `Compare File with Next Commit` command (`gitlens.diffWithNext`) to `Compare File with Next Revision`
|
||||||
|
- Renames `Compare File with Working Tree` command (`gitlens.diffWithWorking`) to `Compare File with Working Revision`
|
||||||
|
- Renames `Compare Line Commit with Previous` command (`gitlens.diffLineWithPrevious`) to `Compare Line Revision with Previous`
|
||||||
|
- Renames `Compare Line Commit with Working Tree` command (`gitlens.diffLineWithWorking`) to `Compare Line Revision with Working`
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Removes `Git Stashes` custom view view - as it's functionality has been folded into the new `GitLens` custom view
|
||||||
|
- Removes `gitlens.stashExplorer.stashFormat` setting
|
||||||
|
- Removes `gitlens.stashExplorer.stashFileFormat` setting
|
||||||
|
- Removes `Stash Unstaged Changes` option from stashed changes quick pick menu -- didn't work as intended
|
||||||
|
- Removes the seeding of the commit search command from the clipboard
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixes an issue where remote branches couldn't be opened properly in their remote service
|
||||||
|
|
||||||
|
## [4.4.3] - 2017-08-30
|
||||||
|
## Fixed
|
||||||
|
- Fixes [#135](https://github.com/eamodio/vscode-gitlens/issues/135) - Full-width characters break gutter annotations (really this time)
|
||||||
|
|
||||||
|
## [4.4.2] - 2017-08-29
|
||||||
|
## Fixed
|
||||||
|
- Fixes [#135](https://github.com/eamodio/vscode-gitlens/issues/135) - Full-width characters break gutter annotations
|
||||||
|
|
||||||
|
## [4.4.1] - 2017-08-23
|
||||||
|
## Fixed
|
||||||
|
- Fixes [#114](https://github.com/eamodio/vscode-gitlens/issues/114) - Stylus files makes code lens freak out
|
||||||
|
|
||||||
|
## [4.4.0] - 2017-08-18
|
||||||
|
## Added
|
||||||
|
- Adds a progress indicator to the `Toggle File Blame Annotations` command (`gitlens.toggleFileBlame`) icon -- pulses while annotations are computed
|
||||||
|
- Adds an active state to the `Toggle File Blame Annotations` command (`gitlens.toggleFileBlame`) icon -- turns orange while the annotations are visible
|
||||||
|
- Adds automatic disabling of the current line blame annotations when starting a debug session and will restore them when the debug session ends -- can still be manually toggled via the `Toggle Line Blame Annotations` command (`gitlens.toggleLineBlame`)
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
- Changes chat links from Gitter to [Slack](https://join.slack.com/t/vscode-gitlens/shared_invite/MjIxOTgxNDE3NzM0LTE1MDE2Nzk1MTgtMjkwMmZjMzcxNQ)
|
||||||
|
- Changes the look of the line separators on the gutter blame annotations
|
||||||
|
- Changes the `gitlens.advanced.toggleWhitespace.enabled` configuration setting to default to `false` -- thanks to the awesome work in vscode by Alexandru Dima ([@alexandrudima](https://github.com/alexandrudima)) this is no longer required!
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
- Removes unneeded `gitlens.stashExplorer.enabled` configuration setting since users can add or remove custom views natively now
|
||||||
|
- Removes unneeded `Toggle Git Stashes Explorer` command (`gitlens.stashExplorer.toggle`) since users can add or remove custom views natively now
|
||||||
|
- Removes the `gitlens.theme.annotations.file.hover.separateLines` configuration setting
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- Fixes jumpiness when opening a diff to a certain line
|
||||||
|
|
||||||
|
## [4.3.3] - 2017-07-28
|
||||||
|
## Added
|
||||||
|
- Adds progress indicator for when computing annotations takes a while
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
- Optimizes performance of the providing blame annotations, especially for large files (saw a 3.5x improvement on some files)
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- Fixes [#107](https://github.com/eamodio/vscode-gitlens/issues/107) - Double-byte characters break blame layout (still requires proper font support)
|
||||||
|
|
||||||
|
## [4.3.2] - 2017-07-20
|
||||||
|
## Fixed
|
||||||
|
- Fixes [#118](https://github.com/eamodio/vscode-gitlens/issues/118) - GitLens stopped working on latest insiders build -- thanks to [PR #121](https://github.com/eamodio/vscode-gitlens/pull/121) by Johannes Rieken ([@jrieken](https://github.com/jrieken))
|
||||||
|
|
||||||
|
## [4.3.1] - 2017-07-03
|
||||||
|
## Added
|
||||||
|
- Adds `gitlens.stashExplorer.enabled` setting to specify whether or not to show the `Git Stashes` custom view
|
||||||
|
- Adds `Toggle Git Stashes Explorer` command (`gitlens.stashExplorer.toggle`) - toggles the `Git Stashes` custom view on and off
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
- Hides the `Git Stashes` custom view by default
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- Fixes [#108](https://github.com/eamodio/vscode-gitlens/issues/108) - Option to remove stash explorer from the main explorer?
|
||||||
|
|
||||||
|
## [4.3.0] - 2017-07-03
|
||||||
|
## Added
|
||||||
|
- Adds `Git Stashes` custom view to the Explorer activity
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Shows all of the stashed changes in the repository
|
||||||
|
- Provides toolbar buttons to `Stash Changes` and `Refresh`
|
||||||
|
- Provides a context menu with `Apply Stashed Changes` and `Delete Stashed Changes` commands - both require a confirmation
|
||||||
|
- Expand each stash to quickly see the set of files changed, complete with status indicators for adds, changes, renames, and deletes
|
||||||
|
- Provides a context menu with `Open Changes`, `Open File`, `Open Stashed File`, `Open File in Remote`, and `Compare File with Working Tree` commands
|
||||||
|
|
||||||
|
## [4.2.0] - 2017-06-27
|
||||||
|
## Added
|
||||||
|
- Adds `Compare File with Revision...` command (`gitlens.diffWithRevision`) - compare the active file with the selected revision of the same file
|
||||||
|
- Adds `Open Changed Files` command (`gitlens.openChangedFiles`) to the source control group context menu
|
||||||
|
- Adds `Close Unchanged Files` command (`gitlens.closeUnchangedFiles`) to the source control group context menu
|
||||||
|
- Adds `Open File in Remote` command (`gitlens.openFileInRemote`) to the source control resource context menu
|
||||||
|
- Adds `Compare File with Revision...` command (`gitlens.diffWithRevision`) to the source control resource context menu
|
||||||
|
- Adds `Show File History` command (`gitlens.showQuickFileHistory`) to the source control resource context menu
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
- Renames `Compare File with...` command to `Compare File with Branch...`
|
||||||
|
- Renames `Open Line Commit in Remote` command to `Open Commit in Remote`
|
||||||
|
- Renames `Show Line Commit Details` command to `Show Commit File Details`
|
||||||
|
- Updates the description of `gitlens.blame.line.enabled` to be clearer about its behavior
|
||||||
|
- Updates the description of `gitlens.codeLens.enabled` to be clearer about its behavior
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixes [#103](https://github.com/eamodio/vscode-gitlens/issues/103) - Toggle file blame annotations disables line blame annotations if line blame annotations are off by default
|
||||||
|
- Fixes another infinite loop in the `Close Unchanged Files` command
|
||||||
|
|
||||||
|
## [4.1.4] - 2017-06-25
|
||||||
|
## Changed
|
||||||
|
- Optimizes performance of the `Compare with Previous` commands - also avoids trying to focus a line if we don't have one
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixes `changes` (diff) hover not showing the correct previous line (for real this time)
|
||||||
|
- Attempts to fix [#99](https://github.com/eamodio/vscode-gitlens/issues/99) - undo/redo spawns too many git processes
|
||||||
|
|
||||||
|
## [4.1.3] - 2017-06-20
|
||||||
|
### Fixed
|
||||||
|
- Fixes `changes` (diff) hover not showing the correct previous line when showing recent changes annotations of the whole-file
|
||||||
|
|
||||||
|
## [4.1.2] - 2017-06-15
|
||||||
|
### Fixed
|
||||||
|
- Fixes [#96](https://github.com/eamodio/vscode-gitlens/issues/96) - External diff command can be unintentionally triggered
|
||||||
|
|
||||||
|
## [4.1.1] - 2017-06-13
|
||||||
|
### Added
|
||||||
|
- Adds an `alt` command to the `Toggle File Blame Annotations` command button, which when you hold down `alt` and click it will execute the `Toggle Recent File Changes Annotations` command instead
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixes missing `Toggle File Blame Annotations` command icon
|
||||||
|
|
||||||
|
## [4.1.0] - 2017-06-13
|
||||||
|
### Added
|
||||||
|
- Adds all-new recent changes annotations of the whole-file - annotates and highlights all of lines changed in the most recent commit
|
||||||
|
- Can customize the [layout](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens#file-recent-changes-annotation-settings), as well as the [theme](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens#theme-settings)
|
||||||
|
- Adds `Toggle Recent File Changes Annotations` command (`gitlens.toggleFileRecentChanges`) - toggles the recent changes annotations on and off
|
||||||
|
- Adds ability to press `Escape` to quickly toggle any whole-file annotations off
|
||||||
|
- Improves performance
|
||||||
|
- Optimized git output parsing to increase speed and dramatically reduce memory usage
|
||||||
|
- Defers diff chunk parsing until it is actually required
|
||||||
|
- Adds `gitlens.defaultDateFormat` setting to specify how all absolute dates will be formatted by default
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixes excessive memory usage when parsing diffs
|
||||||
|
- Fixes extra newline in multiline commit messages
|
||||||
|
- Fixes (again) [#33](https://github.com/eamodio/vscode-gitlens/issues/33) - Commit messages can causes markdown formatting in hovers
|
||||||
|
|
||||||
|
## [4.0.1] - 2017-06-09
|
||||||
|
### Fixed
|
||||||
|
- Fixes [#87](https://github.com/eamodio/vscode-gitlens/issues/87) - Can't open files in remote when using git@ urls (ssh)
|
||||||
|
|
||||||
|
## [4.0.0] - 2017-06-09
|
||||||
|
### Added
|
||||||
|
- Adds all-new, beautiful, highly customizable and themeable, file blame annotations
|
||||||
|
- Can now fully customize the [layout and content](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens#file-blame-annotation-settings), as well as the [theme](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens#theme-settings)
|
||||||
|
- Adds all-new configurability and themeability to the current line blame annotations
|
||||||
|
- Can now fully customize the [layout and content](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens#line-blame-annotation-settings), as well as the [theme](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens#theme-settings)
|
||||||
|
- Adds all-new configurability to the status bar blame information
|
||||||
|
- Can now fully customize the [layout and content](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens#status-bar-settings)
|
||||||
|
- Adds all-new [configurability](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens#advanced-settings) over which commands are added to which menus via the `gitlens.advanced.menus` setting
|
||||||
|
- Adds better [configurability](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens#code-lens-settings) over where Git code lens will be shown -- both by default and per language
|
||||||
|
- Adds an all-new `changes` (diff) hover annotation to the current line - provides instant access to the line's previous version
|
||||||
|
- Adds `Toggle Line Blame Annotations` command (`gitlens.toggleLineBlame`) - toggles the current line blame annotations on and off
|
||||||
|
- Adds `Show Line Blame Annotations` command (`gitlens.showLineBlame`) - shows the current line blame annotations
|
||||||
|
- Adds `Toggle File Blame Annotations` command (`gitlens.toggleFileBlame`) - toggles the file blame annotations on and off
|
||||||
|
- Adds `Show File Blame Annotations` command (`gitlens.showFileBlame`) - shows the file blame annotations
|
||||||
|
- Adds `Open File in Remote` command (`gitlens.openFileInRemote`) to the `editor/title` context menu
|
||||||
|
- Adds `Open Repo in Remote` command (`gitlens.openRepoInRemote`) to the `editor/title` context menu
|
||||||
|
- Adds `gitlens.strings.*` settings to allow for the customization of certain strings displayed
|
||||||
|
- Adds `gitlens.theme.*` settings to allow for the theming of certain elements
|
||||||
|
- Adds `gitlens.advanced.telemetry.enabled` settings to explicitly opt-in or out of telemetry, but still ultimately honors the `telemetry.enableTelemetry` setting
|
||||||
|
- Adds ability to suppress most warning messages - which can be re-enabled using the `Reset Suppressed Warnings` command (`gitlens.resetSuppressedWarnings`)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- (BREAKING) Almost all of the GitLens settings have either been renamed, removed, or otherwise changed - see the [README](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens#extension-settings)`
|
||||||
|
- Changes the positioning of the Git code lens to try to be at the end of any other code lens on the same line
|
||||||
|
- Changes the position of the `Open File in Remote` command (`gitlens.openFileInRemote`) in the context menus - now in the `navigation` group
|
||||||
|
- Changes the `Toggle Git Code Lens` command (`gitlens.toggleCodeLens`) to always toggle the Git code lens on and off
|
||||||
|
- Changes the default of `gitlens.advanced.toggleWhitespace.enabled` back to `true`, but automatically disables whitespace toggling if whitespace rendering is not on
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Removes the on-demand `trailing` file blame annotations -- didn't work out and just ended up with a ton of visual noise
|
||||||
|
- Removes `Toggle Blame Annotations` command (`gitlens.toggleBlame`) - replaced by the `Toggle File Blame Annotations` command (`gitlens.toggleFileBlame`)
|
||||||
|
- Removes `Show Blame Annotations` command (`gitlens.showBlame`) - replaced by the `Show File Blame Annotations` command (`gitlens.showFileBlame`)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixes [#81](https://github.com/eamodio/vscode-gitlens/issues/81) - Current line annotation feels too sticky
|
||||||
|
- Fixes [#83](https://github.com/eamodio/vscode-gitlens/issues/83) - Calling "close unchanged files" results in no new files being openable
|
||||||
|
- Fixes issues with the zone.js monkey patching done by application insights (telemetry) - disables all the monkey patching
|
||||||
|
- Fixes issue with `Open Branch in Remote` & `Open Repository in Remote` not showing when there are no open editors
|
||||||
|
|
||||||
|
## [3.6.1] - 2017-06-07
|
||||||
|
### Fixed
|
||||||
|
- Fixes issues with the zone.js monkey patching done by application insights (telemetry) - disables all the monkey patching
|
||||||
|
|
||||||
## [3.6.0] - 2017-06-02
|
## [3.6.0] - 2017-06-02
|
||||||
### Added
|
### Added
|
||||||
- Adds diff information (the line's previous version) into the active line hover
|
- Adds diff information (the line's previous version) into the active line hover
|
||||||
@@ -96,7 +354,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
|
|||||||
|
|
||||||
## [3.4.5] - 2017-04-13
|
## [3.4.5] - 2017-04-13
|
||||||
### Added
|
### Added
|
||||||
- Completely overhauls the [GitLens documentation](https://github.com/eamodio/vscode-gitlens/blob/master/README.md) and messaging -- make sure to check it out to see all the powerful features GitLen provides!
|
- Completely overhauls the [GitLens documentation](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) and messaging -- make sure to check it out to see all the powerful features GitLen provides!
|
||||||
- Adds `gitlens.blame.annotation.activeLineDarkColor` & `gitlens.blame.annotation.activeLineLightColor` settings to control the colors of the active line blame annotation
|
- Adds `gitlens.blame.annotation.activeLineDarkColor` & `gitlens.blame.annotation.activeLineLightColor` settings to control the colors of the active line blame annotation
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
348
README.md
@@ -1,94 +1,179 @@
|
|||||||
[](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://gitter.im/vscode-gitlens/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://join.slack.com/t/vscode-gitlens/shared_invite/MjIxOTgxNDE3NzM0LTE1MDE2Nzk1MTgtMjkwMmZjMzcxNQ)
|
||||||
|
|
||||||
# GitLens
|
# GitLens
|
||||||
|
|
||||||
GitLens **supercharges** the built-in Visual Studio Code Git capabilities. It helps you to **visualize code authorship** at a glance via inline Git blame annotations and code lens, **seamlessly navigate and explore** the history of a file or branch, **gain valuable insights** via powerful comparision commands, and so much more.
|
GitLens **supercharges** the built-in Visual Studio Code Git capabilities. It helps you to **visualize code authorship** at a glance via Git blame annotations and code lens, **seamlessly navigate and explore** the history of a file or branch, **gain valuable insights** via powerful comparison commands, and so much more.
|
||||||
|
|
||||||
GitLens provides an unobtrusive blame annotation at the end of the selected line, a status bar item showing the commit author and date of the selected line, code lens showing the most recent commit and # of authors of the file and/or code block, and many commands for exploring commits and histories, comparing and navigating revisions, stash access, repository status, and more. GitLens is also [highly customizable](#extension-settings) to meet your specific needs — find code lens intrusive or the selected line blame annotation distracting — no problem, it is easy to [turn them off or change how they behave](#extension-settings).
|
GitLens provides an unobtrusive blame annotation at the end of the current line, a status bar item showing the commit information (author and date, by default) of the current line, code lens showing the most recent commit and # of authors of the file and/or code block, and many commands for exploring commits and histories, comparing and navigating revisions, stash access, repository status, and more. GitLens is also [highly customizable](#extension-settings) to meet your specific needs — find code lens intrusive or the current line blame annotation distracting — no problem, it is easy to [turn them off or change how they behave](#extension-settings).
|
||||||
|
|
||||||
## Previews
|
### Preview — featuring blame annotations, code lens, status bar details, quick pick menus for navigation and exploration, compare with previous, and more
|
||||||
#### Featuring code lens, whole file inline blame annotations, and navigation and exploration via quick pick menus
|

|
||||||

|
|
||||||
|
|
||||||
#### Featuring selected line blame annotation and hovers, status bar commit details, quick pick menus, compare with previous, and more
|
|
||||||

|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
#### Git Blame Annotations
|
### Git Blame Annotations
|
||||||
|
|
||||||
- Adds a **blame annotation** to the end of the selected line showing the commit id and message, with more details (including the line's previous version) in a hover popup ([optional](#extension-settings), on by default)
|
- Adds an unobtrusive, highly [customizable](#line-blame-annotation-settings) and [themeable](#theme-settings), **Git blame annotation** to the end of the current line ([optional](#line-blame-annotation-settings), on by default)
|
||||||
|
|
||||||
- Adds a `Toggle Blame Annotations` command (`gitlens.toggleBlame`) with a shortcut of `alt+b` to toggle **inline Git blame annotations** for a whole file with multiple styles — compact, expanded, and trailing
|

|
||||||
- Also adds a `Show Blame Annotations` command (`gitlens.showBlame`)
|
- Contains the author, date, and message of the line's most recent commit, by [default](#line-blame-annotation-settings)
|
||||||
|
- Adds a `details` hover annotation to the current line annotation, which provides more commit details ([optional](#line-blame-annotation-settings), on by default)
|
||||||
|
- Adds a `changes` (diff) hover annotation to the current line annotation, which provides **instant** access to the line's previous version ([optional](#line-blame-annotation-settings), on by default)
|
||||||
|
|
||||||
- Adds **author and date blame information** about the selected line to the **status bar** ([optional](#extension-settings), on by default)
|

|
||||||
- By default clicking on the status bar shows a **commit details quick pick menu** with commands for comparing, navigating and exploring commits, and more
|
|
||||||
|
|
||||||
- Provides [customizable](#extension-settings) click behavior of the status bar — choose between one of the following
|
- Adds on-demand, beautiful, highly [customizable](#file-blame-annotation-settings) and [themeable](#theme-settings), **Git blame annotations** of the whole file
|
||||||
- Toggle whole file blame annotations on and off
|
|
||||||
- Toggle code lens on and off — only available if [`"gitlens.codeLens.visibility": "ondemand"`](#extension-settings) is set
|

|
||||||
- Compare the file with the previous commit
|
- Choose between `gutter` (default) and `hover` [annotation styles](#file-blame-annotation-settings)
|
||||||
- Show a quick pick menu with details and commands for the commit
|
- Contains the commit message and date, by [default](#file-blame-annotation-settings)
|
||||||
|
- Adds a `details` hover annotation to the line's annotation, which provides more commit details ([optional](#file-blame-annotation-settings), on by default)
|
||||||
|
- Adds a `heatmap` (age) indicator to the gutter annotations (on right edge by [default](#file-blame-annotation-settings)), which provides an easy, at-a-glance way to tell the age of a line ([optional](#file-blame-annotation-settings), on by default)
|
||||||
|
- Indicator ranges from bright yellow (newer) to dark brown (older)
|
||||||
|
- Press `Escape` to quickly toggle the annotations off
|
||||||
|
|
||||||
|
- Adds [customizable](#status-bar-settings) **blame information** about the current line to the **status bar** ([optional](#status-bar-settings), on by default)
|
||||||
|
|
||||||
|

|
||||||
|
- Contains the commit author and date, by [default](#status-bar-settings)
|
||||||
|
- Clicking the status bar item will, by [default](#status-bar-settings), show a **commit details quick pick menu** with commands for comparing, navigating and exploring commits, and more
|
||||||
|
- Provides [customizable](#status-bar-settings) click behavior — choose between one of the following
|
||||||
|
- Toggle file blame annotations on and off
|
||||||
|
- Toggle code lens on and off
|
||||||
|
- Compare the line commit with the previous commit
|
||||||
|
- Compare the line commit with the working tree
|
||||||
|
- Show a quick pick menu with details and commands for the commit (default)
|
||||||
- Show a quick pick menu with file details and commands for the commit
|
- Show a quick pick menu with file details and commands for the commit
|
||||||
- Show a quick pick menu with the commit history of the file
|
- Show a quick pick menu with the commit history of the file
|
||||||
- Show a quick pick menu with the commit history of the current branch
|
- Show a quick pick menu with the commit history of the current branch
|
||||||
|
|
||||||
#### Git Code Lens
|
- Adds a `Toggle File Blame Annotations` command (`gitlens.toggleFileBlame`) with a shortcut of `alt+b` to toggle the file blame annotations on and off
|
||||||
|
- Also adds a `Show File Blame Annotations` command (`gitlens.showFileBlame`)
|
||||||
|
|
||||||
- Adds **code lens** to the top of the file and on code blocks ([optional](#extension-settings), on by default)
|
- Adds a `Toggle Line Blame Annotations` command (`gitlens.toggleLineBlame`) to toggle the current line blame annotations on and off
|
||||||
|
- Also adds a `Show Line Blame Annotations` command (`gitlens.showLineBlame`)
|
||||||
|
|
||||||
|
### Git Recent Changes Annotations
|
||||||
|
|
||||||
|
- Adds on-demand, [customizable](#file-recent-changes-annotation-settings) and [themeable](#theme-settings), **recent changes annotations** of the whole file
|
||||||
|
- Highlights all of lines changed in the most recent commit
|
||||||
|
- Adds a `details` hover annotation to each line, which provides more commit details ([optional](#file-blame-annotation-settings), on by default)
|
||||||
|
- Adds a `changes` (diff) hover annotation to each line, which provides **instant** access to the line's previous version ([optional](#file-recent-changes-annotation-settings), on by default)
|
||||||
|
- Press `Escape` to quickly toggle the annotations off
|
||||||
|
|
||||||
|
- Adds `Toggle Recent File Changes Annotations` command (`gitlens.toggleFileRecentChanges`) to toggle the recent changes annotations on and off
|
||||||
|
|
||||||
|
### Git Code Lens
|
||||||
|
|
||||||
|
- Adds **code lens** to the top of the file and on code blocks ([optional](#code-lens-settings), on by default)
|
||||||
|
|
||||||
|

|
||||||
- **Recent Change** — author and date of the most recent commit for the file or code block
|
- **Recent Change** — author and date of the most recent commit for the file or code block
|
||||||
- By default, clicking on the code lens shows a **commit file details quick pick menu** with commands for comparing, navigating and exploring commits, and more
|
- Clicking the code lens will, by [default](#code-lens-settings), show a **commit file details quick pick menu** with commands for comparing, navigating and exploring commits, and more
|
||||||
- **Authors** — number of authors of the file or code block and the most prominent author (if there is more than one)
|
- **Authors** — number of authors of the file or code block and the most prominent author (if there is more than one)
|
||||||
- By default, clicking on the code lens toggles the inline Git blame annotations on and off for the whole file
|
- Clicking the code lens will, by [default](#code-lens-settings), toggle the file Git blame annotations on and off of the whole file
|
||||||
- Will be hidden if the author of the most recent commit is also the only author of the file or block, to avoid duplicate information and reduce visual noise
|
- Will be hidden if the author of the most recent commit is also the only author of the file or block, to avoid duplicate information and reduce visual noise
|
||||||
|
|
||||||
- Provides [customizable](#extension-settings) click behavior for each code lens — choose between one of the following
|
- Provides [customizable](#code-lens-settings) click behavior for each code lens — choose between one of the following
|
||||||
- Toggle whole file blame annotations on and off
|
- Toggle file blame annotations on and off
|
||||||
- Compare the file with the previous commit
|
- Compare the commit with the previous commit
|
||||||
- Show a quick pick menu with details and commands for the commit
|
- Show a quick pick menu with details and commands for the commit
|
||||||
- Show a quick pick menu with file details and commands for the commit
|
- Show a quick pick menu with file details and commands for the commit
|
||||||
- Show a quick pick menu with the commit history of the file
|
- Show a quick pick menu with the commit history of the file
|
||||||
- Show a quick pick menu with the commit history of the current branch
|
- Show a quick pick menu with the commit history of the current branch
|
||||||
|
|
||||||
- Adds a `Toggle Git Code Lens` command (`gitlens.toggleCodeLens`) with a shortcut of `shift+alt+b` to toggle the code lens on and off — only available if [`"gitlens.codeLens.visibility": "ondemand"`](#extension-settings) is set
|
- Adds a `Toggle Git Code Lens` command (`gitlens.toggleCodeLens`) with a shortcut of `shift+alt+b` to toggle the code lens on and off
|
||||||
|
|
||||||
#### Powerful Comparison Tools
|
### Powerful Comparison Tools
|
||||||
|
|
||||||
- Effortlessly navigate between comparisions via the `alt+,` and `alt+.` shortcut keys to go back and forth through a file's revisions
|
- Effortlessly navigate between comparisons via the `alt+,` and `alt+.` shortcut keys to go back and forth through a file's revisions
|
||||||
|
|
||||||
- Provides easy access to the following comparison commands via the `Command Palette` as well as in context via the many provided quick pick menus
|
- Provides easy access to the following comparison commands via the `Command Palette` as well as in context via the many provided quick pick menus
|
||||||
|
|
||||||
- Adds a `Directory Compare` command (`gitlens.diffDirectory`) to open the configured Git difftool to compare directories between branches
|
- Adds a `Directory Compare` command (`gitlens.diffDirectory`) to open the configured Git difftool to compare directories between branches
|
||||||
|
|
||||||
- Adds a `Compare File with...` command (`gitlens.diffWithBranch`) to compare the active file with the same file on the selected branch
|
- Adds a `Compare File with Branch...` command (`gitlens.diffWithBranch`) to compare the active file with the same file on the selected branch
|
||||||
|
|
||||||
- Adds a `Compare File with Next Commit` command (`gitlens.diffWithNext`) with a shortcut of `alt+.` to compare the active file/diff with the next commit revision
|
- Adds a `Compare File with Next Revision` command (`gitlens.diffWithNext`) with a shortcut of `alt+.` to compare the active file/diff with the next commit revision
|
||||||
|
|
||||||
- Adds a `Compare File with Previous` command (`gitlens.diffWithPrevious`) with a shortcut of `alt+,` to compare the active file/diff with the previous commit revision
|
- Adds a `Compare File with Previous Revision` command (`gitlens.diffWithPrevious`) with a shortcut of `alt+,` to compare the active file/diff with the previous commit revision
|
||||||
|
|
||||||
- Adds a `Compare Line Commit with Previous` command (`gitlens.diffLineWithPrevious`) with a shortcut of `shift+alt+,` to compare the active file/diff with the previous line commit revision
|
- Adds a `Compare Line Revision with Previous` command (`gitlens.diffLineWithPrevious`) with a shortcut of `shift+alt+,` to compare the active file/diff with the previous line commit revision
|
||||||
|
|
||||||
- Adds a `Compare File with Working Tree` command (`gitlens.diffWithWorking`) with a shortcut of `shift+alt+w` to compare the most recent commit revision of the active file/diff with the working tree
|
- Adds a `Compare File with Revision...` command (`gitlens.diffWithRevision`) to compare the active file with the selected revision of the same file
|
||||||
|
|
||||||
- Adds a `Compare Line Commit with Working Tree` command (`gitlens.diffLineWithWorking`) with a shortcut of `alt+w` to compare the commit revision of the active line with the working tree
|
- Adds a `Compare File with Working Revision` command (`gitlens.diffWithWorking`) with a shortcut of `shift+alt+w` to compare the most recent commit revision of the active file/diff with the working tree
|
||||||
|
|
||||||
#### Navigate and Explore
|
- Adds a `Compare Line Revision with Working` command (`gitlens.diffLineWithWorking`) with a shortcut of `alt+w` to compare the commit revision of the active line with the working tree
|
||||||
|
|
||||||
|
### Navigate and Explore
|
||||||
|
|
||||||
|
- Adds a [customizable](#gitlens-custom-view-settings) `GitLens` custom view to the Explorer activity
|
||||||
|
|
||||||
|
- `Repository View` - provides a full repository explorer
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- `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 indicator dots on the repository icon which denote the following:
|
||||||
|
- `None` - up-to-date with the upstream
|
||||||
|
- `Green` - ahead of the upstream
|
||||||
|
- `Red` - 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 a context menu with `Open Repository in Remote`, and `Refresh` commands
|
||||||
|
|
||||||
|
- `Branches` node — provides a list of the local branches
|
||||||
|
- Indicates which branch is the current branch and [optionally](#gitlens-custom-view-settings) shows the remote tracking branch
|
||||||
|
- Expand each branch to easily see its revision (commit) history
|
||||||
|
- Expand each revision (commit) to quickly see the set of files changed, complete with status indicators for adds, changes, renames, and deletes
|
||||||
|
- Provides a context menu on each changed file with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Open Revision in Remote`, `Apply Changes`, and `Show Commit File Details` commands
|
||||||
|
- Provides a context menu on each revision (commit) with `Open Commit in Remote`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`, `Open Files`, `Open Revisions`, `Show Commit Details`, and `Refresh` commands
|
||||||
|
- Provides a context menu on each branch with `Open Branch in Remote`, and `Refresh` commands
|
||||||
|
- Provides a context menu with `Open Branches in Remote`, and `Refresh` commands
|
||||||
|
|
||||||
|
- `Remotes` node — provides a list of remotes
|
||||||
|
- Expand each remote to see its list of branches
|
||||||
|
- Expand each branch to easily see its revision (commit) history
|
||||||
|
- Expand each revision (commit) to quickly see the set of files changed, complete with status indicators for adds, changes, renames, and deletes
|
||||||
|
- Provides a context menu on each changed file with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Open Revision in Remote`, `Apply Changes`, and `Show Commit File Details` commands
|
||||||
|
- Provides a context menu on each revision (commit) with `Open Commit in Remote`, `Copy Commit ID to Clipboard`, `Copy Commit Message to Clipboard`, `Open Files`, `Open Revisions`, `Show Commit Details`, and `Refresh` commands
|
||||||
|
- Provides a context menu on each remote with `Open Branches in Remote`, `Open Repository in Remote`, and `Refresh` commands
|
||||||
|
- Provides a context menu with a `Refresh` command
|
||||||
|
|
||||||
|
- `Stashes` node — provides a list of stashed changes
|
||||||
|
- Expand each stash to quickly see the set of files stashed, complete with status indicators for adds, changes, renames, and deletes
|
||||||
|
- Provides a context menu with `Stash Changes`, and `Refresh` commands
|
||||||
|
- Provides a context menu on each stash with `Apply Stashed Changes` (confirmation required), `Delete Stashed Changes` (confirmation required), `Copy Commit Message to Clipboard`, `Open Files`, `Open Revisions`, and `Refresh` commands
|
||||||
|
- Provides a context menu on each stashed file with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Apply Changes`, and `Show File History` commands
|
||||||
|
|
||||||
|
- `History View` - provides the revision history of the active file
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Automatically updates to track the active editor
|
||||||
|
- Provides a context menu with `Open File`, `Open File in Remote`, and `Refresh` commands
|
||||||
|
- Provides a context menu on each revision (commit) with `Open Changes`, `Open Changes with Working Tree`, `Open File`, `Open Revision`, `Open File in Remote`, `Open Revision in Remote`, `Apply Changes`, and `Show Commit File Details` commands
|
||||||
|
|
||||||
|
- Quickly switch between views using the `Switch to Repository View` or `Switch to History View` commands
|
||||||
|
- Provides toolbar commands to `Search Commits`, `Switch to Repository View` or `Switch to History View`, and `Refresh`
|
||||||
|
|
||||||
- Adds a `Search Commits` command (`gitlens.showCommitSearch`) with a shortcut of `alt+/` to search for commits by message, author, file(s), or commit id
|
- Adds a `Search Commits` command (`gitlens.showCommitSearch`) with a shortcut of `alt+/` to search for commits by message, author, file(s), or commit id
|
||||||
|
|
||||||
- Adds commands to open files, commits, branches, and the repository in the supported remote services, currently **BitBucket, GitHub, GitLab, and Visual Studio Team Services** — only available if a Git upstream service is configured in the repository
|
- Adds commands to open files, commits, branches, and the repository in the supported remote services, currently **BitBucket, GitHub, GitLab, and Visual Studio Team Services** — only available if a Git upstream service is configured in the repository
|
||||||
|
- `Open Branches in Remote` command (`gitlens.openBranchesInRemote`) — opens the branches in the supported remote service
|
||||||
- `Open Branch in Remote` command (`gitlens.openBranchInRemote`) — opens the current branch commits in the supported remote service
|
- `Open Branch in Remote` command (`gitlens.openBranchInRemote`) — opens the current branch commits in the supported remote service
|
||||||
- `Open Line Commit in Remote` command (`gitlens.openCommitInRemote`) — opens the commit revision of the active line in the supported remote service
|
- `Open Commit in Remote` command (`gitlens.openCommitInRemote`) — opens the commit revision of the active line in the supported remote service
|
||||||
- `Open File in Remote` command (`gitlens.openFileInRemote`) — opens the active file/revision in the supported remote service
|
- `Open File in Remote` command (`gitlens.openFileInRemote`) — opens the active file/revision in the supported remote service
|
||||||
- `Open Repository in Remote` command (`gitlens.openRepoInRemote`) — opens the repository in the supported remote service
|
- `Open Repository in Remote` command (`gitlens.openRepoInRemote`) — opens the repository in the supported remote service
|
||||||
|
|
||||||
- Adds a `Show Current Branch History` command (`gitlens.showQuickRepoHistory`) with a shortcut of `shift+alt+h` to show a paged **branch history quick pick menu** of the current branch for exploring its commit history
|
- Adds a `Show Current Branch History` command (`gitlens.showQuickRepoHistory`) with a shortcut of `shift+alt+h` to show a paged **branch history quick pick menu** of the current branch for exploring its commit history
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- Provides entries to `Show Commit Search` and `Open Branch in <remote-service>` when available
|
- Provides entries to `Show Commit Search` and `Open Branch in <remote-service>` when available
|
||||||
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available
|
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available
|
||||||
@@ -99,7 +184,7 @@ GitLens provides an unobtrusive blame annotation at the end of the selected line
|
|||||||
|
|
||||||
- Adds a `Show File History` command (`gitlens.showQuickFileHistory`) to show a paged **file history quick pick menu** of the active file for exploring its commit history
|
- Adds a `Show File History` command (`gitlens.showQuickFileHistory`) to show a paged **file history quick pick menu** of the active file for exploring its commit history
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- Provides entries to `Show Branch History` and `Open File in <remote-service>` when available
|
- Provides entries to `Show Branch History` and `Open File in <remote-service>` when available
|
||||||
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available
|
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available
|
||||||
@@ -107,18 +192,17 @@ GitLens provides an unobtrusive blame annotation at the end of the selected line
|
|||||||
|
|
||||||
- Adds a `Show Commit Details` command (`gitlens.showQuickCommitDetails`) to show a **commit details quick pick menu** of the most recent commit of the active file
|
- Adds a `Show Commit Details` command (`gitlens.showQuickCommitDetails`) to show a **commit details quick pick menu** of the most recent commit of the active file
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- Quickly see the set of files changed in the commit, complete with status indicators for adds, changes, renames, and deletes
|
- Quickly see the set of files changed in the commit, complete with status indicators for adds, changes, renames, and deletes
|
||||||
- Provides entries to `Copy to Clipboard`, `Directory Compare`, `Open Changed Files`, `Open File in <remote-service>` when available, and more
|
- Provides entries to `Copy to Clipboard`, `Directory Compare`, `Open Changed Files`, `Open File in <remote-service>` when available, and more
|
||||||
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available
|
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available
|
||||||
- Use the `alt+right arrow` shortcut on an entry to execute it without closing the quick pick menu, if possible — commands that open windows outside of VS Code will still close the quick pick menu unless [`"gitlens.advanced.quickPick.closeOnFocusOut": false`](#extension-settings) is set
|
- Use the `alt+right arrow` shortcut on an entry to execute it without closing the quick pick menu, if possible — commands that open windows outside of VS Code will still close the quick pick menu unless [`"gitlens.advanced.quickPick.closeOnFocusOut": false`](#extension-settings) is set
|
||||||
- Use the `alt+right arrow` shortcut on a file entry in the `Changed Files` section to preview the current revision of the while leaving the quick pick menu open
|
- Use the `alt+right arrow` shortcut on a file entry in the `Changed Files` section to preview the comparison of the current revision with the previous one
|
||||||
- NOTE: Once [vscode issue #10568](https://github.com/Microsoft/vscode/issues/10568) is resolved this will change to preview the comparison of the current revision with the previous one
|
|
||||||
|
|
||||||
- Adds a `Show Line Commit Details` command (`gitlens.showQuickCommitFileDetails`) with a shortcut of `alt+c` to show a **file commit details quick pick menu** of the most recent commit of the active file
|
- Adds a `Show Commit File Details` command (`gitlens.showQuickCommitFileDetails`) with a shortcut of `alt+c` to show a **file commit details quick pick menu** of the most recent commit of the active file
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- Provides entries to `Show Commit Details`, `Show File History`, `Compare File with...`, `Copy to Clipboard`, `Open File`, `Open File in <remote-service>` when available, and more
|
- Provides entries to `Show Commit Details`, `Show File History`, `Compare File with...`, `Copy to Clipboard`, `Open File`, `Open File in <remote-service>` when available, and more
|
||||||
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available
|
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available
|
||||||
@@ -126,35 +210,33 @@ GitLens provides an unobtrusive blame annotation at the end of the selected line
|
|||||||
|
|
||||||
- Adds a `Show Repository Status` command (`gitlens.showQuickRepoStatus`) with a shortcut of `alt+s` to show a **repository status quick pick menu** for visualizing the current repository status
|
- Adds a `Show Repository Status` command (`gitlens.showQuickRepoStatus`) with a shortcut of `alt+s` to show a **repository status quick pick menu** for visualizing the current repository status
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- Quickly see upstream status (if an Git upstream is configured) — complete with ahead and behind information
|
- Quickly see upstream status (if an Git upstream is configured) — complete with ahead and behind information
|
||||||
- If you are ahead of the upstream, an entry will be shown with the number of commits ahead. Chosing it will show a limited **branch history quick pick menu** containing just the commits ahead of the upstream
|
- If you are ahead of the upstream, an entry will be shown with the number of commits ahead. Choosing it will show a limited **branch history quick pick menu** containing just the commits ahead of the upstream
|
||||||
- If you are behind the upstream, an entry will be shown with the number of commits behind. Chosing it will show a limited **branch history quick pick menu** containing just the commits behind the upstream
|
- If you are behind the upstream, an entry will be shown with the number of commits behind. Choosing it will show a limited **branch history quick pick menu** containing just the commits behind the upstream
|
||||||
- Quickly see all working changes, both staged and unstaged, complete with status indicators for adds, changes, renames, and deletes
|
- Quickly see all working changes, both staged and unstaged, complete with status indicators for adds, changes, renames, and deletes
|
||||||
- Provides entries to `Show Stashed Changes`, `Open Changed Files`, and `Close Unchanged Files`
|
- Provides entries to `Show Stashed Changes`, `Open Changed Files`, and `Close Unchanged Files`
|
||||||
- Use the `alt+right arrow` shortcut on an entry to execute it without closing the quick pick menu, if possible — commands that open windows outside of VS Code will still close the quick pick menu unless [`"gitlens.advanced.quickPick.closeOnFocusOut": false`](#extension-settings) is set
|
- Use the `alt+right arrow` shortcut on an entry to execute it without closing the quick pick menu, if possible — commands that open windows outside of VS Code will still close the quick pick menu unless [`"gitlens.advanced.quickPick.closeOnFocusOut": false`](#extension-settings) is set
|
||||||
- Use the `alt+right arrow` shortcut on a file entry in the `Staged Files` or `Unstaged Files` sections to preview the working file while leaving the quick pick menu open
|
- Use the `alt+right arrow` shortcut on a file entry in the `Staged Files` or `Unstaged Files` sections to preview the comparison of the working file with the previous revision
|
||||||
- NOTE: Once [vscode issue #10568](https://github.com/Microsoft/vscode/issues/10568) is resolved this will change to preview the comparison of the working file with the previous revision
|
|
||||||
|
|
||||||
- Adds a `Show Stashed Changes` command (`gitlens.showQuickStashList`) to show a **stashed changes quick pick menu** for exploring your repository stash history
|
- Adds a `Show Stashed Changes` command (`gitlens.showQuickStashList`) to show a **stashed changes quick pick menu** for exploring your repository stash history
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- Provides entries to `Stash Changes`
|
- Provides entries to `Stash Changes`
|
||||||
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available
|
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available
|
||||||
|
|
||||||
- Chosing a stash entry shows a **stash details quick pick menu** which is very similar to the **commit details quick pick menu** above
|
- Choosing a stash entry shows a **stash details quick pick menu** which is very similar to the **commit details quick pick menu** above
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- Quickly see the set of files changed in the stash, complete with status indicators for adds, changes, renames, and deletes
|
- Quickly see the set of files changed in the stash, complete with status indicators for adds, changes, renames, and deletes
|
||||||
- Provides entries to `Copy Message to Clipboard`, `Directory Compare`, and `Open Changed Files`
|
- Provides entries to `Copy Message to Clipboard`, `Directory Compare`, and `Open Changed Files`
|
||||||
- Provides entries to `Apply Stashed Changes` and `Delete Stashed Changes` — both require a confirmation
|
- Provides entries to `Apply Stashed Changes` and `Delete Stashed Changes` — both require a confirmation
|
||||||
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available
|
- Navigate back to the previous quick pick menu via `alt+left arrow`, if available
|
||||||
- Use the `alt+right arrow` shortcut on an entry to execute it without closing the quick pick menu, if possible — commands that open windows outside of VS Code will still close the quick pick menu unless [`"gitlens.advanced.quickPick.closeOnFocusOut": false`](#extension-settings) is set
|
- Use the `alt+right arrow` shortcut on an entry to execute it without closing the quick pick menu, if possible — commands that open windows outside of VS Code will still close the quick pick menu unless [`"gitlens.advanced.quickPick.closeOnFocusOut": false`](#extension-settings) is set
|
||||||
- Use the `alt+right arrow` shortcut on a file entry in the `Changed Files` section to preview the current revision of the while leaving the quick pick menu open
|
- Use the `alt+right arrow` shortcut on a file entry in the `Changed Files` section to preview the comparison of the current revision with the previous one
|
||||||
- NOTE: Once [vscode issue #10568](https://github.com/Microsoft/vscode/issues/10568) is resolved this will change to preview the comparison of the current revision with the previous one
|
|
||||||
|
|
||||||
- Adds a `Show Last Opened Quick Pick` command (`gitlens.showLastQuickPick`) with a shortcut of `alt+-` to quickly get back to where you were when the last GitLens quick pick menu closed
|
- Adds a `Show Last Opened Quick Pick` command (`gitlens.showLastQuickPick`) with a shortcut of `alt+-` to quickly get back to where you were when the last GitLens quick pick menu closed
|
||||||
|
|
||||||
@@ -164,11 +246,11 @@ GitLens provides an unobtrusive blame annotation at the end of the selected line
|
|||||||
- Adds a `Open Blame History Explorer` command (`gitlens.showBlameHistory`) to show a **blame history explorer** (peek style) to visualize the blame history of a file or code block
|
- Adds a `Open Blame History Explorer` command (`gitlens.showBlameHistory`) to show a **blame history explorer** (peek style) to visualize the blame history of a file or code block
|
||||||
- Likely to be deprecated in a future release, add your voice to [#66](https://github.com/eamodio/vscode-gitlens/issues/66) if you feel it should not be removed
|
- Likely to be deprecated in a future release, add your voice to [#66](https://github.com/eamodio/vscode-gitlens/issues/66) if you feel it should not be removed
|
||||||
|
|
||||||
#### And More
|
### And More
|
||||||
|
|
||||||
- Adds a `Copy Commit ID to Clipboard` command (`gitlens.copyShaToClipboard`) to copy the commit id (sha) of the active line to the clipboard
|
- Adds a `Copy Commit ID to Clipboard` command (`gitlens.copyShaToClipboard`) to copy the commit id (sha) of the active line to the clipboard or from the most recent commit to the current branch, if there is no active editor
|
||||||
|
|
||||||
- Adds a `Copy Commit Message to Clipboard` command (`gitlens.copyMessageToClipboard`) to copy the commit message of the active line to the clipboard
|
- Adds a `Copy Commit Message to Clipboard` command (`gitlens.copyMessageToClipboard`) to copy the commit message of the active line to the clipboard or from the most recent commit to the current branch, if there is no active editor
|
||||||
|
|
||||||
- Adds a `Open Changed Files` command (`gitlens.openChangedFiles`) to open any files with working tree changes
|
- Adds a `Open Changed Files` command (`gitlens.openChangedFiles`) to open any files with working tree changes
|
||||||
|
|
||||||
@@ -177,52 +259,150 @@ GitLens provides an unobtrusive blame annotation at the end of the selected line
|
|||||||
- Adds a `Apply Stashed Changes` command (`gitlens.stashApply`) to chose a stash entry to apply to the working tree from a quick pick menu
|
- Adds a `Apply Stashed Changes` command (`gitlens.stashApply`) to chose a stash entry to apply to the working tree from a quick pick menu
|
||||||
|
|
||||||
- Adds a `Stash Changes` command (`gitlens.stashSave`) to save any working tree changes to the stash — can optionally provide a stash message
|
- Adds a `Stash Changes` command (`gitlens.stashSave`) to save any working tree changes to the stash — can optionally provide a stash message
|
||||||
|
- Also adds the command to the Source Control items context menu to stash an individual or group of files, works with multi-select too!
|
||||||
|
|
||||||
## Insiders
|
## Insiders
|
||||||
|
|
||||||
Add [`"gitlens.insiders": true`](#extension-settings) to your settings to join the insiders channel and get early access to upcoming features. Be aware that because this provides early access expect there to be issues.
|
Add [`"gitlens.insiders": true`](#general-extension-settings) to your settings to join the insiders channel and get early access to upcoming features. Be aware that because this provides early access expect there to be issues.
|
||||||
|
|
||||||
## Extension Settings
|
## Extension Settings
|
||||||
|
|
||||||
GitLens is highly customizable and provides many configuration settings to allow the personalization of almost all features
|
GitLens is highly customizable and provides many configuration settings to allow the personalization of almost all features
|
||||||
|
|
||||||
|
### General Settings
|
||||||
|
|
||||||
|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.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
|
||||||
|`gitlens.blame.annotation.activeLine`|Specifies whether and how to show blame annotations on the active line. `off` - no annotation. `inline` - adds a trailing annotation to the active line. `hover` - adds hover annotation to the active line. `both` - adds both `inline` and `hover` annotations
|
|
||||||
|`gitlens.blame.annotation.activeLineDarkColor`|Specifies the color of the active line blame annotation to use with a dark theme. Must be a valid css color
|
### Blame Annotation Settings
|
||||||
|`gitlens.blame.annotation.activeLineLightColor`|Specifies the color of the active line blame annotation to use with a light theme. Must be a valid css color
|
|
||||||
|`gitlens.blame.annotation.highlight`|Specifies whether and how to highlight blame annotations. `none` - no highlight. `gutter` - adds a gutter icon. `line` - adds a full-line highlight. `both` - adds both `gutter` and `line` highlights
|
#### File Blame Annotation Settings
|
||||||
|`gitlens.blame.annotation.style`|Specifies the style of the blame annotations. `compact` - groups annotations to limit the repetition and also adds author and date when possible. `expanded` - shows an annotation on every line
|
|
||||||
|`gitlens.blame.annotation.author`|Specifies whether the committer will be shown in the blame annotations. Applies only to the `expanded` & `trailing` annotation styles
|
|Name | Description
|
||||||
|`gitlens.blame.annotation.date`|Specifies whether and how the commit date will be shown in the blame annotations. `off` - no date. `relative` - relative date (e.g. 1 day ago). `absolute` - date format specified by `gitlens.blame.annotation.dateFormat`. Applies only to the `expanded` & `trailing` annotation styles
|
|-----|------------
|
||||||
|`gitlens.blame.annotation.dateFormat`|Specifies the date format of how absolute dates will be shown in the blame annotations. See https://momentjs.com/docs/#/displaying/format/ for valid formats
|
|`gitlens.blame.file.annotationType`|Specifies the type of blame annotations that will be shown for the current file<br />`gutter` - adds an annotation to the beginning of each line<br />`hover` - shows annotations when hovering over each line
|
||||||
|`gitlens.blame.annotation.message`|Specifies whether the commit message will be shown in the blame annotations. Applies only to the `expanded` & `trailing` annotation styles
|
|`gitlens.blame.file.lineHighlight.enabled`|Specifies whether or not to highlight lines associated with the current line
|
||||||
|`gitlens.blame.annotation.sha`|Specifies whether the commit sha will be shown in the blame annotations. Applies only to the `expanded` & `trailing` annotation styles
|
|`gitlens.blame.file.lineHighlight.locations`|Specifies where the associated line highlights will be shown<br />`gutter` - adds a gutter glyph<br />`line` - adds a full-line highlight background color<br />`overviewRuler` - adds a decoration to the overviewRuler (scroll bar)
|
||||||
|`gitlens.codeLens.visibility`|Specifies when code lens will be shown in the active document. `auto` - always shown. `ondemand` - never shown, unless toggled via the `gitlens.toggleCodeLens` command. `off` - never shown
|
|`gitlens.annotations.file.gutter.format`|Specifies the format of the gutter blame annotations<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.annotations.file.gutter.dateFormat`)<br />`${authorAgo}` - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
|
||||||
|`gitlens.codeLens.authors.enabled`|Specifies whether the authors code lens is shown
|
|`gitlens.annotations.file.gutter.dateFormat`|Specifies how to format absolute dates (using the `${date}` token) in gutter blame annotations<br />See https://momentjs.com/docs/#/displaying/format/ for valid formats
|
||||||
|`gitlens.codeLens.authors.command`|Specifies the command executed when the authors code lens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current committed file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickRepoHistory` - shows a branch history quick pick
|
|`gitlens.annotations.file.gutter.compact`|Specifies whether or not to compact (deduplicate) matching adjacent gutter blame annotations
|
||||||
|`gitlens.codeLens.recentChange.enabled`|Specifies whether the recent change code lens is shown
|
|`gitlens.annotations.file.gutter.heatmap.enabled`|Specifies whether or not to provide a heatmap indicator in the gutter blame annotations
|
||||||
|`gitlens.codeLens.recentChange.command`|"Specifies the command executed when the recent change code lens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current committed file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickRepoHistory` - shows a branch history quick pick
|
|`gitlens.annotations.file.gutter.heatmap.location`|Specifies where the heatmap indicators will be shown in the gutter blame annotations<br />`left` - adds a heatmap indicator on the left edge of the gutter blame annotations<br />`right` - adds a heatmap indicator on the right edge of the gutter blame annotations
|
||||||
|`gitlens.codeLens.location`|Specifies where code lens will be rendered in the active document. `all` - render at the top of the document, on container-like (classes, modules, etc), and on member-like (methods, functions, properties, etc) lines. `document+containers` - render at the top of the document and on container-like lines. `document` - only render at the top of the document. `custom` - rendering controlled by `gitlens.codeLens.locationCustomSymbols`
|
|`gitlens.annotations.file.gutter.hover.details`|Specifies whether or not to provide a commit details hover annotation over the gutter blame annotations
|
||||||
|`gitlens.codeLens.locationCustomSymbols`|Specifies the set of document symbols to render active document code lens on. Must be a member of `SymbolKind`
|
|`gitlens.annotations.file.gutter.hover.wholeLine`|Specifies whether or not to trigger hover annotations over the whole line
|
||||||
|`gitlens.codeLens.languageLocations`|Specifies where code lens will be rendered in the active document for the specified languages
|
|`gitlens.annotations.file.hover.heatmap.enabled`|Specifies whether or not to provide heatmap indicators on the left edge of each line
|
||||||
|`gitlens.menus.diff.enabled`|Specifies whether diff commands will be added to the context menus
|
|`gitlens.annotations.file.hover.wholeLine`|Specifies whether or not to trigger hover annotations over the whole line
|
||||||
|`gitlens.statusBar.enabled`|Specifies whether blame information is shown in the status bar
|
|
||||||
|`gitlens.statusBar.alignment`|Specifies the blame alignment in the status bar. `left` - align to the left, `right` - align to the right
|
#### Line Blame Annotation Settings
|
||||||
|`gitlens.statusBar.command`|Specifies the command executed when the blame status bar item is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current line commit with the previous. `gitlens.diffWithWorking` - compares the current line commit with the working tree. `gitlens.toggleCodeLens` - toggles Git code lens. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickRepoHistory` - shows a branch history quick pick
|
|
||||||
|`gitlens.statusBar.date`|Specifies whether and how the commit date will be shown in the blame status bar. `off` - no date. `relative` - relative date (e.g. 1 day ago). `absolute` - date format specified by `gitlens.statusBar.dateFormat`
|
|Name | Description
|
||||||
|`gitlens.statusBar.dateFormat`|Specifies the date format of how absolute dates will be shown in the blame status bar. See https://momentjs.com/docs/#/displaying/format/ for valid formats
|
|-----|------------
|
||||||
|
|`gitlens.blame.line.enabled`|Specifies whether or not to provide a blame annotation for the current line, by default<br />Use the `gitlens.toggleLineBlame` command to toggle the annotations on and off for the current session
|
||||||
|
|`gitlens.blame.line.annotationType`|Specifies the type of blame annotations that will be shown for the current line<br />`trailing` - adds an annotation to the end of the current line<br />`hover` - shows annotations when hovering over the current line
|
||||||
|
|`gitlens.annotations.line.trailing.format`|Specifies the format of the trailing blame annotations<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.annotations.line.trailing.dateFormat`)<br />`${authorAgo}` - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
|
||||||
|
|`gitlens.annotations.line.trailing.dateFormat`|Specifies how to format absolute dates (using the `${date}` token) in trailing blame annotations<br />See https://momentjs.com/docs/#/displaying/format/ for valid formats
|
||||||
|
|`gitlens.annotations.line.trailing.hover.details`|Specifies whether or not to provide a commit details hover annotation over the trailing blame annotations
|
||||||
|
|`gitlens.annotations.line.trailing.hover.changes`|Specifies whether or not to provide a changes (diff) hover annotation over the trailing blame annotations
|
||||||
|
|`gitlens.annotations.line.trailing.hover.wholeLine`|Specifies whether or not to trigger hover annotations over the whole line
|
||||||
|
|`gitlens.annotations.line.hover.details`|Specifies whether or not to provide a commit details hover annotation for the current line
|
||||||
|
|`gitlens.annotations.line.hover.changes`|Specifies whether or not to provide a changes (diff) hover annotation for the current line
|
||||||
|
|
||||||
|
### File Recent Changes Annotation Settings
|
||||||
|
|
||||||
|
|Name | Description
|
||||||
|
|-----|------------
|
||||||
|
|`gitlens.recentChanges.file.lineHighlight.locations`|Specifies where the highlights of the recently changed lines will be shown<br />`gutter` - adds a gutter glyph<br />`line` - adds a full-line highlight background color<br />`overviewRuler` - adds a decoration to the overviewRuler (scroll bar)
|
||||||
|
|`gitlens.annotations.file.recentChanges.hover.details`|Specifies whether or not to provide a commit details hover annotation
|
||||||
|
|`gitlens.annotations.file.recentChanges.hover.changes`|Specifies whether or not to provide a changes (diff) hover annotation
|
||||||
|
|`gitlens.annotations.file.recentChanges.hover.wholeLine`|Specifies whether or not to trigger hover annotations over the whole line
|
||||||
|
|
||||||
|
### Code Lens Settings
|
||||||
|
|
||||||
|
|Name | Description
|
||||||
|
|-----|------------
|
||||||
|
|`gitlens.codeLens.enabled`|Specifies whether or not to provide any Git code lens, by default<br />Use the `gitlens.toggleCodeLens` command to toggle the Git code lens on and off for the current session
|
||||||
|
|`gitlens.codeLens.recentChange.enabled`|Specifies whether or not to show a `recent change` code lens showing the author and date of the most recent commit for the file or code block
|
||||||
|
|`gitlens.codeLens.recentChange.command`|Specifies the command to be executed when the `recent change` code lens is clicked<br />`gitlens.toggleFileBlame` - toggles file blame annotations<br />`gitlens.showBlameHistory` - opens the blame history explorer<br />`gitlens.showFileHistory` - opens the file history explorer<br />`gitlens.diffWithPrevious` - compares the current committed file with the previous commit<br />`gitlens.showQuickCommitDetails` - shows a commit details quick pick<br />`gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick<br />`gitlens.showQuickFileHistory` - shows a file history quick pick<br />`gitlens.showQuickRepoHistory` - shows a branch history quick pick
|
||||||
|
|`gitlens.codeLens.authors.enabled`|Specifies whether or not to show an `authors` code lens showing number of authors of the file or code block and the most prominent author (if there is more than one)
|
||||||
|
|`gitlens.codeLens.authors.command`|Specifies the command to be executed when the `authors` code lens is clicked<br />`gitlens.toggleFileBlame` - toggles file blame annotations<br />`gitlens.showBlameHistory` - opens the blame history explorer<br />`gitlens.showFileHistory` - opens the file history explorer<br />`gitlens.diffWithPrevious` - compares the current committed file with the previous commit<br />`gitlens.showQuickCommitDetails` - shows a commit details quick pick<br />`gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick<br />`gitlens.showQuickFileHistory` - shows a file history quick pick<br />`gitlens.showQuickRepoHistory` - shows a branch history quick pick
|
||||||
|
|`gitlens.codeLens.locations`|Specifies where Git code lens will be shown in the document<br />`document` - adds code lens at the top of the document<br />`containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)<br />`blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines<br />`custom` - adds code lens at the start of symbols contained in `gitlens.codeLens.locationCustomSymbols`
|
||||||
|
|`gitlens.codeLens.customLocationSymbols`|Specifies the set of document symbols where Git code lens will be shown in the document
|
||||||
|
|`gitlens.codeLens.perLanguageLocations`|Specifies where Git code lens will be shown in the document for the specified languages
|
||||||
|
|
||||||
|
### GitLens Custom View Settings
|
||||||
|
|
||||||
|
|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.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.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.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
|
||||||
|
|
||||||
|
### Status Bar Settings
|
||||||
|
|
||||||
|
|Name | Description
|
||||||
|
|-----|------------
|
||||||
|
|`gitlens.statusBar.enabled`|Specifies whether or not to provide blame information on the status bar
|
||||||
|
|`gitlens.statusBar.alignment`|Specifies the blame alignment in the status bar<br />`left` - align to the left, `right` - align to the right
|
||||||
|
|`gitlens.statusBar.command`|Specifies the command to be executed when the blame status bar item is clicked<br />`gitlens.toggleFileBlame` - toggles file blame annotations<br />`gitlens.showBlameHistory` - opens the blame history explorer<br />`gitlens.showFileHistory` - opens the file history explorer<br />`gitlens.diffWithPrevious` - compares the current line commit with the previous<br />`gitlens.diffWithWorking` - compares the current line commit with the working tree<br />`gitlens.toggleCodeLens` - toggles Git code lens<br />`gitlens.showQuickCommitDetails` - shows a commit details quick pick<br />`gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick<br />`gitlens.showQuickFileHistory` - shows a file history quick pick<br />`gitlens.showQuickRepoHistory` - shows a branch history quick pick
|
||||||
|
|`gitlens.statusBar.format`|Specifies the format of the blame information on the status bar<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 />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
|
||||||
|
|`gitlens.statusBar.dateFormat`|Specifies the date format of absolute dates shown in the blame information on the status bar<br />See https://momentjs.com/docs/#/displaying/format/ for valid formats
|
||||||
|
|
||||||
|
### Strings Settings
|
||||||
|
|
||||||
|
|Name | Description
|
||||||
|
|-----|------------
|
||||||
|
|`gitlens.strings.codeLens.unsavedChanges.recentChangeAndAuthors`|Specifies the string to be shown in place of both the `recent change` and `authors` code lens when there are unsaved changes
|
||||||
|
|`gitlens.strings.codeLens.unsavedChanges.recentChangeOnly`|Specifies the string to be shown in place of the `recent change` code lens when there are unsaved changes
|
||||||
|
|`gitlens.strings.codeLens.unsavedChanges.authorsOnly`|Specifies the string to be shown in place of the `authors` code lens when there are unsaved changes
|
||||||
|
|
||||||
|
### Theme Settings
|
||||||
|
|
||||||
|
|Name | Description
|
||||||
|
|-----|------------
|
||||||
|
|`gitlens.theme.annotations.file.gutter.separateLines`|Specifies whether or not gutter blame annotations will have line separators
|
||||||
|
|`gitlens.theme.annotations.file.gutter.dark.backgroundColor`|Specifies the dark theme background color of the gutter blame annotations
|
||||||
|
|`gitlens.theme.annotations.file.gutter.light.backgroundColor`|Specifies the light theme background color of the gutter blame annotations
|
||||||
|
|`gitlens.theme.annotations.file.gutter.dark.foregroundColor`|Specifies the dark theme foreground color of the gutter blame annotations
|
||||||
|
|`gitlens.theme.annotations.file.gutter.light.foregroundColor`|Specifies the light theme foreground color of the gutter blame annotations
|
||||||
|
|`gitlens.theme.annotations.file.gutter.dark.uncommittedForegroundColor`|Specifies the dark theme foreground color of an uncommitted line in the gutter blame annotations
|
||||||
|
|`gitlens.theme.annotations.file.gutter.light.uncommittedForegroundColor`|Specifies the light theme foreground color of an uncommitted line in the gutter blame annotations
|
||||||
|
|`gitlens.theme.annotations.line.trailing.dark.backgroundColor`|Specifies the dark theme background color of the trailing blame annotation
|
||||||
|
|`gitlens.theme.annotations.line.trailing.light.backgroundColor`|Specifies the light theme background color of the trailing blame annotation
|
||||||
|
|`gitlens.theme.annotations.line.trailing.dark.foregroundColor`|Specifies the dark theme foreground color of the trailing blame annotation
|
||||||
|
|`gitlens.theme.annotations.line.trailing.light.foregroundColor`|Specifies the light theme foreground color of the trailing blame annotation
|
||||||
|
|`gitlens.theme.lineHighlight.dark.backgroundColor`|Specifies the dark theme background color of the associated line highlights in blame annotations. Must be a valid css color
|
||||||
|
|`gitlens.theme.lineHighlight.light.backgroundColor`|Specifies the light theme background color of the associated line highlights in blame annotations. Must be a valid css color
|
||||||
|
|`gitlens.theme.lineHighlight.dark.overviewRulerColor`|Specifies the dark theme overview ruler color of the associated line highlights in blame annotations
|
||||||
|
|`gitlens.theme.lineHighlight.light.overviewRulerColor`|Specifies the light theme overview ruler color of the associated line highlights in blame annotations
|
||||||
|
|
||||||
|
### Advanced Settings
|
||||||
|
|
||||||
|
|Name | Description
|
||||||
|
|-----|------------
|
||||||
|
|`gitlens.advanced.telemetry.enabled`|Specifies whether or not to enable GitLens telemetry (even if enabled still abides by the overall `telemetry.enableTelemetry` setting
|
||||||
|
|`gitlens.advanced.menus`|Specifies which commands will be added to which menus
|
||||||
|
|`gitlens.advanced.caching.enabled`|Specifies whether git output will be cached
|
||||||
|
|`gitlens.advanced.caching.maxLines`|Specifies the threshold for caching larger documents
|
||||||
|
|`gitlens.advanced.git`|Specifies the git path to use
|
||||||
|
|`gitlens.advanced.gitignore.enabled`|Specifies whether or not to parse the root .gitignore file for better performance (i.e. avoids blaming excluded files)
|
||||||
|
|`gitlens.advanced.maxQuickHistory`|Specifies the maximum number of QuickPick history entries to show
|
||||||
|
|`gitlens.advanced.quickPick.closeOnFocusOut`|Specifies whether or not to close the QuickPick menu when focus is lost
|
||||||
|
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|
||||||
- If the `Copy to * clipboard` commands don't work on Linux -- `xclip` needs to be installed. You can install it via `sudo apt-get install xclip`
|
- If the `Copy to * clipboard` commands don't work on Linux -- `xclip` needs to be installed. You can install it via `sudo apt-get install xclip`
|
||||||
- Visible whitespace causes issues ([vscode issue #11485](https://github.com/Microsoft/vscode/issues/11485)) with the `expanded` and `compact` blame annotation styles when using a non-monospace font -- set `"gitlens.advanced.toggleWhitespace.enabled": true` if you are using a non-monospace font
|
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
A big thanks to the people that have contributed to this project:
|
A big thanks to the people that have contributed to this project:
|
||||||
|
|
||||||
|
- 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))
|
||||||
- Zack Schuster ([@zackschuster](https://github.com/zackschuster)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=zackschuster)
|
- Zack Schuster ([@zackschuster](https://github.com/zackschuster)) — [contributions](https://github.com/eamodio/vscode-gitlens/commits?author=zackschuster)
|
||||||
|
|||||||
BIN
images/chat-badge.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
88
images/chat-badge.svg
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
width="92" height="20" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.white{fill:#FFF;}
|
||||||
|
.shadow{fill:#2e840e;}
|
||||||
|
</style>
|
||||||
|
<linearGradient id="b" x2="0" y2="100%">
|
||||||
|
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||||
|
<stop offset="1" stop-opacity=".1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<clipPath id="a">
|
||||||
|
<rect width="92" height="20" rx="3" fill="#fff"/>
|
||||||
|
</clipPath>
|
||||||
|
<g clip-path="url(#a)">
|
||||||
|
<path fill="#555" d="M0 0h34v20H0z"/>
|
||||||
|
<path fill="#4c1" d="M34 0h58v20H34z"/>
|
||||||
|
<path fill="url(#b)" d="M0 0h92v20H0z"/>
|
||||||
|
</g>
|
||||||
|
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||||
|
<text x="17" y="15" fill="#010101" fill-opacity=".3">chat</text>
|
||||||
|
<text x="17" y="14">chat</text>
|
||||||
|
<g transform="scale(0.1) translate(340, -21)">
|
||||||
|
<path class="shadow" d="M263.5,105.5c3.9,1.7,4.2,3,1.1,8.8c-3.1,6-3.9,6.3-7.7,4.8c-4.8-2.1-11-3.6-15-3.6c-6.5,0-10.9,2.3-10.9,5.9
|
||||||
|
c0,11.7,37.4,5.4,37.4,30.4c0,12.6-10.8,21-27,21c-8.5,0-19-2.8-26.3-6.5c-3.6-1.8-3.8-2.9-0.7-8.9c2.7-5.3,3.6-5.8,7.5-4.2
|
||||||
|
c6.2,2.7,14,4.8,19.3,4.8c6,0,9.9-2.4,9.9-6c0-11.4-38.1-6-38.1-30.2c0-12.9,10.7-21.5,26.8-21.5
|
||||||
|
C247.4,100.2,256.9,102.5,263.5,105.5z"/>
|
||||||
|
<path class="shadow" d="M293.7,75.8V169c0,1.4-1.5,2.9-3.6,2.9h-9.8c-2.1,0-3.6-1.5-3.6-2.9V75.8c0-4.6,1.3-5,8.5-5
|
||||||
|
C293.4,70.7,293.7,71.3,293.7,75.8z"/>
|
||||||
|
<path class="shadow" d="M363.4,128.2v40.1c0,2.1-1.5,3.6-3.6,3.6h-9.7c-2.2,0-3.8-1.6-3.6-3.8l0.1-4.3c-5.3,5.8-12.8,8.9-20.3,8.9
|
||||||
|
c-14.6,0-24.4-8.5-24.4-21.1c0-13.4,11.1-22.5,27.7-22.5c6.3,0,12.1,1.1,16.8,3.1v-4.6c0-7.4-5.8-11.8-15.7-11.8
|
||||||
|
c-4.6,0-10.3,1.8-14.8,4.5c-3.5,2-4.3,1.8-8.1-3.8c-3.7-5.6-3.6-6.7,0-9c6.8-4.4,16.1-7.2,24.6-7.2
|
||||||
|
C351.4,100.2,363.4,110.6,363.4,128.2z M318.2,151.4c0,4.8,4,8,10.2,8c7.4,0,14.1-3.6,18-9.6v-6.3c-3.9-1.5-8.7-2.3-12.9-2.3
|
||||||
|
C324.3,141.2,318.2,145.5,318.2,151.4z"/>
|
||||||
|
<path class="shadow" d="M429.9,105.6c3.6,2.1,3.7,3.2-0.2,9.2c-3.7,5.7-4.3,6-8.2,4c-3-1.6-7.7-2.9-11.7-2.9
|
||||||
|
c-12.3,0-20.5,8.1-20.5,20.3c0,12.7,8.2,21.2,20.5,21.2c4.3,0,9.6-1.6,13.1-3.6c3.6-2.1,4.3-1.9,8.1,3.6c3.4,5.1,3.3,6.3,0.3,8.5
|
||||||
|
c-5.5,3.8-14.1,6.7-21.7,6.7c-22.7,0-37.9-14.6-37.9-36.4c0-21.7,15.2-36.1,38.1-36.1C416.6,100.2,424.8,102.6,429.9,105.6z"/>
|
||||||
|
<path class="shadow" d="M504.1,163.1c2.8,3.6,1.7,4.9-5.4,7.5c-7.2,2.6-8.2,2.5-10.8-0.9l-20.3-27.1l-9.1,8.8V169
|
||||||
|
c0,1.4-1.5,2.9-3.6,2.9H445c-2.1,0-3.6-1.5-3.6-2.9V75.8c0-4.6,1.3-5,8.5-5c8.2,0,8.5,0.6,8.5,5v53l27.8-26.7
|
||||||
|
c3.1-2.9,4.8-2.7,10.2,0.9c6,3.9,6.5,5,3.6,7.8L479.3,131L504.1,163.1z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="scale(0.1) translate(340, -21)">
|
||||||
|
<rect x="115.9" y="126.3" transform="matrix(0.9482 -0.3176 0.3176 0.9482 -36.3582 46.5843)" class="shadow" width="17.6" height="17"/>
|
||||||
|
<g>
|
||||||
|
<rect x="115.9" y="126.3" transform="matrix(0.9482 -0.3176 0.3176 0.9482 -36.3582 46.5843)" class="shadow" width="17.6" height="17"/>
|
||||||
|
<path class="shadow" d="M182,117.5c-12.9-43-31.5-53-74.5-40.1s-53,31.5-40.1,74.5s31.5,53,74.5,40.1S194.9,160.5,182,117.5z
|
||||||
|
M160.3,145.4l-8.1,2.7l2.8,8.4c1.1,3.4-0.7,7.1-4.1,8.2c-0.7,0.2-1.5,0.4-2.2,0.3c-2.6-0.1-5.1-1.8-6-4.4l-2.8-8.4l-16.7,5.6
|
||||||
|
l2.8,8.4c1.1,3.4-0.7,7.1-4.1,8.2c-0.7,0.2-1.5,0.4-2.2,0.3c-2.6-0.1-5.1-1.8-6-4.4l-2.8-8.4l-8.1,2.7c-0.7,0.2-1.5,0.4-2.2,0.3
|
||||||
|
c-2.6-0.1-5.1-1.8-6-4.4c-1.1-3.4,0.7-7.1,4.1-8.2l8.1-2.7l-5.4-16.1l-8.1,2.7c-0.7,0.2-1.5,0.4-2.2,0.3c-2.6-0.1-5.1-1.8-6-4.4
|
||||||
|
c-1.1-3.4,0.7-7.1,4.1-8.2l8.1-2.7l-2.8-8.4c-1.1-3.4,0.7-7.1,4.1-8.2s7.1,0.7,8.2,4.1l2.8,8.4l16.7-5.6l-2.8-8.4
|
||||||
|
c-1.1-3.4,0.7-7.1,4.1-8.2s7.1,0.7,8.2,4.1l2.8,8.4l8.1-2.7c3.4-1.1,7.1,0.7,8.2,4.1c1.1,3.4-0.7,7.1-4.1,8.2l-8.1,2.7l5.4,16.1
|
||||||
|
l8.1-2.7c3.4-1.1,7.1,0.7,8.2,4.1C165.5,140.6,163.7,144.3,160.3,145.4z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g transform="scale(0.1) translate(340, -31)">
|
||||||
|
<path class="white" d="M263.5,105.5c3.9,1.7,4.2,3,1.1,8.8c-3.1,6-3.9,6.3-7.7,4.8c-4.8-2.1-11-3.6-15-3.6c-6.5,0-10.9,2.3-10.9,5.9
|
||||||
|
c0,11.7,37.4,5.4,37.4,30.4c0,12.6-10.8,21-27,21c-8.5,0-19-2.8-26.3-6.5c-3.6-1.8-3.8-2.9-0.7-8.9c2.7-5.3,3.6-5.8,7.5-4.2
|
||||||
|
c6.2,2.7,14,4.8,19.3,4.8c6,0,9.9-2.4,9.9-6c0-11.4-38.1-6-38.1-30.2c0-12.9,10.7-21.5,26.8-21.5
|
||||||
|
C247.4,100.2,256.9,102.5,263.5,105.5z"/>
|
||||||
|
<path class="white" d="M293.7,75.8V169c0,1.4-1.5,2.9-3.6,2.9h-9.8c-2.1,0-3.6-1.5-3.6-2.9V75.8c0-4.6,1.3-5,8.5-5
|
||||||
|
C293.4,70.7,293.7,71.3,293.7,75.8z"/>
|
||||||
|
<path class="white" d="M363.4,128.2v40.1c0,2.1-1.5,3.6-3.6,3.6h-9.7c-2.2,0-3.8-1.6-3.6-3.8l0.1-4.3c-5.3,5.8-12.8,8.9-20.3,8.9
|
||||||
|
c-14.6,0-24.4-8.5-24.4-21.1c0-13.4,11.1-22.5,27.7-22.5c6.3,0,12.1,1.1,16.8,3.1v-4.6c0-7.4-5.8-11.8-15.7-11.8
|
||||||
|
c-4.6,0-10.3,1.8-14.8,4.5c-3.5,2-4.3,1.8-8.1-3.8c-3.7-5.6-3.6-6.7,0-9c6.8-4.4,16.1-7.2,24.6-7.2
|
||||||
|
C351.4,100.2,363.4,110.6,363.4,128.2z M318.2,151.4c0,4.8,4,8,10.2,8c7.4,0,14.1-3.6,18-9.6v-6.3c-3.9-1.5-8.7-2.3-12.9-2.3
|
||||||
|
C324.3,141.2,318.2,145.5,318.2,151.4z"/>
|
||||||
|
<path class="white" d="M429.9,105.6c3.6,2.1,3.7,3.2-0.2,9.2c-3.7,5.7-4.3,6-8.2,4c-3-1.6-7.7-2.9-11.7-2.9
|
||||||
|
c-12.3,0-20.5,8.1-20.5,20.3c0,12.7,8.2,21.2,20.5,21.2c4.3,0,9.6-1.6,13.1-3.6c3.6-2.1,4.3-1.9,8.1,3.6c3.4,5.1,3.3,6.3,0.3,8.5
|
||||||
|
c-5.5,3.8-14.1,6.7-21.7,6.7c-22.7,0-37.9-14.6-37.9-36.4c0-21.7,15.2-36.1,38.1-36.1C416.6,100.2,424.8,102.6,429.9,105.6z"/>
|
||||||
|
<path class="white" d="M504.1,163.1c2.8,3.6,1.7,4.9-5.4,7.5c-7.2,2.6-8.2,2.5-10.8-0.9l-20.3-27.1l-9.1,8.8V169
|
||||||
|
c0,1.4-1.5,2.9-3.6,2.9H445c-2.1,0-3.6-1.5-3.6-2.9V75.8c0-4.6,1.3-5,8.5-5c8.2,0,8.5,0.6,8.5,5v53l27.8-26.7
|
||||||
|
c3.1-2.9,4.8-2.7,10.2,0.9c6,3.9,6.5,5,3.6,7.8L479.3,131L504.1,163.1z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="scale(0.1) translate(340, -31)">
|
||||||
|
<rect x="115.9" y="126.3" transform="matrix(0.9482 -0.3176 0.3176 0.9482 -36.3582 46.5843)" class="white" width="17.6" height="17"/>
|
||||||
|
<g>
|
||||||
|
<rect x="115.9" y="126.3" transform="matrix(0.9482 -0.3176 0.3176 0.9482 -36.3582 46.5843)" class="white" width="17.6" height="17"/>
|
||||||
|
<path class="white" d="M182,117.5c-12.9-43-31.5-53-74.5-40.1s-53,31.5-40.1,74.5s31.5,53,74.5,40.1S194.9,160.5,182,117.5z
|
||||||
|
M160.3,145.4l-8.1,2.7l2.8,8.4c1.1,3.4-0.7,7.1-4.1,8.2c-0.7,0.2-1.5,0.4-2.2,0.3c-2.6-0.1-5.1-1.8-6-4.4l-2.8-8.4l-16.7,5.6
|
||||||
|
l2.8,8.4c1.1,3.4-0.7,7.1-4.1,8.2c-0.7,0.2-1.5,0.4-2.2,0.3c-2.6-0.1-5.1-1.8-6-4.4l-2.8-8.4l-8.1,2.7c-0.7,0.2-1.5,0.4-2.2,0.3
|
||||||
|
c-2.6-0.1-5.1-1.8-6-4.4c-1.1-3.4,0.7-7.1,4.1-8.2l8.1-2.7l-5.4-16.1l-8.1,2.7c-0.7,0.2-1.5,0.4-2.2,0.3c-2.6-0.1-5.1-1.8-6-4.4
|
||||||
|
c-1.1-3.4,0.7-7.1,4.1-8.2l8.1-2.7l-2.8-8.4c-1.1-3.4,0.7-7.1,4.1-8.2s7.1,0.7,8.2,4.1l2.8,8.4l16.7-5.6l-2.8-8.4
|
||||||
|
c-1.1-3.4,0.7-7.1,4.1-8.2s7.1,0.7,8.2,4.1l2.8,8.4l8.1-2.7c3.4-1.1,7.1,0.7,8.2,4.1c1.1,3.4-0.7,7.1-4.1,8.2l-8.1,2.7l5.4,16.1
|
||||||
|
l8.1-2.7c3.4-1.1,7.1,0.7,8.2,4.1C165.5,140.6,163.7,144.3,160.3,145.4z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.1 KiB |
10
images/dark/git-icon-orange.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 93 93" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<path fill="#F05133" d="M90,41.8L49.9,1.7c-2.3-2.3-6.1-2.3-8.4,0L33.2,10l10.6,10.6c2.5-0.8,5.3-0.3,7.2,1.7c2,2,2.5,4.8,1.7,7.3
|
||||||
|
l10.2,10.2c2.5-0.8,5.3-0.3,7.3,1.7c2.8,2.7,2.8,7.2,0,10s-7.2,2.8-10,0c-2.1-2.1-2.6-5.1-1.5-7.7l-9.5-9.5v25
|
||||||
|
c0.7,0.3,1.3,0.8,1.9,1.3c2.8,2.7,2.8,7.2,0,10c-2.8,2.7-7.2,2.7-10,0c-2.8-2.8-2.8-7.2,0-10c0.7-0.7,1.5-1.2,2.3-1.5V33.8
|
||||||
|
c-0.8-0.3-1.6-0.9-2.3-1.5c-2.1-2.1-2.6-5.1-1.5-7.7L29.2,14.2L1.7,41.7c-2.3,2.3-2.3,6.1,0,8.4l40.1,40.1c2.3,2.3,6.1,2.3,8.4,0
|
||||||
|
l39.9-39.9C92.4,47.9,92.4,44.1,90,41.8z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 795 B |
12
images/dark/git-icon-progress.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 93 93" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<path fill="#FFFFFF" fill-opacity="0.74" d="M90,41.8L49.9,1.7c-2.3-2.3-6.1-2.3-8.4,0L33.2,10l10.6,10.6c2.5-0.8,5.3-0.3,7.2,1.7c2,2,2.5,4.8,1.7,7.3
|
||||||
|
l10.2,10.2c2.5-0.8,5.3-0.3,7.3,1.7c2.8,2.7,2.8,7.2,0,10s-7.2,2.8-10,0c-2.1-2.1-2.6-5.1-1.5-7.7l-9.5-9.5v25
|
||||||
|
c0.7,0.3,1.3,0.8,1.9,1.3c2.8,2.7,2.8,7.2,0,10c-2.8,2.7-7.2,2.7-10,0c-2.8-2.8-2.8-7.2,0-10c0.7-0.7,1.5-1.2,2.3-1.5V33.8
|
||||||
|
c-0.8-0.3-1.6-0.9-2.3-1.5c-2.1-2.1-2.6-5.1-1.5-7.7L29.2,14.2L1.7,41.7c-2.3,2.3-2.3,6.1,0,8.4l40.1,40.1c2.3,2.3,6.1,2.3,8.4,0
|
||||||
|
l39.9-39.9C92.4,47.9,92.4,44.1,90,41.8z">
|
||||||
|
<animate attributeName="fill" values="#FFFFFF;#F05133;#FFFFFF" dur="1s" repeatCount="indefinite"/>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 941 B |
|
Before Width: | Height: | Size: 815 B After Width: | Height: | Size: 815 B |
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18" xml:space="preserve">
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18" xml:space="preserve">
|
||||||
<g>
|
<g>
|
||||||
<rect fill="#FFFFFF" fill-opacity="0.75" x="1" y="0" width="4" height="18"/>
|
<rect fill="#00bcf2" fill-opacity="0.6" x="7" y="0" width="3" height="18"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 312 B |
5
images/dark/icon-add.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#C5C5C5" x="6.5" y="5.5" width="3" height="11"/>
|
||||||
|
<rect fill="#C5C5C5" x="2.5" y="9.5" width="11" height="3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 253 B |
4
images/dark/icon-branch.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m13,8c0,-1.11 -0.89,-2 -2,-2c-1.11,0 -2,0.89 -2,2c0,0.73 0.41,1.38 1,1.72l0,0.3c-0.02,0.52 -0.23,0.98 -0.63,1.38c-0.4,0.4 -0.86,0.61 -1.38,0.63c-0.83,0.02 -1.48,0.16 -2,0.45l0,-4.76c0.59,-0.34 1,-0.98 1,-1.72c0,-1.11 -0.89,-2 -2,-2c-1.11,0 -1.99,0.89 -1.99,2c0,0.73 0.41,1.38 1,1.72l0,6.56c-0.59,0.35 -1,0.99 -1,1.72c0,1.11 0.89,2 2,2c1.11,0 2,-0.89 2,-2c0,-0.53 -0.2,-1 -0.53,-1.36c0.09,-0.06 0.48,-0.41 0.59,-0.47c0.25,-0.11 0.56,-0.17 0.94,-0.17c1.05,-0.05 1.95,-0.45 2.75,-1.25c0.8,-0.8 1.2,-1.98 1.25,-3.02l-0.02,0c0.61,-0.36 1.02,-1 1.02,-1.73l0,0zm-8,-3.2c0.66,0 1.2,0.55 1.2,1.2c0,0.65 -0.55,1.2 -1.2,1.2c-0.65,0 -1.2,-0.55 -1.2,-1.2c0,-0.65 0.55,-1.2 1.2,-1.2l0,0zm0,12.41c-0.66,0 -1.2,-0.55 -1.2,-1.2c0,-0.65 0.55,-1.2 1.2,-1.2c0.65,0 1.2,0.55 1.2,1.2c0,0.65 -0.55,1.2 -1.2,1.2l0,0zm6,-8c-0.66,0 -1.2,-0.55 -1.2,-1.2c0,-0.65 0.55,-1.2 1.2,-1.2c0.65,0 1.2,0.55 1.2,1.2c0,0.65 -0.55,1.2 -1.2,1.2l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
4
images/dark/icon-commit.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m11.86,10c-0.45,-1.72 -2,-3 -3.86,-3c-1.86,0 -3.41,1.28 -3.86,3l-3.14,0l0,2l3.14,0c0.45,1.72 2,3 3.86,3c1.86,0 3.41,-1.28 3.86,-3l3.14,0l0,-2l-3.14,0zm-3.86,3.2c-1.22,0 -2.2,-0.98 -2.2,-2.2c0,-1.22 0.98,-2.2 2.2,-2.2c1.22,0 2.2,0.98 2.2,2.2c0,1.22 -0.98,2.2 -2.2,2.2z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 428 B |
4
images/dark/icon-download.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m9,15l2,0l-3,3l-3,-3l2,0l0,-5l2,0l0,5l0,0zm3,-8c0,-0.44 -0.91,-3 -4.5,-3c-2.42,0 -4.5,1.92 -4.5,4c-1.98,0 -3,1.52 -3,3c0,1.53 1,3 3,3l3,0l0,-1.3l-3,0c-1.62,0 -1.7,-1.42 -1.7,-1.7c0,-0.17 0.05,-1.7 1.7,-1.7l1.3,0l0,-1.3c0,-1.39 1.56,-2.7 3.2,-2.7c2.55,0 3.13,1.55 3.2,1.8l0,1.2l1.3,0c0.81,0 2.7,0.22 2.7,2.2c0,2.09 -2.25,2.2 -2.7,2.2l-2,0l0,1.3l2,0c2.08,0 4,-1.16 4,-3.5c0,-2.44 -1.92,-3.5 -4,-3.5l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 557 B |
4
images/dark/icon-history.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m9,16l-2,0l0,-7l5,0l0,2l-3,0l0,5l0,0zm-1,-12c-2.19,0 -4.13,1.02 -5.41,2.59l-1.59,-1.59l0,4l4,0l-1.5,-1.5c1.05,-1.33 2.67,-2.2 4.5,-2.2c3.14,0 5.7,2.56 5.7,5.7c0,3.14 -2.56,5.7 -5.7,5.7c-3.14,0 -5.7,-2.56 -5.7,-5.7c0,-0.34 0.03,-0.67 0.09,-1l-1.31,0c-0.05,0.33 -0.08,0.66 -0.08,1c0,3.86 3.14,7 7,7c3.86,0 7,-3.14 7,-7c0,-3.86 -3.14,-7 -7,-7l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 500 B |
4
images/dark/icon-refresh.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m12.901149,9.091851l-0.094,0.071c-0.269,0.333 -0.746,1.096 -0.91,2.375c0.057,0.277 0.092,0.495 0.092,0.545c0,2.206 -1.794,4 -4,4a3.986,3.986 0 0 1 -2.817,-1.164a3.987,3.987 0 0 1 -1.163,-2.815c0,-2.206 1.794,-4 4,-4l0.351,0.025l0,1.85s1.626,-1.342 1.631,-1.339l1.869,-1.577l-3.5,-2.917l0,2.218l-0.371,-0.03a5.75,5.75 0 0 0 -4.055,9.826a5.75,5.75 0 0 0 9.826,-4.056a5.725,5.725 0 0 0 -0.859,-3.012z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 553 B |
4
images/dark/icon-remote.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m12.519592,15.157073l-9.039184,0c-1.367793,0 -2.480539,-1.112828 -2.480539,-2.480622s1.112746,-2.480539 2.480539,-2.480539l0.281314,0c0.349178,-0.47173 0.888523,-0.771191 1.476341,-0.815025c0.321917,-1.471535 1.619609,-2.537962 3.165139,-2.537962c1.221543,0 2.341663,0.699516 2.889377,1.772488c0.083027,-0.008618 0.166386,-0.012926 0.250076,-0.012926c1.190221,0 2.194667,0.874105 2.378785,2.027867c0.670514,0.460047 1.07869,1.225023 1.07869,2.046096c0,1.367793 -1.112828,2.480622 -2.480539,2.480622z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 655 B |
5
images/dark/icon-repo-green.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
|
||||||
|
<ellipse fill="#32cd32" stroke="#C5C5C5" stroke-width="0.5" rx="3" ry="3" cx="13" cy="4" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 586 B |
5
images/dark/icon-repo-red.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
|
||||||
|
<ellipse fill="#cd3131" stroke="#C5C5C5" stroke-width="0.5" rx="3" ry="3" cx="13" cy="4" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 586 B |
5
images/dark/icon-repo-yellow.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
|
||||||
|
<ellipse fill="#cdcd32" stroke="#C5C5C5" stroke-width="0.5" rx="3" ry="3" cx="13" cy="4" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 586 B |
4
images/dark/icon-repo.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 490 B |
4
images/dark/icon-search.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m14.414133,16.199437l-3.415492,-3.433421c0.627518,-0.878526 0.995065,-1.945307 0.995065,-3.110698c0,-2.967265 -2.411463,-5.378728 -5.378728,-5.378728c-2.967265,0 -5.378728,2.411463 -5.378728,5.378728c0,2.967265 2.411463,5.378728 5.378728,5.378728c1.165391,0 2.223207,-0.367546 3.110698,-0.995065l3.433421,3.415492c0.170326,0.179291 0.403405,0.268936 0.627518,0.268936c0.224114,0 0.466156,-0.080681 0.627518,-0.268936c0.349617,-0.349617 0.349617,-0.914384 0,-1.264001l0,0.008965zm-7.799155,-2.330782c-2.321817,0 -4.213337,-1.891519 -4.213337,-4.213337c0,-2.321817 1.891519,-4.213337 4.213337,-4.213337c2.321817,0 4.213337,1.891519 4.213337,4.213337c0,2.321817 -1.891519,4.213337 -4.213337,4.213337l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 857 B |
4
images/dark/icon-stash.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16px" height="22px">
|
||||||
|
<path fill="#C5C5C5" d="m14.687501,11.955358l-1.079554,-6.821251c-0.076429,-0.458572 -0.477679,-0.821607 -0.955357,-0.821607l-9.30518,0c-0.477679,0 -0.878929,0.363036 -0.955357,0.821607l-1.079554,6.821251l0,4.776787c0,0.525447 0.429911,0.955357 0.955357,0.955357l11.464288,0c0.525447,0 0.955357,-0.429911 0.955357,-0.955357l0,-4.776787l0,0zm-3.133572,0.525447l-0.420357,0.850268c-0.162411,0.324821 -0.496786,0.535 -0.869375,0.535l-4.547501,0c-0.363036,0 -0.687857,-0.210179 -0.850268,-0.525447l-0.420357,-0.869375c-0.162411,-0.315268 -0.496786,-0.525447 -0.850268,-0.525447l-1.327947,0l0.955357,-6.687501l9.553573,0l0.955357,6.687501l-1.318393,0c-0.372589,0 -0.697411,0.210179 -0.869375,0.525447l0.009554,0.009554z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 893 B |
6
images/dark/icon-status-added.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#3c8746" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
A
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/dark/icon-status-conflict.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#7F4E7E" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
C
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/dark/icon-status-copied.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#692C77" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
C
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/dark/icon-status-deleted.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#9E121D" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
D
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/dark/icon-status-ignored.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#969696" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
I
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/dark/icon-status-modified.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#1B80B2" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
M
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/dark/icon-status-renamed.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#CC6633" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
R
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/dark/icon-status-untracked.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#6C6C6C" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
U
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
4
images/dark/icon-upload.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#C5C5C5" d="m7,12l-2,0l3,-3l3,3l-2,0l0,5l-2,0l0,-5l0,0zm5,-4c0,-0.44 -0.91,-3 -4.5,-3c-2.42,0 -4.5,1.92 -4.5,4c-1.98,0 -3,1.52 -3,3c0,1.53 1,3 3,3l3,0l0,-1.3l-3,0c-1.62,0 -1.7,-1.42 -1.7,-1.7c0,-0.17 0.05,-1.7 1.7,-1.7l1.3,0l0,-1.3c0,-1.39 1.56,-2.7 3.2,-2.7c2.55,0 3.13,1.55 3.2,1.8l0,1.2l1.3,0c0.81,0 2.7,0.22 2.7,2.2c0,2.09 -2.25,2.2 -2.7,2.2l-2,0l0,1.3l2,0c2.08,0 4,-1.16 4,-3.5c0,-2.44 -1.92,-3.5 -4,-3.5l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 558 B |
BIN
images/gitlens-icon.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
images/gitlens-preview-full.gif
Normal file
|
After Width: | Height: | Size: 5.2 MiB |
BIN
images/gitlens-preview.gif
Normal file
|
After Width: | Height: | Size: 2.7 MiB |
10
images/light/git-icon-orange.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 93 93" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<path fill="#F05133" d="M90,41.8L49.9,1.7c-2.3-2.3-6.1-2.3-8.4,0L33.2,10l10.6,10.6c2.5-0.8,5.3-0.3,7.2,1.7c2,2,2.5,4.8,1.7,7.3
|
||||||
|
l10.2,10.2c2.5-0.8,5.3-0.3,7.3,1.7c2.8,2.7,2.8,7.2,0,10s-7.2,2.8-10,0c-2.1-2.1-2.6-5.1-1.5-7.7l-9.5-9.5v25
|
||||||
|
c0.7,0.3,1.3,0.8,1.9,1.3c2.8,2.7,2.8,7.2,0,10c-2.8,2.7-7.2,2.7-10,0c-2.8-2.8-2.8-7.2,0-10c0.7-0.7,1.5-1.2,2.3-1.5V33.8
|
||||||
|
c-0.8-0.3-1.6-0.9-2.3-1.5c-2.1-2.1-2.6-5.1-1.5-7.7L29.2,14.2L1.7,41.7c-2.3,2.3-2.3,6.1,0,8.4l40.1,40.1c2.3,2.3,6.1,2.3,8.4,0
|
||||||
|
l39.9-39.9C92.4,47.9,92.4,44.1,90,41.8z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 795 B |
13
images/light/git-icon-progress.svg
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 93 93" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<path fill="#000000" fill-opacity="0.6" d="M90,41.8L49.9,1.7c-2.3-2.3-6.1-2.3-8.4,0L33.2,10l10.6,10.6c2.5-0.8,5.3-0.3,7.2,1.7c2,2,2.5,4.8,1.7,7.3
|
||||||
|
l10.2,10.2c2.5-0.8,5.3-0.3,7.3,1.7c2.8,2.7,2.8,7.2,0,10s-7.2,2.8-10,0c-2.1-2.1-2.6-5.1-1.5-7.7l-9.5-9.5v25
|
||||||
|
c0.7,0.3,1.3,0.8,1.9,1.3c2.8,2.7,2.8,7.2,0,10c-2.8,2.7-7.2,2.7-10,0c-2.8-2.8-2.8-7.2,0-10c0.7-0.7,1.5-1.2,2.3-1.5V33.8
|
||||||
|
c-0.8-0.3-1.6-0.9-2.3-1.5c-2.1-2.1-2.6-5.1-1.5-7.7L29.2,14.2L1.7,41.7c-2.3,2.3-2.3,6.1,0,8.4l40.1,40.1c2.3,2.3,6.1,2.3,8.4,0
|
||||||
|
l39.9-39.9C92.4,47.9,92.4,44.1,90,41.8z">
|
||||||
|
<animate attributeName="fill" values="#000000;#F05133;#000000" dur="1s" repeatCount="indefinite"/>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 941 B |
|
Before Width: | Height: | Size: 814 B After Width: | Height: | Size: 814 B |
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18" xml:space="preserve">
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18" xml:space="preserve">
|
||||||
<g>
|
<g>
|
||||||
<rect fill="#000000" fill-opacity="0.75" x="1" y="0" width="4" height="18"/>
|
<rect fill="#00bcf2" fill-opacity="0.6" x="7" y="0" width="3" height="18"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 314 B After Width: | Height: | Size: 313 B |
5
images/light/icon-add.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#424242" x="6.5" y="5.5" width="3" height="11"/>
|
||||||
|
<rect fill="#424242" x="2.5" y="9.5" width="11" height="3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 253 B |
4
images/light/icon-branch.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m13,8c0,-1.11 -0.89,-2 -2,-2c-1.11,0 -2,0.89 -2,2c0,0.73 0.41,1.38 1,1.72l0,0.3c-0.02,0.52 -0.23,0.98 -0.63,1.38c-0.4,0.4 -0.86,0.61 -1.38,0.63c-0.83,0.02 -1.48,0.16 -2,0.45l0,-4.76c0.59,-0.34 1,-0.98 1,-1.72c0,-1.11 -0.89,-2 -2,-2c-1.11,0 -1.99,0.89 -1.99,2c0,0.73 0.41,1.38 1,1.72l0,6.56c-0.59,0.35 -1,0.99 -1,1.72c0,1.11 0.89,2 2,2c1.11,0 2,-0.89 2,-2c0,-0.53 -0.2,-1 -0.53,-1.36c0.09,-0.06 0.48,-0.41 0.59,-0.47c0.25,-0.11 0.56,-0.17 0.94,-0.17c1.05,-0.05 1.95,-0.45 2.75,-1.25c0.8,-0.8 1.2,-1.98 1.25,-3.02l-0.02,0c0.61,-0.36 1.02,-1 1.02,-1.73l0,0zm-8,-3.2c0.66,0 1.2,0.55 1.2,1.2c0,0.65 -0.55,1.2 -1.2,1.2c-0.65,0 -1.2,-0.55 -1.2,-1.2c0,-0.65 0.55,-1.2 1.2,-1.2l0,0zm0,12.41c-0.66,0 -1.2,-0.55 -1.2,-1.2c0,-0.65 0.55,-1.2 1.2,-1.2c0.65,0 1.2,0.55 1.2,1.2c0,0.65 -0.55,1.2 -1.2,1.2l0,0zm6,-8c-0.66,0 -1.2,-0.55 -1.2,-1.2c0,-0.65 0.55,-1.2 1.2,-1.2c0.65,0 1.2,0.55 1.2,1.2c0,0.65 -0.55,1.2 -1.2,1.2l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
4
images/light/icon-commit.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m11.86,10c-0.45,-1.72 -2,-3 -3.86,-3c-1.86,0 -3.41,1.28 -3.86,3l-3.14,0l0,2l3.14,0c0.45,1.72 2,3 3.86,3c1.86,0 3.41,-1.28 3.86,-3l3.14,0l0,-2l-3.14,0zm-3.86,3.2c-1.22,0 -2.2,-0.98 -2.2,-2.2c0,-1.22 0.98,-2.2 2.2,-2.2c1.22,0 2.2,0.98 2.2,2.2c0,1.22 -0.98,2.2 -2.2,2.2z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 428 B |
4
images/light/icon-download.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m9,15l2,0l-3,3l-3,-3l2,0l0,-5l2,0l0,5l0,0zm3,-8c0,-0.44 -0.91,-3 -4.5,-3c-2.42,0 -4.5,1.92 -4.5,4c-1.98,0 -3,1.52 -3,3c0,1.53 1,3 3,3l3,0l0,-1.3l-3,0c-1.62,0 -1.7,-1.42 -1.7,-1.7c0,-0.17 0.05,-1.7 1.7,-1.7l1.3,0l0,-1.3c0,-1.39 1.56,-2.7 3.2,-2.7c2.55,0 3.13,1.55 3.2,1.8l0,1.2l1.3,0c0.81,0 2.7,0.22 2.7,2.2c0,2.09 -2.25,2.2 -2.7,2.2l-2,0l0,1.3l2,0c2.08,0 4,-1.16 4,-3.5c0,-2.44 -1.92,-3.5 -4,-3.5l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 557 B |
4
images/light/icon-history.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m9,16l-2,0l0,-7l5,0l0,2l-3,0l0,5l0,0zm-1,-12c-2.19,0 -4.13,1.02 -5.41,2.59l-1.59,-1.59l0,4l4,0l-1.5,-1.5c1.05,-1.33 2.67,-2.2 4.5,-2.2c3.14,0 5.7,2.56 5.7,5.7c0,3.14 -2.56,5.7 -5.7,5.7c-3.14,0 -5.7,-2.56 -5.7,-5.7c0,-0.34 0.03,-0.67 0.09,-1l-1.31,0c-0.05,0.33 -0.08,0.66 -0.08,1c0,3.86 3.14,7 7,7c3.86,0 7,-3.14 7,-7c0,-3.86 -3.14,-7 -7,-7l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 500 B |
4
images/light/icon-refresh.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m12.901149,9.091851l-0.094,0.071c-0.269,0.333 -0.746,1.096 -0.91,2.375c0.057,0.277 0.092,0.495 0.092,0.545c0,2.206 -1.794,4 -4,4a3.986,3.986 0 0 1 -2.817,-1.164a3.987,3.987 0 0 1 -1.163,-2.815c0,-2.206 1.794,-4 4,-4l0.351,0.025l0,1.85s1.626,-1.342 1.631,-1.339l1.869,-1.577l-3.5,-2.917l0,2.218l-0.371,-0.03a5.75,5.75 0 0 0 -4.055,9.826a5.75,5.75 0 0 0 9.826,-4.056a5.725,5.725 0 0 0 -0.859,-3.012z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 553 B |
4
images/light/icon-remote.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m12.519592,15.157073l-9.039184,0c-1.367793,0 -2.480539,-1.112828 -2.480539,-2.480622s1.112746,-2.480539 2.480539,-2.480539l0.281314,0c0.349178,-0.47173 0.888523,-0.771191 1.476341,-0.815025c0.321917,-1.471535 1.619609,-2.537962 3.165139,-2.537962c1.221543,0 2.341663,0.699516 2.889377,1.772488c0.083027,-0.008618 0.166386,-0.012926 0.250076,-0.012926c1.190221,0 2.194667,0.874105 2.378785,2.027867c0.670514,0.460047 1.07869,1.225023 1.07869,2.046096c0,1.367793 -1.112828,2.480622 -2.480539,2.480622z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 655 B |
5
images/light/icon-repo-green.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
|
||||||
|
<ellipse fill="#32cd32" stroke="#424242" stroke-width="0.5" rx="3" ry="3" cx="13" cy="4" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 586 B |
5
images/light/icon-repo-red.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
|
||||||
|
<ellipse fill="#cd3131" stroke="#424242" stroke-width="0.5" rx="3" ry="3" cx="13" cy="4" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 586 B |
5
images/light/icon-repo-yellow.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
|
||||||
|
<ellipse fill="#cdcd32" stroke="#424242" stroke-width="0.5" rx="3" ry="3" cx="13" cy="4" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 586 B |
4
images/light/icon-repo.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m6,12l-1,0l0,-1l1,0l0,1l0,0zm0,-3l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm0,-2l-1,0l0,1l1,0l0,-1l0,0zm8,-1l0,12c0,0.55 -0.45,1 -1,1l-5,0l0,2l-1.5,-1.5l-1.5,1.5l0,-2l-2,0c-0.55,0 -1,-0.45 -1,-1l0,-12c0,-0.55 0.45,-1 1,-1l10,0c0.55,0 1,0.45 1,1l0,0zm-1,10l-10,0l0,2l2,0l0,-1l3,0l0,1l5,0l0,-2l0,0zm0,-10l-9,0l0,9l9,0l0,-9l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 490 B |
4
images/light/icon-search.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m14.414133,16.199437l-3.415492,-3.433421c0.627518,-0.878526 0.995065,-1.945307 0.995065,-3.110698c0,-2.967265 -2.411463,-5.378728 -5.378728,-5.378728c-2.967265,0 -5.378728,2.411463 -5.378728,5.378728c0,2.967265 2.411463,5.378728 5.378728,5.378728c1.165391,0 2.223207,-0.367546 3.110698,-0.995065l3.433421,3.415492c0.170326,0.179291 0.403405,0.268936 0.627518,0.268936c0.224114,0 0.466156,-0.080681 0.627518,-0.268936c0.349617,-0.349617 0.349617,-0.914384 0,-1.264001l0,0.008965zm-7.799155,-2.330782c-2.321817,0 -4.213337,-1.891519 -4.213337,-4.213337c0,-2.321817 1.891519,-4.213337 4.213337,-4.213337c2.321817,0 4.213337,1.891519 4.213337,4.213337c0,2.321817 -1.891519,4.213337 -4.213337,4.213337l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 857 B |
4
images/light/icon-stash.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16px" height="22px">
|
||||||
|
<path fill="#424242" d="m14.687501,11.955358l-1.079554,-6.821251c-0.076429,-0.458572 -0.477679,-0.821607 -0.955357,-0.821607l-9.30518,0c-0.477679,0 -0.878929,0.363036 -0.955357,0.821607l-1.079554,6.821251l0,4.776787c0,0.525447 0.429911,0.955357 0.955357,0.955357l11.464288,0c0.525447,0 0.955357,-0.429911 0.955357,-0.955357l0,-4.776787l0,0zm-3.133572,0.525447l-0.420357,0.850268c-0.162411,0.324821 -0.496786,0.535 -0.869375,0.535l-4.547501,0c-0.363036,0 -0.687857,-0.210179 -0.850268,-0.525447l-0.420357,-0.869375c-0.162411,-0.315268 -0.496786,-0.525447 -0.850268,-0.525447l-1.327947,0l0.955357,-6.687501l9.553573,0l0.955357,6.687501l-1.318393,0c-0.372589,0 -0.697411,0.210179 -0.869375,0.525447l0.009554,0.009554z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 893 B |
6
images/light/icon-status-added.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#2d883e" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
A
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/light/icon-status-conflict.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#9B4F96" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
C
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/light/icon-status-copied.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#682079" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
C
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/light/icon-status-deleted.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#B9131A" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
D
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/light/icon-status-ignored.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#969696" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
I
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/light/icon-status-modified.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#007ACC" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
M
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/light/icon-status-renamed.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#CC6633" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
R
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
6
images/light/icon-status-untracked.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect fill="#6C6C6C" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||||
|
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
U
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 431 B |
4
images/light/icon-upload.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="16" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#424242" d="m7,12l-2,0l3,-3l3,3l-2,0l0,5l-2,0l0,-5l0,0zm5,-4c0,-0.44 -0.91,-3 -4.5,-3c-2.42,0 -4.5,1.92 -4.5,4c-1.98,0 -3,1.52 -3,3c0,1.53 1,3 3,3l3,0l0,-1.3l-3,0c-1.62,0 -1.7,-1.42 -1.7,-1.7c0,-0.17 0.05,-1.7 1.7,-1.7l1.3,0l0,-1.3c0,-1.39 1.56,-2.7 3.2,-2.7c2.55,0 3.13,1.55 3.2,1.8l0,1.2l1.3,0c0.81,0 2.7,0.22 2.7,2.2c0,2.09 -2.25,2.2 -2.7,2.2l-2,0l0,1.3l2,0c2.08,0 4,-1.16 4,-3.5c0,-2.44 -1.92,-3.5 -4,-3.5l0,0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 558 B |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 72 KiB |
BIN
images/screenshot-code-lens.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 52 KiB |
BIN
images/screenshot-file-blame-annotations.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 64 KiB |
BIN
images/screenshot-git-custom-view-history.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
images/screenshot-git-custom-view-repository.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
images/screenshot-git-stashes.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
images/screenshot-line-blame-annotation.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
images/screenshot-line-blame-annotations.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 12 KiB |
BIN
images/screenshot-status-bar.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
1370
package-lock.json
generated
1386
package.json
14
src/@types/applicationinsights/index.d.ts
vendored
@@ -16,9 +16,9 @@ interface AutoCollectConsole {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface AutoCollectExceptions {
|
interface AutoCollectExceptions {
|
||||||
constructor(client:Client): AutoCollectExceptions;
|
constructor(client: Client): AutoCollectExceptions;
|
||||||
isInitialized(): boolean;
|
isInitialized(): boolean;
|
||||||
enable(isEnabled:boolean): void;
|
enable(isEnabled: boolean): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AutoCollectPerformance {
|
interface AutoCollectPerformance {
|
||||||
@@ -348,7 +348,7 @@ interface Client {
|
|||||||
* @param max the max sample for this set
|
* @param max the max sample for this set
|
||||||
* @param stdDev the standard deviation of the set
|
* @param stdDev the standard deviation of the set
|
||||||
*/
|
*/
|
||||||
trackMetric(name: string, value: number, count?:number, min?: number, max?: number, stdDev?: number, properties?: {
|
trackMetric(name: string, value: number, count?: number, min?: number, max?: number, stdDev?: number, properties?: {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
}): void;
|
}): void;
|
||||||
|
|
||||||
@@ -374,7 +374,8 @@ interface Client {
|
|||||||
* @param error An error that was returned for this request if it was unsuccessful. Defaults to null.
|
* @param error An error that was returned for this request if it was unsuccessful. Defaults to null.
|
||||||
*/
|
*/
|
||||||
trackRequestSync(request: any /*http.IncomingMessage */, response: any /*http.ServerResponse */, ellapsedMilliseconds?: number, properties?: {
|
trackRequestSync(request: any /*http.IncomingMessage */, response: any /*http.ServerResponse */, ellapsedMilliseconds?: number, properties?: {
|
||||||
[key: string]: string;}, error?: any) : void;
|
[key: string]: string;
|
||||||
|
}, error?: any): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log information about a dependency of your app. Typically used to track the time database calls or outgoing http requests take from your server.
|
* Log information about a dependency of your app. Typically used to track the time database calls or outgoing http requests take from your server.
|
||||||
@@ -503,6 +504,11 @@ interface ApplicationInsights {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
setOfflineMode(value: boolean, resentIntervall?: number): ApplicationInsights;
|
setOfflineMode(value: boolean, resentIntervall?: number): ApplicationInsights;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
setAutoDependencyCorrelation(value: boolean): ApplicationInsights;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module "applicationinsights" {
|
declare module "applicationinsights" {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
import { Functions } from './system';
|
||||||
import { commands, Disposable, TextEditor, window } from 'vscode';
|
import { commands, Disposable, TextEditor, window } from 'vscode';
|
||||||
import { BuiltInCommands } from './constants';
|
import { BuiltInCommands } from './constants';
|
||||||
|
|
||||||
@@ -11,19 +12,20 @@ export class ActiveEditorTracker extends Disposable {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super(() => this.dispose());
|
super(() => this.dispose());
|
||||||
|
|
||||||
this._disposable = window.onDidChangeActiveTextEditor(e => this._resolver && this._resolver(e));
|
const fn = Functions.debounce((e: TextEditor) => this._resolver && this._resolver(e), 50);
|
||||||
|
this._disposable = window.onDidChangeActiveTextEditor(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this._disposable && this._disposable.dispose();
|
this._disposable && this._disposable.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
async awaitClose(timeout: number = 500): Promise<TextEditor> {
|
async awaitClose(timeout: number = 500): Promise<TextEditor | undefined> {
|
||||||
this.close();
|
this.close();
|
||||||
return this.wait(timeout);
|
return this.wait(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
async awaitNext(timeout: number = 500): Promise<TextEditor> {
|
async awaitNext(timeout: number = 500): Promise<TextEditor | undefined> {
|
||||||
this.next();
|
this.next();
|
||||||
return this.wait(timeout);
|
return this.wait(timeout);
|
||||||
}
|
}
|
||||||
@@ -36,15 +38,15 @@ export class ActiveEditorTracker extends Disposable {
|
|||||||
return commands.executeCommand(BuiltInCommands.NextEditor);
|
return commands.executeCommand(BuiltInCommands.NextEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
async wait(timeout: number = 500): Promise<TextEditor> {
|
async wait(timeout: number = 500): Promise<TextEditor | undefined> {
|
||||||
const editor = await new Promise<TextEditor>((resolve, reject) => {
|
const editor = await new Promise<TextEditor>((resolve, reject) => {
|
||||||
let timer: any;
|
let timer: any;
|
||||||
|
|
||||||
this._resolver = (editor: TextEditor) => {
|
this._resolver = (e: TextEditor) => {
|
||||||
if (timer) {
|
if (timer) {
|
||||||
clearTimeout(timer as any);
|
clearTimeout(timer as any);
|
||||||
timer = 0;
|
timer = 0;
|
||||||
resolve(editor);
|
resolve(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -53,6 +55,7 @@ export class ActiveEditorTracker extends Disposable {
|
|||||||
timer = 0;
|
timer = 0;
|
||||||
}, timeout) as any;
|
}, timeout) as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
this._resolver = undefined;
|
this._resolver = undefined;
|
||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
|
|||||||
406
src/annotations/annotationController.ts
Normal file
@@ -0,0 +1,406 @@
|
|||||||
|
'use strict';
|
||||||
|
import { Functions, Objects } from '../system';
|
||||||
|
import { DecorationRenderOptions, Disposable, Event, EventEmitter, ExtensionContext, OverviewRulerLane, Progress, ProgressLocation, TextDocument, TextDocumentChangeEvent, TextEditor, TextEditorDecorationType, TextEditorViewColumnChangeEvent, window, workspace } from 'vscode';
|
||||||
|
import { AnnotationProviderBase } from './annotationProvider';
|
||||||
|
import { Keyboard, KeyboardScope, KeyCommand, Keys } from '../keyboard';
|
||||||
|
import { TextDocumentComparer, TextEditorComparer } from '../comparers';
|
||||||
|
import { ExtensionKey, IConfig, LineHighlightLocations, themeDefaults } from '../configuration';
|
||||||
|
import { CommandContext, setCommandContext } from '../constants';
|
||||||
|
import { BlameabilityChangeEvent, GitContextTracker, GitService, GitUri } from '../gitService';
|
||||||
|
import { GutterBlameAnnotationProvider } from './gutterBlameAnnotationProvider';
|
||||||
|
import { HoverBlameAnnotationProvider } from './hoverBlameAnnotationProvider';
|
||||||
|
import { Logger } from '../logger';
|
||||||
|
import { RecentChangesAnnotationProvider } from './recentChangesAnnotationProvider';
|
||||||
|
import { WhitespaceController } from './whitespaceController';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
export type FileAnnotationType = 'gutter' | 'hover' | 'recentChanges';
|
||||||
|
export const FileAnnotationType = {
|
||||||
|
Gutter: 'gutter' as FileAnnotationType,
|
||||||
|
Hover: 'hover' as FileAnnotationType,
|
||||||
|
RecentChanges: 'recentChanges' as FileAnnotationType
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Decorations = {
|
||||||
|
blameAnnotation: window.createTextEditorDecorationType({
|
||||||
|
isWholeLine: true,
|
||||||
|
textDecoration: 'none'
|
||||||
|
} as DecorationRenderOptions),
|
||||||
|
blameHighlight: undefined as TextEditorDecorationType | undefined,
|
||||||
|
recentChangesAnnotation: undefined as TextEditorDecorationType | undefined,
|
||||||
|
recentChangesHighlight: undefined as TextEditorDecorationType | undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
export class AnnotationController extends Disposable {
|
||||||
|
|
||||||
|
private _onDidToggleAnnotations = new EventEmitter<void>();
|
||||||
|
get onDidToggleAnnotations(): Event<void> {
|
||||||
|
return this._onDidToggleAnnotations.event;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _annotationsDisposable: Disposable | undefined;
|
||||||
|
private _annotationProviders: Map<number, AnnotationProviderBase> = new Map();
|
||||||
|
private _config: IConfig;
|
||||||
|
private _disposable: Disposable;
|
||||||
|
private _whitespaceController: WhitespaceController | undefined;
|
||||||
|
|
||||||
|
constructor(private context: ExtensionContext, private git: GitService, private gitContextTracker: GitContextTracker) {
|
||||||
|
super(() => this.dispose());
|
||||||
|
|
||||||
|
this._onConfigurationChanged();
|
||||||
|
|
||||||
|
const subscriptions: Disposable[] = [];
|
||||||
|
|
||||||
|
subscriptions.push(workspace.onDidChangeConfiguration(this._onConfigurationChanged, this));
|
||||||
|
|
||||||
|
this._disposable = Disposable.from(...subscriptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this._annotationProviders.forEach(async (p, i) => await this.clear(i));
|
||||||
|
|
||||||
|
Decorations.blameAnnotation && Decorations.blameAnnotation.dispose();
|
||||||
|
Decorations.blameHighlight && Decorations.blameHighlight.dispose();
|
||||||
|
|
||||||
|
this._annotationsDisposable && this._annotationsDisposable.dispose();
|
||||||
|
this._whitespaceController && this._whitespaceController.dispose();
|
||||||
|
this._disposable && this._disposable.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onConfigurationChanged() {
|
||||||
|
let toggleWhitespace = workspace.getConfiguration(`${ExtensionKey}.advanced.toggleWhitespace`).get<boolean>('enabled');
|
||||||
|
// Until https://github.com/Microsoft/vscode/issues/11485 is fixed we need to toggle whitespace for non-monospace fonts and ligatures
|
||||||
|
// TODO: detect monospace vs non-monospace font
|
||||||
|
|
||||||
|
// if (!toggleWhitespace) {
|
||||||
|
// // Since we know ligatures will break the whitespace rendering -- turn it back on
|
||||||
|
// toggleWhitespace = workspace.getConfiguration('editor').get<boolean>('fontLigatures', false);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// If the setting is on and we aren't showing any annotations, make sure it is necessary (i.e. only when rendering whitespace)
|
||||||
|
if (toggleWhitespace && this._annotationProviders.size === 0) {
|
||||||
|
toggleWhitespace = (workspace.getConfiguration('editor').get<string>('renderWhitespace') !== 'none');
|
||||||
|
}
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
|
||||||
|
if (toggleWhitespace && this._whitespaceController === undefined) {
|
||||||
|
changed = true;
|
||||||
|
this._whitespaceController = new WhitespaceController();
|
||||||
|
}
|
||||||
|
else if (!toggleWhitespace && this._whitespaceController !== undefined) {
|
||||||
|
changed = true;
|
||||||
|
this._whitespaceController.dispose();
|
||||||
|
this._whitespaceController = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
|
||||||
|
const cfgBlameHighlight = cfg.blame.file.lineHighlight;
|
||||||
|
const cfgChangesHighlight = cfg.recentChanges.file.lineHighlight;
|
||||||
|
const cfgTheme = cfg.theme.lineHighlight;
|
||||||
|
|
||||||
|
if (!Objects.areEquivalent(cfgBlameHighlight, this._config && this._config.blame.file.lineHighlight) ||
|
||||||
|
!Objects.areEquivalent(cfgChangesHighlight, this._config && this._config.recentChanges.file.lineHighlight) ||
|
||||||
|
!Objects.areEquivalent(cfgTheme, this._config && this._config.theme.lineHighlight)) {
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
Decorations.blameHighlight && Decorations.blameHighlight.dispose();
|
||||||
|
|
||||||
|
if (cfgBlameHighlight.enabled) {
|
||||||
|
Decorations.blameHighlight = window.createTextEditorDecorationType({
|
||||||
|
gutterIconSize: 'contain',
|
||||||
|
isWholeLine: true,
|
||||||
|
overviewRulerLane: OverviewRulerLane.Right,
|
||||||
|
dark: {
|
||||||
|
backgroundColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.Line)
|
||||||
|
? cfgTheme.dark.backgroundColor || themeDefaults.lineHighlight.dark.backgroundColor
|
||||||
|
: undefined,
|
||||||
|
gutterIconPath: cfgBlameHighlight.locations.includes(LineHighlightLocations.Gutter)
|
||||||
|
? this.context.asAbsolutePath('images/dark/highlight-gutter.svg')
|
||||||
|
: undefined,
|
||||||
|
overviewRulerColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
|
||||||
|
? cfgTheme.dark.overviewRulerColor || themeDefaults.lineHighlight.dark.overviewRulerColor
|
||||||
|
: undefined
|
||||||
|
},
|
||||||
|
light: {
|
||||||
|
backgroundColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.Line)
|
||||||
|
? cfgTheme.light.backgroundColor || themeDefaults.lineHighlight.light.backgroundColor
|
||||||
|
: undefined,
|
||||||
|
gutterIconPath: cfgBlameHighlight.locations.includes(LineHighlightLocations.Gutter)
|
||||||
|
? this.context.asAbsolutePath('images/light/highlight-gutter.svg')
|
||||||
|
: undefined,
|
||||||
|
overviewRulerColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
|
||||||
|
? cfgTheme.light.overviewRulerColor || themeDefaults.lineHighlight.light.overviewRulerColor
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Decorations.blameHighlight = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
Decorations.recentChangesHighlight && Decorations.recentChangesHighlight.dispose();
|
||||||
|
|
||||||
|
Decorations.recentChangesHighlight = window.createTextEditorDecorationType({
|
||||||
|
gutterIconSize: 'contain',
|
||||||
|
isWholeLine: true,
|
||||||
|
overviewRulerLane: OverviewRulerLane.Right,
|
||||||
|
dark: {
|
||||||
|
backgroundColor: cfgChangesHighlight.locations.includes(LineHighlightLocations.Line)
|
||||||
|
? cfgTheme.dark.backgroundColor || themeDefaults.lineHighlight.dark.backgroundColor
|
||||||
|
: undefined,
|
||||||
|
gutterIconPath: cfgChangesHighlight.locations.includes(LineHighlightLocations.Gutter)
|
||||||
|
? this.context.asAbsolutePath('images/dark/highlight-gutter.svg')
|
||||||
|
: undefined,
|
||||||
|
overviewRulerColor: cfgChangesHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
|
||||||
|
? cfgTheme.dark.overviewRulerColor || themeDefaults.lineHighlight.dark.overviewRulerColor
|
||||||
|
: undefined
|
||||||
|
},
|
||||||
|
light: {
|
||||||
|
backgroundColor: cfgChangesHighlight.locations.includes(LineHighlightLocations.Line)
|
||||||
|
? cfgTheme.light.backgroundColor || themeDefaults.lineHighlight.light.backgroundColor
|
||||||
|
: undefined,
|
||||||
|
gutterIconPath: cfgChangesHighlight.locations.includes(LineHighlightLocations.Gutter)
|
||||||
|
? this.context.asAbsolutePath('images/light/highlight-gutter.svg')
|
||||||
|
: undefined,
|
||||||
|
overviewRulerColor: cfgChangesHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
|
||||||
|
? cfgTheme.light.overviewRulerColor || themeDefaults.lineHighlight.light.overviewRulerColor
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Objects.areEquivalent(cfg.blame.file, this._config && this._config.blame.file) ||
|
||||||
|
!Objects.areEquivalent(cfg.recentChanges.file, this._config && this._config.recentChanges.file) ||
|
||||||
|
!Objects.areEquivalent(cfg.annotations, this._config && this._config.annotations) ||
|
||||||
|
!Objects.areEquivalent(cfg.theme.annotations, this._config && this._config.theme.annotations)) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._config = cfg;
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
// Since the configuration has changed -- reset any visible annotations
|
||||||
|
for (const provider of this._annotationProviders.values()) {
|
||||||
|
if (provider === undefined) continue;
|
||||||
|
|
||||||
|
if (provider.annotationType === FileAnnotationType.RecentChanges) {
|
||||||
|
provider.reset(Decorations.recentChangesAnnotation, Decorations.recentChangesHighlight);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
provider.reset(Decorations.blameAnnotation, Decorations.blameHighlight, this._whitespaceController);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear(column: number) {
|
||||||
|
const provider = this._annotationProviders.get(column);
|
||||||
|
if (provider === undefined) return;
|
||||||
|
|
||||||
|
this._annotationProviders.delete(column);
|
||||||
|
await provider.dispose();
|
||||||
|
|
||||||
|
if (this._annotationProviders.size === 0) {
|
||||||
|
Logger.log(`Remove listener registrations for annotations`);
|
||||||
|
|
||||||
|
await setCommandContext(CommandContext.AnnotationStatus, undefined);
|
||||||
|
|
||||||
|
this._keyboardScope && this._keyboardScope.dispose();
|
||||||
|
this._keyboardScope = undefined;
|
||||||
|
|
||||||
|
this._annotationsDisposable && this._annotationsDisposable.dispose();
|
||||||
|
this._annotationsDisposable = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._onDidToggleAnnotations.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
getAnnotationType(editor: TextEditor | undefined): FileAnnotationType | undefined {
|
||||||
|
const provider = this.getProvider(editor);
|
||||||
|
return provider === undefined ? undefined : provider.annotationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProvider(editor: TextEditor | undefined): AnnotationProviderBase | undefined {
|
||||||
|
if (!editor || !editor.document || !this.git.isEditorBlameable(editor)) return undefined;
|
||||||
|
|
||||||
|
return this._annotationProviders.get(editor.viewColumn || -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _keyboardScope: KeyboardScope | undefined = undefined;
|
||||||
|
|
||||||
|
async showAnnotations(editor: TextEditor, type: FileAnnotationType, shaOrLine?: string | number): Promise<boolean> {
|
||||||
|
if (!editor || !editor.document || !this.git.isEditorBlameable(editor)) return false;
|
||||||
|
|
||||||
|
const currentProvider = this._annotationProviders.get(editor.viewColumn || -1);
|
||||||
|
if (currentProvider && TextEditorComparer.equals(currentProvider.editor, editor)) {
|
||||||
|
await currentProvider.selection(shaOrLine);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.withProgress({ location: ProgressLocation.Window }, async (progress: Progress<{ message: string }>) => {
|
||||||
|
await setCommandContext(CommandContext.AnnotationStatus, 'computing');
|
||||||
|
|
||||||
|
const computingAnnotations = this._showAnnotationsCore(currentProvider, editor, type, shaOrLine, progress);
|
||||||
|
const result = await computingAnnotations;
|
||||||
|
|
||||||
|
await setCommandContext(CommandContext.AnnotationStatus, result ? 'computed' : undefined);
|
||||||
|
|
||||||
|
return computingAnnotations;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _showAnnotationsCore(currentProvider: AnnotationProviderBase | undefined, editor: TextEditor, type: FileAnnotationType, shaOrLine?: string | number, progress?: Progress<{ message: string}>): Promise<boolean> {
|
||||||
|
if (progress !== undefined) {
|
||||||
|
let annotationsLabel = 'annotations';
|
||||||
|
switch (type) {
|
||||||
|
case FileAnnotationType.Gutter:
|
||||||
|
case FileAnnotationType.Hover:
|
||||||
|
annotationsLabel = 'blame annotations';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FileAnnotationType.RecentChanges:
|
||||||
|
annotationsLabel = 'recent changes annotations';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress!.report({ message: `Computing ${annotationsLabel} for ${path.basename(editor.document.fileName)}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allows pressing escape to exit the annotations
|
||||||
|
if (this._keyboardScope === undefined) {
|
||||||
|
this._keyboardScope = await Keyboard.instance.beginScope({
|
||||||
|
escape: {
|
||||||
|
onDidPressKey: (key: Keys) => {
|
||||||
|
const e = window.activeTextEditor;
|
||||||
|
if (e === undefined) return Promise.resolve(undefined);
|
||||||
|
|
||||||
|
this.clear(e.viewColumn || -1);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
} as KeyCommand
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const gitUri = await GitUri.fromUri(editor.document.uri, this.git);
|
||||||
|
|
||||||
|
let provider: AnnotationProviderBase | undefined = undefined;
|
||||||
|
switch (type) {
|
||||||
|
case FileAnnotationType.Gutter:
|
||||||
|
provider = new GutterBlameAnnotationProvider(this.context, editor, Decorations.blameAnnotation, Decorations.blameHighlight, this._whitespaceController, this.git, gitUri);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FileAnnotationType.Hover:
|
||||||
|
provider = new HoverBlameAnnotationProvider(this.context, editor, Decorations.blameAnnotation, Decorations.blameHighlight, this._whitespaceController, this.git, gitUri);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FileAnnotationType.RecentChanges:
|
||||||
|
provider = new RecentChangesAnnotationProvider(this.context, editor, undefined, Decorations.recentChangesHighlight!, this.git, gitUri);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (provider === undefined || !(await provider.validate())) return false;
|
||||||
|
|
||||||
|
if (currentProvider) {
|
||||||
|
await this.clear(currentProvider.editor.viewColumn || -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._annotationsDisposable && this._annotationProviders.size === 0) {
|
||||||
|
Logger.log(`Add listener registrations for annotations`);
|
||||||
|
|
||||||
|
const subscriptions: Disposable[] = [];
|
||||||
|
|
||||||
|
subscriptions.push(window.onDidChangeVisibleTextEditors(Functions.debounce(this._onVisibleTextEditorsChanged, 100), this));
|
||||||
|
subscriptions.push(window.onDidChangeTextEditorViewColumn(this._onTextEditorViewColumnChanged, this));
|
||||||
|
subscriptions.push(workspace.onDidChangeTextDocument(this._onTextDocumentChanged, this));
|
||||||
|
subscriptions.push(workspace.onDidCloseTextDocument(this._onTextDocumentClosed, this));
|
||||||
|
subscriptions.push(this.gitContextTracker.onDidChangeBlameability(this._onBlameabilityChanged, this));
|
||||||
|
|
||||||
|
this._annotationsDisposable = Disposable.from(...subscriptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._annotationProviders.set(editor.viewColumn || -1, provider);
|
||||||
|
if (await provider.provideAnnotation(shaOrLine)) {
|
||||||
|
this._onDidToggleAnnotations.fire();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleAnnotations(editor: TextEditor, type: FileAnnotationType, shaOrLine?: string | number): Promise<boolean> {
|
||||||
|
if (!editor || !editor.document || (type === FileAnnotationType.RecentChanges ? !this.git.isTrackable(editor.document.uri) : !this.git.isEditorBlameable(editor))) return false;
|
||||||
|
|
||||||
|
const provider = this._annotationProviders.get(editor.viewColumn || -1);
|
||||||
|
if (provider === undefined) return this.showAnnotations(editor, type, shaOrLine);
|
||||||
|
|
||||||
|
const reopen = provider.annotationType !== type;
|
||||||
|
await this.clear(provider.editor.viewColumn || -1);
|
||||||
|
|
||||||
|
if (!reopen) return false;
|
||||||
|
|
||||||
|
return this.showAnnotations(editor, type, shaOrLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onBlameabilityChanged(e: BlameabilityChangeEvent) {
|
||||||
|
if (e.blameable || !e.editor) return;
|
||||||
|
|
||||||
|
for (const [key, p] of this._annotationProviders) {
|
||||||
|
if (!TextDocumentComparer.equals(p.document, e.editor.document)) continue;
|
||||||
|
|
||||||
|
Logger.log('BlameabilityChanged:', `Clear annotations for column ${key}`);
|
||||||
|
this.clear(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onTextDocumentChanged(e: TextDocumentChangeEvent) {
|
||||||
|
for (const [key, p] of this._annotationProviders) {
|
||||||
|
if (!TextDocumentComparer.equals(p.document, e.document)) continue;
|
||||||
|
|
||||||
|
// TODO: Rework this once https://github.com/Microsoft/vscode/issues/27231 is released in v1.13
|
||||||
|
// We have to defer because isDirty is not reliable inside this event
|
||||||
|
setTimeout(() => {
|
||||||
|
// If the document is dirty all is fine, just kick out since the GitContextTracker will handle it
|
||||||
|
if (e.document.isDirty) return;
|
||||||
|
|
||||||
|
// If the document isn't dirty, it is very likely this event was triggered by an outside edit of this document
|
||||||
|
// Which means the document has been reloaded and the annotations have been removed, so we need to update (clear) our state tracking
|
||||||
|
Logger.log('TextDocumentChanged:', `Clear annotations for column ${key}`);
|
||||||
|
this.clear(key);
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onTextDocumentClosed(e: TextDocument) {
|
||||||
|
for (const [key, p] of this._annotationProviders) {
|
||||||
|
if (!TextDocumentComparer.equals(p.document, e)) continue;
|
||||||
|
|
||||||
|
Logger.log('TextDocumentClosed:', `Clear annotations for column ${key}`);
|
||||||
|
this.clear(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _onTextEditorViewColumnChanged(e: TextEditorViewColumnChangeEvent) {
|
||||||
|
const viewColumn = e.viewColumn || -1;
|
||||||
|
|
||||||
|
Logger.log('TextEditorViewColumnChanged:', `Clear annotations for column ${viewColumn}`);
|
||||||
|
await this.clear(viewColumn);
|
||||||
|
|
||||||
|
for (const [key, p] of this._annotationProviders) {
|
||||||
|
if (!TextEditorComparer.equals(p.editor, e.textEditor)) continue;
|
||||||
|
|
||||||
|
Logger.log('TextEditorViewColumnChanged:', `Clear annotations for column ${key}`);
|
||||||
|
await this.clear(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _onVisibleTextEditorsChanged(e: TextEditor[]) {
|
||||||
|
if (e.every(_ => _.document.uri.scheme === 'inmemory')) return;
|
||||||
|
|
||||||
|
for (const [key, p] of this._annotationProviders) {
|
||||||
|
if (e.some(_ => TextEditorComparer.equals(p.editor, _))) continue;
|
||||||
|
|
||||||
|
Logger.log('VisibleTextEditorsChanged:', `Clear annotations for column ${key}`);
|
||||||
|
this.clear(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/annotations/annotationProvider.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
'use strict';
|
||||||
|
// import { Functions } from '../system';
|
||||||
|
import { Disposable, ExtensionContext, TextDocument, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, window, workspace } from 'vscode';
|
||||||
|
import { FileAnnotationType } from '../annotations/annotationController';
|
||||||
|
import { TextDocumentComparer } from '../comparers';
|
||||||
|
import { ExtensionKey, IConfig } from '../configuration';
|
||||||
|
import { WhitespaceController } from './whitespaceController';
|
||||||
|
|
||||||
|
export abstract class AnnotationProviderBase extends Disposable {
|
||||||
|
|
||||||
|
public annotationType: FileAnnotationType;
|
||||||
|
public document: TextDocument;
|
||||||
|
|
||||||
|
protected _config: IConfig;
|
||||||
|
protected _disposable: Disposable;
|
||||||
|
|
||||||
|
constructor(context: ExtensionContext, public editor: TextEditor, protected decoration: TextEditorDecorationType | undefined, protected highlightDecoration: TextEditorDecorationType | undefined, protected whitespaceController: WhitespaceController | undefined) {
|
||||||
|
super(() => this.dispose());
|
||||||
|
|
||||||
|
this.document = this.editor.document;
|
||||||
|
|
||||||
|
this._config = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
|
||||||
|
|
||||||
|
const subscriptions: Disposable[] = [];
|
||||||
|
|
||||||
|
subscriptions.push(window.onDidChangeTextEditorSelection(this._onTextEditorSelectionChanged, this));
|
||||||
|
|
||||||
|
this._disposable = Disposable.from(...subscriptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
async dispose() {
|
||||||
|
await this.clear();
|
||||||
|
|
||||||
|
this._disposable && this._disposable.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _onTextEditorSelectionChanged(e: TextEditorSelectionChangeEvent) {
|
||||||
|
if (!TextDocumentComparer.equals(this.document, e.textEditor && e.textEditor.document)) return;
|
||||||
|
|
||||||
|
return this.selection(e.selections[0].active.line);
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear() {
|
||||||
|
if (this.editor !== undefined) {
|
||||||
|
try {
|
||||||
|
if (this.highlightDecoration !== undefined) {
|
||||||
|
this.editor.setDecorations(this.highlightDecoration, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.decoration !== undefined) {
|
||||||
|
this.editor.setDecorations(this.decoration, []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ex) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- restore whitespace
|
||||||
|
this.whitespaceController && await this.whitespaceController.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
async reset(decoration: TextEditorDecorationType | undefined, highlightDecoration: TextEditorDecorationType | undefined, whitespaceController?: WhitespaceController) {
|
||||||
|
await this.clear();
|
||||||
|
|
||||||
|
this._config = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
|
||||||
|
this.decoration = decoration;
|
||||||
|
this.highlightDecoration = highlightDecoration;
|
||||||
|
this.whitespaceController = whitespaceController;
|
||||||
|
|
||||||
|
await this.provideAnnotation(this.editor === undefined ? undefined : this.editor.selection.active.line);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract async provideAnnotation(shaOrLine?: string | number): Promise<boolean>;
|
||||||
|
abstract async selection(shaOrLine?: string | number): Promise<void>;
|
||||||
|
abstract async validate(): Promise<boolean>;
|
||||||
|
}
|
||||||
222
src/annotations/annotations.ts
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
import { Strings } from '../system';
|
||||||
|
import { DecorationInstanceRenderOptions, DecorationOptions, ThemableDecorationRenderOptions } from 'vscode';
|
||||||
|
import { IThemeConfig, themeDefaults } from '../configuration';
|
||||||
|
import { GlyphChars } from '../constants';
|
||||||
|
import { CommitFormatter, GitCommit, GitDiffChunkLine, GitService, GitUri, ICommitFormatOptions } from '../gitService';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
interface IHeatmapConfig {
|
||||||
|
enabled: boolean;
|
||||||
|
location?: 'left' | 'right';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRenderOptions {
|
||||||
|
uncommittedForegroundColor?: {
|
||||||
|
dark: string;
|
||||||
|
light: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
before?: DecorationInstanceRenderOptions & ThemableDecorationRenderOptions & { height?: string };
|
||||||
|
dark?: DecorationInstanceRenderOptions;
|
||||||
|
light?: DecorationInstanceRenderOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const endOfLineIndex = 1000000;
|
||||||
|
const escapeMarkdownRegEx = /[`\>\#\*\_\-\+\.]/g;
|
||||||
|
// const sampleMarkdown = '## message `not code` *not important* _no underline_ \n> don\'t quote me \n- don\'t list me \n+ don\'t list me \n1. don\'t list me \nnot h1 \n=== \nnot h2 \n---\n***\n---\n___';
|
||||||
|
|
||||||
|
export class Annotations {
|
||||||
|
|
||||||
|
static applyHeatmap(decoration: DecorationOptions, date: Date, now: moment.Moment) {
|
||||||
|
const color = this._getHeatmapColor(now, date);
|
||||||
|
(decoration.renderOptions!.before! as any).borderColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _getHeatmapColor(now: moment.Moment, date: Date) {
|
||||||
|
const days = now.diff(moment(date), 'days');
|
||||||
|
|
||||||
|
if (days <= 2) return '#ffeca7';
|
||||||
|
if (days <= 7) return '#ffdd8c';
|
||||||
|
if (days <= 14) return '#ffdd7c';
|
||||||
|
if (days <= 30) return '#fba447';
|
||||||
|
if (days <= 60) return '#f68736';
|
||||||
|
if (days <= 90) return '#f37636';
|
||||||
|
if (days <= 180) return '#ca6632';
|
||||||
|
if (days <= 365) return '#c0513f';
|
||||||
|
if (days <= 730) return '#a2503a';
|
||||||
|
return '#793738';
|
||||||
|
}
|
||||||
|
|
||||||
|
static getHoverMessage(commit: GitCommit, dateFormat: string | null): string | string[] {
|
||||||
|
if (dateFormat === null) {
|
||||||
|
dateFormat = 'MMMM Do, YYYY h:MMa';
|
||||||
|
}
|
||||||
|
|
||||||
|
let message = '';
|
||||||
|
if (!commit.isUncommitted) {
|
||||||
|
message = commit.message
|
||||||
|
// Escape markdown
|
||||||
|
.replace(escapeMarkdownRegEx, '\\$&')
|
||||||
|
// Escape markdown header (since the above regex won't match it)
|
||||||
|
.replace(/^===/gm, `${GlyphChars.ZeroWidthSpace}===`)
|
||||||
|
// Keep under the same block-quote
|
||||||
|
.replace(/\n/g, ' \n');
|
||||||
|
message = `\n\n> ${message}`;
|
||||||
|
}
|
||||||
|
return `\`${commit.shortSha}\` __${commit.author}__, ${moment(commit.date).fromNow()} _(${moment(commit.date).format(dateFormat)})_${message}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getHoverDiffMessage(commit: GitCommit, chunkLine: GitDiffChunkLine | undefined): string | undefined {
|
||||||
|
if (chunkLine === undefined) return undefined;
|
||||||
|
|
||||||
|
const codeDiff = this._getCodeDiff(chunkLine);
|
||||||
|
return commit.isUncommitted
|
||||||
|
? `\`Changes\` ${GlyphChars.Dash} _uncommitted_\n${codeDiff}`
|
||||||
|
: `\`Changes\` ${GlyphChars.Dash} \`${commit.previousShortSha}\` ${GlyphChars.ArrowLeftRight} \`${commit.shortSha}\`\n${codeDiff}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _getCodeDiff(chunkLine: GitDiffChunkLine): string {
|
||||||
|
const previous = chunkLine.previous === undefined ? undefined : chunkLine.previous[0];
|
||||||
|
return `\`\`\`
|
||||||
|
- ${previous === undefined || previous.line === undefined ? '' : previous.line.trim()}
|
||||||
|
+ ${chunkLine.line === undefined ? '' : chunkLine.line.trim()}
|
||||||
|
\`\`\``;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async changesHover(commit: GitCommit, line: number, uri: GitUri, git: GitService): Promise<DecorationOptions> {
|
||||||
|
const chunkLine = await git.getDiffForLine(uri, line + uri.offset, commit.isUncommitted ? undefined : commit.previousSha);
|
||||||
|
const message = this.getHoverDiffMessage(commit, chunkLine);
|
||||||
|
|
||||||
|
return {
|
||||||
|
hoverMessage: message
|
||||||
|
} as DecorationOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
static detailsHover(commit: GitCommit, dateFormat: string | null): DecorationOptions {
|
||||||
|
const message = this.getHoverMessage(commit, dateFormat);
|
||||||
|
return {
|
||||||
|
hoverMessage: message
|
||||||
|
} as 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);
|
||||||
|
|
||||||
|
return {
|
||||||
|
renderOptions: {
|
||||||
|
before: {
|
||||||
|
...renderOptions.before,
|
||||||
|
...{
|
||||||
|
contentText: content
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
before: commit.isUncommitted
|
||||||
|
? { ...renderOptions.dark, ...{ color: renderOptions.uncommittedForegroundColor!.dark } }
|
||||||
|
: { ...renderOptions.dark }
|
||||||
|
},
|
||||||
|
light: {
|
||||||
|
before: commit.isUncommitted
|
||||||
|
? { ...renderOptions.light, ...{ color: renderOptions.uncommittedForegroundColor!.light } }
|
||||||
|
: { ...renderOptions.light }
|
||||||
|
}
|
||||||
|
} as DecorationInstanceRenderOptions
|
||||||
|
} as DecorationOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gutterRenderOptions(cfgTheme: IThemeConfig, heatmap: IHeatmapConfig): IRenderOptions {
|
||||||
|
const cfgFileTheme = cfgTheme.annotations.file.gutter;
|
||||||
|
|
||||||
|
let borderStyle = undefined;
|
||||||
|
let borderWidth = undefined;
|
||||||
|
if (heatmap.enabled) {
|
||||||
|
borderStyle = 'solid';
|
||||||
|
borderWidth = heatmap.location === 'left' ? '0 0 0 2px' : '0 2px 0 0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
uncommittedForegroundColor: {
|
||||||
|
dark: cfgFileTheme.dark.uncommittedForegroundColor || cfgFileTheme.dark.foregroundColor || themeDefaults.annotations.file.gutter.dark.foregroundColor,
|
||||||
|
light: cfgFileTheme.light.uncommittedForegroundColor || cfgFileTheme.light.foregroundColor || themeDefaults.annotations.file.gutter.light.foregroundColor
|
||||||
|
},
|
||||||
|
before: {
|
||||||
|
borderStyle: borderStyle,
|
||||||
|
borderWidth: borderWidth,
|
||||||
|
height: '100%',
|
||||||
|
margin: '0 26px -1px 0'
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
backgroundColor: cfgFileTheme.dark.backgroundColor || undefined,
|
||||||
|
color: cfgFileTheme.dark.foregroundColor || themeDefaults.annotations.file.gutter.dark.foregroundColor,
|
||||||
|
textDecoration: cfgFileTheme.separateLines ? 'overline solid rgba(0, 0, 0, .2)' : 'none'
|
||||||
|
} as DecorationInstanceRenderOptions,
|
||||||
|
light: {
|
||||||
|
backgroundColor: cfgFileTheme.light.backgroundColor || undefined,
|
||||||
|
color: cfgFileTheme.light.foregroundColor || themeDefaults.annotations.file.gutter.light.foregroundColor,
|
||||||
|
textDecoration: cfgFileTheme.separateLines ? 'overline solid rgba(0, 0, 0, .05)' : 'none'
|
||||||
|
} as DecorationInstanceRenderOptions
|
||||||
|
} as IRenderOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
static hover(commit: GitCommit, renderOptions: IRenderOptions, heatmap: boolean, dateFormat: string | null): DecorationOptions {
|
||||||
|
return {
|
||||||
|
hoverMessage: this.getHoverMessage(commit, dateFormat),
|
||||||
|
renderOptions: heatmap ? { before: { ...renderOptions.before } } : undefined
|
||||||
|
} as DecorationOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
static hoverRenderOptions(cfgTheme: IThemeConfig, heatmap: IHeatmapConfig): IRenderOptions {
|
||||||
|
if (!heatmap.enabled) return { before: undefined };
|
||||||
|
|
||||||
|
return {
|
||||||
|
before: {
|
||||||
|
borderStyle: 'solid',
|
||||||
|
borderWidth: '0 0 0 2px',
|
||||||
|
contentText: GlyphChars.ZeroWidthSpace,
|
||||||
|
height: '100%',
|
||||||
|
margin: '0 26px 0 0',
|
||||||
|
textDecoration: 'none'
|
||||||
|
}
|
||||||
|
} as IRenderOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
static trailing(commit: GitCommit, format: string, dateFormat: string | null, cfgTheme: IThemeConfig): DecorationOptions {
|
||||||
|
const message = CommitFormatter.fromTemplate(format, commit, dateFormat);
|
||||||
|
return {
|
||||||
|
renderOptions: {
|
||||||
|
after: {
|
||||||
|
contentText: Strings.pad(message, 1, 1)
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
after: {
|
||||||
|
backgroundColor: cfgTheme.annotations.line.trailing.dark.backgroundColor || undefined,
|
||||||
|
color: cfgTheme.annotations.line.trailing.dark.foregroundColor || themeDefaults.annotations.line.trailing.dark.foregroundColor
|
||||||
|
}
|
||||||
|
},
|
||||||
|
light: {
|
||||||
|
after: {
|
||||||
|
backgroundColor: cfgTheme.annotations.line.trailing.light.backgroundColor || undefined,
|
||||||
|
color: cfgTheme.annotations.line.trailing.light.foregroundColor || themeDefaults.annotations.line.trailing.light.foregroundColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as DecorationInstanceRenderOptions
|
||||||
|
} as DecorationOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
static withRange(decoration: DecorationOptions, start?: number, end?: number): DecorationOptions {
|
||||||
|
let range = decoration.range;
|
||||||
|
if (start !== undefined) {
|
||||||
|
range = range.with({
|
||||||
|
start: range.start.with({ character: start })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined) {
|
||||||
|
range = range.with({
|
||||||
|
end: range.end.with({ character: end })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...decoration, ...{ range: range } };
|
||||||
|
}
|
||||||
|
}
|
||||||
81
src/annotations/blameAnnotationProvider.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
'use strict';
|
||||||
|
import { Iterables } from '../system';
|
||||||
|
import { ExtensionContext, Range, TextEditor, TextEditorDecorationType } from 'vscode';
|
||||||
|
import { AnnotationProviderBase } from './annotationProvider';
|
||||||
|
import { GitBlame, GitService, GitUri } from '../gitService';
|
||||||
|
import { WhitespaceController } from './whitespaceController';
|
||||||
|
|
||||||
|
export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase {
|
||||||
|
|
||||||
|
protected _blame: Promise<GitBlame | undefined>;
|
||||||
|
|
||||||
|
constructor(context: ExtensionContext, editor: TextEditor, decoration: TextEditorDecorationType | undefined, highlightDecoration: TextEditorDecorationType | undefined, whitespaceController: WhitespaceController | undefined, protected git: GitService, protected uri: GitUri) {
|
||||||
|
super(context, editor, decoration, highlightDecoration, whitespaceController);
|
||||||
|
|
||||||
|
this._blame = this.git.getBlameForFile(this.uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
async selection(shaOrLine?: string | number, blame?: GitBlame) {
|
||||||
|
if (!this.highlightDecoration) return;
|
||||||
|
|
||||||
|
if (blame === undefined) {
|
||||||
|
blame = await this._blame;
|
||||||
|
if (!blame || !blame.lines.length) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offset = this.uri.offset;
|
||||||
|
|
||||||
|
let sha: string | undefined = undefined;
|
||||||
|
if (typeof shaOrLine === 'string') {
|
||||||
|
sha = shaOrLine;
|
||||||
|
}
|
||||||
|
else if (typeof shaOrLine === 'number') {
|
||||||
|
const line = shaOrLine - offset;
|
||||||
|
if (line >= 0) {
|
||||||
|
const commitLine = blame.lines[line];
|
||||||
|
sha = commitLine && commitLine.sha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sha = Iterables.first(blame.commits.values()).sha;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sha) {
|
||||||
|
this.editor.setDecorations(this.highlightDecoration, []);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const highlightDecorationRanges = blame.lines
|
||||||
|
.filter(l => l.sha === sha)
|
||||||
|
.map(l => this.editor.document.validateRange(new Range(l.line + offset, 0, l.line + offset, 1000000)));
|
||||||
|
|
||||||
|
this.editor.setDecorations(this.highlightDecoration, highlightDecorationRanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(): Promise<boolean> {
|
||||||
|
const blame = await this._blame;
|
||||||
|
return blame !== undefined && blame.lines.length !== 0;
|
||||||
|
}
|
||||||
|
protected async getBlame(requiresWhitespaceHack: boolean): Promise<GitBlame | undefined> {
|
||||||
|
let whitespacePromise: Promise<void> | undefined;
|
||||||
|
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- override whitespace (turn off)
|
||||||
|
if (requiresWhitespaceHack) {
|
||||||
|
whitespacePromise = this.whitespaceController && this.whitespaceController.override();
|
||||||
|
}
|
||||||
|
|
||||||
|
let blame: GitBlame | undefined;
|
||||||
|
if (whitespacePromise) {
|
||||||
|
[blame] = await Promise.all([this._blame, whitespacePromise]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
blame = await this._blame;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blame === undefined || !blame.lines.length) {
|
||||||
|
this.whitespaceController && await this.whitespaceController.restore();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return blame;
|
||||||
|
}
|
||||||
|
}
|
||||||
123
src/annotations/gutterBlameAnnotationProvider.ts
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
'use strict';
|
||||||
|
import { Strings } from '../system';
|
||||||
|
import { DecorationOptions, Range } from 'vscode';
|
||||||
|
import { FileAnnotationType } from './annotationController';
|
||||||
|
import { Annotations, endOfLineIndex } from './annotations';
|
||||||
|
import { BlameAnnotationProviderBase } from './blameAnnotationProvider';
|
||||||
|
import { GlyphChars } from '../constants';
|
||||||
|
import { GitBlameCommit, ICommitFormatOptions } from '../gitService';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
||||||
|
|
||||||
|
async provideAnnotation(shaOrLine?: string | number, type?: FileAnnotationType): Promise<boolean> {
|
||||||
|
this.annotationType = FileAnnotationType.Gutter;
|
||||||
|
|
||||||
|
const blame = await this.getBlame(true);
|
||||||
|
if (blame === undefined) return false;
|
||||||
|
|
||||||
|
// console.time('Computing blame annotations...');
|
||||||
|
|
||||||
|
const cfg = this._config.annotations.file.gutter;
|
||||||
|
|
||||||
|
// Precalculate the formatting options so we don't need to do it on each iteration
|
||||||
|
const tokenOptions = Strings.getTokensFromTemplate(cfg.format)
|
||||||
|
.reduce((map, token) => {
|
||||||
|
map[token.key] = token.options as ICommitFormatOptions;
|
||||||
|
return map;
|
||||||
|
}, {} as { [token: string]: ICommitFormatOptions });
|
||||||
|
|
||||||
|
const options: ICommitFormatOptions = {
|
||||||
|
dateFormat: cfg.dateFormat === null ? this._config.defaultDateFormat : cfg.dateFormat,
|
||||||
|
tokenOptions: tokenOptions
|
||||||
|
};
|
||||||
|
|
||||||
|
const now = moment();
|
||||||
|
const offset = this.uri.offset;
|
||||||
|
const renderOptions = Annotations.gutterRenderOptions(this._config.theme, cfg.heatmap);
|
||||||
|
const dateFormat = this._config.defaultDateFormat;
|
||||||
|
const separateLines = this._config.theme.annotations.file.gutter.separateLines;
|
||||||
|
|
||||||
|
const decorations: DecorationOptions[] = [];
|
||||||
|
const document = this.document;
|
||||||
|
|
||||||
|
let commit: GitBlameCommit | undefined;
|
||||||
|
let compacted = false;
|
||||||
|
let details: DecorationOptions | undefined;
|
||||||
|
let gutter: DecorationOptions | undefined;
|
||||||
|
let previousSha: string | undefined;
|
||||||
|
|
||||||
|
for (const l of blame.lines) {
|
||||||
|
commit = blame.commits.get(l.sha);
|
||||||
|
if (commit === undefined) continue;
|
||||||
|
|
||||||
|
const line = l.line + offset;
|
||||||
|
|
||||||
|
if (previousSha === l.sha) {
|
||||||
|
// Use a shallow copy of the previous decoration options
|
||||||
|
gutter = { ...gutter } as DecorationOptions;
|
||||||
|
if (cfg.compact && !compacted) {
|
||||||
|
// Since we are wiping out the contextText make sure to copy the objects
|
||||||
|
gutter.renderOptions = { ...gutter.renderOptions };
|
||||||
|
gutter.renderOptions.before = {
|
||||||
|
...gutter.renderOptions.before,
|
||||||
|
...{ contentText: GlyphChars.Space.repeat(Strings.getWidth(gutter.renderOptions!.before!.contentText!)) }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (separateLines) {
|
||||||
|
gutter.renderOptions.dark = { ...gutter.renderOptions.dark };
|
||||||
|
gutter.renderOptions.dark.before = { ...gutter.renderOptions.dark.before, ...{ textDecoration: 'none' } };
|
||||||
|
gutter.renderOptions.light = { ...gutter.renderOptions.light };
|
||||||
|
gutter.renderOptions.light.before = { ...gutter.renderOptions.light.before, ...{ textDecoration: 'none' } };
|
||||||
|
}
|
||||||
|
|
||||||
|
compacted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const endIndex = document.lineAt(line).firstNonWhitespaceCharacterIndex;
|
||||||
|
gutter.range = new Range(line, 0, line, endIndex);
|
||||||
|
decorations.push(gutter);
|
||||||
|
|
||||||
|
if (details !== undefined) {
|
||||||
|
details = { ...details } as DecorationOptions;
|
||||||
|
details.range = cfg.hover.wholeLine
|
||||||
|
? document.validateRange(new Range(line, 0, line, endOfLineIndex))
|
||||||
|
: gutter.range;
|
||||||
|
decorations.push(details);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
compacted = false;
|
||||||
|
previousSha = l.sha;
|
||||||
|
|
||||||
|
gutter = Annotations.gutter(commit, cfg.format, options, renderOptions);
|
||||||
|
|
||||||
|
if (cfg.heatmap.enabled) {
|
||||||
|
Annotations.applyHeatmap(gutter, commit.date, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
const endIndex = document.lineAt(line).firstNonWhitespaceCharacterIndex;
|
||||||
|
gutter.range = new Range(line, 0, line, endIndex);
|
||||||
|
decorations.push(gutter);
|
||||||
|
|
||||||
|
if (cfg.hover.details) {
|
||||||
|
details = Annotations.detailsHover(commit, dateFormat);
|
||||||
|
details.range = cfg.hover.wholeLine
|
||||||
|
? document.validateRange(new Range(line, 0, line, endOfLineIndex))
|
||||||
|
: gutter.range;
|
||||||
|
decorations.push(details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decorations.length) {
|
||||||
|
this.editor.setDecorations(this.decoration!, decorations);
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.timeEnd('Computing blame annotations...');
|
||||||
|
|
||||||
|
this.selection(shaOrLine, blame);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/annotations/hoverBlameAnnotationProvider.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
'use strict';
|
||||||
|
import { DecorationOptions, Range } from 'vscode';
|
||||||
|
import { FileAnnotationType } from './annotationController';
|
||||||
|
import { Annotations, endOfLineIndex } from './annotations';
|
||||||
|
import { BlameAnnotationProviderBase } from './blameAnnotationProvider';
|
||||||
|
import { GitBlameCommit } from '../gitService';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
export class HoverBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
||||||
|
|
||||||
|
async provideAnnotation(shaOrLine?: string | number): Promise<boolean> {
|
||||||
|
this.annotationType = FileAnnotationType.Hover;
|
||||||
|
|
||||||
|
const blame = await this.getBlame(this._config.annotations.file.hover.heatmap.enabled);
|
||||||
|
if (blame === undefined) return false;
|
||||||
|
|
||||||
|
// console.time('Computing blame annotations...');
|
||||||
|
|
||||||
|
const cfg = this._config.annotations.file.hover;
|
||||||
|
|
||||||
|
const now = moment();
|
||||||
|
const offset = this.uri.offset;
|
||||||
|
const renderOptions = Annotations.hoverRenderOptions(this._config.theme, cfg.heatmap);
|
||||||
|
const dateFormat = this._config.defaultDateFormat;
|
||||||
|
|
||||||
|
const decorations: DecorationOptions[] = [];
|
||||||
|
const document = this.document;
|
||||||
|
|
||||||
|
let commit: GitBlameCommit | undefined;
|
||||||
|
let hover: DecorationOptions | undefined;
|
||||||
|
|
||||||
|
for (const l of blame.lines) {
|
||||||
|
commit = blame.commits.get(l.sha);
|
||||||
|
if (commit === undefined) continue;
|
||||||
|
|
||||||
|
const line = l.line + offset;
|
||||||
|
|
||||||
|
hover = Annotations.hover(commit, renderOptions, cfg.heatmap.enabled, dateFormat);
|
||||||
|
|
||||||
|
if (cfg.wholeLine) {
|
||||||
|
hover.range = document.validateRange(new Range(line, 0, line, endOfLineIndex));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const endIndex = document.lineAt(line).firstNonWhitespaceCharacterIndex;
|
||||||
|
hover.range = new Range(line, 0, line, endIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg.heatmap.enabled) {
|
||||||
|
Annotations.applyHeatmap(hover, commit.date, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
decorations.push(hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decorations.length) {
|
||||||
|
this.editor.setDecorations(this.decoration!, decorations);
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.timeEnd('Computing blame annotations...');
|
||||||
|
|
||||||
|
this.selection(shaOrLine, blame);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/annotations/recentChangesAnnotationProvider.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
'use strict';
|
||||||
|
import { DecorationOptions, ExtensionContext, Position, Range, TextEditor, TextEditorDecorationType } from 'vscode';
|
||||||
|
import { Annotations, endOfLineIndex } from './annotations';
|
||||||
|
import { FileAnnotationType } from './annotationController';
|
||||||
|
import { AnnotationProviderBase } from './annotationProvider';
|
||||||
|
import { GitService, GitUri } from '../gitService';
|
||||||
|
|
||||||
|
export class RecentChangesAnnotationProvider extends AnnotationProviderBase {
|
||||||
|
|
||||||
|
constructor(context: ExtensionContext, editor: TextEditor, decoration: TextEditorDecorationType | undefined, highlightDecoration: TextEditorDecorationType | undefined, private git: GitService, private uri: GitUri) {
|
||||||
|
super(context, editor, decoration, highlightDecoration, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
async provideAnnotation(shaOrLine?: string | number): Promise<boolean> {
|
||||||
|
this.annotationType = FileAnnotationType.RecentChanges;
|
||||||
|
|
||||||
|
const commit = await this.git.getLogCommit(this.uri.repoPath, this.uri.fsPath, { previous: true });
|
||||||
|
if (commit === undefined) return false;
|
||||||
|
|
||||||
|
const diff = await this.git.getDiffForFile(this.uri, commit.previousSha);
|
||||||
|
if (diff === undefined) return false;
|
||||||
|
|
||||||
|
const cfg = this._config.annotations.file.recentChanges;
|
||||||
|
const dateFormat = this._config.defaultDateFormat;
|
||||||
|
|
||||||
|
const decorators: DecorationOptions[] = [];
|
||||||
|
|
||||||
|
for (const chunk of diff.chunks) {
|
||||||
|
let count = chunk.currentPosition.start - 2;
|
||||||
|
for (const line of chunk.lines) {
|
||||||
|
if (line.line === undefined) continue;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (line.state === 'unchanged') continue;
|
||||||
|
|
||||||
|
let endingIndex = 0;
|
||||||
|
if (cfg.hover.details || cfg.hover.changes) {
|
||||||
|
endingIndex = cfg.hover.wholeLine ? endOfLineIndex : this.editor.document.lineAt(count).firstNonWhitespaceCharacterIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
const range = this.editor.document.validateRange(new Range(new Position(count, 0), new Position(count, endingIndex)));
|
||||||
|
|
||||||
|
if (cfg.hover.details) {
|
||||||
|
decorators.push({
|
||||||
|
hoverMessage: Annotations.getHoverMessage(commit, dateFormat),
|
||||||
|
range: range
|
||||||
|
} as DecorationOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
let message: string | undefined = undefined;
|
||||||
|
if (cfg.hover.changes) {
|
||||||
|
message = Annotations.getHoverDiffMessage(commit, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
decorators.push({
|
||||||
|
hoverMessage: message,
|
||||||
|
range: range
|
||||||
|
} as DecorationOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.editor.setDecorations(this.highlightDecoration!, decorators);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async selection(shaOrLine?: string | number): Promise<void> {
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Disposable, workspace } from 'vscode';
|
import { Disposable, workspace } from 'vscode';
|
||||||
import { Logger } from './logger';
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
interface ConfigurationInspection {
|
interface ConfigurationInspection {
|
||||||
key: string;
|
key: string;
|
||||||
@@ -118,8 +118,6 @@ export class WhitespaceController extends Disposable {
|
|||||||
if (this._count === 1 && this._configuration.overrideRequired) {
|
if (this._count === 1 && this._configuration.overrideRequired) {
|
||||||
// Override whitespace (turn off)
|
// Override whitespace (turn off)
|
||||||
await this._overrideWhitespace();
|
await this._overrideWhitespace();
|
||||||
// Add a delay to give the editor time to turn off the whitespace
|
|
||||||
await new Promise((resolve, reject) => setTimeout(resolve, 250));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,391 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
import { Functions, Objects } from './system';
|
|
||||||
import { DecorationInstanceRenderOptions, DecorationOptions, DecorationRenderOptions, Disposable, ExtensionContext, Range, StatusBarAlignment, StatusBarItem, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, window, workspace } from 'vscode';
|
|
||||||
import { BlameAnnotationController } from './blameAnnotationController';
|
|
||||||
import { BlameAnnotationFormat, BlameAnnotationFormatter } from './blameAnnotationFormatter';
|
|
||||||
import { Commands } from './commands';
|
|
||||||
import { TextEditorComparer } from './comparers';
|
|
||||||
import { IBlameConfig, IConfig, StatusBarCommand } from './configuration';
|
|
||||||
import { DocumentSchemes, ExtensionKey } from './constants';
|
|
||||||
import { BlameabilityChangeEvent, GitCommit, GitContextTracker, GitService, GitUri, IGitCommitLine } from './gitService';
|
|
||||||
import * as moment from 'moment';
|
|
||||||
|
|
||||||
const activeLineDecoration: TextEditorDecorationType = window.createTextEditorDecorationType({
|
|
||||||
after: {
|
|
||||||
margin: '0 0 0 4em'
|
|
||||||
}
|
|
||||||
} as DecorationRenderOptions);
|
|
||||||
|
|
||||||
export class BlameActiveLineController extends Disposable {
|
|
||||||
|
|
||||||
private _activeEditorLineDisposable: Disposable | undefined;
|
|
||||||
private _blameable: boolean;
|
|
||||||
private _config: IConfig;
|
|
||||||
private _currentLine: number = -1;
|
|
||||||
private _disposable: Disposable;
|
|
||||||
private _editor: TextEditor | undefined;
|
|
||||||
private _statusBarItem: StatusBarItem | undefined;
|
|
||||||
private _updateBlameDebounced: (line: number, editor: TextEditor) => Promise<void>;
|
|
||||||
private _uri: GitUri;
|
|
||||||
|
|
||||||
constructor(context: ExtensionContext, private git: GitService, private gitContextTracker: GitContextTracker, private annotationController: BlameAnnotationController) {
|
|
||||||
super(() => this.dispose());
|
|
||||||
|
|
||||||
this._updateBlameDebounced = Functions.debounce(this._updateBlame, 250);
|
|
||||||
|
|
||||||
this._onConfigurationChanged();
|
|
||||||
|
|
||||||
const subscriptions: Disposable[] = [];
|
|
||||||
|
|
||||||
subscriptions.push(workspace.onDidChangeConfiguration(this._onConfigurationChanged, this));
|
|
||||||
subscriptions.push(git.onDidChangeGitCache(this._onGitCacheChanged, this));
|
|
||||||
subscriptions.push(annotationController.onDidToggleBlameAnnotations(this._onBlameAnnotationToggled, this));
|
|
||||||
|
|
||||||
this._disposable = Disposable.from(...subscriptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
|
||||||
this._editor && this._editor.setDecorations(activeLineDecoration, []);
|
|
||||||
|
|
||||||
this._activeEditorLineDisposable && this._activeEditorLineDisposable.dispose();
|
|
||||||
this._statusBarItem && this._statusBarItem.dispose();
|
|
||||||
this._disposable && this._disposable.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onConfigurationChanged() {
|
|
||||||
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
|
|
||||||
|
|
||||||
let changed = false;
|
|
||||||
|
|
||||||
if (!Objects.areEquivalent(cfg.statusBar, this._config && this._config.statusBar)) {
|
|
||||||
changed = true;
|
|
||||||
if (cfg.statusBar.enabled) {
|
|
||||||
const alignment = cfg.statusBar.alignment !== 'left' ? StatusBarAlignment.Right : StatusBarAlignment.Left;
|
|
||||||
if (this._statusBarItem !== undefined && this._statusBarItem.alignment !== alignment) {
|
|
||||||
this._statusBarItem.dispose();
|
|
||||||
this._statusBarItem = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._statusBarItem = this._statusBarItem || window.createStatusBarItem(alignment, alignment === StatusBarAlignment.Right ? 1000 : 0);
|
|
||||||
this._statusBarItem.command = cfg.statusBar.command;
|
|
||||||
}
|
|
||||||
else if (!cfg.statusBar.enabled && this._statusBarItem) {
|
|
||||||
this._statusBarItem.dispose();
|
|
||||||
this._statusBarItem = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Objects.areEquivalent(cfg.blame.annotation.activeLine, this._config && this._config.blame.annotation.activeLine)) {
|
|
||||||
changed = true;
|
|
||||||
if (cfg.blame.annotation.activeLine !== 'off' && this._editor) {
|
|
||||||
this._editor.setDecorations(activeLineDecoration, []);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!Objects.areEquivalent(cfg.blame.annotation.activeLineDarkColor, this._config && this._config.blame.annotation.activeLineDarkColor) ||
|
|
||||||
!Objects.areEquivalent(cfg.blame.annotation.activeLineLightColor, this._config && this._config.blame.annotation.activeLineLightColor)) {
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._config = cfg;
|
|
||||||
|
|
||||||
if (!changed) return;
|
|
||||||
|
|
||||||
const trackActiveLine = cfg.statusBar.enabled || cfg.blame.annotation.activeLine !== 'off';
|
|
||||||
if (trackActiveLine && !this._activeEditorLineDisposable) {
|
|
||||||
const subscriptions: Disposable[] = [];
|
|
||||||
|
|
||||||
subscriptions.push(window.onDidChangeActiveTextEditor(this._onActiveTextEditorChanged, this));
|
|
||||||
subscriptions.push(window.onDidChangeTextEditorSelection(this._onTextEditorSelectionChanged, this));
|
|
||||||
subscriptions.push(this.gitContextTracker.onDidBlameabilityChange(this._onBlameabilityChanged, this));
|
|
||||||
|
|
||||||
this._activeEditorLineDisposable = Disposable.from(...subscriptions);
|
|
||||||
}
|
|
||||||
else if (!trackActiveLine && this._activeEditorLineDisposable) {
|
|
||||||
this._activeEditorLineDisposable.dispose();
|
|
||||||
this._activeEditorLineDisposable = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._onActiveTextEditorChanged(window.activeTextEditor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private isEditorBlameable(editor: TextEditor | undefined): boolean {
|
|
||||||
if (editor === undefined || editor.document === undefined) return false;
|
|
||||||
|
|
||||||
if (!this.git.isTrackable(editor.document.uri)) return false;
|
|
||||||
if (editor.document.isUntitled && editor.document.uri.scheme === DocumentSchemes.File) return false;
|
|
||||||
|
|
||||||
return this.git.isEditorBlameable(editor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _onActiveTextEditorChanged(editor: TextEditor | undefined) {
|
|
||||||
this._currentLine = -1;
|
|
||||||
|
|
||||||
const previousEditor = this._editor;
|
|
||||||
previousEditor && previousEditor.setDecorations(activeLineDecoration, []);
|
|
||||||
|
|
||||||
if (editor === undefined || !this.isEditorBlameable(editor)) {
|
|
||||||
this.clear(editor);
|
|
||||||
|
|
||||||
this._editor = undefined;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._blameable = editor !== undefined && editor.document !== undefined && !editor.document.isDirty;
|
|
||||||
this._editor = editor;
|
|
||||||
this._uri = await GitUri.fromUri(editor.document.uri, this.git);
|
|
||||||
|
|
||||||
const maxLines = this._config.advanced.caching.statusBar.maxLines;
|
|
||||||
// If caching is on and the file is small enough -- kick off a blame for the whole file
|
|
||||||
if (this._config.advanced.caching.enabled && (maxLines <= 0 || editor.document.lineCount <= maxLines)) {
|
|
||||||
this.git.getBlameForFile(this._uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updateBlameDebounced(editor.selection.active.line, editor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onBlameabilityChanged(e: BlameabilityChangeEvent) {
|
|
||||||
this._blameable = e.blameable;
|
|
||||||
if (!e.blameable || !this._editor) {
|
|
||||||
this.clear(e.editor);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure this is for the editor we are tracking
|
|
||||||
if (!TextEditorComparer.equals(this._editor, e.editor)) return;
|
|
||||||
|
|
||||||
this._updateBlameDebounced(this._editor.selection.active.line, this._editor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onBlameAnnotationToggled() {
|
|
||||||
this._onActiveTextEditorChanged(window.activeTextEditor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onGitCacheChanged() {
|
|
||||||
this._onActiveTextEditorChanged(window.activeTextEditor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _onTextEditorSelectionChanged(e: TextEditorSelectionChangeEvent): Promise<void> {
|
|
||||||
// Make sure this is for the editor we are tracking
|
|
||||||
if (!this._blameable || !TextEditorComparer.equals(this._editor, e.textEditor)) return;
|
|
||||||
|
|
||||||
const line = e.selections[0].active.line;
|
|
||||||
if (line === this._currentLine) return;
|
|
||||||
this._currentLine = line;
|
|
||||||
|
|
||||||
if (!this._uri && e.textEditor) {
|
|
||||||
this._uri = await GitUri.fromUri(e.textEditor.document.uri, this.git);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updateBlameDebounced(line, e.textEditor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _updateBlame(line: number, editor: TextEditor) {
|
|
||||||
line = line - this._uri.offset;
|
|
||||||
|
|
||||||
let commit: GitCommit | undefined = undefined;
|
|
||||||
let commitLine: IGitCommitLine | undefined = undefined;
|
|
||||||
// Since blame information isn't valid when there are unsaved changes -- don't show any status
|
|
||||||
if (this._blameable && line >= 0) {
|
|
||||||
const blameLine = await this.git.getBlameForLine(this._uri, line);
|
|
||||||
commitLine = blameLine === undefined ? undefined : blameLine.line;
|
|
||||||
commit = blameLine === undefined ? undefined : blameLine.commit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commit !== undefined && commitLine !== undefined) {
|
|
||||||
this.show(commit, commitLine, editor);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.clear(editor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clear(editor: TextEditor | undefined, previousEditor?: TextEditor) {
|
|
||||||
editor && editor.setDecorations(activeLineDecoration, []);
|
|
||||||
// I have no idea why the decorators sometimes don't get removed, but if they don't try again with a tiny delay
|
|
||||||
if (editor) {
|
|
||||||
setTimeout(() => editor.setDecorations(activeLineDecoration, []), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._statusBarItem && this._statusBarItem.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
async show(commit: GitCommit, blameLine: IGitCommitLine, editor: TextEditor) {
|
|
||||||
// I have no idea why I need this protection -- but it happens
|
|
||||||
if (!editor.document) return;
|
|
||||||
|
|
||||||
if (this._config.statusBar.enabled && this._statusBarItem !== undefined) {
|
|
||||||
switch (this._config.statusBar.date) {
|
|
||||||
case 'off':
|
|
||||||
this._statusBarItem.text = `$(git-commit) ${commit.author}`;
|
|
||||||
break;
|
|
||||||
case 'absolute':
|
|
||||||
const dateFormat = this._config.statusBar.dateFormat || 'MMMM Do, YYYY h:MMa';
|
|
||||||
let date: string;
|
|
||||||
try {
|
|
||||||
date = moment(commit.date).format(dateFormat);
|
|
||||||
} catch (ex) {
|
|
||||||
date = moment(commit.date).format('MMMM Do, YYYY h:MMa');
|
|
||||||
}
|
|
||||||
this._statusBarItem.text = `$(git-commit) ${commit.author}, ${date}`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this._statusBarItem.text = `$(git-commit) ${commit.author}, ${moment(commit.date).fromNow()}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this._config.statusBar.command) {
|
|
||||||
case StatusBarCommand.BlameAnnotate:
|
|
||||||
this._statusBarItem.tooltip = 'Toggle Blame Annotations';
|
|
||||||
break;
|
|
||||||
case StatusBarCommand.ShowBlameHistory:
|
|
||||||
this._statusBarItem.tooltip = 'Open Blame History Explorer';
|
|
||||||
break;
|
|
||||||
case StatusBarCommand.ShowFileHistory:
|
|
||||||
this._statusBarItem.tooltip = 'Open File History Explorer';
|
|
||||||
break;
|
|
||||||
case StatusBarCommand.DiffWithPrevious:
|
|
||||||
this._statusBarItem.command = Commands.DiffLineWithPrevious;
|
|
||||||
this._statusBarItem.tooltip = 'Compare File with Previous';
|
|
||||||
break;
|
|
||||||
case StatusBarCommand.DiffWithWorking:
|
|
||||||
this._statusBarItem.command = Commands.DiffLineWithWorking;
|
|
||||||
this._statusBarItem.tooltip = 'Compare File with Working Tree';
|
|
||||||
break;
|
|
||||||
case StatusBarCommand.ToggleCodeLens:
|
|
||||||
this._statusBarItem.tooltip = 'Toggle Git CodeLens';
|
|
||||||
break;
|
|
||||||
case StatusBarCommand.ShowQuickCommitDetails:
|
|
||||||
this._statusBarItem.tooltip = 'Show Commit Details';
|
|
||||||
break;
|
|
||||||
case StatusBarCommand.ShowQuickCommitFileDetails:
|
|
||||||
this._statusBarItem.tooltip = 'Show Line Commit Details';
|
|
||||||
break;
|
|
||||||
case StatusBarCommand.ShowQuickFileHistory:
|
|
||||||
this._statusBarItem.tooltip = 'Show File History';
|
|
||||||
break;
|
|
||||||
case StatusBarCommand.ShowQuickCurrentBranchHistory:
|
|
||||||
this._statusBarItem.tooltip = 'Show Branch History';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._statusBarItem.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._config.blame.annotation.activeLine !== 'off') {
|
|
||||||
const activeLine = this._config.blame.annotation.activeLine;
|
|
||||||
const offset = this._uri.offset;
|
|
||||||
|
|
||||||
const cfg = {
|
|
||||||
annotation: {
|
|
||||||
sha: true,
|
|
||||||
author: this._config.statusBar.enabled ? false : this._config.blame.annotation.author,
|
|
||||||
date: this._config.statusBar.enabled ? 'off' : this._config.blame.annotation.date,
|
|
||||||
message: true
|
|
||||||
}
|
|
||||||
} as IBlameConfig;
|
|
||||||
|
|
||||||
const annotation = BlameAnnotationFormatter.getAnnotation(cfg, commit, BlameAnnotationFormat.Unconstrained);
|
|
||||||
|
|
||||||
// Get the full commit message -- since blame only returns the summary
|
|
||||||
let logCommit: GitCommit | undefined = undefined;
|
|
||||||
if (!commit.isUncommitted) {
|
|
||||||
logCommit = await this.git.getLogCommit(this._uri.repoPath, this._uri.fsPath, commit.sha);
|
|
||||||
}
|
|
||||||
|
|
||||||
// I have no idea why I need this protection -- but it happens
|
|
||||||
if (!editor.document) return;
|
|
||||||
|
|
||||||
let hoverMessage: string | string[] | undefined = undefined;
|
|
||||||
if (activeLine !== 'inline') {
|
|
||||||
// If the messages match (or we couldn't find the log), then this is a possible duplicate annotation
|
|
||||||
const possibleDuplicate = !logCommit || logCommit.message === commit.message;
|
|
||||||
// If we don't have a possible dupe or we aren't showing annotations get the hover message
|
|
||||||
if (!commit.isUncommitted && (!possibleDuplicate || !this.annotationController.isAnnotating(editor))) {
|
|
||||||
hoverMessage = BlameAnnotationFormatter.getAnnotationHover(cfg, blameLine, logCommit || commit);
|
|
||||||
|
|
||||||
if (commit.previousSha !== undefined) {
|
|
||||||
const changes = await this.git.getDiffForLine(this._uri, blameLine.line + offset, commit.previousSha);
|
|
||||||
if (changes !== undefined) {
|
|
||||||
let previous = changes[0];
|
|
||||||
if (previous !== undefined) {
|
|
||||||
previous = previous.replace(/\n/g, '\`\n>\n> \`').trim();
|
|
||||||
hoverMessage += `\n\n---\n\`\`\`\n${previous}\n\`\`\``;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (commit.isUncommitted) {
|
|
||||||
const changes = await this.git.getDiffForLine(this._uri, blameLine.line + offset);
|
|
||||||
if (changes !== undefined) {
|
|
||||||
let previous = changes[0];
|
|
||||||
if (previous !== undefined) {
|
|
||||||
previous = previous.replace(/\n/g, '\`\n>\n> \`').trim();
|
|
||||||
hoverMessage = `\`${'0'.repeat(8)}\` __Uncommitted change__\n\n---\n\`\`\`\n${previous}\n\`\`\``;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let decorationOptions: [DecorationOptions] | undefined = undefined;
|
|
||||||
switch (activeLine) {
|
|
||||||
case 'both':
|
|
||||||
case 'inline':
|
|
||||||
const range = editor.document.validateRange(new Range(blameLine.line + offset, 0, blameLine.line + offset, 1000000));
|
|
||||||
decorationOptions = [
|
|
||||||
{
|
|
||||||
range: range.with({
|
|
||||||
start: range.start.with({
|
|
||||||
character: range.end.character
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
hoverMessage: hoverMessage,
|
|
||||||
renderOptions: {
|
|
||||||
after: {
|
|
||||||
contentText: annotation
|
|
||||||
},
|
|
||||||
dark: {
|
|
||||||
after: {
|
|
||||||
color: this._config.blame.annotation.activeLineDarkColor || 'rgba(153, 153, 153, 0.35)'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
light: {
|
|
||||||
after: {
|
|
||||||
color: this._config.blame.annotation.activeLineLightColor || 'rgba(153, 153, 153, 0.35)'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} as DecorationInstanceRenderOptions
|
|
||||||
} as DecorationOptions
|
|
||||||
];
|
|
||||||
|
|
||||||
if (activeLine === 'both') {
|
|
||||||
// Add a hover decoration to the area between the start of the line and the first non-whitespace character
|
|
||||||
decorationOptions.push({
|
|
||||||
range: range.with({
|
|
||||||
end: range.end.with({
|
|
||||||
character: editor.document.lineAt(range.end.line).firstNonWhitespaceCharacterIndex
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
hoverMessage: hoverMessage
|
|
||||||
} as DecorationOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'hover':
|
|
||||||
decorationOptions = [
|
|
||||||
{
|
|
||||||
range: editor.document.validateRange(new Range(blameLine.line + offset, 0, blameLine.line + offset, 1000000)),
|
|
||||||
hoverMessage: hoverMessage
|
|
||||||
} as DecorationOptions
|
|
||||||
];
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (decorationOptions !== undefined) {
|
|
||||||
editor.setDecorations(activeLineDecoration, decorationOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,271 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
import { Functions } from './system';
|
|
||||||
import { DecorationRenderOptions, Disposable, Event, EventEmitter, ExtensionContext, OverviewRulerLane, TextDocument, TextDocumentChangeEvent, TextEditor, TextEditorDecorationType, TextEditorViewColumnChangeEvent, window, workspace } from 'vscode';
|
|
||||||
import { BlameAnnotationProvider } from './blameAnnotationProvider';
|
|
||||||
import { TextDocumentComparer, TextEditorComparer } from './comparers';
|
|
||||||
import { IBlameConfig } from './configuration';
|
|
||||||
import { ExtensionKey } from './constants';
|
|
||||||
import { BlameabilityChangeEvent, GitContextTracker, GitService, GitUri } from './gitService';
|
|
||||||
import { Logger } from './logger';
|
|
||||||
import { WhitespaceController } from './whitespaceController';
|
|
||||||
|
|
||||||
export const BlameDecorations = {
|
|
||||||
annotation: window.createTextEditorDecorationType({
|
|
||||||
before: {
|
|
||||||
margin: '0 1.75em 0 0'
|
|
||||||
},
|
|
||||||
after: {
|
|
||||||
margin: '0 0 0 4em'
|
|
||||||
}
|
|
||||||
} as DecorationRenderOptions),
|
|
||||||
highlight: undefined as TextEditorDecorationType | undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
export class BlameAnnotationController extends Disposable {
|
|
||||||
|
|
||||||
private _onDidToggleBlameAnnotations = new EventEmitter<void>();
|
|
||||||
get onDidToggleBlameAnnotations(): Event<void> {
|
|
||||||
return this._onDidToggleBlameAnnotations.event;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _annotationProviders: Map<number, BlameAnnotationProvider> = new Map();
|
|
||||||
private _blameAnnotationsDisposable: Disposable | undefined;
|
|
||||||
private _config: IBlameConfig;
|
|
||||||
private _disposable: Disposable;
|
|
||||||
private _whitespaceController: WhitespaceController | undefined;
|
|
||||||
|
|
||||||
constructor(private context: ExtensionContext, private git: GitService, private gitContextTracker: GitContextTracker) {
|
|
||||||
super(() => this.dispose());
|
|
||||||
|
|
||||||
this._onConfigurationChanged();
|
|
||||||
|
|
||||||
const subscriptions: Disposable[] = [];
|
|
||||||
|
|
||||||
subscriptions.push(workspace.onDidChangeConfiguration(this._onConfigurationChanged, this));
|
|
||||||
|
|
||||||
this._disposable = Disposable.from(...subscriptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
|
||||||
this._annotationProviders.forEach(async (p, i) => await this.clear(i));
|
|
||||||
|
|
||||||
BlameDecorations.annotation && BlameDecorations.annotation.dispose();
|
|
||||||
BlameDecorations.highlight && BlameDecorations.highlight.dispose();
|
|
||||||
|
|
||||||
this._blameAnnotationsDisposable && this._blameAnnotationsDisposable.dispose();
|
|
||||||
this._whitespaceController && this._whitespaceController.dispose();
|
|
||||||
this._disposable && this._disposable.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onConfigurationChanged() {
|
|
||||||
let toggleWhitespace = workspace.getConfiguration(`${ExtensionKey}.advanced.toggleWhitespace`).get<boolean>('enabled');
|
|
||||||
if (!toggleWhitespace) {
|
|
||||||
// Until https://github.com/Microsoft/vscode/issues/11485 is fixed we need to toggle whitespace for non-monospace fonts and ligatures
|
|
||||||
// TODO: detect monospace font
|
|
||||||
toggleWhitespace = workspace.getConfiguration('editor').get<boolean>('fontLigatures');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toggleWhitespace && !this._whitespaceController) {
|
|
||||||
this._whitespaceController = new WhitespaceController();
|
|
||||||
}
|
|
||||||
else if (!toggleWhitespace && this._whitespaceController) {
|
|
||||||
this._whitespaceController.dispose();
|
|
||||||
this._whitespaceController = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cfg = workspace.getConfiguration(ExtensionKey).get<IBlameConfig>('blame')!;
|
|
||||||
|
|
||||||
if (cfg.annotation.highlight !== (this._config && this._config.annotation.highlight)) {
|
|
||||||
BlameDecorations.highlight && BlameDecorations.highlight.dispose();
|
|
||||||
|
|
||||||
switch (cfg.annotation.highlight) {
|
|
||||||
case 'gutter':
|
|
||||||
BlameDecorations.highlight = window.createTextEditorDecorationType({
|
|
||||||
dark: {
|
|
||||||
gutterIconPath: this.context.asAbsolutePath('images/blame-dark.svg'),
|
|
||||||
overviewRulerColor: 'rgba(255, 255, 255, 0.75)'
|
|
||||||
},
|
|
||||||
light: {
|
|
||||||
gutterIconPath: this.context.asAbsolutePath('images/blame-light.svg'),
|
|
||||||
overviewRulerColor: 'rgba(0, 0, 0, 0.75)'
|
|
||||||
},
|
|
||||||
gutterIconSize: 'contain',
|
|
||||||
overviewRulerLane: OverviewRulerLane.Right
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'line':
|
|
||||||
BlameDecorations.highlight = window.createTextEditorDecorationType({
|
|
||||||
dark: {
|
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.15)',
|
|
||||||
overviewRulerColor: 'rgba(255, 255, 255, 0.75)'
|
|
||||||
},
|
|
||||||
light: {
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.15)',
|
|
||||||
overviewRulerColor: 'rgba(0, 0, 0, 0.75)'
|
|
||||||
},
|
|
||||||
overviewRulerLane: OverviewRulerLane.Right,
|
|
||||||
isWholeLine: true
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'both':
|
|
||||||
BlameDecorations.highlight = window.createTextEditorDecorationType({
|
|
||||||
dark: {
|
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.15)',
|
|
||||||
gutterIconPath: this.context.asAbsolutePath('images/blame-dark.svg'),
|
|
||||||
overviewRulerColor: 'rgba(255, 255, 255, 0.75)'
|
|
||||||
},
|
|
||||||
light: {
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.15)',
|
|
||||||
gutterIconPath: this.context.asAbsolutePath('images/blame-light.svg'),
|
|
||||||
overviewRulerColor: 'rgba(0, 0, 0, 0.75)'
|
|
||||||
},
|
|
||||||
gutterIconSize: 'contain',
|
|
||||||
overviewRulerLane: OverviewRulerLane.Right,
|
|
||||||
isWholeLine: true
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
BlameDecorations.highlight = undefined;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._config = cfg;
|
|
||||||
}
|
|
||||||
|
|
||||||
async clear(column: number) {
|
|
||||||
const provider = this._annotationProviders.get(column);
|
|
||||||
if (!provider) return;
|
|
||||||
|
|
||||||
this._annotationProviders.delete(column);
|
|
||||||
await provider.dispose();
|
|
||||||
|
|
||||||
if (this._annotationProviders.size === 0) {
|
|
||||||
Logger.log(`Remove listener registrations for blame annotations`);
|
|
||||||
this._blameAnnotationsDisposable && this._blameAnnotationsDisposable.dispose();
|
|
||||||
this._blameAnnotationsDisposable = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._onDidToggleBlameAnnotations.fire();
|
|
||||||
}
|
|
||||||
|
|
||||||
async showBlameAnnotation(editor: TextEditor, shaOrLine?: string | number): Promise<boolean> {
|
|
||||||
if (!editor || !editor.document || !this.git.isEditorBlameable(editor)) return false;
|
|
||||||
|
|
||||||
const currentProvider = this._annotationProviders.get(editor.viewColumn || -1);
|
|
||||||
if (currentProvider && TextEditorComparer.equals(currentProvider.editor, editor)) {
|
|
||||||
await currentProvider.setSelection(shaOrLine);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const gitUri = await GitUri.fromUri(editor.document.uri, this.git);
|
|
||||||
const provider = new BlameAnnotationProvider(this.context, this.git, this._whitespaceController, editor, gitUri);
|
|
||||||
if (!await provider.supportsBlame()) return false;
|
|
||||||
|
|
||||||
if (currentProvider) {
|
|
||||||
await this.clear(currentProvider.editor.viewColumn || -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._blameAnnotationsDisposable && this._annotationProviders.size === 0) {
|
|
||||||
Logger.log(`Add listener registrations for blame annotations`);
|
|
||||||
|
|
||||||
const subscriptions: Disposable[] = [];
|
|
||||||
|
|
||||||
subscriptions.push(window.onDidChangeVisibleTextEditors(Functions.debounce(this._onVisibleTextEditorsChanged, 100), this));
|
|
||||||
subscriptions.push(window.onDidChangeTextEditorViewColumn(this._onTextEditorViewColumnChanged, this));
|
|
||||||
subscriptions.push(workspace.onDidChangeTextDocument(this._onTextDocumentChanged, this));
|
|
||||||
subscriptions.push(workspace.onDidCloseTextDocument(this._onTextDocumentClosed, this));
|
|
||||||
subscriptions.push(this.gitContextTracker.onDidBlameabilityChange(this._onBlameabilityChanged, this));
|
|
||||||
|
|
||||||
this._blameAnnotationsDisposable = Disposable.from(...subscriptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._annotationProviders.set(editor.viewColumn || -1, provider);
|
|
||||||
if (await provider.provideBlameAnnotation(shaOrLine)) {
|
|
||||||
this._onDidToggleBlameAnnotations.fire();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
isAnnotating(editor: TextEditor): boolean {
|
|
||||||
if (!editor || !editor.document || !this.git.isEditorBlameable(editor)) return false;
|
|
||||||
|
|
||||||
return !!this._annotationProviders.get(editor.viewColumn || -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
async toggleBlameAnnotation(editor: TextEditor, shaOrLine?: string | number): Promise<boolean> {
|
|
||||||
if (!editor || !editor.document || !this.git.isEditorBlameable(editor)) return false;
|
|
||||||
|
|
||||||
const provider = this._annotationProviders.get(editor.viewColumn || -1);
|
|
||||||
if (!provider) return this.showBlameAnnotation(editor, shaOrLine);
|
|
||||||
|
|
||||||
await this.clear(provider.editor.viewColumn || -1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onBlameabilityChanged(e: BlameabilityChangeEvent) {
|
|
||||||
if (e.blameable || !e.editor) return;
|
|
||||||
|
|
||||||
for (const [key, p] of this._annotationProviders) {
|
|
||||||
if (!TextDocumentComparer.equals(p.document, e.editor.document)) continue;
|
|
||||||
|
|
||||||
Logger.log('BlameabilityChanged:', `Clear blame annotations for column ${key}`);
|
|
||||||
this.clear(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onTextDocumentChanged(e: TextDocumentChangeEvent) {
|
|
||||||
for (const [key, p] of this._annotationProviders) {
|
|
||||||
if (!TextDocumentComparer.equals(p.document, e.document)) continue;
|
|
||||||
|
|
||||||
// We have to defer because isDirty is not reliable inside this event
|
|
||||||
setTimeout(() => {
|
|
||||||
// If the document is dirty all is fine, just kick out since the GitContextTracker will handle it
|
|
||||||
if (e.document.isDirty) return;
|
|
||||||
|
|
||||||
// If the document isn't dirty, it is very likely this event was triggered by an outside edit of this document
|
|
||||||
// Which means the document has been reloaded and the blame annotations have been removed, so we need to update (clear) our state tracking
|
|
||||||
Logger.log('TextDocumentChanged:', `Clear blame annotations for column ${key}`);
|
|
||||||
this.clear(key);
|
|
||||||
}, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onTextDocumentClosed(e: TextDocument) {
|
|
||||||
for (const [key, p] of this._annotationProviders) {
|
|
||||||
if (!TextDocumentComparer.equals(p.document, e)) continue;
|
|
||||||
|
|
||||||
Logger.log('TextDocumentClosed:', `Clear blame annotations for column ${key}`);
|
|
||||||
this.clear(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _onTextEditorViewColumnChanged(e: TextEditorViewColumnChangeEvent) {
|
|
||||||
const viewColumn = e.viewColumn || -1;
|
|
||||||
|
|
||||||
Logger.log('TextEditorViewColumnChanged:', `Clear blame annotations for column ${viewColumn}`);
|
|
||||||
await this.clear(viewColumn);
|
|
||||||
|
|
||||||
for (const [key, p] of this._annotationProviders) {
|
|
||||||
if (!TextEditorComparer.equals(p.editor, e.textEditor)) continue;
|
|
||||||
|
|
||||||
Logger.log('TextEditorViewColumnChanged:', `Clear blame annotations for column ${key}`);
|
|
||||||
await this.clear(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _onVisibleTextEditorsChanged(e: TextEditor[]) {
|
|
||||||
if (e.every(_ => _.document.uri.scheme === 'inmemory')) return;
|
|
||||||
|
|
||||||
for (const [key, p] of this._annotationProviders) {
|
|
||||||
if (e.some(_ => TextEditorComparer.equals(p.editor, _))) continue;
|
|
||||||
|
|
||||||
Logger.log('VisibleTextEditorsChanged:', `Clear blame annotations for column ${key}`);
|
|
||||||
this.clear(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
import { IBlameConfig } from './configuration';
|
|
||||||
import { GitCommit, IGitCommitLine } from './gitService';
|
|
||||||
import * as moment from 'moment';
|
|
||||||
|
|
||||||
export const defaultAbsoluteDateLength = 10;
|
|
||||||
export const defaultRelativeDateLength = 13;
|
|
||||||
export const defaultAuthorLength = 16;
|
|
||||||
export const defaultMessageLength = 32;
|
|
||||||
|
|
||||||
export enum BlameAnnotationFormat {
|
|
||||||
Constrained,
|
|
||||||
Unconstrained
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BlameAnnotationFormatter {
|
|
||||||
|
|
||||||
static getAnnotation(config: IBlameConfig, commit: GitCommit, format: BlameAnnotationFormat) {
|
|
||||||
const sha = commit.shortSha;
|
|
||||||
let message = this.getMessage(config, commit, format === BlameAnnotationFormat.Unconstrained ? 0 : defaultMessageLength);
|
|
||||||
|
|
||||||
if (format === BlameAnnotationFormat.Unconstrained) {
|
|
||||||
const authorAndDate = this.getAuthorAndDate(config, commit, config.annotation.dateFormat || 'MMMM Do, YYYY h:MMa');
|
|
||||||
if (config.annotation.sha) {
|
|
||||||
message = `${sha}${(authorAndDate ? `\u00a0\u2022\u00a0${authorAndDate}` : '')}${(message ? `\u00a0\u2022\u00a0${message}` : '')}`;
|
|
||||||
}
|
|
||||||
else if (config.annotation.author || config.annotation.date) {
|
|
||||||
message = `${authorAndDate}${(message ? `\u00a0\u2022\u00a0${message}` : '')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
const author = this.getAuthor(config, commit, defaultAuthorLength);
|
|
||||||
const date = this.getDate(config, commit, config.annotation.dateFormat || 'MM/DD/YYYY', true);
|
|
||||||
if (config.annotation.sha) {
|
|
||||||
message = `${sha}${(author ? `\u00a0\u2022\u00a0${author}` : '')}${(date ? `\u00a0\u2022\u00a0${date}` : '')}${(message ? `\u00a0\u2022\u00a0${message}` : '')}`;
|
|
||||||
}
|
|
||||||
else if (config.annotation.author) {
|
|
||||||
message = `${author}${(date ? `\u00a0\u2022\u00a0${date}` : '')}${(message ? `\u00a0\u2022\u00a0${message}` : '')}`;
|
|
||||||
}
|
|
||||||
else if (config.annotation.date) {
|
|
||||||
message = `${date}${(message ? `\u00a0\u2022\u00a0${message}` : '')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getAnnotationHover(config: IBlameConfig, line: IGitCommitLine, commit: GitCommit): string | string[] {
|
|
||||||
const message = `> \`${commit.message.replace(/\n/g, '\`\n>\n> \`')}\``;
|
|
||||||
if (commit.isUncommitted) {
|
|
||||||
return `\`${'0'.repeat(8)}\` __Uncommitted change__`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `\`${commit.shortSha}\` __${commit.author}__, ${moment(commit.date).fromNow()} _(${moment(commit.date).format(config.annotation.dateFormat || 'MMMM Do, YYYY h:MMa')})_ \n\n${message}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getAuthorAndDate(config: IBlameConfig, commit: GitCommit, format: string, force: boolean = false) {
|
|
||||||
if (!force && !config.annotation.author && (!config.annotation.date || config.annotation.date === 'off')) return '';
|
|
||||||
|
|
||||||
if (!config.annotation.author) {
|
|
||||||
return this.getDate(config, commit, format);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.annotation.date || config.annotation.date === 'off') {
|
|
||||||
return this.getAuthor(config, commit);
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${this.getAuthor(config, commit)}, ${this.getDate(config, commit, format)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getAuthor(config: IBlameConfig, commit: GitCommit, truncateTo: number = 0, force: boolean = false) {
|
|
||||||
if (!force && !config.annotation.author) return '';
|
|
||||||
|
|
||||||
const author = commit.isUncommitted ? 'Uncommitted' : commit.author;
|
|
||||||
if (!truncateTo) return author;
|
|
||||||
|
|
||||||
if (author.length > truncateTo) {
|
|
||||||
return `${author.substring(0, truncateTo - 1)}\u2026`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (force) return author; // Don't pad when just asking for the value
|
|
||||||
return author + '\u00a0'.repeat(truncateTo - author.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
static getDate(config: IBlameConfig, commit: GitCommit, format: string, truncate: boolean = false, force: boolean = false) {
|
|
||||||
if (!force && (!config.annotation.date || config.annotation.date === 'off')) return '';
|
|
||||||
|
|
||||||
const date = config.annotation.date === 'relative'
|
|
||||||
? moment(commit.date).fromNow()
|
|
||||||
: moment(commit.date).format(format);
|
|
||||||
if (!truncate) return date;
|
|
||||||
|
|
||||||
const truncateTo = config.annotation.date === 'relative' ? defaultRelativeDateLength : defaultAbsoluteDateLength;
|
|
||||||
if (date.length > truncateTo) {
|
|
||||||
return `${date.substring(0, truncateTo - 1)}\u2026`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (force) return date; // Don't pad when just asking for the value
|
|
||||||
return date + '\u00a0'.repeat(truncateTo - date.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
static getMessage(config: IBlameConfig, commit: GitCommit, truncateTo: number = 0, force: boolean = false) {
|
|
||||||
if (!force && !config.annotation.message) return '';
|
|
||||||
|
|
||||||
const message = commit.isUncommitted ? 'Uncommitted change' : commit.message;
|
|
||||||
if (truncateTo && message.length > truncateTo) {
|
|
||||||
return `${message.substring(0, truncateTo - 1)}\u2026`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,302 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
import { Iterables } from './system';
|
|
||||||
import { DecorationInstanceRenderOptions, DecorationOptions, Disposable, ExtensionContext, Range, TextDocument, TextEditor, TextEditorSelectionChangeEvent, window, workspace } from 'vscode';
|
|
||||||
import { BlameAnnotationFormat, BlameAnnotationFormatter, defaultAuthorLength } from './blameAnnotationFormatter';
|
|
||||||
import { BlameDecorations } from './blameAnnotationController';
|
|
||||||
import { TextDocumentComparer } from './comparers';
|
|
||||||
import { BlameAnnotationStyle, IBlameConfig } from './configuration';
|
|
||||||
import { ExtensionKey } from './constants';
|
|
||||||
import { GitService, GitUri, IGitBlame } from './gitService';
|
|
||||||
import { WhitespaceController } from './whitespaceController';
|
|
||||||
|
|
||||||
export class BlameAnnotationProvider extends Disposable {
|
|
||||||
|
|
||||||
public document: TextDocument;
|
|
||||||
|
|
||||||
private _blame: Promise<IGitBlame>;
|
|
||||||
private _config: IBlameConfig;
|
|
||||||
private _disposable: Disposable;
|
|
||||||
|
|
||||||
constructor(context: ExtensionContext, private git: GitService, private whitespaceController: WhitespaceController | undefined, public editor: TextEditor, private uri: GitUri) {
|
|
||||||
super(() => this.dispose());
|
|
||||||
|
|
||||||
this.document = this.editor.document;
|
|
||||||
|
|
||||||
this._blame = this.git.getBlameForFile(this.uri);
|
|
||||||
|
|
||||||
this._config = workspace.getConfiguration(ExtensionKey).get<IBlameConfig>('blame')!;
|
|
||||||
|
|
||||||
const subscriptions: Disposable[] = [];
|
|
||||||
|
|
||||||
subscriptions.push(window.onDidChangeTextEditorSelection(this._onActiveSelectionChanged, this));
|
|
||||||
|
|
||||||
this._disposable = Disposable.from(...subscriptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
async dispose() {
|
|
||||||
if (this.editor) {
|
|
||||||
try {
|
|
||||||
this.editor.setDecorations(BlameDecorations.annotation, []);
|
|
||||||
BlameDecorations.highlight && this.editor.setDecorations(BlameDecorations.highlight, []);
|
|
||||||
// I have no idea why the decorators sometimes don't get removed, but if they don't try again with a tiny delay
|
|
||||||
if (BlameDecorations.highlight !== undefined) {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (BlameDecorations.highlight === undefined) return;
|
|
||||||
|
|
||||||
this.editor.setDecorations(BlameDecorations.highlight, []);
|
|
||||||
}, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ex) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- restore whitespace
|
|
||||||
this.whitespaceController && await this.whitespaceController.restore();
|
|
||||||
|
|
||||||
this._disposable && this._disposable.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _onActiveSelectionChanged(e: TextEditorSelectionChangeEvent) {
|
|
||||||
if (!TextDocumentComparer.equals(this.document, e.textEditor && e.textEditor.document)) return;
|
|
||||||
|
|
||||||
return this.setSelection(e.selections[0].active.line);
|
|
||||||
}
|
|
||||||
|
|
||||||
async supportsBlame(): Promise<boolean> {
|
|
||||||
const blame = await this._blame;
|
|
||||||
return !!(blame && blame.lines.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
async provideBlameAnnotation(shaOrLine?: string | number): Promise<boolean> {
|
|
||||||
let whitespacePromise: Promise<void> | undefined;
|
|
||||||
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- override whitespace (turn off)
|
|
||||||
if (this._config.annotation.style !== BlameAnnotationStyle.Trailing) {
|
|
||||||
whitespacePromise = this.whitespaceController && this.whitespaceController.override();
|
|
||||||
}
|
|
||||||
|
|
||||||
let blame: IGitBlame;
|
|
||||||
if (whitespacePromise) {
|
|
||||||
[blame] = await Promise.all([this._blame, whitespacePromise]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
blame = await this._blame;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!blame || !blame.lines.length) {
|
|
||||||
this.whitespaceController && await this.whitespaceController.restore();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let blameDecorationOptions: DecorationOptions[] | undefined;
|
|
||||||
switch (this._config.annotation.style) {
|
|
||||||
case BlameAnnotationStyle.Compact:
|
|
||||||
blameDecorationOptions = this._getCompactGutterDecorations(blame);
|
|
||||||
break;
|
|
||||||
case BlameAnnotationStyle.Expanded:
|
|
||||||
blameDecorationOptions = this._getExpandedGutterDecorations(blame, false);
|
|
||||||
break;
|
|
||||||
case BlameAnnotationStyle.Trailing:
|
|
||||||
blameDecorationOptions = this._getExpandedGutterDecorations(blame, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blameDecorationOptions) {
|
|
||||||
this.editor.setDecorations(BlameDecorations.annotation, blameDecorationOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._setSelection(blame, shaOrLine);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async setSelection(shaOrLine?: string | number) {
|
|
||||||
const blame = await this._blame;
|
|
||||||
if (!blame || !blame.lines.length) return;
|
|
||||||
|
|
||||||
return this._setSelection(blame, shaOrLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _setSelection(blame: IGitBlame, shaOrLine?: string | number) {
|
|
||||||
if (!BlameDecorations.highlight) return;
|
|
||||||
|
|
||||||
const offset = this.uri.offset;
|
|
||||||
|
|
||||||
let sha: string | undefined = undefined;
|
|
||||||
if (typeof shaOrLine === 'string') {
|
|
||||||
sha = shaOrLine;
|
|
||||||
}
|
|
||||||
else if (typeof shaOrLine === 'number') {
|
|
||||||
const line = shaOrLine - offset;
|
|
||||||
if (line >= 0) {
|
|
||||||
const commitLine = blame.lines[line];
|
|
||||||
sha = commitLine && commitLine.sha;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sha = Iterables.first(blame.commits.values()).sha;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sha) {
|
|
||||||
this.editor.setDecorations(BlameDecorations.highlight, []);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const highlightDecorationRanges = blame.lines
|
|
||||||
.filter(l => l.sha === sha)
|
|
||||||
.map(l => this.editor.document.validateRange(new Range(l.line + offset, 0, l.line + offset, 1000000)));
|
|
||||||
|
|
||||||
this.editor.setDecorations(BlameDecorations.highlight, highlightDecorationRanges);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getCompactGutterDecorations(blame: IGitBlame): DecorationOptions[] {
|
|
||||||
const offset = this.uri.offset;
|
|
||||||
|
|
||||||
let count = 0;
|
|
||||||
let lastSha: string;
|
|
||||||
return blame.lines.map(l => {
|
|
||||||
const commit = blame.commits.get(l.sha);
|
|
||||||
if (commit === undefined) throw new Error(`Cannot find sha ${l.sha}`);
|
|
||||||
|
|
||||||
let color: string;
|
|
||||||
if (commit.isUncommitted) {
|
|
||||||
color = 'rgba(0, 188, 242, 0.6)';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
color = l.previousSha ? '#999999' : '#6b6b6b';
|
|
||||||
}
|
|
||||||
|
|
||||||
let gutter = '';
|
|
||||||
if (lastSha !== l.sha) {
|
|
||||||
count = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isEmptyOrWhitespace = this.document.lineAt(l.line).isEmptyOrWhitespace;
|
|
||||||
if (!isEmptyOrWhitespace) {
|
|
||||||
switch (++count) {
|
|
||||||
case 0:
|
|
||||||
gutter = commit.shortSha;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
gutter = `\u2759 ${BlameAnnotationFormatter.getAuthor(this._config, commit, defaultAuthorLength, true)}`;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
gutter = `\u2759 ${BlameAnnotationFormatter.getDate(this._config, commit, this._config.annotation.dateFormat || 'MM/DD/YYYY', true, true)}`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
gutter = `\u2759`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hoverMessage = BlameAnnotationFormatter.getAnnotationHover(this._config, l, commit);
|
|
||||||
|
|
||||||
lastSha = l.sha;
|
|
||||||
|
|
||||||
return {
|
|
||||||
range: this.editor.document.validateRange(new Range(l.line + offset, 0, l.line + offset, 1000000)),
|
|
||||||
hoverMessage: hoverMessage,
|
|
||||||
renderOptions: {
|
|
||||||
before: {
|
|
||||||
color: color,
|
|
||||||
contentText: gutter,
|
|
||||||
width: '11em'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} as DecorationOptions;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getExpandedGutterDecorations(blame: IGitBlame, trailing: boolean = false): DecorationOptions[] {
|
|
||||||
const offset = this.uri.offset;
|
|
||||||
|
|
||||||
let width = 0;
|
|
||||||
if (!trailing) {
|
|
||||||
if (this._config.annotation.sha) {
|
|
||||||
width += 5;
|
|
||||||
}
|
|
||||||
if (this._config.annotation.date && this._config.annotation.date !== 'off') {
|
|
||||||
if (width > 0) {
|
|
||||||
width += 7;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
width += 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._config.annotation.date === 'relative') {
|
|
||||||
width += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this._config.annotation.author) {
|
|
||||||
if (width > 5 + 6) {
|
|
||||||
width += 12;
|
|
||||||
}
|
|
||||||
else if (width > 0) {
|
|
||||||
width += 11;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
width += 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this._config.annotation.message) {
|
|
||||||
if (width > 5 + 6 + 10) {
|
|
||||||
width += 21;
|
|
||||||
}
|
|
||||||
else if (width > 5 + 6) {
|
|
||||||
width += 21;
|
|
||||||
}
|
|
||||||
else if (width > 0) {
|
|
||||||
width += 21;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
width += 19;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return blame.lines.map(l => {
|
|
||||||
const commit = blame.commits.get(l.sha);
|
|
||||||
if (commit === undefined) throw new Error(`Cannot find sha ${l.sha}`);
|
|
||||||
|
|
||||||
let color: string;
|
|
||||||
if (commit.isUncommitted) {
|
|
||||||
color = 'rgba(0, 188, 242, 0.6)';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (trailing) {
|
|
||||||
color = l.previousSha ? 'rgba(153, 153, 153, 0.5)' : 'rgba(107, 107, 107, 0.5)';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
color = l.previousSha ? 'rgb(153, 153, 153)' : 'rgb(107, 107, 107)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const format = trailing ? BlameAnnotationFormat.Unconstrained : BlameAnnotationFormat.Constrained;
|
|
||||||
const gutter = BlameAnnotationFormatter.getAnnotation(this._config, commit, format);
|
|
||||||
const hoverMessage = BlameAnnotationFormatter.getAnnotationHover(this._config, l, commit);
|
|
||||||
|
|
||||||
let renderOptions: DecorationInstanceRenderOptions;
|
|
||||||
if (trailing) {
|
|
||||||
renderOptions = {
|
|
||||||
after: {
|
|
||||||
color: color,
|
|
||||||
contentText: gutter
|
|
||||||
}
|
|
||||||
} as DecorationInstanceRenderOptions;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
renderOptions = {
|
|
||||||
before: {
|
|
||||||
color: color,
|
|
||||||
contentText: gutter,
|
|
||||||
width: `${width}em`
|
|
||||||
}
|
|
||||||
} as DecorationInstanceRenderOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
range: this.editor.document.validateRange(new Range(l.line + offset, 0, l.line + offset, 1000000)),
|
|
||||||
hoverMessage: hoverMessage,
|
|
||||||
renderOptions: renderOptions
|
|
||||||
} as DecorationOptions;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
81
src/codeLensController.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
'use strict';
|
||||||
|
import { Objects } from './system';
|
||||||
|
import { Disposable, ExtensionContext, languages, TextEditor, workspace } from 'vscode';
|
||||||
|
import { IConfig } from './configuration';
|
||||||
|
import { CommandContext, ExtensionKey, setCommandContext } from './constants';
|
||||||
|
import { GitCodeLensProvider } from './gitCodeLensProvider';
|
||||||
|
import { GitService } from './gitService';
|
||||||
|
import { Logger } from './logger';
|
||||||
|
|
||||||
|
export class CodeLensController extends Disposable {
|
||||||
|
|
||||||
|
private _codeLensProvider: GitCodeLensProvider | undefined;
|
||||||
|
private _codeLensProviderDisposable: Disposable | undefined;
|
||||||
|
private _config: IConfig;
|
||||||
|
private _disposable: Disposable | undefined;
|
||||||
|
|
||||||
|
constructor(private context: ExtensionContext, private git: GitService) {
|
||||||
|
super(() => this.dispose());
|
||||||
|
|
||||||
|
this._onConfigurationChanged();
|
||||||
|
|
||||||
|
const subscriptions: Disposable[] = [];
|
||||||
|
|
||||||
|
subscriptions.push(workspace.onDidChangeConfiguration(this._onConfigurationChanged, this));
|
||||||
|
subscriptions.push(git.onDidChangeGitCache(this._onGitCacheChanged, this));
|
||||||
|
|
||||||
|
this._disposable = Disposable.from(...subscriptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this._disposable && this._disposable.dispose();
|
||||||
|
|
||||||
|
this._codeLensProviderDisposable && this._codeLensProviderDisposable.dispose();
|
||||||
|
this._codeLensProviderDisposable = undefined;
|
||||||
|
this._codeLensProvider = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onConfigurationChanged() {
|
||||||
|
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
|
||||||
|
|
||||||
|
if (!Objects.areEquivalent(cfg.codeLens, this._config && this._config.codeLens)) {
|
||||||
|
Logger.log('CodeLens config changed; resetting CodeLens provider');
|
||||||
|
if (cfg.codeLens.enabled && (cfg.codeLens.recentChange.enabled || cfg.codeLens.authors.enabled)) {
|
||||||
|
if (this._codeLensProvider) {
|
||||||
|
this._codeLensProvider.reset();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._codeLensProvider = new GitCodeLensProvider(this.context, this.git);
|
||||||
|
this._codeLensProviderDisposable = languages.registerCodeLensProvider(GitCodeLensProvider.selector, this._codeLensProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._codeLensProviderDisposable && this._codeLensProviderDisposable.dispose();
|
||||||
|
this._codeLensProviderDisposable = undefined;
|
||||||
|
this._codeLensProvider = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCommandContext(CommandContext.CanToggleCodeLens, cfg.codeLens.recentChange.enabled || cfg.codeLens.authors.enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._config = cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onGitCacheChanged() {
|
||||||
|
Logger.log('Git cache changed; resetting CodeLens provider');
|
||||||
|
this._codeLensProvider && this._codeLensProvider.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCodeLens(editor: TextEditor) {
|
||||||
|
if (!this._config.codeLens.recentChange.enabled && !this._config.codeLens.authors.enabled) return;
|
||||||
|
|
||||||
|
Logger.log(`toggleCodeLens()`);
|
||||||
|
if (this._codeLensProviderDisposable) {
|
||||||
|
this._codeLensProviderDisposable.dispose();
|
||||||
|
this._codeLensProviderDisposable = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._codeLensProviderDisposable = languages.registerCodeLensProvider(GitCodeLensProvider.selector, new GitCodeLensProvider(this.context, this.git));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
export * from './commands/common';
|
export * from './commands/common';
|
||||||
|
|
||||||
export * from './commands/keyboard';
|
export * from './commands/clearFileAnnotations';
|
||||||
|
|
||||||
export * from './commands/closeUnchangedFiles';
|
export * from './commands/closeUnchangedFiles';
|
||||||
export * from './commands/copyMessageToClipboard';
|
export * from './commands/copyMessageToClipboard';
|
||||||
export * from './commands/copyShaToClipboard';
|
export * from './commands/copyShaToClipboard';
|
||||||
@@ -12,27 +11,33 @@ export * from './commands/diffLineWithWorking';
|
|||||||
export * from './commands/diffWithBranch';
|
export * from './commands/diffWithBranch';
|
||||||
export * from './commands/diffWithNext';
|
export * from './commands/diffWithNext';
|
||||||
export * from './commands/diffWithPrevious';
|
export * from './commands/diffWithPrevious';
|
||||||
|
export * from './commands/diffWithRevision';
|
||||||
export * from './commands/diffWithWorking';
|
export * from './commands/diffWithWorking';
|
||||||
export * from './commands/openChangedFiles';
|
export * from './commands/openChangedFiles';
|
||||||
|
export * from './commands/openBranchesInRemote';
|
||||||
export * from './commands/openBranchInRemote';
|
export * from './commands/openBranchInRemote';
|
||||||
export * from './commands/openCommitInRemote';
|
export * from './commands/openCommitInRemote';
|
||||||
export * from './commands/openFileInRemote';
|
export * from './commands/openFileInRemote';
|
||||||
export * from './commands/openInRemote';
|
export * from './commands/openInRemote';
|
||||||
export * from './commands/openRepoInRemote';
|
export * from './commands/openRepoInRemote';
|
||||||
export * from './commands/showBlame';
|
export * from './commands/resetSuppressedWarnings';
|
||||||
export * from './commands/showBlameHistory';
|
export * from './commands/showBlameHistory';
|
||||||
|
export * from './commands/showCommitSearch';
|
||||||
|
export * from './commands/showFileBlame';
|
||||||
export * from './commands/showFileHistory';
|
export * from './commands/showFileHistory';
|
||||||
export * from './commands/showLastQuickPick';
|
export * from './commands/showLastQuickPick';
|
||||||
|
export * from './commands/showLineBlame';
|
||||||
|
export * from './commands/showQuickBranchHistory';
|
||||||
export * from './commands/showQuickCommitDetails';
|
export * from './commands/showQuickCommitDetails';
|
||||||
export * from './commands/showQuickCommitFileDetails';
|
export * from './commands/showQuickCommitFileDetails';
|
||||||
export * from './commands/showCommitSearch';
|
|
||||||
export * from './commands/showQuickFileHistory';
|
|
||||||
export * from './commands/showQuickBranchHistory';
|
|
||||||
export * from './commands/showQuickCurrentBranchHistory';
|
export * from './commands/showQuickCurrentBranchHistory';
|
||||||
|
export * from './commands/showQuickFileHistory';
|
||||||
export * from './commands/showQuickRepoStatus';
|
export * from './commands/showQuickRepoStatus';
|
||||||
export * from './commands/showQuickStashList';
|
export * from './commands/showQuickStashList';
|
||||||
export * from './commands/stashApply';
|
export * from './commands/stashApply';
|
||||||
export * from './commands/stashDelete';
|
export * from './commands/stashDelete';
|
||||||
export * from './commands/stashSave';
|
export * from './commands/stashSave';
|
||||||
export * from './commands/toggleBlame';
|
|
||||||
export * from './commands/toggleCodeLens';
|
export * from './commands/toggleCodeLens';
|
||||||
|
export * from './commands/toggleFileBlame';
|
||||||
|
export * from './commands/toggleFileRecentChanges';
|
||||||
|
export * from './commands/toggleLineBlame';
|
||||||
24
src/commands/clearFileAnnotations.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
'use strict';
|
||||||
|
import { TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
||||||
|
import { AnnotationController } from '../annotations/annotationController';
|
||||||
|
import { Commands, EditorCommand } from './common';
|
||||||
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
|
export class ClearFileAnnotationsCommand extends EditorCommand {
|
||||||
|
|
||||||
|
constructor(private annotationController: AnnotationController) {
|
||||||
|
super(Commands.ClearFileAnnotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri): Promise<any> {
|
||||||
|
if (editor === undefined || editor.document === undefined || editor.document.isDirty) return undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return this.annotationController.clear(editor.viewColumn || -1);
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
Logger.error(ex, 'ClearFileAnnotationsCommand');
|
||||||
|
return window.showErrorMessage(`Unable to clear file annotations. See output channel for more details`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { TextEditor, Uri, window } from 'vscode';
|
import { commands, TextEditor, Uri, window } from 'vscode';
|
||||||
import { ActiveEditorTracker } from '../activeEditorTracker';
|
import { ActiveEditorTracker } from '../activeEditorTracker';
|
||||||
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
|
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
|
||||||
import { TextEditorComparer, UriComparer } from '../comparers';
|
import { TextEditorComparer, UriComparer } from '../comparers';
|
||||||
|
import { BuiltInCommands } from '../constants';
|
||||||
import { GitService } from '../gitService';
|
import { GitService } from '../gitService';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
|
import { Messages } from '../messages';
|
||||||
|
|
||||||
export interface CloseUnchangedFilesCommandArgs {
|
export interface CloseUnchangedFilesCommandArgs {
|
||||||
uris?: Uri[];
|
uris?: Uri[];
|
||||||
@@ -16,13 +18,15 @@ export class CloseUnchangedFilesCommand extends ActiveEditorCommand {
|
|||||||
super(Commands.CloseUnchangedFiles);
|
super(Commands.CloseUnchangedFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(editor: TextEditor, uri?: Uri, args: CloseUnchangedFilesCommandArgs = {}) {
|
async execute(editor?: TextEditor, uri?: Uri, args: CloseUnchangedFilesCommandArgs = {}) {
|
||||||
uri = getCommandUri(uri, editor);
|
uri = getCommandUri(uri, editor);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (args.uris === undefined) {
|
if (args.uris === undefined) {
|
||||||
|
args = { ...args };
|
||||||
|
|
||||||
const repoPath = await this.git.getRepoPathFromUri(uri);
|
const repoPath = await this.git.getRepoPathFromUri(uri);
|
||||||
if (!repoPath) return window.showWarningMessage(`Unable to close unchanged files`);
|
if (!repoPath) return Messages.showNoRepositoryWarningMessage(`Unable to close unchanged files`);
|
||||||
|
|
||||||
const status = await this.git.getStatusForRepo(repoPath);
|
const status = await this.git.getStatusForRepo(repoPath);
|
||||||
if (status === undefined) return window.showWarningMessage(`Unable to close unchanged files`);
|
if (status === undefined) return window.showWarningMessage(`Unable to close unchanged files`);
|
||||||
@@ -30,34 +34,46 @@ export class CloseUnchangedFilesCommand extends ActiveEditorCommand {
|
|||||||
args.uris = status.files.map(_ => _.Uri);
|
args.uris = status.files.map(_ => _.Uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (args.uris.length === 0) return commands.executeCommand(BuiltInCommands.CloseAllEditors);
|
||||||
|
|
||||||
const editorTracker = new ActiveEditorTracker();
|
const editorTracker = new ActiveEditorTracker();
|
||||||
|
|
||||||
let active = window.activeTextEditor;
|
let count = 0;
|
||||||
let editor = active;
|
let previous = undefined;
|
||||||
do {
|
let editor = window.activeTextEditor;
|
||||||
|
while (true) {
|
||||||
if (editor !== undefined) {
|
if (editor !== undefined) {
|
||||||
if ((editor.document !== undefined && editor.document.isDirty) ||
|
if (TextEditorComparer.equals(previous, editor, { useId: true, usePosition: true })) {
|
||||||
args.uris.some(_ => UriComparer.equals(_, editor!.document && editor!.document.uri))) {
|
break;
|
||||||
// If we didn't start with a valid editor, set one once we find it
|
|
||||||
if (active === undefined) {
|
|
||||||
active = editor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (editor.document !== undefined &&
|
||||||
|
(editor.document.isDirty || args.uris.some(_ => UriComparer.equals(_, editor!.document && editor!.document.uri)))) {
|
||||||
|
const lastPrevious = previous;
|
||||||
|
previous = editor;
|
||||||
editor = await editorTracker.awaitNext(500);
|
editor = await editorTracker.awaitNext(500);
|
||||||
|
|
||||||
|
if (TextEditorComparer.equals(lastPrevious, editor, { useId: true, usePosition: true })) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else {
|
continue;
|
||||||
if (active === editor) {
|
|
||||||
active = undefined;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previous = editor;
|
||||||
editor = await editorTracker.awaitClose(500);
|
editor = await editorTracker.awaitClose(500);
|
||||||
|
|
||||||
|
if (previous === undefined && editor === undefined) {
|
||||||
|
count++;
|
||||||
|
// This is such a shitty hack, but I can't figure out any other reliable way to know that we've cycled through all the editors :(
|
||||||
|
if (count >= 4) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (active === editor) {
|
count = 0;
|
||||||
active = undefined;
|
|
||||||
}
|
}
|
||||||
editor = await editorTracker.awaitClose(500);
|
|
||||||
}
|
}
|
||||||
} while ((active === undefined && editor === undefined) || !TextEditorComparer.equals(active, editor, { useId: true, usePosition: true }));
|
|
||||||
|
|
||||||
editorTracker.dispose();
|
editorTracker.dispose();
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,53 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { commands, Disposable, TextDocumentShowOptions, TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
|
import { commands, Disposable, SourceControlResourceGroup, SourceControlResourceState, TextDocumentShowOptions, TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
|
||||||
import { BuiltInCommands } from '../constants';
|
import { ExplorerNode } from '../views/explorerNodes';
|
||||||
|
import { GitBranch, GitCommit, GitRemote } from '../gitService';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
import { Telemetry } from '../telemetry';
|
import { Telemetry } from '../telemetry';
|
||||||
|
|
||||||
export type Commands = 'gitlens.closeUnchangedFiles' | 'gitlens.copyMessageToClipboard' | 'gitlens.copyShaToClipboard' |
|
export type Commands =
|
||||||
'gitlens.diffDirectory' | 'gitlens.diffWithBranch' | 'gitlens.diffWithNext' | 'gitlens.diffWithPrevious' | 'gitlens.diffLineWithPrevious' | 'gitlens.diffWithWorking' | 'gitlens.diffLineWithWorking' |
|
'gitlens.clearFileAnnotations' |
|
||||||
'gitlens.openChangedFiles' | 'gitlens.openBranchInRemote' | 'gitlens.openCommitInRemote' | 'gitlens.openFileInRemote' | 'gitlens.openInRemote' | 'gitlens.openRepoInRemote' |
|
'gitlens.closeUnchangedFiles' |
|
||||||
'gitlens.showBlame' | 'gitlens.showBlameHistory' | 'gitlens.showCommitSearch' | 'gitlens.showFileHistory' |
|
'gitlens.copyMessageToClipboard' |
|
||||||
'gitlens.showLastQuickPick' | 'gitlens.showQuickBranchHistory' |
|
'gitlens.copyShaToClipboard' |
|
||||||
'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' |
|
'gitlens.diffDirectory' |
|
||||||
'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory' |
|
'gitlens.diffWithBranch' |
|
||||||
'gitlens.showQuickRepoStatus' | 'gitlens.showQuickStashList' |
|
'gitlens.diffWithNext' |
|
||||||
'gitlens.stashApply' | 'gitlens.stashDelete' | 'gitlens.stashSave' |
|
'gitlens.diffWithPrevious' |
|
||||||
'gitlens.toggleBlame' | 'gitlens.toggleCodeLens';
|
'gitlens.diffLineWithPrevious' |
|
||||||
|
'gitlens.diffWithRevision' |
|
||||||
|
'gitlens.diffWithWorking' |
|
||||||
|
'gitlens.diffLineWithWorking' |
|
||||||
|
'gitlens.openChangedFiles' |
|
||||||
|
'gitlens.openBranchesInRemote' |
|
||||||
|
'gitlens.openBranchInRemote' |
|
||||||
|
'gitlens.openCommitInRemote' |
|
||||||
|
'gitlens.openFileInRemote' |
|
||||||
|
'gitlens.openInRemote' |
|
||||||
|
'gitlens.openRepoInRemote' |
|
||||||
|
'gitlens.resetSuppressedWarnings' |
|
||||||
|
'gitlens.showBlameHistory' |
|
||||||
|
'gitlens.showCommitSearch' |
|
||||||
|
'gitlens.showFileBlame' |
|
||||||
|
'gitlens.showFileHistory' |
|
||||||
|
'gitlens.showLastQuickPick' |
|
||||||
|
'gitlens.showLineBlame' |
|
||||||
|
'gitlens.showQuickBranchHistory' |
|
||||||
|
'gitlens.showQuickCommitDetails' |
|
||||||
|
'gitlens.showQuickCommitFileDetails' |
|
||||||
|
'gitlens.showQuickFileHistory' |
|
||||||
|
'gitlens.showQuickRepoHistory' |
|
||||||
|
'gitlens.showQuickRepoStatus' |
|
||||||
|
'gitlens.showQuickStashList' |
|
||||||
|
'gitlens.stashApply' |
|
||||||
|
'gitlens.stashDelete' |
|
||||||
|
'gitlens.stashSave' |
|
||||||
|
'gitlens.toggleCodeLens' |
|
||||||
|
'gitlens.toggleFileBlame' |
|
||||||
|
'gitlens.toggleFileRecentChanges' |
|
||||||
|
'gitlens.toggleLineBlame';
|
||||||
export const Commands = {
|
export const Commands = {
|
||||||
|
ClearFileAnnotations: 'gitlens.clearFileAnnotations' as Commands,
|
||||||
CloseUnchangedFiles: 'gitlens.closeUnchangedFiles' as Commands,
|
CloseUnchangedFiles: 'gitlens.closeUnchangedFiles' as Commands,
|
||||||
CopyMessageToClipboard: 'gitlens.copyMessageToClipboard' as Commands,
|
CopyMessageToClipboard: 'gitlens.copyMessageToClipboard' as Commands,
|
||||||
CopyShaToClipboard: 'gitlens.copyShaToClipboard' as Commands,
|
CopyShaToClipboard: 'gitlens.copyShaToClipboard' as Commands,
|
||||||
@@ -23,19 +56,23 @@ export const Commands = {
|
|||||||
DiffWithNext: 'gitlens.diffWithNext' as Commands,
|
DiffWithNext: 'gitlens.diffWithNext' as Commands,
|
||||||
DiffWithPrevious: 'gitlens.diffWithPrevious' as Commands,
|
DiffWithPrevious: 'gitlens.diffWithPrevious' as Commands,
|
||||||
DiffLineWithPrevious: 'gitlens.diffLineWithPrevious' as Commands,
|
DiffLineWithPrevious: 'gitlens.diffLineWithPrevious' 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,
|
||||||
OpenChangedFiles: 'gitlens.openChangedFiles' as Commands,
|
OpenChangedFiles: 'gitlens.openChangedFiles' as Commands,
|
||||||
|
OpenBranchesInRemote: 'gitlens.openBranchesInRemote' as Commands,
|
||||||
OpenBranchInRemote: 'gitlens.openBranchInRemote' as Commands,
|
OpenBranchInRemote: 'gitlens.openBranchInRemote' as Commands,
|
||||||
OpenCommitInRemote: 'gitlens.openCommitInRemote' as Commands,
|
OpenCommitInRemote: 'gitlens.openCommitInRemote' as Commands,
|
||||||
OpenFileInRemote: 'gitlens.openFileInRemote' as Commands,
|
OpenFileInRemote: 'gitlens.openFileInRemote' as Commands,
|
||||||
OpenInRemote: 'gitlens.openInRemote' as Commands,
|
OpenInRemote: 'gitlens.openInRemote' as Commands,
|
||||||
OpenRepoInRemote: 'gitlens.openRepoInRemote' as Commands,
|
OpenRepoInRemote: 'gitlens.openRepoInRemote' as Commands,
|
||||||
ShowBlame: 'gitlens.showBlame' as Commands,
|
ResetSuppressedWarnings: 'gitlens.resetSuppressedWarnings' as Commands,
|
||||||
ShowBlameHistory: 'gitlens.showBlameHistory' as Commands,
|
ShowBlameHistory: 'gitlens.showBlameHistory' as Commands,
|
||||||
ShowCommitSearch: 'gitlens.showCommitSearch' as Commands,
|
ShowCommitSearch: 'gitlens.showCommitSearch' as Commands,
|
||||||
|
ShowFileBlame: 'gitlens.showFileBlame' as Commands,
|
||||||
ShowFileHistory: 'gitlens.showFileHistory' as Commands,
|
ShowFileHistory: 'gitlens.showFileHistory' as Commands,
|
||||||
ShowLastQuickPick: 'gitlens.showLastQuickPick' as Commands,
|
ShowLastQuickPick: 'gitlens.showLastQuickPick' as Commands,
|
||||||
|
ShowLineBlame: 'gitlens.showLineBlame' as Commands,
|
||||||
ShowQuickCommitDetails: 'gitlens.showQuickCommitDetails' as Commands,
|
ShowQuickCommitDetails: 'gitlens.showQuickCommitDetails' as Commands,
|
||||||
ShowQuickCommitFileDetails: 'gitlens.showQuickCommitFileDetails' as Commands,
|
ShowQuickCommitFileDetails: 'gitlens.showQuickCommitFileDetails' as Commands,
|
||||||
ShowQuickFileHistory: 'gitlens.showQuickFileHistory' as Commands,
|
ShowQuickFileHistory: 'gitlens.showQuickFileHistory' as Commands,
|
||||||
@@ -46,8 +83,10 @@ export const Commands = {
|
|||||||
StashApply: 'gitlens.stashApply' as Commands,
|
StashApply: 'gitlens.stashApply' as Commands,
|
||||||
StashDelete: 'gitlens.stashDelete' as Commands,
|
StashDelete: 'gitlens.stashDelete' as Commands,
|
||||||
StashSave: 'gitlens.stashSave' as Commands,
|
StashSave: 'gitlens.stashSave' as Commands,
|
||||||
ToggleBlame: 'gitlens.toggleBlame' as Commands,
|
ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands,
|
||||||
ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands
|
ToggleFileBlame: 'gitlens.toggleFileBlame' as Commands,
|
||||||
|
ToggleFileRecentChanges: 'gitlens.toggleFileRecentChanges' as Commands,
|
||||||
|
ToggleLineBlame: 'gitlens.toggleLineBlame' as Commands
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getCommandUri(uri?: Uri, editor?: TextEditor): Uri | undefined {
|
export function getCommandUri(uri?: Uri, editor?: TextEditor): Uri | undefined {
|
||||||
@@ -56,27 +95,84 @@ export function getCommandUri(uri?: Uri, editor?: TextEditor): Uri | undefined {
|
|||||||
return editor.document.uri;
|
return editor.document.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CommandContext = 'gitlens:canToggleCodeLens' | 'gitlens:enabled' | 'gitlens:hasRemotes' | 'gitlens:isBlameable' | 'gitlens:isRepository' | 'gitlens:isTracked' | 'gitlens:key';
|
export interface CommandContextParsingOptions {
|
||||||
export const CommandContext = {
|
editor: boolean;
|
||||||
CanToggleCodeLens: 'gitlens:canToggleCodeLens' as CommandContext,
|
uri: boolean;
|
||||||
Enabled: 'gitlens:enabled' as CommandContext,
|
}
|
||||||
HasRemotes: 'gitlens:hasRemotes' as CommandContext,
|
|
||||||
IsBlameable: 'gitlens:isBlameable' as CommandContext,
|
|
||||||
IsRepository: 'gitlens:isRepository' as CommandContext,
|
|
||||||
IsTracked: 'gitlens:isTracked' as CommandContext,
|
|
||||||
Key: 'gitlens:key' as CommandContext
|
|
||||||
};
|
|
||||||
|
|
||||||
export function setCommandContext(key: CommandContext | string, value: any) {
|
export interface CommandBaseContext {
|
||||||
return commands.executeCommand(BuiltInCommands.SetContext, key, value);
|
editor?: TextEditor;
|
||||||
|
uri?: Uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommandScmGroupsContext extends CommandBaseContext {
|
||||||
|
type: 'scm-groups';
|
||||||
|
scmResourceGroups: SourceControlResourceGroup[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommandScmStatesContext extends CommandBaseContext {
|
||||||
|
type: 'scm-states';
|
||||||
|
scmResourceStates: SourceControlResourceState[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommandUnknownContext extends CommandBaseContext {
|
||||||
|
type: 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommandUriContext extends CommandBaseContext {
|
||||||
|
type: 'uri';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommandViewContext extends CommandBaseContext {
|
||||||
|
type: 'view';
|
||||||
|
node: ExplorerNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCommandViewContextWithBranch(context: CommandContext): context is CommandViewContext & { node: (ExplorerNode & { branch: GitBranch }) } {
|
||||||
|
return context.type === 'view' && (context.node as any).branch && (context.node as any).branch instanceof GitBranch;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICommandViewContextWithCommit<T extends GitCommit> extends CommandViewContext {
|
||||||
|
node: (ExplorerNode & { commit: T });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCommandViewContextWithCommit<T extends GitCommit>(context: CommandContext): context is ICommandViewContextWithCommit<T> {
|
||||||
|
return context.type === 'view' && (context.node as any).commit && (context.node as any).commit instanceof GitCommit;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCommandViewContextWithRemote(context: CommandContext): context is CommandViewContext & { node: (ExplorerNode & { remote: GitRemote }) } {
|
||||||
|
return context.type === 'view' && (context.node as any).remote && (context.node as any).remote instanceof GitRemote;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CommandContext = CommandScmGroupsContext | CommandScmStatesContext | CommandUnknownContext | CommandUriContext | CommandViewContext;
|
||||||
|
|
||||||
|
function isScmResourceGroup(group: any): group is SourceControlResourceGroup {
|
||||||
|
if (group === undefined) return false;
|
||||||
|
|
||||||
|
return (group as SourceControlResourceGroup).id !== undefined && (group.handle !== undefined || (group as SourceControlResourceGroup).label !== undefined || (group as SourceControlResourceGroup).resourceStates !== undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isScmResourceState(state: any): state is SourceControlResourceState {
|
||||||
|
if (state === undefined) return false;
|
||||||
|
|
||||||
|
return (state as SourceControlResourceState).resourceUri !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTextEditor(editor: any): editor is TextEditor {
|
||||||
|
if (editor === undefined) return false;
|
||||||
|
|
||||||
|
return editor.id !== undefined && ((editor as TextEditor).edit !== undefined || (editor as TextEditor).document !== undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class Command extends Disposable {
|
export abstract class Command extends Disposable {
|
||||||
|
|
||||||
|
protected readonly contextParsingOptions: CommandContextParsingOptions = { editor: false, uri: false };
|
||||||
|
|
||||||
private _disposable: Disposable;
|
private _disposable: Disposable;
|
||||||
|
|
||||||
constructor(protected command: Commands) {
|
constructor(protected command: Commands) {
|
||||||
super(() => this.dispose());
|
super(() => this.dispose());
|
||||||
|
|
||||||
this._disposable = commands.registerCommand(command, this._execute, this);
|
this._disposable = commands.registerCommand(command, this._execute, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,46 +180,86 @@ export abstract class Command extends Disposable {
|
|||||||
this._disposable && this._disposable.dispose();
|
this._disposable && this._disposable.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _execute(...args: any[]): any {
|
protected async preExecute(context: CommandContext, ...args: any[]): Promise<any> {
|
||||||
Telemetry.trackEvent(this.command);
|
|
||||||
return this.execute(...args);
|
return this.execute(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract execute(...args: any[]): any;
|
abstract execute(...args: any[]): any;
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class EditorCommand extends Disposable {
|
protected _execute(...args: any[]): any {
|
||||||
|
|
||||||
private _disposable: Disposable;
|
|
||||||
|
|
||||||
constructor(public readonly command: Commands) {
|
|
||||||
super(() => this.dispose());
|
|
||||||
this._disposable = commands.registerTextEditorCommand(command, this._execute, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
|
||||||
this._disposable && this._disposable.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _execute(editor: TextEditor, edit: TextEditorEdit, ...args: any[]): any {
|
|
||||||
Telemetry.trackEvent(this.command);
|
Telemetry.trackEvent(this.command);
|
||||||
return this.execute(editor, edit, ...args);
|
|
||||||
|
const [context, rest] = Command._parseContext(this.contextParsingOptions, ...args);
|
||||||
|
return this.preExecute(context, ...rest);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract execute(editor: TextEditor, edit: TextEditorEdit, ...args: any[]): any;
|
private static _parseContext(options: CommandContextParsingOptions, ...args: any[]): [CommandContext, any[]] {
|
||||||
|
let editor: TextEditor | undefined = undefined;
|
||||||
|
|
||||||
|
let firstArg = args[0];
|
||||||
|
if (options.editor && (firstArg === undefined || isTextEditor(firstArg))) {
|
||||||
|
editor = firstArg;
|
||||||
|
args = args.slice(1);
|
||||||
|
firstArg = args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.uri && (firstArg === undefined || firstArg instanceof Uri)) {
|
||||||
|
const [uri, ...rest] = args as [Uri, any];
|
||||||
|
return [{ type: 'uri', editor: editor, uri: uri }, rest];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstArg instanceof ExplorerNode) {
|
||||||
|
const [node, ...rest] = args as [ExplorerNode, any];
|
||||||
|
return [{ type: 'view', node: node, uri: node.uri }, rest];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isScmResourceState(firstArg)) {
|
||||||
|
const states = [];
|
||||||
|
let count = 0;
|
||||||
|
for (const arg of args) {
|
||||||
|
if (!isScmResourceState(arg)) break;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
states.push(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{ type: 'scm-states', scmResourceStates: states, uri: states[0].resourceUri }, args.slice(count)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isScmResourceGroup(firstArg)) {
|
||||||
|
const groups = [];
|
||||||
|
let count = 0;
|
||||||
|
for (const arg of args) {
|
||||||
|
if (!isScmResourceGroup(arg)) break;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
groups.push(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{ type: 'scm-groups', scmResourceGroups: groups }, args.slice(count)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{ type: 'unknown', editor: editor }, args];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class ActiveEditorCommand extends Command {
|
export abstract class ActiveEditorCommand extends Command {
|
||||||
|
|
||||||
|
protected readonly contextParsingOptions: CommandContextParsingOptions = { editor: true, uri: true };
|
||||||
|
|
||||||
constructor(public readonly command: Commands) {
|
constructor(public readonly command: Commands) {
|
||||||
super(command);
|
super(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async preExecute(context: CommandContext, ...args: any[]): Promise<any> {
|
||||||
|
return this.execute(context.editor, context.uri, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
protected _execute(...args: any[]): any {
|
protected _execute(...args: any[]): any {
|
||||||
return super._execute(window.activeTextEditor, ...args);
|
return super._execute(window.activeTextEditor, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract execute(editor: TextEditor, ...args: any[]): any;
|
abstract execute(editor?: TextEditor, ...args: any[]): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastCommand: { command: string, args: any[] } | undefined = undefined;
|
let lastCommand: { command: string, args: any[] } | undefined = undefined;
|
||||||
@@ -148,6 +284,27 @@ export abstract class ActiveEditorCachedCommand extends ActiveEditorCommand {
|
|||||||
abstract execute(editor: TextEditor, ...args: any[]): any;
|
abstract execute(editor: TextEditor, ...args: any[]): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export abstract class EditorCommand extends Disposable {
|
||||||
|
|
||||||
|
private _disposable: Disposable;
|
||||||
|
|
||||||
|
constructor(public readonly command: Commands) {
|
||||||
|
super(() => this.dispose());
|
||||||
|
this._disposable = commands.registerTextEditorCommand(command, this._execute, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this._disposable && this._disposable.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _execute(editor: TextEditor, edit: TextEditorEdit, ...args: any[]): any {
|
||||||
|
Telemetry.trackEvent(this.command);
|
||||||
|
return this.execute(editor, edit, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract execute(editor: TextEditor, edit: TextEditorEdit, ...args: any[]): any;
|
||||||
|
}
|
||||||
|
|
||||||
export async function openEditor(uri: Uri, options?: TextDocumentShowOptions): Promise<TextEditor | undefined> {
|
export async function openEditor(uri: Uri, options?: TextDocumentShowOptions): Promise<TextEditor | undefined> {
|
||||||
try {
|
try {
|
||||||
const defaults: TextDocumentShowOptions = {
|
const defaults: TextDocumentShowOptions = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Iterables } from '../system';
|
import { Iterables } from '../system';
|
||||||
import { TextEditor, Uri, window } from 'vscode';
|
import { TextEditor, Uri, window } from 'vscode';
|
||||||
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
|
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
|
||||||
import { GitService, GitUri } from '../gitService';
|
import { GitService, GitUri } from '../gitService';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
import { copy } from 'copy-paste';
|
import { copy } from 'copy-paste';
|
||||||
@@ -17,10 +17,22 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
|
|||||||
super(Commands.CopyMessageToClipboard);
|
super(Commands.CopyMessageToClipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(editor: TextEditor, uri?: Uri, args: CopyMessageToClipboardCommandArgs = {}): Promise<any> {
|
protected async preExecute(context: CommandContext, args: CopyMessageToClipboardCommandArgs = {}): Promise<any> {
|
||||||
|
if (isCommandViewContextWithCommit(context)) {
|
||||||
|
args = { ...args };
|
||||||
|
args.sha = context.node.commit.sha;
|
||||||
|
return this.execute(context.editor, context.node.commit.uri, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.execute(context.editor, context.uri, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(editor?: TextEditor, uri?: Uri, args: CopyMessageToClipboardCommandArgs = {}): Promise<any> {
|
||||||
uri = getCommandUri(uri, editor);
|
uri = getCommandUri(uri, editor);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
args = { ...args };
|
||||||
|
|
||||||
// If we don't have an editor then get the message of the last commit to the branch
|
// If we don't have an editor then get the message of the last commit to the branch
|
||||||
if (uri === undefined) {
|
if (uri === undefined) {
|
||||||
if (!this.git.repoPath) return undefined;
|
if (!this.git.repoPath) return undefined;
|
||||||
@@ -62,7 +74,7 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
|
|||||||
|
|
||||||
// Get the full commit message -- since blame only returns the summary
|
// Get the full commit message -- since blame only returns the summary
|
||||||
const commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, args.sha);
|
const commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, args.sha);
|
||||||
if (!commit) return undefined;
|
if (commit === undefined) return undefined;
|
||||||
|
|
||||||
args.message = commit.message;
|
args.message = commit.message;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Iterables } from '../system';
|
import { Iterables } from '../system';
|
||||||
import { TextEditor, Uri, window } from 'vscode';
|
import { TextEditor, Uri, window } from 'vscode';
|
||||||
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
|
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
|
||||||
import { GitService, GitUri } from '../gitService';
|
import { GitService, GitUri } from '../gitService';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
import { copy } from 'copy-paste';
|
import { copy } from 'copy-paste';
|
||||||
@@ -16,10 +16,22 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
|
|||||||
super(Commands.CopyShaToClipboard);
|
super(Commands.CopyShaToClipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(editor: TextEditor, uri?: Uri, args: CopyShaToClipboardCommandArgs = {}): Promise<any> {
|
protected async preExecute(context: CommandContext, args: CopyShaToClipboardCommandArgs = {}): Promise<any> {
|
||||||
|
if (isCommandViewContextWithCommit(context)) {
|
||||||
|
args = { ...args };
|
||||||
|
args.sha = context.node.commit.sha;
|
||||||
|
return this.execute(context.editor, context.node.commit.uri, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.execute(context.editor, context.uri, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(editor?: TextEditor, uri?: Uri, args: CopyShaToClipboardCommandArgs = {}): Promise<any> {
|
||||||
uri = getCommandUri(uri, editor);
|
uri = getCommandUri(uri, editor);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
args = { ...args };
|
||||||
|
|
||||||
// If we don't have an editor then get the sha of the last commit to the branch
|
// If we don't have an editor then get the sha of the last commit to the branch
|
||||||
if (uri === undefined) {
|
if (uri === undefined) {
|
||||||
if (!this.git.repoPath) return undefined;
|
if (!this.git.repoPath) return undefined;
|
||||||
@@ -43,7 +55,7 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const blame = await this.git.getBlameForLine(gitUri, blameline);
|
const blame = await this.git.getBlameForLine(gitUri, blameline);
|
||||||
if (!blame) return undefined;
|
if (blame === undefined) return undefined;
|
||||||
|
|
||||||
args.sha = blame.commit.sha;
|
args.sha = blame.commit.sha;
|
||||||
}
|
}
|
||||||
|
|||||||