3 Commits

Author SHA1 Message Date
Eric Amodio
6b9fc3f9d2 Preps v4.0.0-alpha.2 2017-06-05 19:05:20 -04:00
Eric Amodio
5af70fd2bf Adds welcome message for first-time users 2017-06-05 19:04:29 -04:00
Eric Amodio
cecd3524b8 Major refactor/rework -- many new features and breaking changes
Adds all-new, beautiful, highly customizable and themeable, file blame annotations
Adds all-new configurability and themeability to the current line blame annotations
Adds all-new configurability to the status bar blame information
Adds all-new configurability over which commands are added to which menus via the `gitlens.advanced.menus` setting
Adds better configurability 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
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
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`)
2017-06-05 19:04:18 -04:00
85 changed files with 1469 additions and 2056 deletions

View File

@@ -4,32 +4,16 @@ 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/).
## [4.1.0-beta] - 2017-06-10
### 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
- Improves performance
- Optimized git output parsing to increase speed and reduce memory usage
- Defers diff chunk parsing until it is actually required
### Fixed
- Fixes excessive memory usage when parsing diffs
## [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
## [4.0.0-alpha.2] - 2017-06-05
### 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)
- Can now fully customize the [layout and content](https://github.com/eamodio/vscode-gitlens/blob/develop/README.md#file-blame-annotation-settings), as well as the [theme](https://github.com/eamodio/vscode-gitlens/blob/develop/README.md#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)
- Can now fully customize the [layout and content](https://github.com/eamodio/vscode-gitlens/blob/develop/README.md#line-blame-annotation-settings), as well as the [theme](https://github.com/eamodio/vscode-gitlens/blob/develop/README.md#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
- Can now fully customize the [layout and content](https://github.com/eamodio/vscode-gitlens/blob/develop/README.md#status-bar-settings)
- Adds all-new [configurability](https://github.com/eamodio/vscode-gitlens/blob/develop/README.md#advanced-settings) over which commands are added to which menus via the `gitlens.advanced.menus` setting
- Adds better [configurability](https://github.com/eamodio/vscode-gitlens/blob/develop/README.md#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
@@ -37,33 +21,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
- 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
- (BREAKING) Almost all of the GitLens settings have either been renamed, removed, or otherwise changed - see the [README](https://github.com/eamodio/vscode-gitlens/blob/develop/README.md#extension-settings)`
- 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
### Added
- Adds diff information (the line's previous version) into the active line hover
@@ -156,7 +124,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
## [3.4.5] - 2017-04-13
### Added
- 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!
- 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!
- Adds `gitlens.blame.annotation.activeLineDarkColor` & `gitlens.blame.annotation.activeLineLightColor` settings to control the colors of the active line blame annotation
### Changed

View File

@@ -9,32 +9,28 @@ GitLens **supercharges** the built-in Visual Studio Code Git capabilities. It he
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).
### Preview — featuring blame annotations, code lens, status bar details, quick pick menus for navigation and exploration, compare with previous, and more
![GitLens preview](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/gitlens-preview.gif)
## Previews
#### Featuring code lens, file blame annotations, and navigation and exploration via quick pick menus
![GitLens preview 1](https://raw.githubusercontent.com/eamodio/vscode-git-codelens/master/images/gitlens-preview1.gif)
#### Featuring current line blame annotation and hovers, status bar commit details, quick pick menus, compare with previous, and more
![GitLens preview 2](https://raw.githubusercontent.com/eamodio/vscode-git-codelens/master/images/gitlens-preview2.gif)
## Features
### Git Blame Annotations
#### Git Blame Annotations
- 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)
![Line Blame Annotation](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-line-blame-annotation.png)
- Contains the author, date, and message of the line's most recent commit, by [default](#line-blame-annotation-settings)
- Also adds a `details` hover annotation to the current line annotation which provides more commit details ([optional](#line-blame-annotation-settings), on by default)
- Also 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)
![Line Blame Annotations](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-line-blame-annotations.png)
- Adds on-demand, beautiful, highly [customizable](#file-blame-annotation-settings) and [themeable](#theme-settings), **Git blame annotations** of the whole file
![File Blame Annotation](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-file-blame-annotations.png)
- Choose between `gutter` (default) and `hover` [annotation styles](#file-blame-annotation-settings)
- Contains the commit message and date, by [default](#file-blame-annotation-settings)
- Also adds a `details` hover annotation to the line's annotation which provides more commit details ([optional](#file-blame-annotation-settings), on by default)
- Adds [customizable](#status-bar-settings) **blame information** about the current line to the **status bar** ([optional](#status-bar-settings), on by default)
![Status Bar Blame](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-status-bar.png)
- 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
@@ -53,19 +49,9 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- 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
- Also adds a `changes` (diff) hover annotation to the current line annotation which provides **instant** access to the line's previous version ([optional](#file-recent-changes-annotation-settings), on by default)
- Adds `Toggle Recent File Changes Annotations` command (`gitlens.toggleFileRecentChanges`) to toggle the recent changes annotations on and off
### Git Code Lens
#### Git Code Lens
- Adds **code lens** to the top of the file and on code blocks ([optional](#code-lens-settings), on by default)
![Git Code Lens](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-code-lens.png)
- **Recent Change** — author and date of the most recent commit for the file or code block
- 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)
@@ -82,7 +68,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- 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
@@ -102,7 +88,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- 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
### Navigate and Explore
#### Navigate and Explore
- Adds a `Search Commits` command (`gitlens.showCommitSearch`) with a shortcut of `alt+/` to search for commits by message, author, file(s), or commit id
@@ -114,7 +100,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- 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
![Branch History Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-branch-history.png)
![Branch History Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-git-codelens/master/images/screenshot-branch-history.png)
- 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
@@ -125,7 +111,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current 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
![File History Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-file-history.png)
![File History Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-git-codelens/master/images/screenshot-file-history.png)
- 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
@@ -133,7 +119,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current 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
![Commit Details Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-commit-details.png)
![Commit Details Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-git-codelens/master/images/screenshot-commit-details.png)
- 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
@@ -144,7 +130,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- 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
![Line Commit Details Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-commit-file-details.png)
![Line Commit Details Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-git-codelens/master/images/screenshot-commit-file-details.png)
- 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
@@ -152,7 +138,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current 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
![Repository Status Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-repo-status.png)
![Repository Status Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-git-codelens/master/images/screenshot-repo-status.png)
- 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
@@ -165,14 +151,14 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
- Adds a `Show Stashed Changes` command (`gitlens.showQuickStashList`) to show a **stashed changes quick pick menu** for exploring your repository stash history
![Stashed Changes Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-stash-list.png)
![Stashed Changes Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-git-codelens/master/images/screenshot-stash-list.png)
- Provides entries to `Stash Changes`
- 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
![Stash Details Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-stash-details.png)
![Stash Details Quick Pick Menu](https://raw.githubusercontent.com/eamodio/vscode-git-codelens/master/images/screenshot-stash-details.png)
- 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`
@@ -190,7 +176,7 @@ GitLens provides an unobtrusive blame annotation at the end of the current 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
- 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
@@ -225,14 +211,14 @@ GitLens is highly customizable and provides many configuration settings to allow
|Name | Description
|-----|------------
|`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.file.annotationType`|Specifies the type of blame annotations that will be shown for the current file. `gutter` - adds an annotation to the beginning of each line. `hover` - shows annotations when hovering over each line
|`gitlens.blame.file.lineHighlight.enabled`|Specifies whether or not to highlight lines associated with the current line
|`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.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.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.blame.file.lineHighlight.locations`|Specifies where the associated line highlights will be shown. `gutter` - adds a gutter glyph. `line` - adds a full-line highlight background color. `overviewRuler` - adds a decoration to the overviewRuler (scroll bar)
|`gitlens.annotations.file.gutter.format`|Specifies the format of the gutter blame annotations. Available tokens: `${id}` - commit id, `${author}` - commit author, `${message}` - commit message, `${ago}` - relative commit date (e.g. 1 day ago), `${date}` - formatted commit date (format specified by `gitlens.annotations.file.dateFormat`), `${authorAgo}` - commit author, relative commit date
|`gitlens.annotations.file.gutter.dateFormat`|Specifies how to format absolute dates (using the `${date}` token) in gutter blame annotations. See https://momentjs.com/docs/#/displaying/format/ for valid formats
|`gitlens.annotations.file.gutter.compact`|Specifies whether or not to compact (deduplicate) matching adjacent gutter blame annotations
|`gitlens.annotations.file.gutter.heatmap.enabled`|Specifies whether or not to provide a heatmap indicator in the gutter blame annotations
|`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.annotations.file.gutter.heatmap.location`|Specifies where the heatmap indicators will be shown in the gutter blame annotations. `left` - adds a heatmap indicator on the left edge of the gutter blame annotations. `right` - adds a heatmap indicator on the right edge of the gutter blame annotations
|`gitlens.annotations.file.gutter.hover.details`|Specifies whether or not to provide a commit details hover annotation over the gutter blame annotations
|`gitlens.annotations.file.gutter.hover.wholeLine`|Specifies whether or not to trigger hover annotations over the whole line
|`gitlens.annotations.file.hover.heatmap.enabled`|Specifies whether or not to provide heatmap indicators on the left edge of each line
@@ -243,33 +229,25 @@ GitLens is highly customizable and provides many configuration settings to allow
|Name | Description
|-----|------------
|`gitlens.blame.line.enabled`|Specifies whether or not to provide a blame annotation for the current line
|`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.blame.line.annotationType`|Specifies the type of blame annotations that will be shown for the current line. `trailing` - adds an annotation to the end of the current line. `hover` - shows annotations when hovering over the current line
|`gitlens.annotations.line.trailing.format`|Specifies the format of the trailing blame annotations. Available tokens: `${id}` - commit id, `${author}` - commit author, `${message}` - commit message, `${ago}` - relative commit date (e.g. 1 day ago), `${date}` - formatted commit date (format specified by `gitlens.annotations.currentLine.dateFormat`), `${authorAgo}` - commit author, relative commit date
|`gitlens.annotations.line.trailing.dateFormat`|Specifies how to format absolute dates (using the `${date}` token) in trailing blame annotations. 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.changes`|Specifies whether or not to provide a changes (diff) hover annotations
|`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
|`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.recentChange.command`|Specifies the command to be executed when the `recent change` code lens is clicked. `gitlens.toggleFileBlame` - toggles file 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.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.authors.command`|Specifies the command to be executed when the `authors` code lens is clicked. `gitlens.toggleFileBlame` - toggles file 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.codeLens.locations`|Specifies where Git code lens will be shown in the document. `document` - adds code lens at the top of the document. `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc). `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines. `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
@@ -278,18 +256,11 @@ GitLens is highly customizable and provides many configuration settings to allow
|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
|`gitlens.statusBar.alignment`|Specifies the blame alignment in the status bar. `left` - align to the left, `right` - align to the right
|`gitlens.statusBar.format`|Specifies the format of the blame information on the status bar. Available tokens: `${id}` - commit id, `${author}` - commit author, `${message}` - commit message, `${ago}` - relative commit date (e.g. 1 day ago), `${date}` - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)
|`gitlens.statusBar.command`|Specifies the command to be executed when the blame status bar item is clicked. `gitlens.toggleFileBlame` - toggles file 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.format`|Specifies the format of the status bar blame information. Available tokens: `${id}` - commit id, `${author}` - commit author, `${message}` - commit message, `${ago}` - relative commit date (e.g. 1 day ago), `${date}` - formatted commit date (format specified by `gitlens.statusBar.dateFormat`), `${authorAgo}` - commit author, relative commit date
|`gitlens.statusBar.dateFormat`|Specifies the date format of absolute dates shown in the blame information on the status bar. See https://momentjs.com/docs/#/displaying/format/ for valid formats
### Theme Settings
@@ -317,7 +288,6 @@ GitLens is highly customizable and provides many configuration settings to allow
|Name | Description
|-----|------------
|`gitlens.advanced.toggleWhitespace.enabled`|Specifies whether or not to toggle whitespace off then showing blame annotations (*may* be required by certain fonts/themes)
|`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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

57
package-lock.json generated
View File

@@ -1,8 +1,14 @@
{
"name": "gitlens",
"version": "4.0.0",
"version": "4.0.0-alpha.2",
"lockfileVersion": 1,
"dependencies": {
"@types/applicationinsights": {
"version": "0.15.33",
"resolved": "https://registry.npmjs.org/@types/applicationinsights/-/applicationinsights-0.15.33.tgz",
"integrity": "sha1-yohXeRuaxSzlKplCXksJrRfrlwg=",
"dev": true
},
"@types/copy-paste": {
"version": "1.1.30",
"resolved": "https://registry.npmjs.org/@types/copy-paste/-/copy-paste-1.1.30.tgz",
@@ -22,9 +28,9 @@
"dev": true
},
"@types/node": {
"version": "7.0.29",
"resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.29.tgz",
"integrity": "sha512-+8JrLZny/uR+d/jLK9eaV63buRM7X/gNzQk57q76NS4KNKLSKOmxJYFIlwuP2zDvA7wqZj05POPhSd9Z1hYQpQ==",
"version": "7.0.27",
"resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.27.tgz",
"integrity": "sha512-2QMiuVOEye2yKmMwE1V96C9HSShmT0WSm6dv2WjacvePEjQNNJGAerTO5hdYhj5lpdK5MW+FVxmyzDhr4omIdw==",
"dev": true
},
"@types/tmp": {
@@ -1026,6 +1032,13 @@
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"dev": true
},
"jodid25519": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz",
"integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=",
"dev": true,
"optional": true
},
"js-tokens": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz",
@@ -1496,9 +1509,9 @@
"dev": true
},
"readable-stream": {
"version": "2.2.11",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.11.tgz",
"integrity": "sha512-h+8+r3MKEhkiVrwdKL8aWs1oc1VvBu33ueshOvS26RsZQ3Amhx/oO3TKe4lApSV9ueY6as8EAh7mtuFjdlhg9Q==",
"version": "2.2.10",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.10.tgz",
"integrity": "sha512-HQEnnoV404e0EtwB9yNiuk2tJ+egeVC8Y9QBAxzDg8DBJt4BzRp+yQuIb/t3FIWkSTmIi+sgx7yVv/ZM0GNoqw==",
"dev": true
},
"regex-cache": {
@@ -1508,9 +1521,9 @@
"dev": true
},
"remove-trailing-separator": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz",
"integrity": "sha1-abBi2XhyetFNxrVrpKt3L9jXBRE=",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz",
"integrity": "sha1-YV67lq9VlVLUv0BXyENtSGq2PMQ=",
"dev": true
},
"repeat-element": {
@@ -1581,9 +1594,9 @@
"integrity": "sha1-p9sUqxV/nXqsalbmVeejhg05vyY="
},
"safe-buffer": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
"integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz",
"integrity": "sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ==",
"dev": true
},
"semver": {
@@ -1627,9 +1640,9 @@
"dev": true
},
"sshpk": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
"integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz",
"integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=",
"dev": true,
"dependencies": {
"assert-plus": {
@@ -1671,9 +1684,9 @@
"dev": true
},
"string_decoder": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz",
"integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz",
"integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=",
"dev": true
},
"stringstream": {
@@ -1771,9 +1784,9 @@
"dev": true
},
"tslint": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.4.3.tgz",
"integrity": "sha1-dhyEArgONHt3M6BDkKdXslNYBGc=",
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.4.2.tgz",
"integrity": "sha1-YJtmQMwEJPSjlamt9ow3VWPFScc=",
"dev": true
},
"tsutils": {

View File

@@ -1,6 +1,6 @@
{
"name": "gitlens",
"version": "4.1.0-beta",
"version": "4.0.0-alpha.2",
"author": {
"name": "Eric Amodio",
"email": "eamodio@gmail.com"
@@ -76,12 +76,12 @@
"gitlens.annotations.file.gutter.format": {
"type": "string",
"default": "${message|40?} ${ago|14-}",
"description": "Specifies the format of the gutter blame annotations\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.annotations.file.gutter.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
"description": "Specifies the format of the gutter blame annotations. Available tokens: `${id}` - commit id, `${author}` - commit author, `${message}` - commit message, `${ago}` - relative commit date (e.g. 1 day ago), `${date}` - formatted commit date (format specified by `gitlens.annotations.file.dateFormat`), `${authorAgo}` - commit author, relative commit date"
},
"gitlens.annotations.file.gutter.dateFormat": {
"type": "string",
"default": null,
"description": "Specifies how to format absolute dates (using the `${date}` token) in gutter blame annotations\nSee https://momentjs.com/docs/#/displaying/format/ for valid formats"
"description": "Specifies how to format absolute dates (using the `${date}` token) in gutter blame annotations. See https://momentjs.com/docs/#/displaying/format/ for valid formats"
},
"gitlens.annotations.file.gutter.compact": {
"type": "boolean",
@@ -100,7 +100,7 @@
"left",
"right"
],
"description": "Specifies where the heatmap indicators will be shown in the gutter blame annotations\n `left` - adds a heatmap indicator on the left edge of the gutter blame annotations\n `right` - adds a heatmap indicator on the right edge of the gutter blame annotations"
"description": "Specifies where the heatmap indicators will be shown in the gutter blame annotations. `left` - adds a heatmap indicator on the left edge of the gutter blame annotations. `right` - adds a heatmap indicator on the right edge of the gutter blame annotations"
},
"gitlens.annotations.file.gutter.hover.details": {
"type": "boolean",
@@ -122,16 +122,6 @@
"default": true,
"description": "Specifies whether or not to trigger hover annotations over the whole line"
},
"gitlens.annotations.file.recentChanges.hover.changes": {
"type": "boolean",
"default": true,
"description": "Specifies whether or not to provide a changes (diff) hover annotations"
},
"gitlens.annotations.file.recentChanges.hover.wholeLine": {
"type": "boolean",
"default": true,
"description": "Specifies whether or not to trigger hover annotations over the whole line"
},
"gitlens.annotations.line.hover.details": {
"type": "boolean",
"default": true,
@@ -145,12 +135,12 @@
"gitlens.annotations.line.trailing.format": {
"type": "string",
"default": "${authorAgo} \u2022 ${message}",
"description": "Specifies the format of the trailing blame annotations\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.annotations.line.trailing.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
"description": "Specifies the format of the trailing blame annotations. Available tokens: `${id}` - commit id, `${author}` - commit author, `${message}` - commit message, `${ago}` - relative commit date (e.g. 1 day ago), `${date}` - formatted commit date (format specified by `gitlens.annotations.currentLine.dateFormat`), `${authorAgo}` - commit author, relative commit date"
},
"gitlens.annotations.line.trailing.dateFormat": {
"type": "string",
"default": null,
"description": "Specifies how to format absolute dates (using the `${date}` token) in trailing blame annotations\nSee https://momentjs.com/docs/#/displaying/format/ for valid formats"
"description": "Specifies how to format absolute dates (using the `${date}` token) in trailing blame annotations. See https://momentjs.com/docs/#/displaying/format/ for valid formats"
},
"gitlens.annotations.line.trailing.hover.details": {
"type": "boolean",
@@ -174,7 +164,7 @@
"gutter",
"hover"
],
"description": "Specifies the type of blame annotations that will be shown for the current file\n `gutter` - adds an annotation to the beginning of each line\n `hover` - shows annotations when hovering over each line"
"description": "Specifies the type of blame annotations that will be shown for the current file. `gutter` - adds an annotation to the beginning of each line. `hover` - shows annotations when hovering over each line"
},
"gitlens.blame.file.lineHighlight.enabled": {
"type": "boolean",
@@ -199,7 +189,7 @@
"minItems": 1,
"maxItems": 3,
"uniqueItems": true,
"description": "Specifies where the associated line highlights will be shown\n `gutter` - adds a gutter glyph\n `line` - adds a full-line highlight background color\n `overviewRuler` - adds a decoration to the overviewRuler (scroll bar)"
"description": "Specifies where the associated line highlights will be shown. `gutter` - adds a gutter glyph. `line` - adds a full-line highlight background color. `overviewRuler` - adds a decoration to the overviewRuler (scroll bar)"
},
"gitlens.blame.line.enabled": {
"type": "boolean",
@@ -213,27 +203,7 @@
"trailing",
"hover"
],
"description": "Specifies the type of blame annotations that will be shown for the current line\n `trailing` - adds an annotation to the end of the current line\n `hover` - shows annotations when hovering over the current line"
},
"gitlens.recentChanges.file.lineHighlight.locations": {
"type": "array",
"default": [
"gutter",
"line",
"overviewRuler"
],
"items": {
"type": "string",
"enum": [
"gutter",
"line",
"overviewRuler"
]
},
"minItems": 1,
"maxItems": 3,
"uniqueItems": true,
"description": "Specifies where the highlights of the recently changed lines will be shown\n `gutter` - adds a gutter glyph\n `line` - adds a full-line highlight background color\n `overviewRuler` - adds a decoration to the overviewRuler (scroll bar)"
"description": "Specifies the type of blame annotations that will be shown for the current line. `trailing` - adds an annotation to the end of the current line. `hover` - shows annotations when hovering over the current line"
},
"gitlens.codeLens.enabled": {
"type": "boolean",
@@ -258,7 +228,7 @@
"gitlens.showQuickFileHistory",
"gitlens.showQuickRepoHistory"
],
"description": "Specifies the command to be executed when the `recent change` code lens is clicked\n `gitlens.toggleFileBlame` - toggles file blame annotations\n `gitlens.showBlameHistory` - opens the blame history explorer\n `gitlens.showFileHistory` - opens the file history explorer\n `gitlens.diffWithPrevious` - compares the current committed file with the previous commit\n `gitlens.showQuickCommitDetails` - shows a commit details quick pick\n `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick\n `gitlens.showQuickFileHistory` - shows a file history quick pick\n `gitlens.showQuickRepoHistory` - shows a branch history quick pick"
"description": "Specifies the command to be executed when the `recent change` code lens is clicked. `gitlens.toggleFileBlame` - toggles file 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.codeLens.authors.enabled": {
"type": "boolean",
@@ -278,7 +248,7 @@
"gitlens.showQuickFileHistory",
"gitlens.showQuickRepoHistory"
],
"description": "Specifies the command to be executed when the `authors` code lens is clicked\n `gitlens.toggleFileBlame` - toggles file blame annotations\n `gitlens.showBlameHistory` - opens the blame history explorer\n `gitlens.showFileHistory` - opens the file history explorer\n `gitlens.diffWithPrevious` - compares the current committed file with the previous commit\n `gitlens.showQuickCommitDetails` - shows a commit details quick pick\n `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick\n `gitlens.showQuickFileHistory` - shows a file history quick pick\n `gitlens.showQuickRepoHistory` - shows a branch history quick pick"
"description": "Specifies the command to be executed when the `authors` code lens is clicked. `gitlens.toggleFileBlame` - toggles file 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.codeLens.locations": {
"type": "array",
@@ -298,7 +268,7 @@
"minItems": 1,
"maxItems": 4,
"uniqueItems": true,
"description": "Specifies where Git code lens will be shown in the document\n `document` - adds code lens at the top of the document\n `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)\n `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines\n `custom` - adds code lens at the start of symbols contained in `gitlens.codeLens.locationCustomSymbols`"
"description": "Specifies where Git code lens will be shown in the document. `document` - adds code lens at the top of the document. `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc). `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines. `custom` - adds code lens at the start of symbols contained in `gitlens.codeLens.locationCustomSymbols`"
},
"gitlens.codeLens.customLocationSymbols": {
"type": "array",
@@ -306,7 +276,7 @@
"type": "string"
},
"uniqueItems": true,
"description": "Specifies the set of document symbols where Git code lens will be shown in the document\nMust be a member of `SymbolKind`"
"description": "Specifies the set of document symbols where Git code lens will be shown in the document. Must be a member of `SymbolKind`"
},
"gitlens.codeLens.perLanguageLocations": {
"type": "array",
@@ -377,7 +347,7 @@
"minItems": 1,
"maxItems": 4,
"uniqueItems": true,
"description": "Specifies where Git code lens will be shown in the document for the specified language\n `document` - adds code lens at the top of the document\n `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc)\n `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines\n `custom` - adds code lens at the start of symbols contained in `customSymbols`"
"description": "Specifies where Git code lens will be shown in the document for the specified language. `document` - adds code lens at the top of the document. `containers` - adds code lens at the start of container-like symbols (modules, classes, interfaces, etc). `blocks` - adds code lens at the start of block-like symbols (functions, methods, properties, etc) lines. `custom` - adds code lens at the start of symbols contained in `customSymbols`"
},
"customSymbols": {
"type": "array",
@@ -385,7 +355,7 @@
"type": "string"
},
"uniqueItems": true,
"description": "Specifies the set of document symbols where Git code lens will be shown in the document for the specified language\nMust be a member of `SymbolKind`"
"description": "Specifies the set of document symbols where Git code lens will be shown in the document for the specified language. Must be a member of `SymbolKind`"
}
}
},
@@ -409,7 +379,7 @@
"left",
"right"
],
"description": "Specifies the blame alignment in the status bar\n `left` - align to the left\n `right` - align to the right"
"description": "Specifies the blame alignment in the status bar. `left` - align to the left, `right` - align to the right"
},
"gitlens.statusBar.command": {
"type": "string",
@@ -426,33 +396,18 @@
"gitlens.showQuickFileHistory",
"gitlens.showQuickRepoHistory"
],
"description": "Specifies the command to be executed when the blame status bar item is clicked\n `gitlens.toggleFileBlame` - toggles file blame annotations\n `gitlens.showBlameHistory` - opens the blame history explorer\n `gitlens.showFileHistory` - opens the file history explorer\n `gitlens.diffWithPrevious` - compares the current line commit with the previous\n `gitlens.diffWithWorking` - compares the current line commit with the working tree\n `gitlens.toggleCodeLens` - toggles Git code lens\n `gitlens.showQuickCommitDetails` - shows a commit details quick pick\n `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick\n `gitlens.showQuickFileHistory` - shows a file history quick pick\n `gitlens.showQuickRepoHistory` - shows a branch history quick pick"
"description": "Specifies the command to be executed when the blame status bar item is clicked. `gitlens.toggleFileBlame` - toggles file 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.format": {
"type": "string",
"default": "${authorAgo}",
"description": "Specifies the format of the status bar blame information\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
"description": "Specifies the format of the status bar blame information. Available tokens: `${id}` - commit id, `${author}` - commit author, `${message}` - commit message, `${ago}` - relative commit date (e.g. 1 day ago), `${date}` - formatted commit date (format specified by `gitlens.statusBar.dateFormat`), `${authorAgo}` - commit author, relative commit date"
},
"gitlens.statusBar.dateFormat": {
"type": "string",
"default": null,
"description": "Specifies the date format of absolute dates shown in the blame information on the status bar. See https://momentjs.com/docs/#/displaying/format/ for valid formats"
},
"gitlens.strings.codeLens.unsavedChanges.recentChangeAndAuthors": {
"type": "string",
"default": "Cannot determine recent change or authors (unsaved changes)",
"description": "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": {
"type": "string",
"default": "Cannot determine recent change (unsaved changes)",
"description": "Specifies the string to be shown in place of the `recent change` code lens when there are unsaved changes"
},
"gitlens.strings.codeLens.unsavedChanges.authorsOnly": {
"type": "string",
"default": "Cannot determine authors (unsaved changes)",
"description": "Specifies the string to be shown in place of the `authors` code lens when there are unsaved changes"
},
"gitlens.theme.annotations.file.gutter.separateLines": {
"type": "boolean",
"default": true,
@@ -561,10 +516,14 @@
"gitlens.advanced.menus": {
"type": "object",
"default": {
"explorerContext": {
"fileDiff": true,
"history": true,
"remote": true
},
"editorContext": {
"blame": true,
"copy": true,
"details": true,
"fileDiff": true,
"history": true,
"lineDiff": true,
@@ -574,7 +533,6 @@
"blame": true,
"fileDiff": true,
"history": true,
"remote": true,
"status": true
},
"editorTitleContext": {
@@ -582,21 +540,37 @@
"fileDiff": true,
"history": true,
"remote": true
},
"explorerContext": {
"fileDiff": true,
"history": true,
"remote": true
}
},
"description": "Specifies which commands will be added to which menus",
"properties": {
"explorerContext": {
"type": "object",
"default": {
"fileDiff": true,
"history": true,
"remote": true
},
"properties": {
"fileDiff": {
"type": "boolean",
"default": true
},
"history": {
"type": "boolean",
"default": true
},
"remote": {
"type": "boolean",
"default": true
}
}
},
"editorContext": {
"type": "object",
"default": {
"blame": true,
"copy": true,
"details": true,
"fileDiff": true,
"history": true,
"lineDiff": true,
@@ -639,7 +613,6 @@
"blame": true,
"fileDiff": true,
"history": true,
"remote": true,
"status": true
},
"properties": {
@@ -655,10 +628,6 @@
"type": "boolean",
"default": true
},
"remote": {
"type": "boolean",
"default": true
},
"status": {
"type": "boolean",
"default": true
@@ -691,28 +660,6 @@
"default": true
}
}
},
"explorerContext": {
"type": "object",
"default": {
"fileDiff": true,
"history": true,
"remote": true
},
"properties": {
"fileDiff": {
"type": "boolean",
"default": true
},
"history": {
"type": "boolean",
"default": true
},
"remote": {
"type": "boolean",
"default": true
}
}
}
}
},
@@ -721,14 +668,9 @@
"default": true,
"description": "Specifies whether or not to close the QuickPick menu when focus is lost"
},
"gitlens.advanced.telemetry.enabled": {
"type": "boolean",
"default": true,
"description": "Specifies whether or not to enable GitLens telemetry (even if enabled still abides by the overall `telemetry.enableTelemetry` setting"
},
"gitlens.advanced.toggleWhitespace.enabled": {
"type": "boolean",
"default": true,
"default": false,
"description": "Specifies whether or not to toggle whitespace off then showing blame annotations (*may* be required by certain fonts/themes)"
}
}
@@ -788,11 +730,6 @@
"light": "images/git-icon-light.svg"
}
},
{
"command": "gitlens.toggleFileRecentChanges",
"title": "Toggle Recent File Changes Annotations",
"category": "GitLens"
},
{
"command": "gitlens.toggleLineBlame",
"title": "Toggle Line Blame Annotations",
@@ -907,11 +844,6 @@
"command": "gitlens.stashSave",
"title": "Stash Changes",
"category": "GitLens"
},
{
"command": "gitlens.resetSuppressedWarnings",
"title": "Reset Suppressed Warnings",
"category": "GitLens"
}
],
"menus": {
@@ -956,10 +888,6 @@
"command": "gitlens.toggleFileBlame",
"when": "gitlens:isBlameable"
},
{
"command": "gitlens.toggleFileRecentChanges",
"when": "gitlens:isTracked"
},
{
"command": "gitlens.toggleLineBlame",
"when": "gitlens:isBlameable"
@@ -1047,62 +975,28 @@
{
"command": "gitlens.stashSave",
"when": "gitlens:enabled"
},
{
"command": "gitlens.resetSuppressedWarnings",
"when": "gitlens:enabled"
}
],
"editor/context": [
"explorer/context": [
{
"command": "gitlens.openFileInRemote",
"when": "editorTextFocus && gitlens:isTracked && gitlens:hasRemotes && config.gitlens.advanced.menus.editorContext.remote",
"when": "gitlens:enabled && config.gitlens.advanced.menus.explorerContext.remote",
"group": "navigation@100"
},
{
"command": "gitlens.diffLineWithPrevious",
"when": "editorTextFocus && gitlens:isBlameable && config.gitlens.advanced.menus.editorContext.lineDiff",
"command": "gitlens.diffWithPrevious",
"when": "gitlens:enabled && config.gitlens.advanced.menus.explorerContext.fileDiff",
"group": "1_gitlens@1"
},
{
"command": "gitlens.diffLineWithWorking",
"when": "editorTextFocus && gitlens:isBlameable && config.gitlens.advanced.menus.editorContext.lineDiff",
"command": "gitlens.diffWithWorking",
"when": "gitlens:enabled && config.gitlens.advanced.menus.explorerContext.fileDiff",
"group": "1_gitlens@2"
},
{
"command": "gitlens.showQuickCommitFileDetails",
"when": "editorTextFocus && gitlens:isBlameable && config.gitlens.advanced.menus.editorContext.details",
"group": "1_gitlens@3"
},
{
"command": "gitlens.diffWithPrevious",
"when": "editorTextFocus && gitlens:isTracked && config.gitlens.advanced.menus.editorContext.fileDiff",
"group": "1_gitlens_1@1"
},
{
"command": "gitlens.diffWithWorking",
"when": "editorTextFocus && gitlens:isTracked && config.gitlens.advanced.menus.editorContext.fileDiff",
"group": "1_gitlens_1@2"
},
{
"command": "gitlens.showQuickFileHistory",
"when": "gitlens:isTracked && config.gitlens.advanced.menus.editorContext.history",
"group": "3_gitlens@1"
},
{
"command": "gitlens.toggleFileBlame",
"when": "editorTextFocus && gitlens:isBlameable && config.gitlens.advanced.menus.editorContext.blame",
"group": "3_gitlens@2"
},
{
"command": "gitlens.copyShaToClipboard",
"when": "editorTextFocus && gitlens:isBlameable && config.gitlens.advanced.menus.editorContext.copy",
"group": "9_gitlens@1"
},
{
"command": "gitlens.copyMessageToClipboard",
"when": "editorTextFocus && gitlens:isBlameable && config.gitlens.advanced.menus.editorContext.copy",
"group": "9_gitlens@2"
"when": "gitlens:enabled && config.gitlens.advanced.menus.explorerContext.history",
"group": "1_gitlens_1@1"
}
],
"editor/title": [
@@ -1174,26 +1068,56 @@
"group": "1_gitlens_2@2"
}
],
"explorer/context": [
"editor/context": [
{
"command": "gitlens.openFileInRemote",
"when": "gitlens:enabled && config.gitlens.advanced.menus.explorerContext.remote",
"when": "editorTextFocus && gitlens:isTracked && gitlens:hasRemotes && config.gitlens.advanced.menus.editorContext.remote",
"group": "navigation@100"
},
{
"command": "gitlens.diffWithPrevious",
"when": "gitlens:enabled && config.gitlens.advanced.menus.explorerContext.fileDiff",
"command": "gitlens.diffLineWithPrevious",
"when": "editorTextFocus && gitlens:isBlameable && config.gitlens.advanced.menus.editorContext.lineDiff",
"group": "1_gitlens@1"
},
{
"command": "gitlens.diffWithWorking",
"when": "gitlens:enabled && config.gitlens.advanced.menus.explorerContext.fileDiff",
"command": "gitlens.diffLineWithWorking",
"when": "editorTextFocus && gitlens:isBlameable && config.gitlens.advanced.menus.editorContext.lineDiff",
"group": "1_gitlens@2"
},
{
"command": "gitlens.showQuickFileHistory",
"when": "gitlens:enabled && config.gitlens.advanced.menus.explorerContext.history",
"command": "gitlens.showQuickCommitFileDetails",
"when": "editorTextFocus && gitlens:isBlameable && config.gitlens.advanced.menus.editorContext.details",
"group": "1_gitlens@3"
},
{
"command": "gitlens.diffWithPrevious",
"when": "editorTextFocus && gitlens:isTracked && config.gitlens.advanced.menus.editorContext.fileDiff",
"group": "1_gitlens_1@1"
},
{
"command": "gitlens.diffWithWorking",
"when": "editorTextFocus && gitlens:isTracked && config.gitlens.advanced.menus.editorContext.fileDiff",
"group": "1_gitlens_1@2"
},
{
"command": "gitlens.showQuickFileHistory",
"when": "gitlens:isTracked && config.gitlens.advanced.menus.editorContext.history",
"group": "3_gitlens@1"
},
{
"command": "gitlens.toggleFileBlame",
"when": "editorTextFocus && gitlens:isBlameable && config.gitlens.advanced.menus.editorContext.blame",
"group": "3_gitlens@2"
},
{
"command": "gitlens.copyShaToClipboard",
"when": "editorTextFocus && gitlens:isBlameable && config.gitlens.advanced.menus.editorContext.copy",
"group": "9_gitlens@1"
},
{
"command": "gitlens.copyMessageToClipboard",
"when": "editorTextFocus && gitlens:isBlameable && config.gitlens.advanced.menus.editorContext.copy",
"group": "9_gitlens@2"
}
]
},
@@ -1310,7 +1234,7 @@
"postinstall": "node ./node_modules/vscode/bin/install",
"pub": "git clean -xdf && vsce publish",
"reset": "git clean -xdf && npm install",
"vscode:prepublish": "npm install --no-save && npm run compile"
"vscode:prepublish": "npm install && npm run compile"
},
"dependencies": {
"applicationinsights": "0.20.1",
@@ -1326,13 +1250,14 @@
"tmp": "0.0.31"
},
"devDependencies": {
"@types/applicationinsights": "0.15.33",
"@types/copy-paste": "1.1.30",
"@types/iconv-lite": "0.0.1",
"@types/mocha": "2.2.41",
"@types/node": "7.0.29",
"@types/node": "7.0.27",
"@types/tmp": "0.0.33",
"mocha": "3.4.2",
"tslint": "5.4.3",
"tslint": "5.4.2",
"typescript": "2.3.4",
"vscode": "1.1.0"
}

View File

@@ -16,9 +16,9 @@ interface AutoCollectConsole {
}
interface AutoCollectExceptions {
constructor(client: Client): AutoCollectExceptions;
constructor(client:Client): AutoCollectExceptions;
isInitialized(): boolean;
enable(isEnabled: boolean): void;
enable(isEnabled:boolean): void;
}
interface AutoCollectPerformance {
@@ -348,7 +348,7 @@ interface Client {
* @param max the max sample for this 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;
}): void;
@@ -374,8 +374,7 @@ interface Client {
* @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?: {
[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.
@@ -504,11 +503,6 @@ interface ApplicationInsights {
*
*/
setOfflineMode(value: boolean, resentIntervall?: number): ApplicationInsights;
/**
*
*/
setAutoDependencyCorrelation(value: boolean): ApplicationInsights;
}
declare module "applicationinsights" {

View File

@@ -1,6 +1,5 @@
'use strict';
import { Functions } from './system';
import { commands, Disposable, TextEditor, window } from 'vscode';
import { BuiltInCommands } from './constants';
@@ -12,20 +11,19 @@ export class ActiveEditorTracker extends Disposable {
constructor() {
super(() => this.dispose());
const fn = Functions.debounce((e: TextEditor) => this._resolver && this._resolver(e), 50);
this._disposable = window.onDidChangeActiveTextEditor(fn);
this._disposable = window.onDidChangeActiveTextEditor(e => this._resolver && this._resolver(e));
}
dispose() {
this._disposable && this._disposable.dispose();
}
async awaitClose(timeout: number = 500): Promise<TextEditor | undefined> {
async awaitClose(timeout: number = 500): Promise<TextEditor> {
this.close();
return this.wait(timeout);
}
async awaitNext(timeout: number = 500): Promise<TextEditor | undefined> {
async awaitNext(timeout: number = 500): Promise<TextEditor> {
this.next();
return this.wait(timeout);
}
@@ -38,15 +36,15 @@ export class ActiveEditorTracker extends Disposable {
return commands.executeCommand(BuiltInCommands.NextEditor);
}
async wait(timeout: number = 500): Promise<TextEditor | undefined> {
async wait(timeout: number = 500): Promise<TextEditor> {
const editor = await new Promise<TextEditor>((resolve, reject) => {
let timer: any;
this._resolver = (e: TextEditor) => {
this._resolver = (editor: TextEditor) => {
if (timer) {
clearTimeout(timer as any);
timer = 0;
resolve(e);
resolve(editor);
}
};
@@ -55,7 +53,6 @@ export class ActiveEditorTracker extends Disposable {
timer = 0;
}, timeout) as any;
});
this._resolver = undefined;
return editor;
}

View File

@@ -3,29 +3,18 @@ import { Functions, Objects } from '../system';
import { DecorationRenderOptions, Disposable, Event, EventEmitter, ExtensionContext, OverviewRulerLane, TextDocument, TextDocumentChangeEvent, TextEditor, TextEditorDecorationType, TextEditorViewColumnChangeEvent, window, workspace } from 'vscode';
import { AnnotationProviderBase } from './annotationProvider';
import { TextDocumentComparer, TextEditorComparer } from '../comparers';
import { ExtensionKey, IConfig, LineHighlightLocations, themeDefaults } from '../configuration';
import { BlameLineHighlightLocations, ExtensionKey, FileAnnotationType, IConfig, themeDefaults } from '../configuration';
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';
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'
annotation: window.createTextEditorDecorationType({
isWholeLine: true
} as DecorationRenderOptions),
blameHighlight: undefined as TextEditorDecorationType | undefined,
recentChangesAnnotation: undefined as TextEditorDecorationType | undefined,
recentChangesHighlight: undefined as TextEditorDecorationType | undefined
highlight: undefined as TextEditorDecorationType | undefined
};
export class AnnotationController extends Disposable {
@@ -56,8 +45,8 @@ export class AnnotationController extends Disposable {
dispose() {
this._annotationProviders.forEach(async (p, i) => await this.clear(i));
Decorations.blameAnnotation && Decorations.blameAnnotation.dispose();
Decorations.blameHighlight && Decorations.blameHighlight.dispose();
Decorations.annotation && Decorations.annotation.dispose();
Decorations.highlight && Decorations.highlight.dispose();
this._annotationsDisposable && this._annotationsDisposable.dispose();
this._whitespaceController && this._whitespaceController.dispose();
@@ -66,109 +55,67 @@ export class AnnotationController extends Disposable {
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');
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');
}
let changed = false;
if (toggleWhitespace && this._whitespaceController === undefined) {
changed = true;
if (toggleWhitespace && !this._whitespaceController) {
this._whitespaceController = new WhitespaceController();
}
else if (!toggleWhitespace && this._whitespaceController !== undefined) {
changed = true;
else if (!toggleWhitespace && this._whitespaceController) {
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 cfgHighlight = cfg.blame.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) ||
let changed = false;
if (!Objects.areEquivalent(cfgHighlight, this._config && this._config.blame.file.lineHighlight) ||
!Objects.areEquivalent(cfgTheme, this._config && this._config.theme.lineHighlight)) {
changed = true;
Decorations.blameHighlight && Decorations.blameHighlight.dispose();
Decorations.highlight && Decorations.highlight.dispose();
if (cfgBlameHighlight.enabled) {
Decorations.blameHighlight = window.createTextEditorDecorationType({
if (cfgHighlight.enabled) {
Decorations.highlight = window.createTextEditorDecorationType({
gutterIconSize: 'contain',
isWholeLine: true,
overviewRulerLane: OverviewRulerLane.Right,
dark: {
backgroundColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.Line)
backgroundColor: cfgHighlight.locations.includes(BlameLineHighlightLocations.Line)
? cfgTheme.dark.backgroundColor || themeDefaults.lineHighlight.dark.backgroundColor
: undefined,
gutterIconPath: cfgBlameHighlight.locations.includes(LineHighlightLocations.Gutter)
gutterIconPath: cfgHighlight.locations.includes(BlameLineHighlightLocations.Gutter)
? this.context.asAbsolutePath('images/blame-dark.svg')
: undefined,
overviewRulerColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
overviewRulerColor: cfgHighlight.locations.includes(BlameLineHighlightLocations.OverviewRuler)
? cfgTheme.dark.overviewRulerColor || themeDefaults.lineHighlight.dark.overviewRulerColor
: undefined
},
light: {
backgroundColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.Line)
backgroundColor: cfgHighlight.locations.includes(BlameLineHighlightLocations.Line)
? cfgTheme.light.backgroundColor || themeDefaults.lineHighlight.light.backgroundColor
: undefined,
gutterIconPath: cfgBlameHighlight.locations.includes(LineHighlightLocations.Gutter)
gutterIconPath: cfgHighlight.locations.includes(BlameLineHighlightLocations.Gutter)
? this.context.asAbsolutePath('images/blame-light.svg')
: undefined,
overviewRulerColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
overviewRulerColor: cfgHighlight.locations.includes(BlameLineHighlightLocations.OverviewRuler)
? cfgTheme.light.overviewRulerColor || themeDefaults.lineHighlight.light.overviewRulerColor
: undefined
}
});
}
else {
Decorations.blameHighlight = undefined;
Decorations.highlight = 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/blame-dark.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/blame-light.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;
@@ -181,12 +128,7 @@ export class AnnotationController extends Disposable {
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);
}
provider.reset();
}
}
}
@@ -232,15 +174,10 @@ export class AnnotationController extends Disposable {
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);
provider = new GutterBlameAnnotationProvider(this.context, editor, Decorations.annotation, Decorations.highlight, 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);
provider = new HoverBlameAnnotationProvider(this.context, editor, Decorations.annotation, Decorations.highlight, this._whitespaceController, this.git, gitUri);
break;
}
if (provider === undefined || !(await provider.validate())) return false;
@@ -272,17 +209,13 @@ export class AnnotationController extends Disposable {
}
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;
if (!editor || !editor.document || !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);
return false;
}
private _onBlameabilityChanged(e: BlameabilityChangeEvent) {
@@ -300,7 +233,6 @@ export class AnnotationController extends Disposable {
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

View File

@@ -1,9 +1,8 @@
'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 { ExtensionKey, FileAnnotationType, IConfig } from '../configuration';
import { WhitespaceController } from './whitespaceController';
export abstract class AnnotationProviderBase extends Disposable {
@@ -14,7 +13,7 @@ import { WhitespaceController } from './whitespaceController';
protected _config: IConfig;
protected _disposable: Disposable;
constructor(context: ExtensionContext, public editor: TextEditor, protected decoration: TextEditorDecorationType | undefined, protected highlightDecoration: TextEditorDecorationType | undefined, protected whitespaceController: WhitespaceController | undefined) {
constructor(context: ExtensionContext, public editor: TextEditor, protected decoration: TextEditorDecorationType, protected highlightDecoration: TextEditorDecorationType | undefined, protected whitespaceController: WhitespaceController | undefined) {
super(() => this.dispose());
this.document = this.editor.document;
@@ -43,14 +42,10 @@ import { WhitespaceController } from './whitespaceController';
async clear() {
if (this.editor !== undefined) {
try {
if (this.decoration !== undefined) {
this.editor.setDecorations(this.decoration, []);
}
this.editor.setDecorations(this.decoration, []);
this.highlightDecoration && this.editor.setDecorations(this.highlightDecoration, []);
// I have no idea why the decorators sometimes don't get removed, but if they don't try again with a tiny delay
if (this.highlightDecoration !== undefined) {
this.editor.setDecorations(this.highlightDecoration, []);
// I have no idea why the decorators sometimes don't get removed, but if they don't try again with a tiny delay
await Functions.wait(1);
if (this.highlightDecoration === undefined) return;
@@ -65,13 +60,10 @@ import { WhitespaceController } from './whitespaceController';
this.whitespaceController && await this.whitespaceController.restore();
}
async reset(decoration: TextEditorDecorationType | undefined, highlightDecoration: TextEditorDecorationType | undefined, whitespaceController?: WhitespaceController) {
async reset() {
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);
}

View File

@@ -77,7 +77,8 @@ export class Annotations {
before: {
...renderOptions.before,
...{
contentText: content
contentText: content,
margin: '0 26px 0 0'
}
},
dark: {
@@ -112,9 +113,7 @@ export class Annotations {
before: {
borderStyle: borderStyle,
borderWidth: borderWidth,
height: cfgFileTheme.separateLines ? 'calc(100% - 1px)' : '100%',
margin: '0 26px 0 0',
textDecoration: 'none'
height: cfgFileTheme.separateLines ? 'calc(100% - 1px)' : '100%'
},
dark: {
backgroundColor: cfgFileTheme.dark.backgroundColor || undefined,
@@ -124,7 +123,7 @@ export class Annotations {
backgroundColor: cfgFileTheme.light.backgroundColor || undefined,
color: cfgFileTheme.light.foregroundColor || themeDefaults.annotations.file.gutter.light.foregroundColor
} as DecorationInstanceRenderOptions
} as IRenderOptions;
};
}
static hover(commit: GitCommit, renderOptions: IRenderOptions, heatmap: boolean): DecorationOptions {
@@ -143,8 +142,7 @@ export class Annotations {
borderWidth: '0 0 0 2px',
contentText: '\u200B',
height: cfgTheme.annotations.file.hover.separateLines ? 'calc(100% - 1px)' : '100%',
margin: '0 26px 0 0',
textDecoration: 'none'
margin: '0 26px 0 0'
}
} as IRenderOptions;
}

View File

@@ -2,20 +2,20 @@
import { Iterables } from '../system';
import { ExtensionContext, Range, TextEditor, TextEditorDecorationType } from 'vscode';
import { AnnotationProviderBase } from './annotationProvider';
import { GitBlame, GitService, GitUri } from '../gitService';
import { GitService, GitUri, IGitBlame } from '../gitService';
import { WhitespaceController } from './whitespaceController';
export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase {
protected _blame: Promise<GitBlame>;
protected _blame: Promise<IGitBlame>;
constructor(context: ExtensionContext, editor: TextEditor, decoration: TextEditorDecorationType | undefined, highlightDecoration: TextEditorDecorationType | undefined, whitespaceController: WhitespaceController | undefined, protected git: GitService, protected uri: GitUri) {
constructor(context: ExtensionContext, editor: TextEditor, decoration: TextEditorDecorationType, 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) {
async selection(shaOrLine?: string | number, blame?: IGitBlame) {
if (!this.highlightDecoration) return;
if (blame === undefined) {
@@ -56,14 +56,15 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
const blame = await this._blame;
return blame !== undefined && blame.lines.length !== 0;
}
protected async getBlame(requiresWhitespaceHack: boolean): Promise<GitBlame | undefined> {
protected async getBlame(requiresWhitespaceHack: boolean): Promise<IGitBlame | 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;
let blame: IGitBlame;
if (whitespacePromise) {
[blame] = await Promise.all([this._blame, whitespacePromise]);
}

View File

@@ -0,0 +1,69 @@
'use strict';
import { DecorationOptions, ExtensionContext, Position, Range, TextEditor, TextEditorDecorationType } from 'vscode';
import { AnnotationProviderBase } from './annotationProvider';
import { GitService, GitUri } from '../gitService';
import { WhitespaceController } from './whitespaceController';
export class DiffAnnotationProvider extends AnnotationProviderBase {
constructor(context: ExtensionContext, editor: TextEditor, decoration: TextEditorDecorationType, highlightDecoration: TextEditorDecorationType | undefined, whitespaceController: WhitespaceController | undefined, private git: GitService, private uri: GitUri) {
super(context, editor, decoration, highlightDecoration, whitespaceController);
}
async provideAnnotation(shaOrLine?: string | number): Promise<boolean> {
// let sha1: string | undefined = undefined;
// let sha2: string | undefined = undefined;
// if (shaOrLine === undefined) {
// const commit = await this.git.getLogCommit(this.uri.repoPath, this.uri.fsPath, { previous: true });
// if (commit === undefined) return false;
// sha1 = commit.previousSha;
// }
// else if (typeof shaOrLine === 'string') {
// sha1 = shaOrLine;
// }
// else {
// const blame = await this.git.getBlameForLine(this.uri, shaOrLine);
// if (blame === undefined) return false;
// sha1 = blame.commit.previousSha;
// sha2 = blame.commit.sha;
// }
// if (sha1 === undefined) return false;
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 decorators: DecorationOptions[] = [];
for (const chunk of diff.chunks) {
let count = chunk.currentStart - 2;
for (const change of chunk.current) {
if (change === undefined) continue;
count++;
if (change.state === 'unchanged') continue;
decorators.push({
range: new Range(new Position(count, 0), new Position(count, 0))
} as DecorationOptions);
}
}
this.editor.setDecorations(this.decoration, decorators);
return true;
}
async selection(shaOrLine?: string | number): Promise<void> {
}
async validate(): Promise<boolean> {
return true;
}
}

View File

@@ -1,9 +1,9 @@
'use strict';
import { Strings } from '../system';
import { DecorationOptions, Range } from 'vscode';
import { FileAnnotationType } from './annotationController';
import { BlameAnnotationProviderBase } from './blameAnnotationProvider';
import { Annotations, endOfLineIndex } from './annotations';
import { FileAnnotationType } from '../configuration';
import { ICommitFormatOptions } from '../gitService';
import * as moment from 'moment';
@@ -67,7 +67,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
}
if (decorations.length) {
this.editor.setDecorations(this.decoration!, decorations);
this.editor.setDecorations(this.decoration, decorations);
}
this.selection(shaOrLine, blame);

View File

@@ -1,8 +1,8 @@
'use strict';
import { DecorationOptions, Range } from 'vscode';
import { FileAnnotationType } from './annotationController';
import { BlameAnnotationProviderBase } from './blameAnnotationProvider';
import { Annotations, endOfLineIndex } from './annotations';
import { FileAnnotationType } from '../configuration';
import * as moment from 'moment';
export class HoverBlameAnnotationProvider extends BlameAnnotationProviderBase {
@@ -40,7 +40,7 @@ export class HoverBlameAnnotationProvider extends BlameAnnotationProviderBase {
}
if (decorations.length) {
this.editor.setDecorations(this.decoration!, decorations);
this.editor.setDecorations(this.decoration, decorations);
}
this.selection(shaOrLine, blame);

View File

@@ -1,61 +0,0 @@
'use strict';
import { DecorationOptions, ExtensionContext, Position, Range, TextEditor, TextEditorDecorationType } from 'vscode';
import { endOfLineIndex } from './annotations';
import { FileAnnotationType } from './annotationController';
import { AnnotationProviderBase } from './annotationProvider';
import { CommitFormatter, 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 decorators: DecorationOptions[] = [];
for (const chunk of diff.chunks) {
let count = chunk.currentPosition.start - 2;
for (const change of chunk.current) {
if (change === undefined) continue;
count++;
if (change.state === 'unchanged') continue;
let endingIndex = 0;
let message: string | undefined = undefined;
if (cfg.hover.changes) {
message = CommitFormatter.toHoverDiff(commit, chunk.previous[count], change);
endingIndex = cfg.hover.wholeLine ? endOfLineIndex : this.editor.document.lineAt(count).firstNonWhitespaceCharacterIndex;
}
decorators.push({
hoverMessage: message,
range: this.editor.document.validateRange(new Range(new Position(count, 0), new Position(count, endingIndex)))
} as DecorationOptions);
}
}
this.editor.setDecorations(this.highlightDecoration!, decorators);
return true;
}
async selection(shaOrLine?: string | number): Promise<void> {
}
async validate(): Promise<boolean> {
return true;
}
}

View File

@@ -19,7 +19,6 @@ export * from './commands/openCommitInRemote';
export * from './commands/openFileInRemote';
export * from './commands/openInRemote';
export * from './commands/openRepoInRemote';
export * from './commands/resetSuppressedWarnings';
export * from './commands/showBlameHistory';
export * from './commands/showFileBlame';
export * from './commands/showFileHistory';
@@ -38,5 +37,4 @@ export * from './commands/stashDelete';
export * from './commands/stashSave';
export * from './commands/toggleCodeLens';
export * from './commands/toggleFileBlame';
export * from './commands/toggleFileRecentChanges';
export * from './commands/toggleLineBlame';

View File

@@ -1,12 +1,10 @@
'use strict';
import { commands, TextEditor, Uri, window } from 'vscode';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorTracker } from '../activeEditorTracker';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { TextEditorComparer, UriComparer } from '../comparers';
import { BuiltInCommands } from '../constants';
import { GitService } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
export interface CloseUnchangedFilesCommandArgs {
uris?: Uri[];
@@ -24,7 +22,7 @@ export class CloseUnchangedFilesCommand extends ActiveEditorCommand {
try {
if (args.uris === undefined) {
const repoPath = await this.git.getRepoPathFromUri(uri);
if (!repoPath) return Messages.showNoRepositoryWarningMessage(`Unable to close unchanged files`);
if (!repoPath) return window.showWarningMessage(`Unable to close unchanged files`);
const status = await this.git.getStatusForRepo(repoPath);
if (status === undefined) return window.showWarningMessage(`Unable to close unchanged files`);
@@ -32,41 +30,34 @@ export class CloseUnchangedFilesCommand extends ActiveEditorCommand {
args.uris = status.files.map(_ => _.Uri);
}
if (args.uris.length === 0) return commands.executeCommand(BuiltInCommands.CloseAllEditors);
const editorTracker = new ActiveEditorTracker();
let count = 0;
let previous = undefined;
let editor = window.activeTextEditor;
while (true) {
let active = window.activeTextEditor;
let editor = active;
do {
if (editor !== undefined) {
if (TextEditorComparer.equals(previous, editor, { useId: true, usePosition: true })) {
break;
}
if (editor.document !== undefined &&
(editor.document.isDirty || args.uris.some(_ => UriComparer.equals(_, editor!.document && editor!.document.uri)))) {
previous = editor;
if ((editor.document !== undefined && editor.document.isDirty) ||
args.uris.some(_ => UriComparer.equals(_, editor!.document && editor!.document.uri))) {
// If we didn't start with a valid editor, set one once we find it
if (active === undefined) {
active = editor;
}
editor = await editorTracker.awaitNext(500);
continue;
}
}
previous = editor;
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 {
if (active === editor) {
active = undefined;
}
editor = await editorTracker.awaitClose(500);
}
}
else {
count = 0;
if (active === editor) {
active = undefined;
}
editor = await editorTracker.awaitClose(500);
}
}
} while ((active === undefined && editor === undefined) || !TextEditorComparer.equals(active, editor, { useId: true, usePosition: true }));
editorTracker.dispose();

View File

@@ -4,43 +4,16 @@ import { BuiltInCommands } from '../constants';
import { Logger } from '../logger';
import { Telemetry } from '../telemetry';
export type Commands = 'gitlens.closeUnchangedFiles' |
'gitlens.copyMessageToClipboard' |
'gitlens.copyShaToClipboard' |
'gitlens.diffDirectory' |
'gitlens.diffWithBranch' |
'gitlens.diffWithNext' |
'gitlens.diffWithPrevious' |
'gitlens.diffLineWithPrevious' |
'gitlens.diffWithWorking' |
'gitlens.diffLineWithWorking' |
'gitlens.openChangedFiles' |
'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 type Commands = 'gitlens.closeUnchangedFiles' | 'gitlens.copyMessageToClipboard' | 'gitlens.copyShaToClipboard' |
'gitlens.diffDirectory' | 'gitlens.diffWithBranch' | 'gitlens.diffWithNext' | 'gitlens.diffWithPrevious' | 'gitlens.diffLineWithPrevious' | 'gitlens.diffWithWorking' | 'gitlens.diffLineWithWorking' |
'gitlens.openChangedFiles' | 'gitlens.openBranchInRemote' | 'gitlens.openCommitInRemote' | 'gitlens.openFileInRemote' | 'gitlens.openInRemote' | 'gitlens.openRepoInRemote' |
'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.toggleLineBlame';
export const Commands = {
CloseUnchangedFiles: 'gitlens.closeUnchangedFiles' as Commands,
CopyMessageToClipboard: 'gitlens.copyMessageToClipboard' as Commands,
@@ -58,13 +31,12 @@ export const Commands = {
OpenFileInRemote: 'gitlens.openFileInRemote' as Commands,
OpenInRemote: 'gitlens.openInRemote' as Commands,
OpenRepoInRemote: 'gitlens.openRepoInRemote' as Commands,
ResetSuppressedWarnings: 'gitlens.resetSuppressedWarnings' as Commands,
ShowFileBlame: 'gitlens.showFileBlame' as Commands,
ShowLineBlame: 'gitlens.showLineBlame' as Commands,
ShowBlameHistory: 'gitlens.showBlameHistory' as Commands,
ShowCommitSearch: 'gitlens.showCommitSearch' as Commands,
ShowFileBlame: 'gitlens.showFileBlame' as Commands,
ShowFileHistory: 'gitlens.showFileHistory' as Commands,
ShowLastQuickPick: 'gitlens.showLastQuickPick' as Commands,
ShowLineBlame: 'gitlens.showLineBlame' as Commands,
ShowQuickCommitDetails: 'gitlens.showQuickCommitDetails' as Commands,
ShowQuickCommitFileDetails: 'gitlens.showQuickCommitFileDetails' as Commands,
ShowQuickFileHistory: 'gitlens.showQuickFileHistory' as Commands,
@@ -75,10 +47,9 @@ export const Commands = {
StashApply: 'gitlens.stashApply' as Commands,
StashDelete: 'gitlens.stashDelete' as Commands,
StashSave: 'gitlens.stashSave' as Commands,
ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands,
ToggleFileBlame: 'gitlens.toggleFileBlame' as Commands,
ToggleFileRecentChanges: 'gitlens.toggleFileRecentChanges' as Commands,
ToggleLineBlame: 'gitlens.toggleLineBlame' as Commands
ToggleLineBlame: 'gitlens.toggleLineBlame' as Commands,
ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands
};
export function getCommandUri(uri?: Uri, editor?: TextEditor): Uri | undefined {
@@ -87,13 +58,7 @@ export function getCommandUri(uri?: Uri, editor?: TextEditor): Uri | undefined {
return editor.document.uri;
}
export type CommandContext = 'gitlens:canToggleCodeLens' |
'gitlens:enabled' |
'gitlens:hasRemotes' |
'gitlens:isBlameable' |
'gitlens:isRepository' |
'gitlens:isTracked' |
'gitlens:key';
export type CommandContext = 'gitlens:canToggleCodeLens' | 'gitlens:enabled' | 'gitlens:hasRemotes' | 'gitlens:isBlameable' | 'gitlens:isRepository' | 'gitlens:isTracked' | 'gitlens:key';
export const CommandContext = {
CanToggleCodeLens: 'gitlens:canToggleCodeLens' as CommandContext,
Enabled: 'gitlens:enabled' as CommandContext,

View File

@@ -5,7 +5,6 @@ import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitService } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { BranchesQuickPick, CommandQuickPickItem } from '../quickPicks';
export interface DiffDirectoryCommandCommandArgs {
@@ -32,7 +31,7 @@ export class DiffDirectoryCommand extends ActiveEditorCommand {
try {
const repoPath = await this.git.getRepoPathFromUri(uri);
if (!repoPath) return Messages.showNoRepositoryWarningMessage(`Unable to open directory compare`);
if (!repoPath) return window.showWarningMessage(`Unable to open directory compare`);
if (!args.shaOrBranch1) {
const branches = await this.git.getBranches(repoPath);

View File

@@ -6,7 +6,6 @@ import { DiffWithPreviousCommandArgs } from './diffWithPrevious';
import { DiffWithWorkingCommandArgs } from './diffWithWorking';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import * as path from 'path';
export interface DiffLineWithPreviousCommandArgs {
@@ -36,7 +35,7 @@ export class DiffLineWithPreviousCommand extends ActiveEditorCommand {
try {
const blame = await this.git.getBlameForLine(gitUri, blameline);
if (blame === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to open compare');
if (blame === undefined) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
args.commit = blame.commit;

View File

@@ -3,7 +3,6 @@ import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vsco
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { DiffWithWorkingCommandArgs } from './diffWithWorking';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Messages } from '../messages';
import { Logger } from '../logger';
export interface DiffLineWithWorkingCommandArgs {
@@ -33,7 +32,7 @@ export class DiffLineWithWorkingCommand extends ActiveEditorCommand {
try {
const blame = await this.git.getBlameForLine(gitUri, blameline);
if (blame === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to open compare');
if (blame === undefined) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
args.commit = blame.commit;
// If the line is uncommitted, find the previous commit

View File

@@ -4,7 +4,6 @@ import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { BranchesQuickPick, CommandQuickPickItem } from '../quickPicks';
import * as path from 'path';
@@ -28,7 +27,7 @@ export class DiffWithBranchCommand extends ActiveEditorCommand {
args.line = args.line || (editor === undefined ? 0 : editor.selection.active.line);
const gitUri = await GitUri.fromUri(uri, this.git);
if (!gitUri.repoPath) return Messages.showNoRepositoryWarningMessage(`Unable to open branch compare`);
if (!gitUri.repoPath) return window.showWarningMessage(`Unable to open branch compare`);
const branches = await this.git.getBranches(gitUri.repoPath);
const pick = await BranchesQuickPick.show(branches, `Compare ${path.basename(gitUri.fsPath)} to \u2026`, args.goBackCommand);

View File

@@ -5,7 +5,6 @@ import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitLogCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import * as path from 'path';
export interface DiffWithNextCommandArgs {
@@ -38,8 +37,8 @@ export class DiffWithNextCommand extends ActiveEditorCommand {
const sha = args.commit === undefined ? gitUri.sha : args.commit.sha;
const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, undefined, sha !== undefined ? undefined : 2, args.range!);
if (log === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to open compare');
const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, undefined, sha ? undefined : 2, args.range!);
if (log === undefined) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
args.commit = (sha && log.commits.get(sha)) || Iterables.first(log.commits.values());
}

View File

@@ -6,7 +6,7 @@ import { BuiltInCommands } from '../constants';
import { DiffWithWorkingCommandArgs } from './diffWithWorking';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import * as moment from 'moment';
import * as path from 'path';
export interface DiffWithPreviousCommandArgs {
@@ -34,13 +34,13 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
try {
const sha = args.commit === undefined ? gitUri.sha : args.commit.sha;
const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, undefined, sha !== undefined ? undefined : 2, args.range!);
if (log === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to open compare');
const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, undefined, sha ? undefined : 2, args.range!);
if (log === undefined) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
args.commit = (sha && log.commits.get(sha)) || Iterables.first(log.commits.values());
// If the sha is missing and the file is uncommitted, then treat it as a DiffWithWorking
if (gitUri.sha === undefined && await this.git.isFileUncommitted(gitUri)) return commands.executeCommand(Commands.DiffWithWorking, uri, { commit: args.commit, showOptions: args.showOptions } as DiffWithWorkingCommandArgs);
// If the sha is missing, treat it as a DiffWithWorking
if (gitUri.sha === undefined) return commands.executeCommand(Commands.DiffWithWorking, uri, { commit: args.commit, showOptions: args.showOptions } as DiffWithWorkingCommandArgs);
}
catch (ex) {
Logger.error(ex, 'DiffWithPreviousCommand', `getLogForFile(${gitUri.repoPath}, ${gitUri.fsPath})`);
@@ -48,7 +48,7 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
}
}
if (args.commit.previousSha === undefined) return Messages.showCommitHasNoPreviousCommitWarningMessage(args.commit);
if (args.commit.previousSha === undefined) return window.showInformationMessage(`Commit ${args.commit.shortSha} (${args.commit.author}, ${moment(args.commit.date).fromNow()}) has no previous commit`);
try {
const [rhs, lhs] = await Promise.all([

View File

@@ -4,7 +4,6 @@ import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import * as path from 'path';
export interface DiffWithWorkingCommandArgs {
@@ -32,7 +31,7 @@ export class DiffWithWorkingCommand extends ActiveEditorCommand {
try {
args.commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, gitUri.sha, { firstIfMissing: true });
if (args.commit === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to open compare');
if (args.commit === undefined) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
}
catch (ex) {
Logger.error(ex, 'DiffWithWorkingCommand', `getLogCommit(${gitUri.repoPath}, ${gitUri.fsPath}, ${gitUri.sha})`);

View File

@@ -3,7 +3,6 @@ import { TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri, openEditor } from './common';
import { GitService } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
export interface OpenChangedFilesCommandArgs {
uris?: Uri[];
@@ -21,7 +20,7 @@ export class OpenChangedFilesCommand extends ActiveEditorCommand {
try {
if (args.uris === undefined) {
const repoPath = await this.git.getRepoPathFromUri(uri);
if (!repoPath) return Messages.showNoRepositoryWarningMessage(`Unable to open changed files`);
if (!repoPath) return window.showWarningMessage(`Unable to open changed files`);
const status = await this.git.getStatusForRepo(repoPath);
if (status === undefined) return window.showWarningMessage(`Unable to open changed files`);

View File

@@ -2,9 +2,8 @@
import { Arrays } from '../system';
import { commands, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { GitBlameCommit, GitService, GitUri } from '../gitService';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { OpenInRemoteCommandArgs } from './openInRemote';
export class OpenCommitInRemoteCommand extends ActiveEditorCommand {
@@ -28,12 +27,12 @@ export class OpenCommitInRemoteCommand extends ActiveEditorCommand {
if (blameline < 0) return undefined;
const blame = await this.git.getBlameForLine(gitUri, blameline);
if (blame === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to open commit in remote provider');
if (blame === undefined) return window.showWarningMessage(`Unable to open commit in remote provider. File is probably not under source control`);
let commit = blame.commit;
// If the line is uncommitted, find the previous commit
if (commit.isUncommitted) {
commit = new GitBlameCommit(commit.repoPath, commit.previousSha!, commit.previousFileName!, commit.author, commit.date, commit.message, []);
commit = new GitCommit(commit.type, commit.repoPath, commit.previousSha!, commit.previousFileName!, commit.author, commit.date, commit.message);
}
const remotes = Arrays.uniqueBy(await this.git.getRemotes(gitUri.repoPath), _ => _.url, _ => !!_.provider);

View File

@@ -1,18 +0,0 @@
'use strict';
import { Objects } from '../system';
import { ExtensionContext } from 'vscode';
import { Command, Commands } from './common';
import { SuppressedKeys } from '../messages';
export class ResetSuppressedWarningsCommand extends Command {
constructor(private context: ExtensionContext) {
super(Commands.ResetSuppressedWarnings);
}
async execute() {
for (const key of Objects.values<string>(SuppressedKeys)) {
await this.context.globalState.update(key, false);
}
}
}

View File

@@ -3,7 +3,6 @@ import { commands, Position, Range, TextEditor, TextEditorEdit, Uri, window } fr
import { Commands, EditorCommand, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitService, GitUri } from '../gitService';
import { Messages } from '../messages';
import { Logger } from '../logger';
export interface ShowBlameHistoryCommandArgs {
@@ -33,7 +32,7 @@ export class ShowBlameHistoryCommand extends EditorCommand {
try {
const locations = await this.git.getBlameLocations(gitUri, args.range, args.sha, args.line);
if (locations === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show blame history');
if (locations === undefined) return window.showWarningMessage(`Unable to show blame history. File is probably not under source control`);
return commands.executeCommand(BuiltInCommands.ShowReferences, uri, args.position, locations);
}

View File

@@ -3,7 +3,6 @@ import { commands, InputBoxOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitRepoSearchBy, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { CommandQuickPickItem, CommitsQuickPick } from '../quickPicks';
import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails';
import { paste } from 'copy-paste';
@@ -34,7 +33,7 @@ export class ShowCommitSearchCommand extends ActiveEditorCachedCommand {
const gitUri = uri === undefined ? undefined : await GitUri.fromUri(uri, this.git);
const repoPath = gitUri === undefined ? this.git.repoPath : gitUri.repoPath;
if (!repoPath) return Messages.showNoRepositoryWarningMessage(`Unable to show commit search`);
if (!repoPath) return window.showWarningMessage(`Unable to show commit search`);
if (!args.search || args.searchBy == null) {
try {

View File

@@ -1,8 +1,8 @@
'use strict';
import { TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
import { AnnotationController, FileAnnotationType } from '../annotations/annotationController';
import { AnnotationController } from '../annotations/annotationController';
import { Commands, EditorCommand } from './common';
import { ExtensionKey, IConfig } from '../configuration';
import { ExtensionKey, FileAnnotationType, IConfig } from '../configuration';
import { Logger } from '../logger';
export interface ShowFileBlameCommandArgs {

View File

@@ -3,7 +3,6 @@ import { commands, Position, Range, TextEditor, TextEditorEdit, Uri, window } fr
import { Commands, EditorCommand, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitService, GitUri } from '../gitService';
import { Messages } from '../messages';
import { Logger } from '../logger';
export interface ShowFileHistoryCommandArgs {
@@ -31,7 +30,7 @@ export class ShowFileHistoryCommand extends EditorCommand {
try {
const locations = await this.git.getLogLocations(gitUri, args.sha, args.line);
if (locations === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show file history');
if (locations === undefined) return window.showWarningMessage(`Unable to show file history. File is probably not under source control`);
return commands.executeCommand(BuiltInCommands.ShowReferences, uri, args.position, locations);
}

View File

@@ -1,8 +1,8 @@
'use strict';
import { TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
import { CurrentLineController, LineAnnotationType } from '../currentLineController';
import { CurrentLineController } from '../currentLineController';
import { Commands, EditorCommand } from './common';
import { ExtensionKey, IConfig } from '../configuration';
import { ExtensionKey, IConfig, LineAnnotationType } from '../configuration';
import { Logger } from '../logger';
export interface ShowLineBlameCommandArgs {

View File

@@ -1,15 +1,14 @@
'use strict';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitLog, GitService, GitUri } from '../gitService';
import { GitService, GitUri, IGitLog } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { BranchesQuickPick, BranchHistoryQuickPick, CommandQuickPickItem } from '../quickPicks';
import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails';
export interface ShowQuickBranchHistoryCommandArgs {
branch?: string;
log?: GitLog;
log?: IGitLog;
maxCount?: number;
goBackCommand?: CommandQuickPickItem;
@@ -34,7 +33,7 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand {
let progressCancellation = args.branch === undefined ? undefined : BranchHistoryQuickPick.showProgress(args.branch);
try {
const repoPath = gitUri === undefined ? this.git.repoPath : gitUri.repoPath;
if (!repoPath) return Messages.showNoRepositoryWarningMessage(`Unable to show branch history`);
if (!repoPath) return window.showWarningMessage(`Unable to show branch history`);
if (args.branch === undefined) {
const branches = await this.git.getBranches(repoPath);

View File

@@ -1,17 +1,16 @@
'use strict';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitCommit, GitLog, GitLogCommit, GitService, GitUri } from '../gitService';
import { GitCommit, GitLogCommit, GitService, GitUri, IGitLog } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, CommitDetailsQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks';
import { ShowQuickCommitFileDetailsCommandArgs } from './showQuickCommitFileDetails';
import { Messages } from '../messages';
import * as path from 'path';
export interface ShowQuickCommitDetailsCommandArgs {
sha?: string;
commit?: GitCommit | GitLogCommit;
repoLog?: GitLog;
repoLog?: IGitLog;
goBackCommand?: CommandQuickPickItem;
}
@@ -39,12 +38,9 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
try {
const blame = await this.git.getBlameForLine(gitUri, blameline);
if (blame === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show commit details');
if (blame === undefined) return window.showWarningMessage(`Unable to show commit details. File is probably not under source control`);
// Because the previous sha of an uncommitted file isn't trust worthy we just have to kick out
if (blame.commit.isUncommitted) return Messages.showLineUncommittedWarningMessage('Unable to show commit details');
args.sha = blame.commit.sha;
args.sha = blame.commit.isUncommitted ? blame.commit.previousSha : blame.commit.sha;
repoPath = blame.commit.repoPath;
workingFileName = blame.commit.fileName;
@@ -68,13 +64,13 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
if (args.repoLog === undefined) {
const log = await this.git.getLogForRepo(repoPath!, args.sha, 2);
if (log === undefined) return Messages.showCommitNotFoundWarningMessage(`Unable to show commit details`);
if (log === undefined) return window.showWarningMessage(`Unable to show commit details`);
args.commit = log.commits.get(args.sha!);
}
}
if (args.commit === undefined) return Messages.showCommitNotFoundWarningMessage(`Unable to show commit details`);
if (args.commit === undefined) return window.showWarningMessage(`Unable to show commit details`);
if (args.commit.workingFileName === undefined) {
args.commit.workingFileName = workingFileName;

View File

@@ -1,17 +1,16 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitCommit, GitLog, GitLogCommit, GitService, GitUri } from '../gitService';
import { GitCommit, GitLogCommit, GitService, GitUri, IGitLog } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, CommitFileDetailsQuickPick } from '../quickPicks';
import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails';
import { Messages } from '../messages';
import * as path from 'path';
export interface ShowQuickCommitFileDetailsCommandArgs {
sha?: string;
commit?: GitCommit | GitLogCommit;
fileLog?: GitLog;
fileLog?: IGitLog;
goBackCommand?: CommandQuickPickItem;
}
@@ -38,12 +37,9 @@ export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand
try {
const blame = await this.git.getBlameForLine(gitUri, blameline);
if (blame === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show commit file details');
if (blame === undefined) return window.showWarningMessage(`Unable to show commit file details. File is probably not under source control`);
// Because the previous sha of an uncommitted file isn't trust worthy we just have to kick out
if (blame.commit.isUncommitted) return Messages.showLineUncommittedWarningMessage('Unable to show commit file details');
args.sha = blame.commit.sha;
args.sha = blame.commit.isUncommitted ? blame.commit.previousSha : blame.commit.sha;
args.commit = blame.commit;
workingFileName = path.relative(args.commit.repoPath, gitUri.fsPath);
@@ -69,12 +65,12 @@ export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand
}
if (args.fileLog === undefined) {
args.commit = await this.git.getLogCommit(args.commit === undefined ? gitUri.repoPath : args.commit.repoPath, gitUri.fsPath, args.sha, { previous: true });
if (args.commit === undefined) return Messages.showCommitNotFoundWarningMessage(`Unable to show commit file details`);
args.commit = await this.git.getLogCommit(args.commit ? args.commit.repoPath : gitUri.repoPath, gitUri.fsPath, args.sha, { previous: true });
if (args.commit === undefined) return window.showWarningMessage(`Unable to show commit file details`);
}
}
if (args.commit === undefined) return Messages.showCommitNotFoundWarningMessage(`Unable to show commit file details`);
if (args.commit === undefined) return window.showWarningMessage(`Unable to show commit file details`);
// Attempt to the most recent commit -- so that we can find the real working filename if there was a rename
args.commit.workingFileName = workingFileName;

View File

@@ -4,7 +4,6 @@ import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { ShowQuickBranchHistoryCommandArgs } from './showQuickBranchHistory';
import { GitService } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { CommandQuickPickItem } from '../quickPicks';
export interface ShowQuickCurrentBranchHistoryCommandArgs {
@@ -22,7 +21,7 @@ export class ShowQuickCurrentBranchHistoryCommand extends ActiveEditorCachedComm
try {
const repoPath = await this.git.getRepoPathFromUri(uri);
if (!repoPath) return Messages.showNoRepositoryWarningMessage(`Unable to show branch history`);
if (!repoPath) return window.showWarningMessage(`Unable to show branch history`);
const branch = await this.git.getBranch(repoPath);
if (branch === undefined) return undefined;

View File

@@ -1,15 +1,14 @@
'use strict';
import { commands, Range, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitLog, GitService, GitUri } from '../gitService';
import { GitService, GitUri, IGitLog } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, FileHistoryQuickPick } from '../quickPicks';
import { ShowQuickCommitFileDetailsCommandArgs } from './showQuickCommitFileDetails';
import { Messages } from '../messages';
import * as path from 'path';
export interface ShowQuickFileHistoryCommandArgs {
log?: GitLog;
log?: IGitLog;
maxCount?: number;
range?: Range;
@@ -37,7 +36,7 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand {
try {
if (args.log === undefined) {
args.log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, gitUri.sha, args.maxCount, args.range);
if (args.log === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show file history');
if (args.log === undefined) return window.showWarningMessage(`Unable to show file history. File is probably not under source control`);
}
if (progressCancellation.token.isCancellationRequested) return undefined;

View File

@@ -3,7 +3,6 @@ import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitService } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { CommandQuickPickItem, RepoStatusQuickPick } from '../quickPicks';
export interface ShowQuickRepoStatusCommandArgs {
@@ -21,7 +20,7 @@ export class ShowQuickRepoStatusCommand extends ActiveEditorCachedCommand {
try {
const repoPath = await this.git.getRepoPathFromUri(uri);
if (!repoPath) return Messages.showNoRepositoryWarningMessage(`Unable to show repository status`);
if (!repoPath) return window.showWarningMessage(`Unable to show repository status`);
const status = await this.git.getStatusForRepo(repoPath);
if (status === undefined) return window.showWarningMessage(`Unable to show repository status`);

View File

@@ -3,7 +3,6 @@ import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { CommandQuickPickItem, StashListQuickPick } from '../quickPicks';
import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails';
@@ -22,7 +21,7 @@ export class ShowQuickStashListCommand extends ActiveEditorCachedCommand {
try {
const repoPath = await this.git.getRepoPathFromUri(uri);
if (!repoPath) return Messages.showNoRepositoryWarningMessage(`Unable to show stashed changes`);
if (!repoPath) return window.showWarningMessage(`Unable to show stashed changes`);
const stash = await this.git.getStashList(repoPath);
if (stash === undefined) return window.showWarningMessage(`Unable to show stashed changes`);

View File

@@ -1,8 +1,8 @@
'use strict';
import { TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
import { AnnotationController, FileAnnotationType } from '../annotations/annotationController';
import { AnnotationController } from '../annotations/annotationController';
import { Commands, EditorCommand } from './common';
import { ExtensionKey, IConfig } from '../configuration';
import { ExtensionKey, FileAnnotationType, IConfig } from '../configuration';
import { Logger } from '../logger';
export interface ToggleFileBlameCommandArgs {

View File

@@ -1,24 +0,0 @@
'use strict';
import { TextEditor, TextEditorEdit, Uri, window } from 'vscode';
import { AnnotationController, FileAnnotationType } from '../annotations/annotationController';
import { Commands, EditorCommand } from './common';
import { Logger } from '../logger';
export class ToggleFileRecentChangesCommand extends EditorCommand {
constructor(private annotationController: AnnotationController) {
super(Commands.ToggleFileRecentChanges);
}
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.toggleAnnotations(editor, FileAnnotationType.RecentChanges);
}
catch (ex) {
Logger.error(ex, 'ToggleFileRecentChangesCommand');
return window.showErrorMessage(`Unable to toggle recent file changes annotations. See output channel for more details`);
}
}
}

View File

@@ -1,8 +1,8 @@
'use strict';
import { TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
import { CurrentLineController, LineAnnotationType } from '../currentLineController';
import { CurrentLineController } from '../currentLineController';
import { Commands, EditorCommand } from './common';
import { ExtensionKey, IConfig } from '../configuration';
import { ExtensionKey, IConfig, LineAnnotationType } from '../configuration';
import { Logger } from '../logger';
export interface ToggleLineBlameCommandArgs {

View File

@@ -1,19 +1,17 @@
'use strict';
import { FileAnnotationType } from './annotations/annotationController';
import { Commands } from './commands';
import { LineAnnotationType } from './currentLineController';
import { OutputLevel } from './logger';
export { ExtensionKey } from './constants';
export type CodeLensCommand = 'gitlens.toggleFileBlame' |
'gitlens.showBlameHistory' |
'gitlens.showFileHistory' |
'gitlens.diffWithPrevious' |
'gitlens.showQuickCommitDetails' |
'gitlens.showQuickCommitFileDetails' |
'gitlens.showQuickFileHistory' |
'gitlens.showQuickRepoHistory';
export type BlameLineHighlightLocations = 'gutter' | 'line' | 'overviewRuler';
export const BlameLineHighlightLocations = {
Gutter: 'gutter' as BlameLineHighlightLocations,
Line: 'line' as BlameLineHighlightLocations,
OverviewRuler: 'overviewRuler' as BlameLineHighlightLocations
};
export type CodeLensCommand = 'gitlens.toggleFileBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.diffWithPrevious' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory';
export const CodeLensCommand = {
BlameAnnotate: Commands.ToggleFileBlame as CodeLensCommand,
ShowBlameHistory: Commands.ShowBlameHistory as CodeLensCommand,
@@ -33,23 +31,19 @@ export const CodeLensLocations = {
Custom: 'custom' as CodeLensLocations
};
export type LineHighlightLocations = 'gutter' | 'line' | 'overviewRuler';
export const LineHighlightLocations = {
Gutter: 'gutter' as LineHighlightLocations,
Line: 'line' as LineHighlightLocations,
OverviewRuler: 'overviewRuler' as LineHighlightLocations
export type FileAnnotationType = 'gutter' | 'hover';
export const FileAnnotationType = {
Gutter: 'gutter' as FileAnnotationType,
Hover: 'hover' as FileAnnotationType
};
export type StatusBarCommand = 'gitlens.toggleFileBlame' |
'gitlens.showBlameHistory' |
'gitlens.showFileHistory' |
'gitlens.toggleCodeLens' |
'gitlens.diffWithPrevious' |
'gitlens.diffWithWorking' |
'gitlens.showQuickCommitDetails' |
'gitlens.showQuickCommitFileDetails' |
'gitlens.showQuickFileHistory' |
'gitlens.showQuickRepoHistory';
export type LineAnnotationType = 'trailing' | 'hover';
export const LineAnnotationType = {
Trailing: 'trailing' as LineAnnotationType,
Hover: 'hover' as LineAnnotationType
};
export type StatusBarCommand = 'gitlens.toggleFileBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.toggleCodeLens' | 'gitlens.diffWithPrevious' | 'gitlens.diffWithWorking' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory';
export const StatusBarCommand = {
BlameAnnotate: Commands.ToggleFileBlame as StatusBarCommand,
ShowBlameHistory: Commands.ShowBlameHistory as StatusBarCommand,
@@ -104,9 +98,6 @@ export interface IAdvancedConfig {
quickPick: {
closeOnFocusOut: boolean;
};
telemetry: {
enabled: boolean;
};
toggleWhitespace: {
enabled: boolean;
};
@@ -234,13 +225,6 @@ export interface IConfig {
};
wholeLine: boolean;
};
recentChanges: {
hover: {
changes: boolean;
wholeLine: boolean;
};
};
};
line: {
@@ -266,7 +250,7 @@ export interface IConfig {
annotationType: FileAnnotationType;
lineHighlight: {
enabled: boolean;
locations: LineHighlightLocations[];
locations: BlameLineHighlightLocations[];
};
};
@@ -276,14 +260,6 @@ export interface IConfig {
};
};
recentChanges: {
file: {
lineHighlight: {
locations: LineHighlightLocations[];
};
}
};
codeLens: {
enabled: boolean;
recentChange: {
@@ -308,16 +284,6 @@ export interface IConfig {
dateFormat: string;
};
strings: {
codeLens: {
unsavedChanges: {
recentChangeAndAuthors: string;
recentChangeOnly: string;
authorsOnly: string;
};
};
};
theme: IThemeConfig;
debug: boolean;

View File

@@ -1,51 +1,40 @@
'use strict';
export const ExtensionId = 'gitlens';
export const ExtensionKey = ExtensionId;
export const ExtensionOutputChannelName = 'GitLens';
export const QualifiedExtensionId = `eamodio.${ExtensionId}`;
export const ApplicationInsightsKey = 'a9c302f8-6483-4d01-b92c-c159c799c679';
export type BuiltInCommands = 'cursorMove' |
'editor.action.showReferences' |
'editor.action.toggleRenderWhitespace' |
'editorScroll' |
'revealLine' |
'setContext' |
'vscode.diff' |
'vscode.executeDocumentSymbolProvider' |
'vscode.executeCodeLensProvider' |
'vscode.open' |
'vscode.previewHtml' |
'workbench.action.closeActiveEditor' |
'workbench.action.closeAllEditors' |
'workbench.action.nextEditor';
export const BuiltInCommands = {
CloseActiveEditor: 'workbench.action.closeActiveEditor' as BuiltInCommands,
CloseAllEditors: 'workbench.action.closeAllEditors' as BuiltInCommands,
CursorMove: 'cursorMove' as BuiltInCommands,
Diff: 'vscode.diff' as BuiltInCommands,
EditorScroll: 'editorScroll' as BuiltInCommands,
ExecuteDocumentSymbolProvider: 'vscode.executeDocumentSymbolProvider' as BuiltInCommands,
ExecuteCodeLensProvider: 'vscode.executeCodeLensProvider' as BuiltInCommands,
Open: 'vscode.open' as BuiltInCommands,
NextEditor: 'workbench.action.nextEditor' as BuiltInCommands,
PreviewHtml: 'vscode.previewHtml' as BuiltInCommands,
RevealLine: 'revealLine' as BuiltInCommands,
SetContext: 'setContext' as BuiltInCommands,
ShowReferences: 'editor.action.showReferences' as BuiltInCommands,
ToggleRenderWhitespace: 'editor.action.toggleRenderWhitespace' as BuiltInCommands
};
export type DocumentSchemes = 'file' | 'git' | 'gitlens-git';
export const DocumentSchemes = {
File: 'file' as DocumentSchemes,
Git: 'git' as DocumentSchemes,
GitLensGit: 'gitlens-git' as DocumentSchemes
};
export type WorkspaceState = 'gitlensVersion';
export const WorkspaceState = {
GitLensVersion: 'gitlensVersion' as WorkspaceState
'use strict';
export const ExtensionId = 'gitlens';
export const ExtensionKey = ExtensionId;
export const ExtensionOutputChannelName = 'GitLens';
export const QualifiedExtensionId = `eamodio.${ExtensionId}`;
export const ApplicationInsightsKey = 'a9c302f8-6483-4d01-b92c-c159c799c679';
export type BuiltInCommands = 'cursorMove' | 'editor.action.showReferences' | 'editor.action.toggleRenderWhitespace' | 'editorScroll' | 'revealLine' | 'setContext' | 'vscode.diff' | 'vscode.executeDocumentSymbolProvider' | 'vscode.executeCodeLensProvider' | 'vscode.open' | 'vscode.previewHtml' | 'workbench.action.closeActiveEditor' | 'workbench.action.nextEditor';
export const BuiltInCommands = {
CloseActiveEditor: 'workbench.action.closeActiveEditor' as BuiltInCommands,
CursorMove: 'cursorMove' as BuiltInCommands,
Diff: 'vscode.diff' as BuiltInCommands,
EditorScroll: 'editorScroll' as BuiltInCommands,
ExecuteDocumentSymbolProvider: 'vscode.executeDocumentSymbolProvider' as BuiltInCommands,
ExecuteCodeLensProvider: 'vscode.executeCodeLensProvider' as BuiltInCommands,
Open: 'vscode.open' as BuiltInCommands,
NextEditor: 'workbench.action.nextEditor' as BuiltInCommands,
PreviewHtml: 'vscode.previewHtml' as BuiltInCommands,
RevealLine: 'revealLine' as BuiltInCommands,
SetContext: 'setContext' as BuiltInCommands,
ShowReferences: 'editor.action.showReferences' as BuiltInCommands,
ToggleRenderWhitespace: 'editor.action.toggleRenderWhitespace' as BuiltInCommands
};
export type DocumentSchemes = 'file' | 'git' | 'gitlens-git';
export const DocumentSchemes = {
File: 'file' as DocumentSchemes,
Git: 'git' as DocumentSchemes,
GitLensGit: 'gitlens-git' as DocumentSchemes
};
export type WorkspaceState = 'repoPath' | 'suppressGitVersionWarning' | 'suppressUpdateNotice' | 'suppressWelcomeNotice';
export const WorkspaceState = {
GitLensVersion: 'gitlensVersion' as WorkspaceState,
SuppressGitVersionWarning: 'suppressGitVersionWarning' as WorkspaceState,
SuppressUpdateNotice: 'suppressUpdateNotice' as WorkspaceState,
SuppressWelcomeNotice: 'suppressWelcomeNotice' as WorkspaceState
};

View File

@@ -1,461 +1,437 @@
'use strict';
import { Functions, Objects } from './system';
import { DecorationOptions, DecorationRenderOptions, Disposable, ExtensionContext, Range, StatusBarAlignment, StatusBarItem, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, window, workspace } from 'vscode';
import { AnnotationController, FileAnnotationType } from './annotations/annotationController';
import { Annotations, endOfLineIndex } from './annotations/annotations';
import { Commands } from './commands';
import { TextEditorComparer } from './comparers';
import { IConfig, StatusBarCommand } from './configuration';
import { DocumentSchemes, ExtensionKey } from './constants';
import { BlameabilityChangeEvent, CommitFormatter, GitCommit, GitCommitLine, GitContextTracker, GitService, GitUri } from './gitService';
const annotationDecoration: TextEditorDecorationType = window.createTextEditorDecorationType({
after: {
margin: '0 0 0 3em',
textDecoration: 'none'
}
} as DecorationRenderOptions);
export type LineAnnotationType = 'trailing' | 'hover';
export const LineAnnotationType = {
Trailing: 'trailing' as LineAnnotationType,
Hover: 'hover' as LineAnnotationType
};
export class CurrentLineController extends Disposable {
private _activeEditorLineDisposable: Disposable | undefined;
private _blameable: boolean;
private _config: IConfig;
private _currentLine: number = -1;
private _disposable: Disposable;
private _editor: TextEditor | undefined;
private _isAnnotating: boolean = false;
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: AnnotationController) {
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.onDidToggleAnnotations(this._onAnnotationsToggled, this));
this._disposable = Disposable.from(...subscriptions);
}
dispose() {
this._clearAnnotations(this._editor, true);
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.blame.line, this._config && this._config.blame.line) ||
!Objects.areEquivalent(cfg.annotations.line.trailing, this._config && this._config.annotations.line.trailing) ||
!Objects.areEquivalent(cfg.annotations.line.hover, this._config && this._config.annotations.line.hover) ||
!Objects.areEquivalent(cfg.theme.annotations.line.trailing, this._config && this._config.theme.annotations.line.trailing)) {
changed = true;
this._clearAnnotations(this._editor);
}
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;
}
}
this._config = cfg;
if (!changed) return;
const trackCurrentLine = cfg.statusBar.enabled || cfg.blame.line.enabled;
if (trackCurrentLine && !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 (!trackCurrentLine && 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;
this._clearAnnotations(this._editor);
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.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 _onAnnotationsToggled() {
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 !== undefined) {
this._uri = await GitUri.fromUri(e.textEditor.document.uri, this.git);
}
this._clearAnnotations(e.textEditor);
this._updateBlameDebounced(line, e.textEditor);
}
private async _updateBlame(line: number, editor: TextEditor) {
line = line - this._uri.offset;
let commit: GitCommit | undefined = undefined;
let commitLine: GitCommitLine | 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, line);
}
else {
this.clear(editor);
}
}
async clear(editor: TextEditor | undefined) {
this._clearAnnotations(editor, true);
this._statusBarItem && this._statusBarItem.hide();
}
private async _clearAnnotations(editor: TextEditor | undefined, force: boolean = false) {
if (editor === undefined || (!this._isAnnotating && !force)) return;
editor.setDecorations(annotationDecoration, []);
this._isAnnotating = false;
if (!force) return;
// I have no idea why the decorators sometimes don't get removed, but if they don't try again with a tiny delay
await Functions.wait(1);
editor.setDecorations(annotationDecoration, []);
}
async show(commit: GitCommit, blameLine: GitCommitLine, editor: TextEditor, line: number) {
// I have no idea why I need this protection -- but it happens
if (editor.document === undefined) return;
this._updateStatusBar(commit);
await this._updateAnnotations(commit, blameLine, editor, line);
}
async showAnnotations(editor: TextEditor, type: LineAnnotationType) {
if (editor === undefined) return;
const cfg = this._config.blame.line;
if (!cfg.enabled || cfg.annotationType !== type) {
cfg.enabled = true;
cfg.annotationType = type;
await this._clearAnnotations(editor);
await this._updateBlame(editor.selection.active.line, editor);
}
}
async toggleAnnotations(editor: TextEditor, type: LineAnnotationType) {
if (editor === undefined) return;
const cfg = this._config.blame.line;
cfg.enabled = !cfg.enabled;
cfg.annotationType = type;
await this._clearAnnotations(editor);
await this._updateBlame(editor.selection.active.line, editor);
}
private async _updateAnnotations(commit: GitCommit, blameLine: GitCommitLine, editor: TextEditor, line?: number) {
const cfg = this._config.blame.line;
if (!cfg.enabled) return;
line = line === undefined ? blameLine.line + this._uri.offset : line;
const decorationOptions: DecorationOptions[] = [];
let showChanges = false;
let showChangesStartIndex = 0;
let showChangesInStartingWhitespace = false;
let showDetails = false;
let showDetailsStartIndex = 0;
let showDetailsInStartingWhitespace = false;
switch (cfg.annotationType) {
case LineAnnotationType.Trailing: {
const cfgAnnotations = this._config.annotations.line.trailing;
showChanges = cfgAnnotations.hover.changes;
showDetails = cfgAnnotations.hover.details;
if (cfgAnnotations.hover.wholeLine) {
showChangesStartIndex = 0;
showChangesInStartingWhitespace = false;
showDetailsStartIndex = 0;
showDetailsInStartingWhitespace = false;
}
else {
showChangesStartIndex = endOfLineIndex;
showChangesInStartingWhitespace = true;
showDetailsStartIndex = endOfLineIndex;
showDetailsInStartingWhitespace = true;
}
const decoration = Annotations.trailing(commit, cfgAnnotations.format, cfgAnnotations.dateFormat, this._config.theme);
decoration.range = editor.document.validateRange(new Range(line, endOfLineIndex, line, endOfLineIndex));
decorationOptions.push(decoration);
break;
}
case LineAnnotationType.Hover: {
const cfgAnnotations = this._config.annotations.line.hover;
showChanges = cfgAnnotations.changes;
showChangesStartIndex = 0;
showChangesInStartingWhitespace = false;
showDetails = cfgAnnotations.details;
showDetailsStartIndex = 0;
showDetailsInStartingWhitespace = false;
break;
}
}
if (showDetails || showChanges) {
const annotationType = this.annotationController.getAnnotationType(editor);
const firstNonWhitespace = editor.document.lineAt(line).firstNonWhitespaceCharacterIndex;
switch (annotationType) {
case FileAnnotationType.Gutter: {
const cfgHover = this._config.annotations.file.gutter.hover;
if (cfgHover.details) {
showDetailsInStartingWhitespace = false;
if (cfgHover.wholeLine) {
// Avoid double annotations if we are showing the whole-file hover blame annotations
showDetails = false;
}
else {
if (showDetailsStartIndex === 0) {
showDetailsStartIndex = firstNonWhitespace === 0 ? 1 : firstNonWhitespace;
}
if (showChangesStartIndex === 0) {
showChangesInStartingWhitespace = true;
showChangesStartIndex = firstNonWhitespace === 0 ? 1 : firstNonWhitespace;
}
}
}
break;
}
case FileAnnotationType.Hover: {
const cfgHover = this._config.annotations.file.hover;
showDetailsInStartingWhitespace = false;
if (cfgHover.wholeLine) {
// Avoid double annotations if we are showing the whole-file hover blame annotations
showDetails = false;
showChangesStartIndex = 0;
}
else {
if (showDetailsStartIndex === 0) {
showDetailsStartIndex = firstNonWhitespace === 0 ? 1 : firstNonWhitespace;
}
if (showChangesStartIndex === 0) {
showChangesInStartingWhitespace = true;
showChangesStartIndex = firstNonWhitespace === 0 ? 1 : firstNonWhitespace;
}
}
break;
}
case FileAnnotationType.RecentChanges: {
const cfgChanges = this._config.annotations.file.recentChanges.hover;
if (cfgChanges.changes) {
if (cfgChanges.wholeLine) {
// Avoid double annotations if we are showing the whole-file hover blame annotations
showChanges = false;
}
else {
showChangesInStartingWhitespace = false;
}
}
break;
}
}
if (showDetails) {
// 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 === undefined) return;
const decoration = Annotations.detailsHover(logCommit || commit);
decoration.range = editor.document.validateRange(new Range(line, showDetailsStartIndex, line, endOfLineIndex));
decorationOptions.push(decoration);
if (showDetailsInStartingWhitespace && showDetailsStartIndex !== 0) {
decorationOptions.push(Annotations.withRange(decoration, 0, firstNonWhitespace));
}
}
if (showChanges) {
const decoration = await Annotations.changesHover(commit, line, this._uri, this.git);
// I have no idea why I need this protection -- but it happens
if (editor.document === undefined) return;
decoration.range = editor.document.validateRange(new Range(line, showChangesStartIndex, line, endOfLineIndex));
decorationOptions.push(decoration);
if (showChangesInStartingWhitespace && showChangesStartIndex !== 0) {
decorationOptions.push(Annotations.withRange(decoration, 0, firstNonWhitespace));
}
}
}
if (decorationOptions.length) {
editor.setDecorations(annotationDecoration, decorationOptions);
this._isAnnotating = true;
}
}
private _updateStatusBar(commit: GitCommit) {
const cfg = this._config.statusBar;
if (!cfg.enabled || this._statusBarItem === undefined) return;
this._statusBarItem.text = `$(git-commit) ${CommitFormatter.fromTemplate(cfg.format, commit, cfg.dateFormat)}`;
switch (cfg.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 Line Commit with Previous';
break;
case StatusBarCommand.DiffWithWorking:
this._statusBarItem.command = Commands.DiffLineWithWorking;
this._statusBarItem.tooltip = 'Compare Line Commit 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();
}
'use strict';
import { Functions, Objects } from './system';
import { DecorationOptions, DecorationRenderOptions, Disposable, ExtensionContext, Range, StatusBarAlignment, StatusBarItem, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, window, workspace } from 'vscode';
import { AnnotationController } from './annotations/annotationController';
import { Annotations, endOfLineIndex } from './annotations/annotations';
import { Commands } from './commands';
import { TextEditorComparer } from './comparers';
import { FileAnnotationType, IConfig, LineAnnotationType, StatusBarCommand } from './configuration';
import { DocumentSchemes, ExtensionKey } from './constants';
import { BlameabilityChangeEvent, CommitFormatter, GitCommit, GitContextTracker, GitService, GitUri, IGitCommitLine } from './gitService';
const annotationDecoration: TextEditorDecorationType = window.createTextEditorDecorationType({
after: {
margin: '0 0 0 4em'
}
} as DecorationRenderOptions);
export class CurrentLineController 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: AnnotationController) {
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.onDidToggleAnnotations(this._onAnnotationsToggled, this));
this._disposable = Disposable.from(...subscriptions);
}
dispose() {
this._editor && this._editor.setDecorations(annotationDecoration, []);
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.blame.line, this._config && this._config.blame.line) ||
!Objects.areEquivalent(cfg.annotations.line.trailing, this._config && this._config.annotations.line.trailing) ||
!Objects.areEquivalent(cfg.annotations.line.hover, this._config && this._config.annotations.line.hover) ||
!Objects.areEquivalent(cfg.theme.annotations.line.trailing, this._config && this._config.theme.annotations.line.trailing)) {
changed = true;
if (this._editor) {
this._editor.setDecorations(annotationDecoration, []);
}
}
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;
}
}
this._config = cfg;
if (!changed) return;
const trackCurrentLine = cfg.statusBar.enabled || cfg.blame.line.enabled;
if (trackCurrentLine && !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 (!trackCurrentLine && 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(annotationDecoration, []);
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.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 _onAnnotationsToggled() {
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);
}
}
async clear(editor: TextEditor | undefined, previousEditor?: TextEditor) {
this._clearAnnotations(editor, previousEditor);
this._statusBarItem && this._statusBarItem.hide();
}
private async _clearAnnotations(editor: TextEditor | undefined, previousEditor?: TextEditor) {
editor && editor.setDecorations(annotationDecoration, []);
// 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 !== undefined) {
await Functions.wait(1);
editor.setDecorations(annotationDecoration, []);
}
}
async show(commit: GitCommit, blameLine: IGitCommitLine, editor: TextEditor) {
// I have no idea why I need this protection -- but it happens
if (editor.document === undefined) return;
this._updateStatusBar(commit);
await this._updateAnnotations(commit, blameLine, editor);
}
async showAnnotations(editor: TextEditor, type: LineAnnotationType) {
if (editor === undefined) return;
const cfg = this._config.blame.line;
if (!cfg.enabled || cfg.annotationType !== type) {
cfg.enabled = true;
cfg.annotationType = type;
await this._clearAnnotations(editor);
await this._updateBlame(editor.selection.active.line, editor);
}
}
async toggleAnnotations(editor: TextEditor, type: LineAnnotationType) {
if (editor === undefined) return;
const cfg = this._config.blame.line;
cfg.enabled = !cfg.enabled;
cfg.annotationType = type;
await this._clearAnnotations(editor);
await this._updateBlame(editor.selection.active.line, editor);
}
private async _updateAnnotations(commit: GitCommit, blameLine: IGitCommitLine, editor: TextEditor) {
const cfg = this._config.blame.line;
if (!cfg.enabled) return;
const line = blameLine.line + this._uri.offset;
const decorationOptions: DecorationOptions[] = [];
let showChanges = false;
let showChangesStartIndex = 0;
let showChangesInStartingWhitespace = false;
let showDetails = false;
let showDetailsStartIndex = 0;
let showDetailsInStartingWhitespace = false;
switch (cfg.annotationType) {
case LineAnnotationType.Trailing: {
const cfgAnnotations = this._config.annotations.line.trailing;
showChanges = cfgAnnotations.hover.changes;
showDetails = cfgAnnotations.hover.details;
if (cfgAnnotations.hover.wholeLine) {
showChangesStartIndex = 0;
showChangesInStartingWhitespace = false;
showDetailsStartIndex = 0;
showDetailsInStartingWhitespace = false;
}
else {
showChangesStartIndex = endOfLineIndex;
showChangesInStartingWhitespace = true;
showDetailsStartIndex = endOfLineIndex;
showDetailsInStartingWhitespace = true;
}
const decoration = Annotations.trailing(commit, cfgAnnotations.format, cfgAnnotations.dateFormat, this._config.theme);
decoration.range = editor.document.validateRange(new Range(line, endOfLineIndex, line, endOfLineIndex));
decorationOptions.push(decoration);
break;
}
case LineAnnotationType.Hover: {
const cfgAnnotations = this._config.annotations.line.hover;
showChanges = cfgAnnotations.changes;
showChangesStartIndex = 0;
showChangesInStartingWhitespace = false;
showDetails = cfgAnnotations.details;
showDetailsStartIndex = 0;
showDetailsInStartingWhitespace = false;
break;
}
}
if (showDetails || showChanges) {
const annotationType = this.annotationController.getAnnotationType(editor);
const firstNonWhitespace = editor.document.lineAt(line).firstNonWhitespaceCharacterIndex;
switch (annotationType) {
case FileAnnotationType.Gutter: {
const cfgHover = this._config.annotations.file.gutter.hover;
if (cfgHover.details) {
showDetailsInStartingWhitespace = false;
if (cfgHover.wholeLine) {
// Avoid double annotations if we are showing the whole-file hover blame annotations
showDetails = false;
}
else {
if (showDetailsStartIndex === 0) {
showDetailsStartIndex = firstNonWhitespace === 0 ? 1 : firstNonWhitespace;
}
if (showChangesStartIndex === 0) {
showChangesInStartingWhitespace = true;
showChangesStartIndex = firstNonWhitespace === 0 ? 1 : firstNonWhitespace;
}
}
}
break;
}
case FileAnnotationType.Hover: {
const cfgHover = this._config.annotations.file.hover;
showDetailsInStartingWhitespace = false;
if (cfgHover.wholeLine) {
// Avoid double annotations if we are showing the whole-file hover blame annotations
showDetails = false;
showChangesStartIndex = 0;
}
else {
if (showDetailsStartIndex === 0) {
showDetailsStartIndex = firstNonWhitespace === 0 ? 1 : firstNonWhitespace;
}
if (showChangesStartIndex === 0) {
showChangesInStartingWhitespace = true;
showChangesStartIndex = firstNonWhitespace === 0 ? 1 : firstNonWhitespace;
}
}
break;
}
}
if (showDetails) {
// 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 === undefined) return;
const decoration = Annotations.detailsHover(logCommit || commit);
decoration.range = editor.document.validateRange(new Range(line, showDetailsStartIndex, line, endOfLineIndex));
decorationOptions.push(decoration);
if (showDetailsInStartingWhitespace && showDetailsStartIndex !== 0) {
decorationOptions.push(Annotations.withRange(decoration, 0, firstNonWhitespace));
}
}
if (showChanges) {
const decoration = await Annotations.changesHover(commit, line, this._uri, this.git);
// I have no idea why I need this protection -- but it happens
if (editor.document === undefined) return;
decoration.range = editor.document.validateRange(new Range(line, showChangesStartIndex, line, endOfLineIndex));
decorationOptions.push(decoration);
if (showChangesInStartingWhitespace && showChangesStartIndex !== 0) {
decorationOptions.push(Annotations.withRange(decoration, 0, firstNonWhitespace));
}
}
}
if (decorationOptions.length) {
editor.setDecorations(annotationDecoration, decorationOptions);
}
}
private _updateStatusBar(commit: GitCommit) {
const cfg = this._config.statusBar;
if (!cfg.enabled || this._statusBarItem === undefined) return;
this._statusBarItem.text = `$(git-commit) ${CommitFormatter.fromTemplate(cfg.format, commit, cfg.dateFormat)}`;
switch (cfg.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 Line Commit with Previous';
break;
case StatusBarCommand.DiffWithWorking:
this._statusBarItem.command = Commands.DiffLineWithWorking;
this._statusBarItem.tooltip = 'Compare Line Commit 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();
}
}

View File

@@ -1,14 +1,13 @@
'use strict';
// import { Objects } from './system';
import { ExtensionContext, extensions, languages, window, workspace } from 'vscode';
import { Objects } from './system';
import { commands, ExtensionContext, extensions, languages, Uri, window, workspace } from 'vscode';
import { AnnotationController } from './annotations/annotationController';
import { CommandContext, setCommandContext } from './commands';
import { CloseUnchangedFilesCommand, OpenChangedFilesCommand } from './commands';
import { OpenBranchInRemoteCommand, OpenCommitInRemoteCommand, OpenFileInRemoteCommand, OpenInRemoteCommand, OpenRepoInRemoteCommand } from './commands';
import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands';
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithWorkingCommand} from './commands';
import { ResetSuppressedWarningsCommand } from './commands';
import { ShowFileBlameCommand, ShowLineBlameCommand, ToggleFileBlameCommand, ToggleFileRecentChangesCommand, ToggleLineBlameCommand } from './commands';
import { ShowFileBlameCommand, ShowLineBlameCommand, ToggleFileBlameCommand, ToggleLineBlameCommand } from './commands';
import { ShowBlameHistoryCommand, ShowFileHistoryCommand } from './commands';
import { ShowLastQuickPickCommand } from './commands';
import { ShowQuickBranchHistoryCommand, ShowQuickCurrentBranchHistoryCommand, ShowQuickFileHistoryCommand } from './commands';
@@ -17,20 +16,18 @@ import { ShowQuickRepoStatusCommand, ShowQuickStashListCommand } from './command
import { StashApplyCommand, StashDeleteCommand, StashSaveCommand } from './commands';
import { ToggleCodeLensCommand } from './commands';
import { Keyboard } from './commands';
import { CodeLensLocations, IConfig, LineHighlightLocations } from './configuration';
import { ApplicationInsightsKey, ExtensionKey, QualifiedExtensionId, WorkspaceState } from './constants';
import { CurrentLineController, LineAnnotationType } from './currentLineController';
import { IConfig } from './configuration';
import { ApplicationInsightsKey, BuiltInCommands, ExtensionKey, QualifiedExtensionId, WorkspaceState } from './constants';
import { CurrentLineController } from './currentLineController';
import { GitContentProvider } from './gitContentProvider';
import { GitContextTracker, GitService } from './gitService';
import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider';
import { Logger } from './logger';
import { Messages, SuppressedKeys } from './messages';
import { Telemetry } from './telemetry';
// this method is called when your extension is activated
export async function activate(context: ExtensionContext) {
Logger.configure(context);
Messages.configure(context);
Telemetry.configure(ApplicationInsightsKey);
const gitlens = extensions.getExtension(QualifiedExtensionId)!;
@@ -64,12 +61,9 @@ export async function activate(context: ExtensionContext) {
telemetryContext['git.version'] = gitVersion;
Telemetry.setContext(telemetryContext);
await migrateSettings(context);
notifyOnUnsupportedGitVersion(context, gitVersion);
notifyOnNewGitLensVersion(context, gitlensVersion);
await context.globalState.update(WorkspaceState.GitLensVersion, gitlensVersion);
const git = new GitService(context, repoPath);
context.subscriptions.push(git);
@@ -107,9 +101,7 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new ShowFileBlameCommand(annotationController));
context.subscriptions.push(new ShowLineBlameCommand(currentLineController));
context.subscriptions.push(new ToggleFileBlameCommand(annotationController));
context.subscriptions.push(new ToggleFileRecentChangesCommand(annotationController));
context.subscriptions.push(new ToggleLineBlameCommand(currentLineController));
context.subscriptions.push(new ResetSuppressedWarningsCommand(context));
context.subscriptions.push(new ShowBlameHistoryCommand(git));
context.subscriptions.push(new ShowFileHistoryCommand(git));
context.subscriptions.push(new ShowLastQuickPickCommand());
@@ -126,157 +118,58 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new StashSaveCommand(git));
context.subscriptions.push(new ToggleCodeLensCommand(git));
// Constantly over my data cap so stop collecting initialized event
// Telemetry.trackEvent('initialized', Objects.flatten(cfg, 'config', true));
Telemetry.trackEvent('initialized', Objects.flatten(cfg, 'config', true));
}
// this method is called when your extension is deactivated
export function deactivate() { }
async function migrateSettings(context: ExtensionContext) {
const previousVersion = context.globalState.get<string>(WorkspaceState.GitLensVersion);
if (previousVersion === undefined) return;
const [major] = previousVersion.split('.');
if (parseInt(major, 10) >= 4) return;
try {
const cfg = workspace.getConfiguration(ExtensionKey);
const prevCfg = workspace.getConfiguration().get<any>(ExtensionKey)!;
if (prevCfg.blame !== undefined && prevCfg.blame.annotation !== undefined) {
switch (prevCfg.blame.annotation.activeLine) {
case 'off':
await cfg.update('blame.line.enabled', false, true);
break;
case 'hover':
await cfg.update('blame.line.annotationType', LineAnnotationType.Hover, true);
break;
}
if (prevCfg.blame.annotation.activeLineDarkColor != null) {
await cfg.update('theme.annotations.line.trailing.dark.foregroundColor', prevCfg.blame.annotation.activeLineDarkColor, true);
}
if (prevCfg.blame.annotation.activeLineLightColor != null) {
await cfg.update('theme.annotations.line.trailing.light.foregroundColor', prevCfg.blame.annotation.activeLineLightColor, true);
}
switch (prevCfg.blame.annotation.highlight) {
case 'none':
await cfg.update('blame.file.lineHighlight.enabled', false);
break;
case 'gutter':
await cfg.update('blame.file.lineHighlight.locations', [LineHighlightLocations.Gutter, LineHighlightLocations.OverviewRuler], true);
break;
case 'line':
await cfg.update('blame.file.lineHighlight.locations', [LineHighlightLocations.Line, LineHighlightLocations.OverviewRuler], true);
break;
case 'both':
}
if (prevCfg.blame.annotation.dateFormat != null) {
await cfg.update('annotations.file.gutter.dateFormat', prevCfg.blame.annotation.dateFormat, true);
await cfg.update('annotations.line.trailing.dateFormat', prevCfg.blame.annotation.dateFormat, true);
}
}
if (prevCfg.codeLens !== undefined) {
switch (prevCfg.codeLens.visibility) {
case 'ondemand':
case 'off':
await cfg.update('codeLens.enabled', false);
}
switch (prevCfg.codeLens.location) {
case 'all':
await cfg.update('codeLens.locations', [CodeLensLocations.Document, CodeLensLocations.Containers, CodeLensLocations.Blocks], true);
break;
case 'document+containers':
await cfg.update('codeLens.locations', [CodeLensLocations.Document, CodeLensLocations.Containers], true);
break;
case 'document':
await cfg.update('codeLens.locations', [CodeLensLocations.Document], true);
break;
case 'custom':
await cfg.update('codeLens.locations', [CodeLensLocations.Custom], true);
break;
}
if (prevCfg.codeLens.locationCustomSymbols != null) {
await cfg.update('codeLens.customLocationSymbols', prevCfg.codeLens.locationCustomSymbols, true);
}
}
if ((prevCfg.menus && prevCfg.menus.diff && prevCfg.menus.diff.enabled) === false) {
await cfg.update('advanced.menus', {
editorContext: {
blame: true,
copy: true,
details: true,
fileDiff: false,
history: true,
lineDiff: false,
remote: true
},
editorTitle: {
blame: true,
fileDiff: false,
history: true,
remote: true,
status: true
},
editorTitleContext: {
blame: true,
fileDiff: false,
history: true,
remote: true
},
explorerContext: {
fileDiff: false,
history: true,
remote: true
}
}, true);
}
switch (prevCfg.statusBar && prevCfg.statusBar.date) {
case 'off':
await cfg.update('statusBar.format', '${author}', true);
break;
case 'absolute':
await cfg.update('statusBar.format', '${author}, ${date}', true);
break;
}
}
catch (ex) {
Logger.error(ex, 'migrateSettings');
}
finally {
window.showInformationMessage(`GitLens v4 adds many new settings and removes a few old ones, so please review your settings to ensure they are configured properly.`);
}
}
async function notifyOnNewGitLensVersion(context: ExtensionContext, version: string) {
if (context.globalState.get(SuppressedKeys.UpdateNotice, false)) return;
if (context.globalState.get(WorkspaceState.SuppressUpdateNotice, false)) return;
const previousVersion = context.globalState.get<string>(WorkspaceState.GitLensVersion);
if (previousVersion === undefined) {
await Messages.showWelcomeMessage();
return;
if (!context.globalState.get(WorkspaceState.SuppressWelcomeNotice, false)) {
await context.globalState.update(WorkspaceState.SuppressWelcomeNotice, true);
if (previousVersion === undefined) {
const result = await window.showInformationMessage(`Thank you for choosing GitLens! GitLens is powerful, feature rich, and highly configurable, so please be sure to view the docs and tailor it to suit your needs.`, 'View Docs');
if (result === 'View Docs') {
// TODO: Reset before release
// commands.executeCommand(BuiltInCommands.Open, Uri.parse('https://marketplace.visualstudio.com/items/eamodio.gitlens'));
commands.executeCommand(BuiltInCommands.Open, Uri.parse('https://github.com/eamodio/vscode-gitlens/blob/develop/README.md'));
}
return;
}
}
const [major, minor] = version.split('.');
const [prevMajor, prevMinor] = previousVersion.split('.');
if (major === prevMajor && minor === prevMinor) return;
await context.globalState.update(WorkspaceState.GitLensVersion, version);
await Messages.showUpdateMessage(version);
if (previousVersion) {
const [major, minor] = version.split('.');
const [prevMajor, prevMinor] = previousVersion.split('.');
if (major === prevMajor && minor === prevMinor) return;
}
const result = await window.showInformationMessage(`GitLens has been updated to v${version}`, 'View Release Notes', `Don't Show Again`);
if (result === 'View Release Notes') {
// TODO: Reset before release
// commands.executeCommand(BuiltInCommands.Open, Uri.parse('https://marketplace.visualstudio.com/items/eamodio.gitlens/changelog'));
commands.executeCommand(BuiltInCommands.Open, Uri.parse('https://github.com/eamodio/vscode-gitlens/blob/develop/CHANGELOG.md'));
}
else if (result === `Don't Show Again`) {
context.globalState.update(WorkspaceState.SuppressUpdateNotice, true);
}
}
async function notifyOnUnsupportedGitVersion(context: ExtensionContext, version: string) {
if (GitService.validateGitVersion(2, 2)) return;
if (context.globalState.get(WorkspaceState.SuppressGitVersionWarning, false)) return;
// If git is less than v2.2.0
await Messages.showUnsupportedGitVersionErrorMessage(version);
if (!GitService.validateGitVersion(2, 2)) {
const result = await window.showErrorMessage(`GitLens requires a newer version of Git (>= 2.2.0) than is currently installed (${version}). Please install a more recent version of Git.`, `Don't Show Again`);
if (result === `Don't Show Again`) {
context.globalState.update(WorkspaceState.SuppressGitVersionWarning, true);
}
}
}

View File

@@ -1,7 +1,7 @@
'use strict';
import { Strings } from '../../system';
import { GitCommit } from '../models/commit';
import { GitDiffLine } from '../models/diff';
import { IGitDiffLine } from '../models/diff';
import * as moment from 'moment';
export interface ICommitFormatOptions {
@@ -134,7 +134,7 @@ export class CommitFormatter {
options = dateFormatOrOptions;
}
return Strings.interpolate(template, new CommitFormatter(commit, options));
return Strings.interpolateLazy(template, new CommitFormatter(commit, options));
}
static toHoverAnnotation(commit: GitCommit, dateFormat: string = 'MMMM Do, YYYY h:MMa'): string | string[] {
@@ -142,7 +142,7 @@ export class CommitFormatter {
return `\`${commit.shortSha}\` &nbsp; __${commit.author}__, ${moment(commit.date).fromNow()} &nbsp; _(${moment(commit.date).format(dateFormat)})_${message}`;
}
static toHoverDiff(commit: GitCommit, previous: GitDiffLine | undefined, current: GitDiffLine | undefined): string | undefined {
static toHoverDiff(commit: GitCommit, previous: IGitDiffLine | undefined, current: IGitDiffLine | undefined): string | undefined {
if (previous === undefined && current === undefined) return undefined;
const codeDiff = this._getCodeDiff(previous, current);
@@ -151,7 +151,7 @@ export class CommitFormatter {
: `\`Changes\` &nbsp; \u2014 &nbsp; \`${commit.previousShortSha}\` \u2194 \`${commit.shortSha}\`\n${codeDiff}`;
}
private static _getCodeDiff(previous: GitDiffLine | undefined, current: GitDiffLine | undefined): string {
private static _getCodeDiff(previous: IGitDiffLine | undefined, current: IGitDiffLine | undefined): string {
return `\`\`\`
- ${previous === undefined ? '' : previous.line.trim()}
+ ${current === undefined ? '' : current.line.trim()}

View File

@@ -75,7 +75,6 @@ export class GitContextTracker extends Disposable {
// this._unsubscribeToDocumentChanges();
// this.updateBlameability(false);
// 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(() => this._updateBlameability(!e.document.isDirty), 1);
}
@@ -114,15 +113,13 @@ export class GitContextTracker extends Disposable {
private async _updateContextHasRemotes(uri: GitUri | undefined) {
try {
let repoPath = this.git.repoPath;
if (uri !== undefined && this.git.isTrackable(uri)) {
repoPath = uri.repoPath || this.git.repoPath;
}
let hasRemotes = false;
if (repoPath) {
const remotes = await this.git.getRemotes(repoPath);
hasRemotes = remotes.length !== 0;
if (uri && this.git.isTrackable(uri)) {
const repoPath = uri.repoPath || this.git.repoPath;
if (repoPath) {
const remotes = await this.git.getRemotes(repoPath);
hasRemotes = remotes.length !== 0;
}
}
setCommandContext(CommandContext.HasRemotes, hasRemotes);

View File

@@ -1,26 +1,25 @@
'use strict';
import { GitAuthor, GitCommitLine } from './commit';
import { GitBlameCommit } from './blameCommit';
import { GitCommit, IGitAuthor, IGitCommitLine } from './commit';
export interface GitBlame {
export interface IGitBlame {
repoPath: string;
authors: Map<string, GitAuthor>;
commits: Map<string, GitBlameCommit>;
lines: GitCommitLine[];
authors: Map<string, IGitAuthor>;
commits: Map<string, GitCommit>;
lines: IGitCommitLine[];
}
export interface GitBlameLine {
author: GitAuthor;
commit: GitBlameCommit;
line: GitCommitLine;
export interface IGitBlameLine {
author: IGitAuthor;
commit: GitCommit;
line: IGitCommitLine;
}
export interface GitBlameLines extends GitBlame {
allLines: GitCommitLine[];
export interface IGitBlameLines extends IGitBlame {
allLines: IGitCommitLine[];
}
export interface GitBlameCommitLines {
author: GitAuthor;
commit: GitBlameCommit;
lines: GitCommitLine[];
export interface IGitBlameCommitLines {
author: IGitAuthor;
commit: GitCommit;
lines: IGitCommitLine[];
}

View File

@@ -1,20 +0,0 @@
'use strict';
import { GitCommit, GitCommitLine } from './commit';
export class GitBlameCommit extends GitCommit {
constructor(
repoPath: string,
sha: string,
fileName: string,
author: string,
date: Date,
message: string,
public lines: GitCommitLine[],
originalFileName?: string,
previousSha?: string,
previousFileName?: string
) {
super('blame', repoPath, sha, fileName, author, date, message, originalFileName, previousSha, previousFileName);
}
}

View File

@@ -3,12 +3,30 @@ import { Uri } from 'vscode';
import { Git } from '../git';
import * as path from 'path';
export interface GitAuthor {
export interface IGitAuthor {
name: string;
lineCount: number;
}
export interface GitCommitLine {
export interface IGitCommit {
type: GitCommitType;
repoPath: string;
sha: string;
fileName: string;
author?: string;
date: Date;
message: string;
lines: IGitCommitLine[];
originalFileName?: string;
previousSha?: string;
previousFileName?: string;
readonly isUncommitted: boolean;
previousUri: Uri;
uri: Uri;
}
export interface IGitCommitLine {
sha: string;
previousSha?: string;
line: number;
@@ -18,10 +36,10 @@ export interface GitCommitLine {
export type GitCommitType = 'blame' | 'branch' | 'file' | 'stash';
export class GitCommit {
export class GitCommit implements IGitCommit {
type: GitCommitType;
// lines: GitCommitLine[];
lines: IGitCommitLine[];
originalFileName?: string;
previousSha?: string;
previousFileName?: string;
@@ -36,7 +54,7 @@ export class GitCommit {
public author: string,
public date: Date,
public message: string,
// lines?: GitCommitLine[],
lines?: IGitCommitLine[],
originalFileName?: string,
previousSha?: string,
previousFileName?: string
@@ -44,7 +62,7 @@ export class GitCommit {
this.type = type;
this.fileName = this.fileName && this.fileName.replace(/, ?$/, '');
// this.lines = lines || [];
this.lines = lines || [];
this.originalFileName = originalFileName;
this.previousSha = previousSha;
this.previousFileName = previousFileName;

View File

@@ -1,45 +1,24 @@
'use strict';
import { GitDiffParser } from '../parsers/diffParser';
export interface GitDiffLine {
export interface IGitDiffLine {
line: string;
state: 'added' | 'removed' | 'unchanged';
}
export class GitDiffChunk {
export interface IGitDiffChunk {
current: (IGitDiffLine | undefined)[];
currentStart: number;
currentEnd: number;
private _chunk: string | undefined;
private _current: (GitDiffLine | undefined)[] | undefined;
private _previous: (GitDiffLine | undefined)[] | undefined;
previous: (IGitDiffLine | undefined)[];
previousStart: number;
previousEnd: number;
constructor(chunk: string, public currentPosition: { start: number, end: number }, public previousPosition: { start: number, end: number }) {
this._chunk = chunk;
}
get current(): (GitDiffLine | undefined)[] {
if (this._chunk !== undefined) {
this.parseChunk();
}
return this._current!;
}
get previous(): (GitDiffLine | undefined)[] {
if (this._chunk !== undefined) {
this.parseChunk();
}
return this._previous!;
}
private parseChunk() {
[this._current, this._previous] = GitDiffParser.parseChunk(this._chunk!);
this._chunk = undefined;
}
chunk?: string;
}
export interface GitDiff {
chunks: GitDiffChunk[];
export interface IGitDiff {
chunks: IGitDiffChunk[];
diff?: string;
}

View File

@@ -1,11 +1,11 @@
'use strict';
import { Range } from 'vscode';
import { GitAuthor } from './commit';
import { IGitAuthor } from './commit';
import { GitLogCommit } from './logCommit';
export interface GitLog {
export interface IGitLog {
repoPath: string;
authors: Map<string, GitAuthor>;
authors: Map<string, IGitAuthor>;
commits: Map<string, GitLogCommit>;
sha: string | undefined;

View File

@@ -1,6 +1,6 @@
'use strict';
import { Uri } from 'vscode';
import { GitCommit, GitCommitType } from './commit';
import { GitCommit, GitCommitType, IGitCommitLine } from './commit';
import { GitStatusFileStatus, IGitStatusFile } from './status';
import * as path from 'path';
@@ -23,11 +23,12 @@ export class GitLogCommit extends GitCommit {
message: string,
status?: GitStatusFileStatus,
fileStatuses?: IGitStatusFile[],
lines?: IGitCommitLine[],
originalFileName?: string,
previousSha?: string,
previousFileName?: string
) {
super(type, repoPath, sha, fileName, author, date, message, originalFileName, previousSha, previousFileName);
super(type, repoPath, sha, fileName, author, date, message, lines, originalFileName, previousSha, previousFileName);
this.fileNames = this.fileName;

View File

@@ -1,6 +1,5 @@
'use strict';
export * from './blame';
export * from './blameCommit';
export * from './branch';
export * from './commit';
export * from './diff';

View File

@@ -1,7 +1,7 @@
'use strict';
import { GitStashCommit } from './stashCommit';
export interface GitStash {
export interface IGitStash {
repoPath: string;
commits: Map<string, GitStashCommit>;
}

View File

@@ -1,4 +1,5 @@
'use strict';
import { IGitCommitLine } from './commit';
import { GitLogCommit } from './logCommit';
import { GitStatusFileStatus, IGitStatusFile } from './status';
@@ -13,11 +14,12 @@ export class GitStashCommit extends GitLogCommit {
message: string,
status?: GitStatusFileStatus,
fileStatuses?: IGitStatusFile[],
lines?: IGitCommitLine[],
originalFileName?: string,
previousSha?: string,
previousFileName?: string
) {
super('stash', repoPath, sha, fileName, 'You', date, message, status, fileStatuses, originalFileName, previousSha, previousFileName);
super('stash', repoPath, sha, fileName, 'You', date, message, status, fileStatuses, lines, originalFileName, previousSha, previousFileName);
}
get shortSha() {

View File

@@ -2,7 +2,7 @@
import { Uri } from 'vscode';
import * as path from 'path';
export interface GitStatus {
export interface IGitStatus {
branch: string;
repoPath: string;

View File

@@ -1,10 +1,9 @@
'use strict';
import { Strings } from '../../system';
import { Git, GitAuthor, GitBlame, GitBlameCommit, GitCommitLine } from './../git';
import { Git, GitCommit, IGitAuthor, IGitBlame, IGitCommitLine } from './../git';
import * as moment from 'moment';
import * as path from 'path';
interface BlameEntry {
interface IBlameEntry {
sha: string;
line: number;
@@ -12,9 +11,15 @@ interface BlameEntry {
lineCount: number;
author: string;
// authorEmail?: string;
authorDate?: string;
authorTimeZone?: string;
// committer?: string;
// committerEmail?: string;
// committerDate?: string;
// committerTimeZone?: string;
previousSha?: string;
previousFileName?: string;
@@ -25,26 +30,21 @@ interface BlameEntry {
export class GitBlameParser {
static parse(data: string, repoPath: string | undefined, fileName: string): GitBlame | undefined {
private static _parseEntries(data: string): IBlameEntry[] | undefined {
if (!data) return undefined;
const authors: Map<string, GitAuthor> = new Map();
const commits: Map<string, GitBlameCommit> = new Map();
const lines: GitCommitLine[] = [];
const lines = data.split('\n');
if (!lines.length) return undefined;
let relativeFileName = repoPath && fileName;
const entries: IBlameEntry[] = [];
let entry: BlameEntry | undefined = undefined;
let line: string;
let lineParts: string[];
let i = -1;
let first = true;
for (line of Strings.lines(data)) {
i++;
lineParts = line.split(' ');
if (lineParts.length < 2) continue;
let entry: IBlameEntry | undefined = undefined;
let position = -1;
while (++position < lines.length) {
const lineParts = lines[position].split(' ');
if (lineParts.length < 2) {
continue;
}
if (entry === undefined) {
entry = {
@@ -52,7 +52,7 @@ export class GitBlameParser {
originalLine: parseInt(lineParts[1], 10) - 1,
line: parseInt(lineParts[2], 10) - 1,
lineCount: parseInt(lineParts[3], 10)
} as BlameEntry;
} as IBlameEntry;
continue;
}
@@ -64,6 +64,10 @@ export class GitBlameParser {
: lineParts.slice(1).join(' ').trim();
break;
// case 'author-mail':
// entry.authorEmail = lineParts[1].trim();
// break;
case 'author-time':
entry.authorDate = lineParts[1];
break;
@@ -72,6 +76,22 @@ export class GitBlameParser {
entry.authorTimeZone = lineParts[1];
break;
// case 'committer':
// entry.committer = lineParts.slice(1).join(' ').trim();
// break;
// case 'committer-mail':
// entry.committerEmail = lineParts[1].trim();
// break;
// case 'committer-time':
// entry.committerDate = lineParts[1];
// break;
// case 'committer-tz':
// entry.committerTimeZone = lineParts[1];
// break;
case 'summary':
entry.summary = lineParts.slice(1).join(' ').trim();
break;
@@ -84,15 +104,7 @@ export class GitBlameParser {
case 'filename':
entry.fileName = lineParts.slice(1).join(' ');
if (first && repoPath === undefined) {
// Try to get the repoPath from the most recent commit
repoPath = Git.normalizePath(fileName.replace(fileName.startsWith('/') ? `/${entry.fileName}` : entry.fileName!, ''));
relativeFileName = Git.normalizePath(path.relative(repoPath, fileName));
}
first = false;
GitBlameParser._parseEntry(entry, repoPath, relativeFileName, commits, authors, lines);
entries.push(entry);
entry = undefined;
break;
@@ -101,6 +113,71 @@ export class GitBlameParser {
}
}
return entries;
}
static parse(data: string, repoPath: string | undefined, fileName: string): IGitBlame | undefined {
const entries = this._parseEntries(data);
if (!entries) return undefined;
const authors: Map<string, IGitAuthor> = new Map();
const commits: Map<string, GitCommit> = new Map();
const lines: IGitCommitLine[] = [];
let relativeFileName = repoPath && fileName;
for (let i = 0, len = entries.length; i < len; i++) {
const entry = entries[i];
if (i === 0 && repoPath === undefined) {
// Try to get the repoPath from the most recent commit
repoPath = Git.normalizePath(fileName.replace(fileName.startsWith('/') ? `/${entry.fileName}` : entry.fileName!, ''));
relativeFileName = Git.normalizePath(path.relative(repoPath, fileName));
}
let commit = commits.get(entry.sha);
if (commit === undefined) {
if (entry.author !== undefined) {
let author = authors.get(entry.author);
if (author === undefined) {
author = {
name: entry.author,
lineCount: 0
};
authors.set(entry.author, author);
}
}
commit = new GitCommit('blame', repoPath!, entry.sha, relativeFileName!, entry.author, moment(`${entry.authorDate} ${entry.authorTimeZone}`, 'X +-HHmm').toDate(), entry.summary!);
if (relativeFileName !== entry.fileName) {
commit.originalFileName = entry.fileName;
}
if (entry.previousSha) {
commit.previousSha = entry.previousSha;
commit.previousFileName = entry.previousFileName;
}
commits.set(entry.sha, commit);
}
for (let j = 0, len = entry.lineCount; j < len; j++) {
const line: IGitCommitLine = {
sha: entry.sha,
line: entry.line + j,
originalLine: entry.originalLine + j
};
if (commit.previousSha) {
line.previousSha = commit.previousSha;
}
commit.lines.push(line);
lines[line.line] = line;
}
}
commits.forEach(c => {
if (c.author === undefined) return;
@@ -110,57 +187,23 @@ export class GitBlameParser {
author.lineCount += c.lines.length;
});
const sortedAuthors = new Map([...authors.entries()].sort((a, b) => b[1].lineCount - a[1].lineCount));
const sortedAuthors: Map<string, IGitAuthor> = new Map();
// const values =
Array.from(authors.values())
.sort((a, b) => b.lineCount - a.lineCount)
.forEach(a => sortedAuthors.set(a.name, a));
// const sortedCommits: Map<string, IGitCommit> = new Map();
// Array.from(commits.values())
// .sort((a, b) => b.date.getTime() - a.date.getTime())
// .forEach(c => sortedCommits.set(c.sha, c));
return {
repoPath: repoPath,
authors: sortedAuthors,
// commits: sortedCommits,
commits: commits,
lines: lines
} as GitBlame;
}
private static _parseEntry(entry: BlameEntry, repoPath: string | undefined, fileName: string | undefined, commits: Map<string, GitBlameCommit>, authors: Map<string, GitAuthor>, lines: GitCommitLine[]) {
let commit = commits.get(entry.sha);
if (commit === undefined) {
if (entry.author !== undefined) {
let author = authors.get(entry.author);
if (author === undefined) {
author = {
name: entry.author,
lineCount: 0
};
authors.set(entry.author, author);
}
}
commit = new GitBlameCommit(repoPath!, entry.sha, fileName!, entry.author, moment(`${entry.authorDate} ${entry.authorTimeZone}`, 'X +-HHmm').toDate(), entry.summary!, []);
if (fileName !== entry.fileName) {
commit.originalFileName = entry.fileName;
}
if (entry.previousSha) {
commit.previousSha = entry.previousSha;
commit.previousFileName = entry.previousFileName;
}
commits.set(entry.sha, commit);
}
for (let i = 0, len = entry.lineCount; i < len; i++) {
const line: GitCommitLine = {
sha: entry.sha,
line: entry.line + i,
originalLine: entry.originalLine + i
};
if (commit.previousSha) {
line.previousSha = commit.previousSha;
}
commit.lines.push(line);
lines[line.line] = line;
}
} as IGitBlame;
}
}

View File

@@ -1,33 +1,62 @@
'use strict';
import { Iterables, Strings } from '../../system';
import { GitDiff, GitDiffChunk, GitDiffLine } from './../git';
import { IGitDiff, IGitDiffChunk, IGitDiffLine } from './../git';
const unifiedDiffRegex = /^@@ -([\d]+),([\d]+) [+]([\d]+),([\d]+) @@([\s\S]*?)(?=^@@)/gm;
export class GitDiffParser {
static parse(data: string, debug: boolean = false): GitDiff | undefined {
static parse(data: string, debug: boolean = false): IGitDiff | undefined {
if (!data) return undefined;
const chunks: GitDiffChunk[] = [];
const chunks: IGitDiffChunk[] = [];
let match: RegExpExecArray | null = null;
let chunk: string;
let currentStart: number;
let previousStart: number;
do {
match = unifiedDiffRegex.exec(`${data}\n@@`);
if (match == null) break;
// Stops excessive memory usage
// https://bugs.chromium.org/p/v8/issues/detail?id=2869
chunk = (' ' + match[5]).substr(1);
currentStart = parseInt(match[3], 10);
previousStart = parseInt(match[1], 10);
const previousStart = +match[1];
const currentStart = +match[3];
chunks.push(new GitDiffChunk(chunk, { start: currentStart, end: currentStart + parseInt(match[4], 10) }, { start: previousStart, end: previousStart + parseInt(match[2], 10) }));
const chunk = match[5];
const lines = chunk.split('\n').slice(1);
const current: (IGitDiffLine | undefined)[] = [];
const previous: (IGitDiffLine | undefined)[] = [];
for (const l of lines) {
switch (l[0]) {
case '+':
current.push({
line: ` ${l.substring(1)}`,
state: 'added'
});
previous.push(undefined);
break;
case '-':
current.push(undefined);
previous.push({
line: ` ${l.substring(1)}`,
state: 'removed'
});
break;
default:
current.push({ line: l, state: 'unchanged' });
previous.push({ line: l, state: 'unchanged' });
break;
}
}
chunks.push({
chunk: debug ? chunk : undefined,
current: current,
currentStart: currentStart,
currentEnd: currentStart + +match[4],
previous: previous,
previousStart: previousStart,
previousEnd: previousStart + +match[2]
});
} while (match != null);
if (!chunks.length) return undefined;
@@ -35,40 +64,7 @@ export class GitDiffParser {
const diff = {
diff: debug ? data : undefined,
chunks: chunks
} as GitDiff;
} as IGitDiff;
return diff;
}
static parseChunk(chunk: string): [(GitDiffLine | undefined)[], (GitDiffLine | undefined)[]] {
const lines = Iterables.skip(Strings.lines(chunk), 1);
const current: (GitDiffLine | undefined)[] = [];
const previous: (GitDiffLine | undefined)[] = [];
for (const l of lines) {
switch (l[0]) {
case '+':
current.push({
line: ` ${l.substring(1)}`,
state: 'added'
});
previous.push(undefined);
break;
case '-':
current.push(undefined);
previous.push({
line: ` ${l.substring(1)}`,
state: 'removed'
});
break;
default:
current.push({ line: l, state: 'unchanged' });
previous.push({ line: l, state: 'unchanged' });
break;
}
}
return [current, previous];
}
}

View File

@@ -1,17 +1,19 @@
'use strict';
import { Strings } from '../../system';
import { Range } from 'vscode';
import { Git, GitAuthor, GitCommitType, GitLog, GitLogCommit, GitStatusFileStatus, IGitStatusFile } from './../git';
import { Git, GitCommitType, GitLogCommit, GitStatusFileStatus, IGitAuthor, IGitLog, IGitStatusFile } from './../git';
// import { Logger } from '../../logger';
import * as moment from 'moment';
import * as path from 'path';
interface LogEntry {
interface ILogEntry {
sha: string;
author: string;
authorDate?: string;
// committer?: string;
// committerDate?: string;
parentShas?: string[];
fileName?: string;
@@ -27,54 +29,31 @@ const diffRegex = /diff --git a\/(.*) b\/(.*)/;
export class GitLogParser {
static parse(data: string, type: GitCommitType, repoPath: string | undefined, fileName: string | undefined, sha: string | undefined, maxCount: number | undefined, reverse: boolean, range: Range | undefined): GitLog | undefined {
private static _parseEntries(data: string, type: GitCommitType, maxCount: number | undefined, reverse: boolean): ILogEntry[] | undefined {
if (!data) return undefined;
const authors: Map<string, GitAuthor> = new Map();
const commits: Map<string, GitLogCommit> = new Map();
const lines = data.split('\n');
if (!lines.length) return undefined;
let relativeFileName: string;
let recentCommit: GitLogCommit | undefined = undefined;
if (repoPath !== undefined) {
repoPath = Git.normalizePath(repoPath);
}
let entry: LogEntry | undefined = undefined;
let line: string | undefined = undefined;
let lineParts: string[];
let next: IteratorResult<string> | undefined = undefined;
let i = -1;
let first = true;
let skip = false;
const lines = Strings.lines(data);
// for (line of lines) {
while (true) {
if (!skip) {
next = lines.next();
if (next.done) break;
line = next.value;
i++;
}
else {
skip = false;
}
const entries: ILogEntry[] = [];
let entry: ILogEntry | undefined = undefined;
let position = -1;
while (++position < lines.length) {
// Since log --reverse doesn't properly honor a max count -- enforce it here
if (reverse && maxCount && (i >= maxCount)) break;
if (reverse && maxCount && (entries.length >= maxCount)) break;
lineParts = line!.split(' ');
if (lineParts.length < 2) continue;
let lineParts = lines[position].split(' ');
if (lineParts.length < 2) {
continue;
}
if (entry === undefined) {
if (!Git.shaRegex.test(lineParts[0])) continue;
entry = {
sha: lineParts[0]
} as LogEntry;
} as ILogEntry;
continue;
}
@@ -90,54 +69,47 @@ export class GitLogParser {
entry.authorDate = `${lineParts[1]}T${lineParts[2]}${lineParts[3]}`;
break;
// case 'committer':
// entry.committer = lineParts.slice(1).join(' ').trim();
// break;
// case 'committer-date':
// entry.committerDate = lineParts.slice(1).join(' ').trim();
// break;
case 'parents':
entry.parentShas = lineParts.slice(1);
break;
case 'summary':
entry.summary = lineParts.slice(1).join(' ').trim();
while (true) {
next = lines.next();
if (next.done) break;
i++;
line = next.value;
if (!line) break;
if (line === 'filename ?') {
skip = true;
while (++position < lines.length) {
const next = lines[position];
if (!next) break;
if (next === 'filename ?') {
position--;
break;
}
entry.summary += `\n${line}`;
entry.summary += `\n${lines[position]}`;
}
break;
case 'filename':
if (type === 'branch') {
next = lines.next();
if (next.done) break;
i++;
line = next.value;
const nextLine = lines[position + 1];
// If the next line isn't blank, make sure it isn't starting a new commit
if (line && Git.shaRegex.test(line)) {
skip = true;
continue;
}
if (nextLine && Git.shaRegex.test(nextLine)) continue;
position++;
let diff = false;
while (true) {
next = lines.next();
if (next.done) break;
i++;
line = next.value;
while (++position < lines.length) {
const line = lines[position];
lineParts = line.split(' ');
if (Git.shaRegex.test(lineParts[0])) {
skip = true;
position--;
break;
}
@@ -175,88 +147,125 @@ export class GitLogParser {
}
}
else {
next = lines.next();
next = lines.next();
i += 2;
line = next.value;
position += 2;
const line = lines[position];
entry.status = line[0] as GitStatusFileStatus;
entry.fileName = line.substring(1);
this._parseFileName(entry);
}
if (first && repoPath === undefined && type === 'file' && fileName !== undefined) {
// Try to get the repoPath from the most recent commit
repoPath = Git.normalizePath(fileName.replace(fileName.startsWith('/') ? `/${entry.fileName}` : entry.fileName!, ''));
relativeFileName = Git.normalizePath(path.relative(repoPath, fileName));
}
else {
relativeFileName = entry.fileName!;
}
first = false;
recentCommit = GitLogParser._parseEntry(entry, type, repoPath, relativeFileName, commits, authors, recentCommit);
entries.push(entry);
entry = undefined;
break;
default:
break;
}
}
return entries;
}
static parse(data: string, type: GitCommitType, repoPath: string | undefined, fileName: string | undefined, sha: string | undefined, maxCount: number | undefined, reverse: boolean, range: Range | undefined): IGitLog | undefined {
const entries = this._parseEntries(data, type, maxCount, reverse);
if (!entries) return undefined;
const authors: Map<string, IGitAuthor> = new Map();
const commits: Map<string, GitLogCommit> = new Map();
let relativeFileName: string;
let recentCommit: GitLogCommit | undefined = undefined;
if (repoPath !== undefined) {
repoPath = Git.normalizePath(repoPath);
}
for (let i = 0, len = entries.length; i < len; i++) {
// Since log --reverse doesn't properly honor a max count -- enforce it here
if (reverse && maxCount && (i >= maxCount)) break;
const entry = entries[i];
if (i === 0 && repoPath === undefined && type === 'file' && fileName !== undefined) {
// Try to get the repoPath from the most recent commit
repoPath = Git.normalizePath(fileName.replace(fileName.startsWith('/') ? `/${entry.fileName}` : entry.fileName!, ''));
relativeFileName = Git.normalizePath(path.relative(repoPath, fileName));
}
else {
relativeFileName = entry.fileName!;
}
if (next!.done) break;
let commit = commits.get(entry.sha);
if (commit === undefined) {
if (entry.author !== undefined) {
let author = authors.get(entry.author);
if (author === undefined) {
author = {
name: entry.author,
lineCount: 0
};
authors.set(entry.author, author);
}
}
commit = new GitLogCommit(type, repoPath!, entry.sha, relativeFileName, entry.author, moment(entry.authorDate).toDate(), entry.summary!, entry.status, entry.fileStatuses, undefined, entry.originalFileName);
commit.parentShas = entry.parentShas!;
if (relativeFileName !== entry.fileName) {
commit.originalFileName = entry.fileName;
}
commits.set(entry.sha, commit);
}
// else {
// Logger.log(`merge commit? ${entry.sha}`);
// }
if (recentCommit !== undefined) {
recentCommit.previousSha = commit.sha;
// If the commit sha's match (merge commit), just forward it along
commit.nextSha = commit.sha !== recentCommit.sha ? recentCommit.sha : recentCommit.nextSha;
// Only add a filename if this is a file log
if (type === 'file') {
recentCommit.previousFileName = commit.originalFileName || commit.fileName;
commit.nextFileName = recentCommit.originalFileName || recentCommit.fileName;
}
}
recentCommit = commit;
}
commits.forEach(c => {
if (c.author === undefined) return;
const author = authors.get(c.author);
if (author === undefined) return;
author.lineCount += c.lines.length;
});
const sortedAuthors: Map<string, IGitAuthor> = new Map();
// const values =
Array.from(authors.values())
.sort((a, b) => b.lineCount - a.lineCount)
.forEach(a => sortedAuthors.set(a.name, a));
// const sortedCommits: Map<string, IGitCommit> = new Map();
// Array.from(commits.values())
// .sort((a, b) => b.date.getTime() - a.date.getTime())
// .forEach(c => sortedCommits.set(c.sha, c));
return {
repoPath: repoPath,
authors: authors,
authors: sortedAuthors,
// commits: sortedCommits,
commits: commits,
sha: sha,
maxCount: maxCount,
range: range,
truncated: !!(maxCount && i >= maxCount)
} as GitLog;
}
private static _parseEntry(entry: LogEntry, type: GitCommitType, repoPath: string | undefined, relativeFileName: string, commits: Map<string, GitLogCommit>, authors: Map<string, GitAuthor>, recentCommit: GitLogCommit | undefined): GitLogCommit | undefined {
let commit = commits.get(entry.sha);
if (commit === undefined) {
if (entry.author !== undefined) {
let author = authors.get(entry.author);
if (author === undefined) {
author = {
name: entry.author,
lineCount: 0
};
authors.set(entry.author, author);
}
}
commit = new GitLogCommit(type, repoPath!, entry.sha, relativeFileName, entry.author, moment(entry.authorDate).toDate(), entry.summary!, entry.status, entry.fileStatuses, undefined, entry.originalFileName);
commit.parentShas = entry.parentShas!;
if (relativeFileName !== entry.fileName) {
commit.originalFileName = entry.fileName;
}
commits.set(entry.sha, commit);
}
// else {
// Logger.log(`merge commit? ${entry.sha}`);
// }
if (recentCommit !== undefined) {
recentCommit.previousSha = commit.sha;
// If the commit sha's match (merge commit), just forward it along
commit.nextSha = commit.sha !== recentCommit.sha ? recentCommit.sha : recentCommit.nextSha;
// Only add a filename if this is a file log
if (type === 'file') {
recentCommit.previousFileName = commit.originalFileName || commit.fileName;
commit.nextFileName = recentCommit.originalFileName || recentCommit.fileName;
}
}
return commit;
truncated: !!(maxCount && entries.length >= maxCount)
} as IGitLog;
}
private static _parseFileName(entry: { fileName?: string, originalFileName?: string }) {

View File

@@ -1,9 +1,9 @@
'use strict';
import { Git, GitStash, GitStashCommit, GitStatusFileStatus, IGitStatusFile } from './../git';
import { Git, GitStashCommit, GitStatusFileStatus, IGitStash, IGitStatusFile } from './../git';
// import { Logger } from '../../logger';
import * as moment from 'moment';
interface StashEntry {
interface IStashEntry {
sha: string;
date?: string;
fileNames: string;
@@ -14,15 +14,15 @@ interface StashEntry {
export class GitStashParser {
private static _parseEntries(data: string): StashEntry[] | undefined {
private static _parseEntries(data: string): IStashEntry[] | undefined {
if (!data) return undefined;
const lines = data.split('\n');
if (!lines.length) return undefined;
const entries: StashEntry[] = [];
const entries: IStashEntry[] = [];
let entry: StashEntry | undefined = undefined;
let entry: IStashEntry | undefined = undefined;
let position = -1;
while (++position < lines.length) {
let lineParts = lines[position].split(' ');
@@ -35,7 +35,7 @@ export class GitStashParser {
entry = {
sha: lineParts[0]
} as StashEntry;
} as IStashEntry;
continue;
}
@@ -109,7 +109,7 @@ export class GitStashParser {
return entries;
}
static parse(data: string, repoPath: string): GitStash | undefined {
static parse(data: string, repoPath: string): IGitStash | undefined {
const entries = this._parseEntries(data);
if (entries === undefined) return undefined;
@@ -128,7 +128,7 @@ export class GitStashParser {
return {
repoPath: repoPath,
commits: commits
} as GitStash;
} as IGitStash;
}
private static _parseFileName(entry: { fileName?: string, originalFileName?: string }) {

View File

@@ -1,7 +1,7 @@
'use strict';
import { Git, GitStatus, GitStatusFile, GitStatusFileStatus } from './../git';
import { Git, GitStatusFile, GitStatusFileStatus, IGitStatus } from './../git';
interface FileStatusEntry {
interface IFileStatusEntry {
staged: boolean;
status: GitStatusFileStatus;
fileName: string;
@@ -13,7 +13,7 @@ const behindStatusV1Regex = /(?:behind ([0-9]+))/;
export class GitStatusParser {
static parse(data: string, repoPath: string, porcelainVersion: number): GitStatus | undefined {
static parse(data: string, repoPath: string, porcelainVersion: number): IGitStatus | undefined {
if (!data) return undefined;
const lines = data.split('\n').filter(_ => !!_);
@@ -40,7 +40,7 @@ export class GitStatusParser {
return status;
}
private static _parseV1(lines: string[], repoPath: string, status: GitStatus) {
private static _parseV1(lines: string[], repoPath: string, status: IGitStatus) {
let position = -1;
while (++position < lines.length) {
const line = lines[position];
@@ -59,7 +59,7 @@ export class GitStatusParser {
}
}
else {
let entry: FileStatusEntry;
let entry: IFileStatusEntry;
const rawStatus = line.substring(0, 2);
const fileName = line.substring(3);
if (rawStatus[0] === 'R') {
@@ -74,7 +74,7 @@ export class GitStatusParser {
}
}
private static _parseV2(lines: string[], repoPath: string, status: GitStatus) {
private static _parseV2(lines: string[], repoPath: string, status: IGitStatus) {
let position = -1;
while (++position < lines.length) {
const line = lines[position];
@@ -99,7 +99,7 @@ export class GitStatusParser {
}
else {
const lineParts = line.split(' ');
let entry: FileStatusEntry | undefined = undefined;
let entry: IFileStatusEntry | undefined = undefined;
switch (lineParts[0][0]) {
case '1': // normal
entry = this._parseFileEntry(lineParts[1], lineParts.slice(8).join(' '));
@@ -123,7 +123,7 @@ export class GitStatusParser {
}
}
private static _parseFileEntry(rawStatus: string, fileName: string, originalFileName?: string): FileStatusEntry {
private static _parseFileEntry(rawStatus: string, fileName: string, originalFileName?: string): IFileStatusEntry {
const indexStatus = rawStatus[0] !== '.' ? rawStatus[0].trim() : undefined;
const workTreeStatus = rawStatus[1] !== '.' ? rawStatus[1].trim() : undefined;
@@ -132,6 +132,6 @@ export class GitStatusParser {
fileName: fileName,
originalFileName: originalFileName,
staged: !!indexStatus
} as FileStatusEntry;
} as IFileStatusEntry;
}
}

View File

@@ -15,7 +15,7 @@ const providerMap = new Map<string, (domain: string, path: string) => RemoteProv
['visualstudio.com', (domain: string, path: string) => new VisualStudioService(domain, path)]
]);
const UrlRegex = /^(?:git:\/\/(.*?)\/|https:\/\/(.*?)\/|http:\/\/(.*?)\/|git@(.*):|ssh:\/\/(?:.*@)?(.*?)(?::.*?)?\/)(.*)$/;
const UrlRegex = /^(?:git:\/\/(.*?)\/|https:\/\/(.*?)\/|http:\/\/(.*?)\/|git@(.*):\/\/|ssh:\/\/git@(.*?)\/)(.*)$/;
export class RemoteProviderFactory {
@@ -25,7 +25,7 @@ export class RemoteProviderFactory {
if (match == null) return undefined;
const domain = match[1] || match[2] || match[3] || match[4] || match[5];
const path = match[6].replace(/\.git\/?$/, '');
const path = match[6].replace(/\.git/, '');
const key = domain.toLowerCase().endsWith('visualstudio.com')
? 'visualstudio.com'

View File

@@ -1,31 +1,31 @@
'use strict';
import { Functions, Iterables } from './system';
import { Functions, Iterables, Strings } from './system';
import { CancellationToken, CodeLens, CodeLensProvider, Command, commands, DocumentSelector, Event, EventEmitter, ExtensionContext, Position, Range, SymbolInformation, SymbolKind, TextDocument, Uri, workspace } from 'vscode';
import { Commands, DiffWithPreviousCommandArgs, ShowBlameHistoryCommandArgs, ShowFileHistoryCommandArgs, ShowQuickCommitDetailsCommandArgs, ShowQuickCommitFileDetailsCommandArgs, ShowQuickFileHistoryCommandArgs } from './commands';
import { BuiltInCommands, DocumentSchemes, ExtensionKey } from './constants';
import { CodeLensCommand, CodeLensLocations, ICodeLensLanguageLocation, IConfig } from './configuration';
import { GitBlame, GitBlameCommit, GitBlameLines, GitService, GitUri } from './gitService';
import { GitCommit, GitService, GitUri, IGitBlame, IGitBlameLines } from './gitService';
import { Logger } from './logger';
import * as moment from 'moment';
export class GitRecentChangeCodeLens extends CodeLens {
constructor(private blame: () => GitBlameLines | undefined, public uri: GitUri, public symbolKind: SymbolKind, public blameRange: Range, public isFullRange: boolean, range: Range) {
constructor(private blame: () => IGitBlameLines | undefined, public uri: GitUri, public symbolKind: SymbolKind, public blameRange: Range, public isFullRange: boolean, range: Range) {
super(range);
}
getBlame(): GitBlameLines | undefined {
getBlame(): IGitBlameLines | undefined {
return this.blame();
}
}
export class GitAuthorsCodeLens extends CodeLens {
constructor(private blame: () => GitBlameLines | undefined, public uri: GitUri, public symbolKind: SymbolKind, public blameRange: Range, public isFullRange: boolean, range: Range) {
constructor(private blame: () => IGitBlameLines | undefined, public uri: GitUri, public symbolKind: SymbolKind, public blameRange: Range, public isFullRange: boolean, range: Range) {
super(range);
}
getBlame(): GitBlameLines | undefined {
getBlame(): IGitBlameLines | undefined {
return this.blame();
}
}
@@ -70,7 +70,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
const gitUri = await GitUri.fromUri(document.uri, this.git);
const blamePromise = this.git.getBlameForFile(gitUri);
let blame: GitBlame | undefined;
let blame: IGitBlame | undefined;
if (languageLocations.locations.length === 1 && languageLocations.locations.includes(CodeLensLocations.Document)) {
blame = await blamePromise;
if (blame === undefined || !blame.lines.length) return lenses;
@@ -81,7 +81,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
commands.executeCommand(BuiltInCommands.ExecuteDocumentSymbolProvider, document.uri) as Promise<any>
]);
blame = values[0] as GitBlame;
blame = values[0] as IGitBlame;
if (blame === undefined || !blame.lines.length) return lenses;
const symbols = values[1] as SymbolInformation[];
@@ -94,7 +94,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
// Check if we have a lens for the whole document -- if not add one
if (!lenses.find(l => l.range.start.line === 0 && l.range.end.line === 0)) {
const blameRange = document.validateRange(new Range(0, 1000000, 1000000, 1000000));
let blameForRangeFn: (() => GitBlameLines | undefined) | undefined = undefined;
let blameForRangeFn: (() => IGitBlameLines | undefined) | undefined = undefined;
if (this._documentIsDirty || this._config.codeLens.recentChange.enabled) {
blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame!, gitUri, blameRange));
lenses.push(new GitRecentChangeCodeLens(blameForRangeFn, gitUri, SymbolKind.File, blameRange, true, new Range(0, 0, 0, blameRange.start.character)));
@@ -176,7 +176,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
return valid ? range || symbol.location.range : undefined;
}
private _provideCodeLens(gitUri: GitUri, document: TextDocument, symbol: SymbolInformation, languageLocation: ICodeLensLanguageLocation, blame: GitBlame, lenses: CodeLens[]): void {
private _provideCodeLens(gitUri: GitUri, document: TextDocument, symbol: SymbolInformation, languageLocation: ICodeLensLanguageLocation, blame: IGitBlame, lenses: CodeLens[]): void {
const blameRange = this._validateSymbolAndGetBlameRange(document, symbol, languageLocation);
if (!blameRange) return;
@@ -184,10 +184,19 @@ export class GitCodeLensProvider implements CodeLensProvider {
// Make sure there is only 1 lens per line
if (lenses.length && lenses[lenses.length - 1].range.start.line === line.lineNumber) return;
// Anchor the code lens to the end of the line -- so they are somewhat consistenly placed
let startChar = line.range.end.character - 1;
let startChar = -1;
try {
startChar = line.text.search(`\\b${Strings.escapeRegExp(symbol.name)}\\b`);
}
catch (ex) { }
if (startChar === -1) {
startChar = line.firstNonWhitespaceCharacterIndex;
}
else {
startChar += Math.floor(symbol.name.length / 2);
}
let blameForRangeFn: (() => GitBlameLines | undefined) | undefined = undefined;
let blameForRangeFn: (() => IGitBlameLines | undefined) | undefined = undefined;
if (this._documentIsDirty || this._config.codeLens.recentChange.enabled) {
blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame, gitUri, blameRange));
lenses.push(new GitRecentChangeCodeLens(blameForRangeFn, gitUri, symbol.kind, blameRange, false, line.range.with(new Position(line.range.start.line, startChar))));
@@ -237,13 +246,13 @@ export class GitCodeLensProvider implements CodeLensProvider {
let title: string;
if (this._documentIsDirty) {
if (this._config.codeLens.recentChange.enabled && this._config.codeLens.authors.enabled) {
title = this._config.strings.codeLens.unsavedChanges.recentChangeAndAuthors;
title = 'Cannot determine recent change or authors (unsaved changes)';
}
else if (this._config.codeLens.recentChange.enabled) {
title = this._config.strings.codeLens.unsavedChanges.recentChangeOnly;
title = 'Cannot determine recent change (unsaved changes)';
}
else {
title = this._config.strings.codeLens.unsavedChanges.authorsOnly;
title = 'Cannot determine authors (unsaved changes)';
}
lens.command = { title: title } as Command;
@@ -256,7 +265,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
const recentCommit = Iterables.first(blame.commits.values());
title = `${recentCommit.author}, ${moment(recentCommit.date).fromNow()}`;
if (this._config.codeLens.debug) {
title += ` [${SymbolKind[lens.symbolKind]}(${lens.range.start.character}-${lens.range.end.character}), Lines (${lens.blameRange.start.line + 1}-${lens.blameRange.end.line + 1}), Commit (${recentCommit.shortSha})]`;
title += ` [${SymbolKind[lens.symbolKind]}(${lens.blameRange.start.line + 1}-${lens.blameRange.end.line + 1}), Commit (${recentCommit.shortSha})]`;
}
switch (this._config.codeLens.recentChange.command) {
@@ -279,7 +288,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
const count = blame.authors.size;
let title = `${count} ${count > 1 ? 'authors' : 'author'} (${Iterables.first(blame.authors.values()).name}${count > 1 ? ' and others' : ''})`;
if (this._config.codeLens.debug) {
title += ` [${SymbolKind[lens.symbolKind]}(${lens.range.start.character}-${lens.range.end.character}), Lines (${lens.blameRange.start.line + 1}-${lens.blameRange.end.line + 1}), Authors (${Iterables.join(Iterables.map(blame.authors.values(), _ => _.name), ', ')})]`;
title += ` [${SymbolKind[lens.symbolKind]}(${lens.blameRange.start.line + 1}-${lens.blameRange.end.line + 1}), Authors (${Iterables.join(Iterables.map(blame.authors.values(), _ => _.name), ', ')})]`;
}
switch (this._config.codeLens.authors.command) {
@@ -295,7 +304,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
}
}
_applyBlameAnnotateCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: GitBlameLines): T {
_applyBlameAnnotateCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines): T {
lens.command = {
title: title,
command: Commands.ToggleFileBlame,
@@ -304,7 +313,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
return lens;
}
_applyShowBlameHistoryCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: GitBlameLines, commit?: GitBlameCommit): T {
_applyShowBlameHistoryCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit): T {
let line = lens.range.start.line;
if (commit) {
const blameLine = commit.lines.find(_ => _.line === line);
@@ -330,7 +339,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
return lens;
}
_applyShowFileHistoryCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: GitBlameLines, commit?: GitBlameCommit): T {
_applyShowFileHistoryCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit): T {
let line = lens.range.start.line;
if (commit) {
const blameLine = commit.lines.find(_ => _.line === line);
@@ -355,7 +364,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
return lens;
}
_applyDiffWithPreviousCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: GitBlameLines, commit?: GitBlameCommit): T {
_applyDiffWithPreviousCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit): T {
if (commit === undefined) {
const blameLine = blame.allLines[lens.range.start.line];
commit = blame.commits.get(blameLine.sha);
@@ -375,7 +384,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
return lens;
}
_applyShowQuickCommitDetailsCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: GitBlameLines, commit?: GitBlameCommit): T {
_applyShowQuickCommitDetailsCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit): T {
lens.command = {
title: title,
command: commit !== undefined && commit.isUncommitted ? '' : CodeLensCommand.ShowQuickCommitDetails,
@@ -389,7 +398,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
return lens;
}
_applyShowQuickCommitFileDetailsCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: GitBlameLines, commit?: GitBlameCommit): T {
_applyShowQuickCommitFileDetailsCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit): T {
lens.command = {
title: title,
command: commit !== undefined && commit.isUncommitted ? '' : CodeLensCommand.ShowQuickCommitFileDetails,
@@ -403,7 +412,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
return lens;
}
_applyShowQuickFileHistoryCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: GitBlameLines, commit?: GitBlameCommit): T {
_applyShowQuickFileHistoryCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit): T {
lens.command = {
title: title,
command: CodeLensCommand.ShowQuickFileHistory,
@@ -417,7 +426,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
return lens;
}
_applyShowQuickBranchHistoryCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: GitBlameLines, commit?: GitBlameCommit): T {
_applyShowQuickBranchHistoryCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit): T {
lens.command = {
title: title,
command: CodeLensCommand.ShowQuickCurrentBranchHistory,

View File

@@ -4,7 +4,7 @@ import { Disposable, Event, EventEmitter, ExtensionContext, FileSystemWatcher, l
import { CommandContext, setCommandContext } from './commands';
import { IConfig } from './configuration';
import { DocumentSchemes, ExtensionKey } from './constants';
import { Git, GitAuthor, GitBlame, GitBlameCommit, GitBlameLine, GitBlameLines, GitBlameParser, GitBranch, GitCommit, GitDiff, GitDiffLine, GitDiffParser, GitLog, GitLogCommit, GitLogParser, GitRemote, GitStash, GitStashParser, GitStatus, GitStatusFile, GitStatusParser, IGit, setDefaultEncoding } from './git/git';
import { Git, GitBlameParser, GitBranch, GitCommit, GitDiffParser, GitLogCommit, GitLogParser, GitRemote, GitStashParser, GitStatusFile, GitStatusParser, IGit, IGitAuthor, IGitBlame, IGitBlameLine, IGitBlameLines, IGitDiff, IGitDiffLine, IGitLog, IGitStash, IGitStatus, setDefaultEncoding } from './git/git';
import { GitUri, IGitCommitInfo, IGitUriData } from './git/gitUri';
import { GitCodeLensProvider } from './gitCodeLensProvider';
import { Logger } from './logger';
@@ -26,7 +26,7 @@ class UriCacheEntry {
class GitCacheEntry {
private cache: Map<string, CachedBlame | CachedDiff | CachedLog> = new Map();
private cache: Map<string, ICachedBlame | ICachedDiff | ICachedLog> = new Map();
constructor(public key: string) { }
@@ -34,23 +34,23 @@ class GitCacheEntry {
return Iterables.every(this.cache.values(), _ => _.errorMessage !== undefined);
}
get<T extends CachedBlame | CachedDiff | CachedLog>(key: string): T | undefined {
get<T extends ICachedBlame | ICachedDiff | ICachedLog>(key: string): T | undefined {
return this.cache.get(key) as T;
}
set<T extends CachedBlame | CachedDiff | CachedLog>(key: string, value: T) {
set<T extends ICachedBlame | ICachedDiff | ICachedLog>(key: string, value: T) {
this.cache.set(key, value);
}
}
interface CachedItem<T> {
interface ICachedItem<T> {
item: Promise<T>;
errorMessage?: string;
}
interface CachedBlame extends CachedItem<GitBlame> { }
interface CachedDiff extends CachedItem<GitDiff> { }
interface CachedLog extends CachedItem<GitLog> { }
interface ICachedBlame extends ICachedItem<IGitBlame> { }
interface ICachedDiff extends ICachedItem<IGitDiff> { }
interface ICachedLog extends ICachedItem<IGitLog> { }
enum RemoveCacheReason {
DocumentClosed,
@@ -89,7 +89,7 @@ export class GitService extends Disposable {
private _fsWatcher: FileSystemWatcher | undefined;
private _gitignore: Promise<ignore.Ignore>;
static EmptyPromise: Promise<GitBlame | GitDiff | GitLog | undefined> = Promise.resolve(undefined);
static EmptyPromise: Promise<IGitBlame | IGitDiff | IGitLog | undefined> = Promise.resolve(undefined);
constructor(private context: ExtensionContext, public repoPath: string) {
super(() => this.dispose());
@@ -214,7 +214,6 @@ export class GitService extends Disposable {
if (!this.UseCaching) return;
if (e.document.uri.scheme !== DocumentSchemes.File) return;
// 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, we'll just wait for the save before clearing our cache
@@ -287,18 +286,18 @@ export class GitService extends Disposable {
if (sha === undefined) {
// Get the most recent commit for this file name
const c = await this.getLogCommit(repoPath, fileName);
if (c === undefined) return undefined;
if (!c) return undefined;
sha = c.sha;
}
// Get the full commit (so we can see if there are any matching renames in the file statuses)
const log = await this.getLogForRepo(repoPath, sha, 1);
if (log === undefined) return undefined;
if (!log) return undefined;
const c = Iterables.first(log.commits.values());
const status = c.fileStatuses.find(_ => _.originalFileName === fileName);
if (status === undefined) return undefined;
if (!status) return undefined;
return status.fileName;
}
@@ -338,7 +337,7 @@ export class GitService extends Disposable {
return !entry.hasErrors;
}
async getBlameForFile(uri: GitUri): Promise<GitBlame | undefined> {
async getBlameForFile(uri: GitUri): Promise<IGitBlame | undefined> {
let key = 'blame';
if (uri.sha !== undefined) {
key += `:${uri.sha}`;
@@ -350,7 +349,7 @@ export class GitService extends Disposable {
entry = this._gitCache.get(cacheKey);
if (entry !== undefined) {
const cachedBlame = entry.get<CachedBlame>(key);
const cachedBlame = entry.get<ICachedBlame>(key);
if (cachedBlame !== undefined) {
Logger.log(`Cached(${key}): getBlameForFile('${uri.repoPath}', '${uri.fsPath}', ${uri.sha})`);
return cachedBlame.item;
@@ -373,15 +372,15 @@ export class GitService extends Disposable {
if (entry) {
Logger.log(`Add blame cache for '${entry.key}:${key}'`);
entry.set<CachedBlame>(key, {
entry.set<ICachedBlame>(key, {
item: promise
} as CachedBlame);
} as ICachedBlame);
}
return promise;
}
private async _getBlameForFile(uri: GitUri, entry: GitCacheEntry | undefined, key: string): Promise<GitBlame | undefined> {
private async _getBlameForFile(uri: GitUri, entry: GitCacheEntry | undefined, key: string): Promise<IGitBlame | undefined> {
const [file, root] = Git.splitPath(uri.fsPath, uri.repoPath, false);
const ignore = await this._gitignore;
@@ -390,13 +389,12 @@ export class GitService extends Disposable {
if (entry && entry.key) {
this._onDidBlameFail.fire(entry.key);
}
return await GitService.EmptyPromise as GitBlame;
return await GitService.EmptyPromise as IGitBlame;
}
try {
const data = await Git.blame(root, file, uri.sha);
const blame = GitBlameParser.parse(data, root, file);
return blame;
return GitBlameParser.parse(data, root, file);
}
catch (ex) {
// Trap and cache expected blame errors
@@ -404,31 +402,28 @@ export class GitService extends Disposable {
const msg = ex && ex.toString();
Logger.log(`Replace blame cache with empty promise for '${entry.key}:${key}'`);
entry.set<CachedBlame>(key, {
entry.set<ICachedBlame>(key, {
item: GitService.EmptyPromise,
errorMessage: msg
} as CachedBlame);
} as ICachedBlame);
this._onDidBlameFail.fire(entry.key);
return await GitService.EmptyPromise as GitBlame;
return await GitService.EmptyPromise as IGitBlame;
}
return undefined;
}
}
async getBlameForLine(uri: GitUri, line: number): Promise<GitBlameLine | undefined> {
async getBlameForLine(uri: GitUri, line: number): Promise<IGitBlameLine | undefined> {
Logger.log(`getBlameForLine('${uri.repoPath}', '${uri.fsPath}', ${line}, ${uri.sha})`);
if (this.UseCaching) {
const blame = await this.getBlameForFile(uri);
if (blame === undefined) return undefined;
let blameLine = blame.lines[line];
if (blameLine === undefined) {
if (blame.lines.length !== line) return undefined;
blameLine = blame.lines[line - 1];
}
const blameLine = blame.lines[line];
if (blameLine === undefined) return undefined;
const commit = blame.commits.get(blameLine.sha);
if (commit === undefined) return undefined;
@@ -437,7 +432,7 @@ export class GitService extends Disposable {
author: Object.assign({}, blame.authors.get(commit.author), { lineCount: commit.lines.length }),
commit: commit,
line: blameLine
} as GitBlameLine;
} as IGitBlameLine;
}
const fileName = uri.fsPath;
@@ -445,7 +440,7 @@ export class GitService extends Disposable {
try {
const data = await Git.blame(uri.repoPath, fileName, uri.sha, line + 1, line + 1);
const blame = GitBlameParser.parse(data, uri.repoPath, fileName);
if (blame === undefined) return undefined;
if (!blame) return undefined;
const commit = Iterables.first(blame.commits.values());
if (uri.repoPath) {
@@ -455,40 +450,41 @@ export class GitService extends Disposable {
author: Iterables.first(blame.authors.values()),
commit: commit,
line: blame.lines[line]
} as GitBlameLine;
} as IGitBlameLine;
}
catch (ex) {
return undefined;
}
}
async getBlameForRange(uri: GitUri, range: Range): Promise<GitBlameLines | undefined> {
async getBlameForRange(uri: GitUri, range: Range): Promise<IGitBlameLines | undefined> {
Logger.log(`getBlameForRange('${uri.repoPath}', '${uri.fsPath}', [${range.start.line}, ${range.end.line}], ${uri.sha})`);
const blame = await this.getBlameForFile(uri);
if (blame === undefined) return undefined;
if (!blame) return undefined;
return this.getBlameForRangeSync(blame, uri, range);
}
getBlameForRangeSync(blame: GitBlame, uri: GitUri, range: Range): GitBlameLines | undefined {
getBlameForRangeSync(blame: IGitBlame, uri: GitUri, range: Range): IGitBlameLines | undefined {
Logger.log(`getBlameForRangeSync('${uri.repoPath}', '${uri.fsPath}', [${range.start.line}, ${range.end.line}], ${uri.sha})`);
if (blame.lines.length === 0) return Object.assign({ allLines: blame.lines }, blame);
if (!blame.lines.length) return Object.assign({ allLines: blame.lines }, blame);
if (range.start.line === 0 && range.end.line === blame.lines.length - 1) {
return Object.assign({ allLines: blame.lines }, blame);
}
const lines = blame.lines.slice(range.start.line, range.end.line + 1);
const shas = new Set(lines.map(l => l.sha));
const shas: Set<string> = new Set();
lines.forEach(l => shas.add(l.sha));
const authors: Map<string, GitAuthor> = new Map();
const commits: Map<string, GitBlameCommit> = new Map();
for (const c of blame.commits.values()) {
const authors: Map<string, IGitAuthor> = new Map();
const commits: Map<string, GitCommit> = new Map();
blame.commits.forEach(c => {
if (!shas.has(c.sha)) return;
const commit = new GitBlameCommit(c.repoPath, c.sha, c.fileName, c.author, c.date, c.message,
const commit: GitCommit = new GitCommit('blame', c.repoPath, c.sha, c.fileName, c.author, c.date, c.message,
c.lines.filter(l => l.line >= range.start.line && l.line <= range.end.line), c.originalFileName, c.previousSha, c.previousFileName);
commits.set(c.sha, commit);
@@ -502,23 +498,26 @@ export class GitService extends Disposable {
}
author.lineCount += commit.lines.length;
}
});
const sortedAuthors = new Map([...authors.entries()].sort((a, b) => b[1].lineCount - a[1].lineCount));
const sortedAuthors: Map<string, IGitAuthor> = new Map();
Array.from(authors.values())
.sort((a, b) => b.lineCount - a.lineCount)
.forEach(a => sortedAuthors.set(a.name, a));
return {
authors: sortedAuthors,
commits: commits,
lines: lines,
allLines: blame.lines
} as GitBlameLines;
} as IGitBlameLines;
}
async getBlameLocations(uri: GitUri, range: Range, selectedSha?: string, line?: number): Promise<Location[] | undefined> {
Logger.log(`getBlameLocations('${uri.repoPath}', '${uri.fsPath}', [${range.start.line}, ${range.end.line}], ${uri.sha})`);
const blame = await this.getBlameForRange(uri, range);
if (blame === undefined) return undefined;
if (!blame) return undefined;
const commitCount = blame.commits.size;
@@ -571,7 +570,7 @@ export class GitService extends Disposable {
return entry && entry.uri;
}
async getDiffForFile(uri: GitUri, sha1?: string, sha2?: string): Promise<GitDiff | undefined> {
async getDiffForFile(uri: GitUri, sha1?: string, sha2?: string): Promise<IGitDiff | undefined> {
if (sha1 !== undefined && sha2 === undefined && uri.sha !== undefined) {
sha2 = uri.sha;
}
@@ -590,7 +589,7 @@ export class GitService extends Disposable {
entry = this._gitCache.get(cacheKey);
if (entry !== undefined) {
const cachedDiff = entry.get<CachedDiff>(key);
const cachedDiff = entry.get<ICachedDiff>(key);
if (cachedDiff !== undefined) {
Logger.log(`Cached(${key}): getDiffForFile('${uri.repoPath}', '${uri.fsPath}', ${sha1}, ${sha2})`);
return cachedDiff.item;
@@ -613,21 +612,20 @@ export class GitService extends Disposable {
if (entry) {
Logger.log(`Add log cache for '${entry.key}:${key}'`);
entry.set<CachedDiff>(key, {
entry.set<ICachedDiff>(key, {
item: promise
} as CachedDiff);
} as ICachedDiff);
}
return promise;
}
private async _getDiffForFile(repoPath: string | undefined, fileName: string, sha1: string | undefined, sha2: string | undefined, entry: GitCacheEntry | undefined, key: string): Promise<GitDiff | undefined> {
private async _getDiffForFile(repoPath: string | undefined, fileName: string, sha1: string | undefined, sha2: string | undefined, entry: GitCacheEntry | undefined, key: string): Promise<IGitDiff | undefined> {
const [file, root] = Git.splitPath(fileName, repoPath, false);
try {
const data = await Git.diff(root, file, sha1, sha2);
const diff = GitDiffParser.parse(data);
return diff;
return GitDiffParser.parse(data);
}
catch (ex) {
// Trap and cache expected diff errors
@@ -635,29 +633,29 @@ export class GitService extends Disposable {
const msg = ex && ex.toString();
Logger.log(`Replace diff cache with empty promise for '${entry.key}:${key}'`);
entry.set<CachedDiff>(key, {
entry.set<ICachedDiff>(key, {
item: GitService.EmptyPromise,
errorMessage: msg
} as CachedDiff);
} as ICachedDiff);
return await GitService.EmptyPromise as GitDiff;
return await GitService.EmptyPromise as IGitDiff;
}
return undefined;
}
}
async getDiffForLine(uri: GitUri, line: number, sha1?: string, sha2?: string): Promise<[GitDiffLine | undefined, GitDiffLine | undefined]> {
async getDiffForLine(uri: GitUri, line: number, sha1?: string, sha2?: string): Promise<[IGitDiffLine | undefined, IGitDiffLine | undefined]> {
try {
const diff = await this.getDiffForFile(uri, sha1, sha2);
if (diff === undefined) return [undefined, undefined];
const chunk = diff.chunks.find(_ => _.currentPosition.start <= line && _.currentPosition.end >= line);
const chunk = diff.chunks.find(_ => _.currentStart <= line && _.currentEnd >= line);
if (chunk === undefined) return [undefined, undefined];
// Search for the line (skipping deleted lines -- since they don't currently exist in the editor)
// Keep track of the deleted lines for the original version
line = line - chunk.currentPosition.start + 1;
line = line - chunk.currentStart + 1;
let count = 0;
let deleted = 0;
for (const l of chunk.current) {
@@ -674,7 +672,7 @@ export class GitService extends Disposable {
return [
chunk.previous[line + deleted - 1],
chunk.current[line + deleted + (chunk.currentPosition.start - chunk.previousPosition.start)]
chunk.current[line + deleted + (chunk.currentStart - chunk.previousStart)]
];
}
catch (ex) {
@@ -689,22 +687,22 @@ export class GitService extends Disposable {
if (typeof shaOrOptions === 'string') {
sha = shaOrOptions;
}
else if (options === undefined) {
else if (!options) {
options = shaOrOptions;
}
options = options || {};
const log = await this.getLogForFile(repoPath, fileName, sha, options.previous ? 2 : 1);
if (log === undefined) return undefined;
if (!log) return undefined;
const commit = sha && log.commits.get(sha);
if (commit === undefined && sha && !options.firstIfMissing) return undefined;
if (!commit && sha && !options.firstIfMissing) return undefined;
return commit || Iterables.first(log.commits.values());
}
async getLogForRepo(repoPath: string, sha?: string, maxCount?: number, reverse: boolean = false): Promise<GitLog | undefined> {
async getLogForRepo(repoPath: string, sha?: string, maxCount?: number, reverse: boolean = false): Promise<IGitLog | undefined> {
Logger.log(`getLogForRepo('${repoPath}', ${sha}, ${maxCount})`);
if (maxCount == null) {
@@ -713,15 +711,14 @@ export class GitService extends Disposable {
try {
const data = await Git.log(repoPath, sha, maxCount, reverse);
const log = GitLogParser.parse(data, 'branch', repoPath, undefined, sha, maxCount, reverse, undefined);
return log;
return GitLogParser.parse(data, 'branch', repoPath, undefined, sha, maxCount, reverse, undefined);
}
catch (ex) {
return undefined;
}
}
async getLogForRepoSearch(repoPath: string, search: string, searchBy: GitRepoSearchBy, maxCount?: number): Promise<GitLog | undefined> {
async getLogForRepoSearch(repoPath: string, search: string, searchBy: GitRepoSearchBy, maxCount?: number): Promise<IGitLog | undefined> {
Logger.log(`getLogForRepoSearch('${repoPath}', ${search}, ${searchBy}, ${maxCount})`);
if (maxCount == null) {
@@ -747,15 +744,14 @@ export class GitService extends Disposable {
try {
const data = await Git.log_search(repoPath, searchArgs, maxCount);
const log = GitLogParser.parse(data, 'branch', repoPath, undefined, undefined, maxCount, false, undefined);
return log;
return GitLogParser.parse(data, 'branch', repoPath, undefined, undefined, maxCount, false, undefined);
}
catch (ex) {
return undefined;
}
}
async getLogForFile(repoPath: string | undefined, fileName: string, sha?: string, maxCount?: number, range?: Range, reverse: boolean = false): Promise<GitLog | undefined> {
async getLogForFile(repoPath: string | undefined, fileName: string, sha?: string, maxCount?: number, range?: Range, reverse: boolean = false): Promise<IGitLog | undefined> {
let key = 'log';
if (sha !== undefined) {
key += `:${sha}`;
@@ -770,7 +766,7 @@ export class GitService extends Disposable {
entry = this._gitCache.get(cacheKey);
if (entry !== undefined) {
const cachedLog = entry.get<CachedLog>(key);
const cachedLog = entry.get<ICachedLog>(key);
if (cachedLog !== undefined) {
Logger.log(`Cached(${key}): getLogForFile('${repoPath}', '${fileName}', ${sha}, ${maxCount}, undefined, false)`);
return cachedLog.item;
@@ -778,7 +774,7 @@ export class GitService extends Disposable {
if (key !== 'log') {
// Since we are looking for partial log, see if we have the log of the whole file
const cachedLog = entry.get<CachedLog>('log');
const cachedLog = entry.get<ICachedLog>('log');
if (cachedLog !== undefined) {
if (sha === undefined) {
Logger.log(`Cached(~${key}): getLogForFile('${repoPath}', '${fileName}', ${sha}, ${maxCount}, undefined, false)`);
@@ -811,27 +807,26 @@ export class GitService extends Disposable {
if (entry) {
Logger.log(`Add log cache for '${entry.key}:${key}'`);
entry.set<CachedLog>(key, {
entry.set<ICachedLog>(key, {
item: promise
} as CachedLog);
} as ICachedLog);
}
return promise;
}
private async _getLogForFile(repoPath: string | undefined, fileName: string, sha: string | undefined, range: Range | undefined, maxCount: number | undefined, reverse: boolean, entry: GitCacheEntry | undefined, key: string): Promise<GitLog | undefined> {
private async _getLogForFile(repoPath: string | undefined, fileName: string, sha: string | undefined, range: Range | undefined, maxCount: number | undefined, reverse: boolean, entry: GitCacheEntry | undefined, key: string): Promise<IGitLog | undefined> {
const [file, root] = Git.splitPath(fileName, repoPath, false);
const ignore = await this._gitignore;
if (ignore && !ignore.filter([file]).length) {
Logger.log(`Skipping log; '${fileName}' is gitignored`);
return await GitService.EmptyPromise as GitLog;
return await GitService.EmptyPromise as IGitLog;
}
try {
const data = await Git.log_file(root, file, sha, maxCount, reverse, range && range.start.line + 1, range && range.end.line + 1);
const log = GitLogParser.parse(data, 'file', root, file, sha, maxCount, reverse, range);
return log;
return GitLogParser.parse(data, 'file', root, file, sha, maxCount, reverse, range);
}
catch (ex) {
// Trap and cache expected log errors
@@ -839,12 +834,12 @@ export class GitService extends Disposable {
const msg = ex && ex.toString();
Logger.log(`Replace log cache with empty promise for '${entry.key}:${key}'`);
entry.set<CachedLog>(key, {
entry.set<ICachedLog>(key, {
item: GitService.EmptyPromise,
errorMessage: msg
} as CachedLog);
} as ICachedLog);
return await GitService.EmptyPromise as GitLog;
return await GitService.EmptyPromise as IGitLog;
}
return undefined;
@@ -855,7 +850,7 @@ export class GitService extends Disposable {
Logger.log(`getLogLocations('${uri.repoPath}', '${uri.fsPath}', ${uri.sha}, ${selectedSha}, ${line})`);
const log = await this.getLogForFile(uri.repoPath, uri.fsPath, uri.sha);
if (log === undefined) return undefined;
if (!log) return undefined;
const commitCount = log.commits.size;
@@ -912,12 +907,11 @@ export class GitService extends Disposable {
return repoPath;
}
async getStashList(repoPath: string): Promise<GitStash | undefined> {
async getStashList(repoPath: string): Promise<IGitStash | undefined> {
Logger.log(`getStash('${repoPath}')`);
const data = await Git.stash_list(repoPath);
const stash = GitStashParser.parse(data, repoPath);
return stash;
return GitStashParser.parse(data, repoPath);
}
async getStatusForFile(repoPath: string, fileName: string): Promise<GitStatusFile | undefined> {
@@ -932,14 +926,13 @@ export class GitService extends Disposable {
return status.files[0];
}
async getStatusForRepo(repoPath: string): Promise<GitStatus | undefined> {
async getStatusForRepo(repoPath: string): Promise<IGitStatus | undefined> {
Logger.log(`getStatusForRepo('${repoPath}')`);
const porcelainVersion = Git.validateVersion(2, 11) ? 2 : 1;
const data = await Git.status(repoPath, porcelainVersion);
const status = GitStatusParser.parse(data, repoPath, porcelainVersion);
return status;
return GitStatusParser.parse(data, repoPath, porcelainVersion);
}
async getVersionedFile(repoPath: string | undefined, fileName: string, sha: string) {

View File

@@ -1,103 +0,0 @@
'use strict';
import { commands, ExtensionContext, Uri, window } from 'vscode';
import { BuiltInCommands } from './constants';
import { GitCommit } from './gitService';
import * as moment from 'moment';
export type SuppressedKeys = 'suppressCommitHasNoPreviousCommitWarning' |
'suppressCommitNotFoundWarning' |
'suppressFileNotUnderSourceControlWarning' |
'suppressGitVersionWarning' |
'suppressLineUncommittedWarning' |
'suppressNoRepositoryWarning' |
'suppressUpdateNotice';
export const SuppressedKeys = {
CommitHasNoPreviousCommitWarning: 'suppressCommitHasNoPreviousCommitWarning' as SuppressedKeys,
CommitNotFoundWarning: 'suppressCommitNotFoundWarning' as SuppressedKeys,
FileNotUnderSourceControlWarning: 'suppressFileNotUnderSourceControlWarning' as SuppressedKeys,
GitVersionWarning: 'suppressGitVersionWarning' as SuppressedKeys,
LineUncommittedWarning: 'suppressLineUncommittedWarning' as SuppressedKeys,
NoRepositoryWarning: 'suppressNoRepositoryWarning' as SuppressedKeys,
UpdateNotice: 'suppressUpdateNotice' as SuppressedKeys
};
export class Messages {
static context: ExtensionContext;
static configure(context: ExtensionContext) {
this.context = context;
}
static showCommitHasNoPreviousCommitWarningMessage(commit: GitCommit): Promise<string | undefined> {
return Messages._showMessage('info', `Commit ${commit.shortSha} (${commit.author}, ${moment(commit.date).fromNow()}) has no previous commit`, SuppressedKeys.CommitHasNoPreviousCommitWarning);
}
static showCommitNotFoundWarningMessage(message: string): Promise<string | undefined> {
return Messages._showMessage('warn', `${message}. The commit could not be found`, SuppressedKeys.CommitNotFoundWarning);
}
static showFileNotUnderSourceControlWarningMessage(message: string): Promise<string | undefined> {
return Messages._showMessage('warn', `${message}. The file is probably not under source control`, SuppressedKeys.FileNotUnderSourceControlWarning);
}
static showLineUncommittedWarningMessage(message: string): Promise<string | undefined> {
return Messages._showMessage('warn', `${message}. The line has uncommitted changes`, SuppressedKeys.LineUncommittedWarning);
}
static showNoRepositoryWarningMessage(message: string): Promise<string | undefined> {
return Messages._showMessage('warn', `${message}. No repository could be found`, SuppressedKeys.NoRepositoryWarning);
}
static showUnsupportedGitVersionErrorMessage(version: string): Promise<string | undefined> {
return Messages._showMessage('error', `GitLens requires a newer version of Git (>= 2.2.0) than is currently installed (${version}). Please install a more recent version of Git.`, SuppressedKeys.GitVersionWarning);
}
static async showUpdateMessage(version: string): Promise<string | undefined> {
const viewReleaseNotes = 'View Release Notes';
const result = await Messages._showMessage('info', `GitLens has been updated to v${version}`, SuppressedKeys.UpdateNotice, undefined, viewReleaseNotes);
if (result === viewReleaseNotes) {
commands.executeCommand(BuiltInCommands.Open, Uri.parse('https://marketplace.visualstudio.com/items/eamodio.gitlens/changelog'));
}
return result;
}
static async showWelcomeMessage(): Promise<string | undefined> {
const viewDocs = 'View Docs';
const result = await window.showInformationMessage(`Thank you for choosing GitLens! GitLens is powerful, feature rich, and highly configurable, so please be sure to view the docs and tailor it to suit your needs.`, viewDocs);
if (result === viewDocs) {
commands.executeCommand(BuiltInCommands.Open, Uri.parse('https://marketplace.visualstudio.com/items/eamodio.gitlens'));
}
return result;
}
private static async _showMessage(type: 'info' | 'warn' | 'error', message: string, suppressionKey: SuppressedKeys, dontShowAgain: string | null = 'Don\'t Show Again', ...actions: any[]): Promise<string | undefined> {
if (Messages.context.globalState.get(suppressionKey, false)) return undefined;
if (dontShowAgain !== null) {
actions.push(dontShowAgain);
}
let result: string | undefined = undefined;
switch (type) {
case 'info':
result = await window.showInformationMessage(message, ...actions);
break;
case 'warn':
result = await window.showWarningMessage(message, ...actions);
break;
case 'error':
result = await window.showErrorMessage(message, ...actions);
break;
}
if (dontShowAgain === null || result === dontShowAgain) {
await Messages.context.globalState.update(suppressionKey, true);
return undefined;
}
return result;
}
}

View File

@@ -3,7 +3,7 @@ import { Arrays, Iterables } from '../system';
import { CancellationTokenSource, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard, KeyNoopCommand, ShowCommitSearchCommandArgs, ShowQuickBranchHistoryCommandArgs } from '../commands';
import { CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut, showQuickPickProgress } from './common';
import { GitLog, GitService, GitUri, RemoteResource } from '../gitService';
import { GitService, GitUri, IGitLog, RemoteResource } from '../gitService';
import { OpenRemotesCommandQuickPickItem } from './remotes';
export class BranchHistoryQuickPick {
@@ -17,7 +17,7 @@ export class BranchHistoryQuickPick {
});
}
static async show(git: GitService, log: GitLog, uri: GitUri | undefined, branch: string, progressCancellation: CancellationTokenSource, goBackCommand?: CommandQuickPickItem, nextPageCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
static async show(git: GitService, log: IGitLog, uri: GitUri | undefined, branch: string, progressCancellation: CancellationTokenSource, goBackCommand?: CommandQuickPickItem, nextPageCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as (CommitQuickPickItem | CommandQuickPickItem)[];
const currentCommand = new CommandQuickPickItem({

View File

@@ -3,7 +3,7 @@ import { Arrays, Iterables } from '../system';
import { commands, QuickPickOptions, TextDocumentShowOptions, Uri, window } from 'vscode';
import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffDirectoryCommandCommandArgs, DiffWithPreviousCommandArgs, Keyboard, KeyNoopCommand, Keys, ShowQuickCommitDetailsCommandArgs, StashApplyCommandArgs, StashDeleteCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem, QuickPickItem } from './common';
import { getGitStatusIcon, GitCommit, GitLog, GitLogCommit, GitService, GitStashCommit, GitStatusFileStatus, GitUri, IGitCommitInfo, IGitStatusFile, RemoteResource } from '../gitService';
import { getGitStatusIcon, GitCommit, GitLogCommit, GitService, GitStashCommit, GitStatusFileStatus, GitUri, IGitCommitInfo, IGitLog, IGitStatusFile, RemoteResource } from '../gitService';
import { OpenRemotesCommandQuickPickItem } from './remotes';
import * as moment from 'moment';
import * as path from 'path';
@@ -103,7 +103,7 @@ export class OpenCommitWorkingTreeFilesCommandQuickPickItem extends OpenFilesCom
export class CommitDetailsQuickPick {
static async show(git: GitService, commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, repoLog?: GitLog): Promise<CommitWithFileStatusQuickPickItem | CommandQuickPickItem | undefined> {
static async show(git: GitService, commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, repoLog?: IGitLog): Promise<CommitWithFileStatusQuickPickItem | CommandQuickPickItem | undefined> {
const items: (CommitWithFileStatusQuickPickItem | CommandQuickPickItem)[] = commit.fileStatuses.map(fs => new CommitWithFileStatusQuickPickItem(commit, fs));
const stash = commit.type === 'stash';

View File

@@ -3,7 +3,7 @@ import { Arrays, Iterables } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, Keyboard, KeyNoopCommand, ShowQuickCommitDetailsCommandArgs, ShowQuickCommitFileDetailsCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem } from './common';
import { GitBranch, GitLog, GitLogCommit, GitService, GitUri, RemoteResource } from '../gitService';
import { GitBranch, GitLogCommit, GitService, GitUri, IGitLog, RemoteResource } from '../gitService';
import { OpenRemotesCommandQuickPickItem } from './remotes';
import * as moment from 'moment';
import * as path from 'path';
@@ -41,7 +41,7 @@ export class OpenCommitWorkingTreeFileCommandQuickPickItem extends OpenFileComma
export class CommitFileDetailsQuickPick {
static async show(git: GitService, commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, fileLog?: GitLog): Promise<CommandQuickPickItem | undefined> {
static async show(git: GitService, commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, fileLog?: IGitLog): Promise<CommandQuickPickItem | undefined> {
const items: CommandQuickPickItem[] = [];
const stash = commit.type === 'stash';

View File

@@ -2,12 +2,12 @@
import { Iterables } from '../system';
import { QuickPickOptions, window } from 'vscode';
import { Keyboard } from '../commands';
import { GitLog, GitService } from '../gitService';
import { GitService, IGitLog } from '../gitService';
import { CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut } from '../quickPicks';
export class CommitsQuickPick {
static async show(git: GitService, log: GitLog, placeHolder: string, goBackCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
static async show(git: GitService, log: IGitLog, placeHolder: string, goBackCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
const items = ((log && Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c)))) || []) as (CommitQuickPickItem | CommandQuickPickItem)[];
if (goBackCommand) {

View File

@@ -3,7 +3,7 @@ import { Arrays, Iterables } from '../system';
import { CancellationTokenSource, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard, KeyNoopCommand, ShowQuickCurrentBranchHistoryCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands';
import { CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut, showQuickPickProgress } from './common';
import { GitLog, GitService, GitUri, RemoteResource } from '../gitService';
import { GitService, GitUri, IGitLog, RemoteResource } from '../gitService';
import { OpenRemotesCommandQuickPickItem } from './remotes';
import * as path from 'path';
@@ -18,7 +18,7 @@ export class FileHistoryQuickPick {
});
}
static async show(git: GitService, log: GitLog, uri: GitUri, progressCancellation: CancellationTokenSource, goBackCommand?: CommandQuickPickItem, nextPageCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
static async show(git: GitService, log: IGitLog, uri: GitUri, progressCancellation: CancellationTokenSource, goBackCommand?: CommandQuickPickItem, nextPageCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as (CommitQuickPickItem | CommandQuickPickItem)[];
let previousPageCommand: CommandQuickPickItem | undefined = undefined;

View File

@@ -3,7 +3,7 @@ import { Iterables } from '../system';
import { commands, QuickPickOptions, TextDocumentShowOptions, Uri, window } from 'vscode';
import { Commands, DiffWithWorkingCommandArgs, Keyboard, Keys, OpenChangedFilesCommandArgs, ShowQuickBranchHistoryCommandArgs, ShowQuickRepoStatusCommandArgs, ShowQuickStashListCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem, QuickPickItem } from './common';
import { GitService, GitStatus, GitStatusFile, GitUri } from '../gitService';
import { GitService, GitStatusFile, GitUri, IGitStatus } from '../gitService';
import * as path from 'path';
export class OpenStatusFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
@@ -58,7 +58,7 @@ export class OpenStatusFilesCommandQuickPickItem extends CommandQuickPickItem {
export class RepoStatusQuickPick {
static async show(status: GitStatus, goBackCommand?: CommandQuickPickItem): Promise<OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem | undefined> {
static async show(status: IGitStatus, goBackCommand?: CommandQuickPickItem): Promise<OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem | undefined> {
// Sort the status by staged and then filename
const files = status.files;
files.sort((a, b) => (a.staged ? -1 : 1) - (b.staged ? -1 : 1) || a.fileName.localeCompare(b.fileName));

View File

@@ -2,12 +2,12 @@
import { Iterables } from '../system';
import { QuickPickOptions, window } from 'vscode';
import { Commands, Keyboard, StashSaveCommandArgs } from '../commands';
import { GitService, GitStash } from '../gitService';
import { GitService, IGitStash } from '../gitService';
import { CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut } from '../quickPicks';
export class StashListQuickPick {
static async show(git: GitService, stash: GitStash, mode: 'list' | 'apply', goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
static async show(git: GitService, stash: IGitStash, mode: 'list' | 'apply', goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
const items = ((stash && Array.from(Iterables.map(stash.commits.values(), c => new CommitQuickPickItem(c)))) || []) as (CommitQuickPickItem | CommandQuickPickItem)[];
if (mode === 'list') {

View File

@@ -92,7 +92,7 @@ export namespace Iterables {
return source.next().value;
}
export function* skip<T>(source: Iterable<T> | IterableIterator<T>, count: number): Iterable<T> | IterableIterator<T> {
export function* skip<T>(source: Iterable<T> | IterableIterator<T>, count: number): Iterable<T> {
let i = 0;
for (const item of source) {
if (i >= count) yield item;

View File

@@ -56,9 +56,9 @@ export namespace Objects {
}
}
export function* values<T>(o: any): IterableIterator<T> {
export function* values(o: any): IterableIterator<[any]> {
for (const key in o) {
yield o[key];
yield [o[key]];
}
}
}

View File

@@ -1,4 +1,5 @@
'use strict';
import { Objects } from './object';
const _escapeRegExp = require('lodash.escaperegexp');
export namespace Strings {
@@ -36,24 +37,13 @@ export namespace Strings {
return tokens;
}
export function interpolate(template: string, context: object): string {
if (!template) return template;
template = template.replace(TokenSanitizeRegex, '$${this.$1}');
return new Function(`return \`${template}\`;`).call(context);
export function interpolate(template: string, tokens: { [key: string]: any }): string {
return new Function(...Object.keys(tokens), `return \`${template}\`;`)(...Objects.values(tokens));
}
export function* lines(s: string): IterableIterator<string> {
let i = 0;
while (i < s.length) {
let j = s.indexOf('\n', i);
if (j === -1) {
j = s.length;
}
yield s.substring(i, j);
i = j + 1;
}
export function interpolateLazy(template: string, context: object): string {
template = template.replace(TokenSanitizeRegex, '$${c.$1}');
return new Function('c', `return \`${template}\`;`)(context);
}
export function padLeft(s: string, padTo: number, padding: string = '\u00a0') {

View File

@@ -1,6 +1,5 @@
'use strict';
import { Disposable, env, version, workspace } from 'vscode';
import { ExtensionKey, IConfig } from './configuration';
import * as os from 'os';
let _reporter: TelemetryReporter;
@@ -8,28 +7,21 @@ let _reporter: TelemetryReporter;
export class Telemetry extends Disposable {
static configure(key: string) {
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
if (!cfg.advanced.telemetry.enabled || !workspace.getConfiguration('telemetry').get<boolean>('enableTelemetry', true)) return;
if (!workspace.getConfiguration('telemetry').get<boolean>('enableTelemetry', true)) return;
_reporter = new TelemetryReporter(key);
}
static setContext(context?: { [key: string]: string }) {
if (_reporter === undefined) return;
_reporter.setContext(context);
_reporter && _reporter.setContext(context);
}
static trackEvent(name: string, properties?: { [key: string]: string }, measurements?: { [key: string]: number; }) {
if (_reporter === undefined) return;
_reporter.trackEvent(name, properties, measurements);
_reporter && _reporter.trackEvent(name, properties, measurements);
}
static trackException(ex: Error) {
if (_reporter === undefined) return;
_reporter.trackException(ex);
_reporter && _reporter.trackException(ex);
}
}
@@ -52,12 +44,11 @@ export class TelemetryReporter {
}
else {
this._client = this.appInsights.setup(key)
.setAutoCollectRequests(false)
.setAutoCollectPerformance(false)
.setAutoCollectExceptions(false)
.setAutoCollectDependencies(false)
.setAutoCollectConsole(false)
.setAutoDependencyCorrelation(false)
.setAutoCollectDependencies(false)
.setAutoCollectExceptions(false)
.setAutoCollectPerformance(false)
.setAutoCollectRequests(false)
.setOfflineMode(true)
.start()
.client;