mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-02-12 11:08:34 -05:00
Compare commits
10 Commits
v4.0.2-bet
...
v4.1.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0160e064be | ||
|
|
76961d6697 | ||
|
|
cc827f4711 | ||
|
|
6f3441a9e7 | ||
|
|
64ae82075e | ||
|
|
5624567daa | ||
|
|
c1c65127b7 | ||
|
|
ce98f02706 | ||
|
|
7f2ec30164 | ||
|
|
48a1ca704d |
@@ -4,14 +4,20 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
## [4.0.2-beta] - 2017-06-10
|
## [4.1.0-beta.2] - 2017-06-12
|
||||||
### Added
|
### 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
|
- Improves performance
|
||||||
- Optimized git output parsing to increase speed and reduce memory usage
|
- Optimized git output parsing to increase speed and reduce memory usage
|
||||||
- Defers diff chunk parsing until it is actually required
|
- Defers diff chunk parsing until it is actually required
|
||||||
|
- Adds `gitlens.defaultDateFormat` setting to specify how all absolute dates will be formatted by default
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixes excessive memory usage when parsing diffs
|
- Fixes excessive memory usage when parsing diffs
|
||||||
|
- Fixes extra newline in multiline commit messages
|
||||||
|
- Fixes (again) [#33](https://github.com/eamodio/vscode-gitlens/issues/33) - Commit messages can causes markdown formatting in hovers
|
||||||
|
|
||||||
## [4.0.1] - 2017-06-09
|
## [4.0.1] - 2017-06-09
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
31
README.md
31
README.md
@@ -20,8 +20,8 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
|
|||||||
|
|
||||||

|

|
||||||
- Contains the author, date, and message of the line's most recent commit, by [default](#line-blame-annotation-settings)
|
- 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)
|
- 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)
|
- 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)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -30,7 +30,9 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
|
|||||||

|

|
||||||
- Choose between `gutter` (default) and `hover` [annotation styles](#file-blame-annotation-settings)
|
- Choose between `gutter` (default) and `hover` [annotation styles](#file-blame-annotation-settings)
|
||||||
- Contains the commit message and date, by [default](#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 a `details` hover annotation to the line's annotation, which provides more commit details ([optional](#file-blame-annotation-settings), on by default)
|
||||||
|
- Adds a `heatmap` (age) indicator to the gutter annotations (on right edge by [default](#file-blame-annotation-settings)), which provides an easy, at-a-glance way to tell the age of a line ([optional](#file-blame-annotation-settings), on by default)
|
||||||
|
- Indicator ranges from bright yellow (newer) to dark brown (older)
|
||||||
|
|
||||||
- Adds [customizable](#status-bar-settings) **blame information** about the current line to the **status bar** ([optional](#status-bar-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)
|
||||||
|
|
||||||
@@ -53,6 +55,15 @@ 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
|
- 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`)
|
- Also adds a `Show Line Blame Annotations` command (`gitlens.showLineBlame`)
|
||||||
|
|
||||||
|
### Git Recent Changes Annotations
|
||||||
|
|
||||||
|
- Adds on-demand, [customizable](#file-recent-changes-annotation-settings) and [themeable](#theme-settings), **recent changes annotations** of the whole file
|
||||||
|
- Highlights all of lines changed in the most recent commit
|
||||||
|
- Adds a `details` hover annotation to each line, which provides more commit details ([optional](#file-blame-annotation-settings), on by default)
|
||||||
|
- Adds a `changes` (diff) hover annotation to each line, which provides **instant** access to the line's previous version ([optional](#file-recent-changes-annotation-settings), on by default)
|
||||||
|
|
||||||
|
- 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)
|
- Adds **code lens** to the top of the file and on code blocks ([optional](#code-lens-settings), on by default)
|
||||||
@@ -184,9 +195,9 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
|
|||||||
|
|
||||||
### And More
|
### And More
|
||||||
|
|
||||||
- Adds a `Copy Commit ID to Clipboard` command (`gitlens.copyShaToClipboard`) to copy the commit id (sha) of the active line to the clipboard
|
- Adds a `Copy Commit ID to Clipboard` command (`gitlens.copyShaToClipboard`) to copy the commit id (sha) of the active line to the clipboard or from the most recent commit to the current branch, if there is no active editor
|
||||||
|
|
||||||
- Adds a `Copy Commit Message to Clipboard` command (`gitlens.copyMessageToClipboard`) to copy the commit message of the active line to the clipboard
|
- Adds a `Copy Commit Message to Clipboard` command (`gitlens.copyMessageToClipboard`) to copy the commit message of the active line to the clipboard or from the most recent commit to the current branch, if there is no active editor
|
||||||
|
|
||||||
- Adds a `Open Changed Files` command (`gitlens.openChangedFiles`) to open any files with working tree changes
|
- Adds a `Open Changed Files` command (`gitlens.openChangedFiles`) to open any files with working tree changes
|
||||||
|
|
||||||
@@ -208,6 +219,7 @@ GitLens is highly customizable and provides many configuration settings to allow
|
|||||||
|
|
||||||
|Name | Description
|
|Name | Description
|
||||||
|-----|------------
|
|-----|------------
|
||||||
|
|`gitlens.defaultDateFormat`|Specifies how all absolute dates will be formatted by default\nSee https://momentjs.com/docs/#/displaying/format/ for valid formats
|
||||||
|`gitlens.insiders`|Opts into the insiders channel -- provides access to upcoming features
|
|`gitlens.insiders`|Opts into the insiders channel -- provides access to upcoming features
|
||||||
|`gitlens.outputLevel`|Specifies how much (if any) output will be sent to the GitLens output channel
|
|`gitlens.outputLevel`|Specifies how much (if any) output will be sent to the GitLens output channel
|
||||||
|
|
||||||
@@ -244,6 +256,15 @@ GitLens is highly customizable and provides many configuration settings to allow
|
|||||||
|`gitlens.annotations.line.hover.details`|Specifies whether or not to provide a commit details hover annotation for the current 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
|
|`gitlens.annotations.line.hover.changes`|Specifies whether or not to provide a changes (diff) hover annotation for the current line
|
||||||
|
|
||||||
|
### File Recent Changes Annotation Settings
|
||||||
|
|
||||||
|
|Name | Description
|
||||||
|
|-----|------------
|
||||||
|
|`gitlens.recentChanges.file.lineHighlight.locations`|Specifies where the highlights of the recently changed lines will be shown<br />`gutter` - adds a gutter glyph<br />`line` - adds a full-line highlight background color<br />`overviewRuler` - adds a decoration to the overviewRuler (scroll bar)
|
||||||
|
|`gitlens.annotations.file.recentChanges.hover.details`|Specifies whether or not to provide a commit details hover annotation
|
||||||
|
|`gitlens.annotations.file.recentChanges.hover.changes`|Specifies whether or not to provide a changes (diff) hover annotation
|
||||||
|
|`gitlens.annotations.file.recentChanges.hover.wholeLine`|Specifies whether or not to trigger hover annotations over the whole line
|
||||||
|
|
||||||
### Code Lens Settings
|
### Code Lens Settings
|
||||||
|
|
||||||
|Name | Description
|
|Name | Description
|
||||||
|
|||||||
51
package.json
51
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gitlens",
|
"name": "gitlens",
|
||||||
"version": "4.0.2-beta",
|
"version": "4.1.0-beta.2",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Eric Amodio",
|
"name": "Eric Amodio",
|
||||||
"email": "eamodio@gmail.com"
|
"email": "eamodio@gmail.com"
|
||||||
@@ -122,6 +122,21 @@
|
|||||||
"default": true,
|
"default": true,
|
||||||
"description": "Specifies whether or not to trigger hover annotations over the whole line"
|
"description": "Specifies whether or not to trigger hover annotations over the whole line"
|
||||||
},
|
},
|
||||||
|
"gitlens.annotations.file.recentChanges.hover.details": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Specifies whether or not to provide a commit details hover annotation"
|
||||||
|
},
|
||||||
|
"gitlens.annotations.file.recentChanges.hover.changes": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Specifies whether or not to provide a changes (diff) hover annotation"
|
||||||
|
},
|
||||||
|
"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": {
|
"gitlens.annotations.line.hover.details": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true,
|
"default": true,
|
||||||
@@ -205,6 +220,26 @@
|
|||||||
],
|
],
|
||||||
"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"
|
"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)"
|
||||||
|
},
|
||||||
"gitlens.codeLens.enabled": {
|
"gitlens.codeLens.enabled": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true,
|
"default": true,
|
||||||
@@ -367,6 +402,11 @@
|
|||||||
"default": false,
|
"default": false,
|
||||||
"description": "Specifies whether or not to show debug information in code lens"
|
"description": "Specifies whether or not to show debug information in code lens"
|
||||||
},
|
},
|
||||||
|
"gitlens.defaultDateFormat": {
|
||||||
|
"type": "string",
|
||||||
|
"default": null,
|
||||||
|
"description": "Specifies how all absolute dates will be formatted by default\nSee https://momentjs.com/docs/#/displaying/format/ for valid formats"
|
||||||
|
},
|
||||||
"gitlens.statusBar.enabled": {
|
"gitlens.statusBar.enabled": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true,
|
"default": true,
|
||||||
@@ -758,6 +798,11 @@
|
|||||||
"light": "images/git-icon-light.svg"
|
"light": "images/git-icon-light.svg"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.toggleFileRecentChanges",
|
||||||
|
"title": "Toggle Recent File Changes Annotations",
|
||||||
|
"category": "GitLens"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.toggleLineBlame",
|
"command": "gitlens.toggleLineBlame",
|
||||||
"title": "Toggle Line Blame Annotations",
|
"title": "Toggle Line Blame Annotations",
|
||||||
@@ -921,6 +966,10 @@
|
|||||||
"command": "gitlens.toggleFileBlame",
|
"command": "gitlens.toggleFileBlame",
|
||||||
"when": "gitlens:isBlameable"
|
"when": "gitlens:isBlameable"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.toggleFileRecentChanges",
|
||||||
|
"when": "gitlens:isTracked"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.toggleLineBlame",
|
"command": "gitlens.toggleLineBlame",
|
||||||
"when": "gitlens:isBlameable"
|
"when": "gitlens:isBlameable"
|
||||||
|
|||||||
@@ -3,19 +3,29 @@ import { Functions, Objects } from '../system';
|
|||||||
import { DecorationRenderOptions, Disposable, Event, EventEmitter, ExtensionContext, OverviewRulerLane, TextDocument, TextDocumentChangeEvent, TextEditor, TextEditorDecorationType, TextEditorViewColumnChangeEvent, window, workspace } from 'vscode';
|
import { DecorationRenderOptions, Disposable, Event, EventEmitter, ExtensionContext, OverviewRulerLane, TextDocument, TextDocumentChangeEvent, TextEditor, TextEditorDecorationType, TextEditorViewColumnChangeEvent, window, workspace } from 'vscode';
|
||||||
import { AnnotationProviderBase } from './annotationProvider';
|
import { AnnotationProviderBase } from './annotationProvider';
|
||||||
import { TextDocumentComparer, TextEditorComparer } from '../comparers';
|
import { TextDocumentComparer, TextEditorComparer } from '../comparers';
|
||||||
import { BlameLineHighlightLocations, ExtensionKey, FileAnnotationType, IConfig, themeDefaults } from '../configuration';
|
import { ExtensionKey, IConfig, LineHighlightLocations, themeDefaults } from '../configuration';
|
||||||
import { BlameabilityChangeEvent, GitContextTracker, GitService, GitUri } from '../gitService';
|
import { BlameabilityChangeEvent, GitContextTracker, GitService, GitUri } from '../gitService';
|
||||||
import { GutterBlameAnnotationProvider } from './gutterBlameAnnotationProvider';
|
import { GutterBlameAnnotationProvider } from './gutterBlameAnnotationProvider';
|
||||||
import { HoverBlameAnnotationProvider } from './hoverBlameAnnotationProvider';
|
import { HoverBlameAnnotationProvider } from './hoverBlameAnnotationProvider';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
|
import { RecentChangesAnnotationProvider } from './recentChangesAnnotationProvider';
|
||||||
import { WhitespaceController } from './whitespaceController';
|
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 = {
|
export const Decorations = {
|
||||||
annotation: window.createTextEditorDecorationType({
|
blameAnnotation: window.createTextEditorDecorationType({
|
||||||
isWholeLine: true,
|
isWholeLine: true,
|
||||||
textDecoration: 'none'
|
textDecoration: 'none'
|
||||||
} as DecorationRenderOptions),
|
} as DecorationRenderOptions),
|
||||||
highlight: undefined as TextEditorDecorationType | undefined
|
blameHighlight: undefined as TextEditorDecorationType | undefined,
|
||||||
|
recentChangesAnnotation: undefined as TextEditorDecorationType | undefined,
|
||||||
|
recentChangesHighlight: undefined as TextEditorDecorationType | undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
export class AnnotationController extends Disposable {
|
export class AnnotationController extends Disposable {
|
||||||
@@ -46,8 +56,8 @@ export class AnnotationController extends Disposable {
|
|||||||
dispose() {
|
dispose() {
|
||||||
this._annotationProviders.forEach(async (p, i) => await this.clear(i));
|
this._annotationProviders.forEach(async (p, i) => await this.clear(i));
|
||||||
|
|
||||||
Decorations.annotation && Decorations.annotation.dispose();
|
Decorations.blameAnnotation && Decorations.blameAnnotation.dispose();
|
||||||
Decorations.highlight && Decorations.highlight.dispose();
|
Decorations.blameHighlight && Decorations.blameHighlight.dispose();
|
||||||
|
|
||||||
this._annotationsDisposable && this._annotationsDisposable.dispose();
|
this._annotationsDisposable && this._annotationsDisposable.dispose();
|
||||||
this._whitespaceController && this._whitespaceController.dispose();
|
this._whitespaceController && this._whitespaceController.dispose();
|
||||||
@@ -82,50 +92,83 @@ export class AnnotationController extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
|
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
|
||||||
const cfgHighlight = cfg.blame.file.lineHighlight;
|
const cfgBlameHighlight = cfg.blame.file.lineHighlight;
|
||||||
|
const cfgChangesHighlight = cfg.recentChanges.file.lineHighlight;
|
||||||
const cfgTheme = cfg.theme.lineHighlight;
|
const cfgTheme = cfg.theme.lineHighlight;
|
||||||
|
|
||||||
if (!Objects.areEquivalent(cfgHighlight, this._config && this._config.blame.file.lineHighlight) ||
|
if (!Objects.areEquivalent(cfgBlameHighlight, this._config && this._config.blame.file.lineHighlight) ||
|
||||||
|
!Objects.areEquivalent(cfgChangesHighlight, this._config && this._config.recentChanges.file.lineHighlight) ||
|
||||||
!Objects.areEquivalent(cfgTheme, this._config && this._config.theme.lineHighlight)) {
|
!Objects.areEquivalent(cfgTheme, this._config && this._config.theme.lineHighlight)) {
|
||||||
changed = true;
|
changed = true;
|
||||||
|
|
||||||
Decorations.highlight && Decorations.highlight.dispose();
|
Decorations.blameHighlight && Decorations.blameHighlight.dispose();
|
||||||
|
|
||||||
if (cfgHighlight.enabled) {
|
if (cfgBlameHighlight.enabled) {
|
||||||
Decorations.highlight = window.createTextEditorDecorationType({
|
Decorations.blameHighlight = window.createTextEditorDecorationType({
|
||||||
gutterIconSize: 'contain',
|
gutterIconSize: 'contain',
|
||||||
isWholeLine: true,
|
isWholeLine: true,
|
||||||
overviewRulerLane: OverviewRulerLane.Right,
|
overviewRulerLane: OverviewRulerLane.Right,
|
||||||
dark: {
|
dark: {
|
||||||
backgroundColor: cfgHighlight.locations.includes(BlameLineHighlightLocations.Line)
|
backgroundColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.Line)
|
||||||
? cfgTheme.dark.backgroundColor || themeDefaults.lineHighlight.dark.backgroundColor
|
? cfgTheme.dark.backgroundColor || themeDefaults.lineHighlight.dark.backgroundColor
|
||||||
: undefined,
|
: undefined,
|
||||||
gutterIconPath: cfgHighlight.locations.includes(BlameLineHighlightLocations.Gutter)
|
gutterIconPath: cfgBlameHighlight.locations.includes(LineHighlightLocations.Gutter)
|
||||||
? this.context.asAbsolutePath('images/blame-dark.svg')
|
? this.context.asAbsolutePath('images/blame-dark.svg')
|
||||||
: undefined,
|
: undefined,
|
||||||
overviewRulerColor: cfgHighlight.locations.includes(BlameLineHighlightLocations.OverviewRuler)
|
overviewRulerColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
|
||||||
? cfgTheme.dark.overviewRulerColor || themeDefaults.lineHighlight.dark.overviewRulerColor
|
? cfgTheme.dark.overviewRulerColor || themeDefaults.lineHighlight.dark.overviewRulerColor
|
||||||
: undefined
|
: undefined
|
||||||
},
|
},
|
||||||
light: {
|
light: {
|
||||||
backgroundColor: cfgHighlight.locations.includes(BlameLineHighlightLocations.Line)
|
backgroundColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.Line)
|
||||||
? cfgTheme.light.backgroundColor || themeDefaults.lineHighlight.light.backgroundColor
|
? cfgTheme.light.backgroundColor || themeDefaults.lineHighlight.light.backgroundColor
|
||||||
: undefined,
|
: undefined,
|
||||||
gutterIconPath: cfgHighlight.locations.includes(BlameLineHighlightLocations.Gutter)
|
gutterIconPath: cfgBlameHighlight.locations.includes(LineHighlightLocations.Gutter)
|
||||||
? this.context.asAbsolutePath('images/blame-light.svg')
|
? this.context.asAbsolutePath('images/blame-light.svg')
|
||||||
: undefined,
|
: undefined,
|
||||||
overviewRulerColor: cfgHighlight.locations.includes(BlameLineHighlightLocations.OverviewRuler)
|
overviewRulerColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
|
||||||
? cfgTheme.light.overviewRulerColor || themeDefaults.lineHighlight.light.overviewRulerColor
|
? cfgTheme.light.overviewRulerColor || themeDefaults.lineHighlight.light.overviewRulerColor
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Decorations.highlight = undefined;
|
Decorations.blameHighlight = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Decorations.recentChangesHighlight && Decorations.recentChangesHighlight.dispose();
|
||||||
|
|
||||||
|
Decorations.recentChangesHighlight = window.createTextEditorDecorationType({
|
||||||
|
gutterIconSize: 'contain',
|
||||||
|
isWholeLine: true,
|
||||||
|
overviewRulerLane: OverviewRulerLane.Right,
|
||||||
|
dark: {
|
||||||
|
backgroundColor: cfgChangesHighlight.locations.includes(LineHighlightLocations.Line)
|
||||||
|
? cfgTheme.dark.backgroundColor || themeDefaults.lineHighlight.dark.backgroundColor
|
||||||
|
: undefined,
|
||||||
|
gutterIconPath: cfgChangesHighlight.locations.includes(LineHighlightLocations.Gutter)
|
||||||
|
? this.context.asAbsolutePath('images/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) ||
|
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.annotations, this._config && this._config.annotations) ||
|
||||||
!Objects.areEquivalent(cfg.theme.annotations, this._config && this._config.theme.annotations)) {
|
!Objects.areEquivalent(cfg.theme.annotations, this._config && this._config.theme.annotations)) {
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -138,7 +181,12 @@ export class AnnotationController extends Disposable {
|
|||||||
for (const provider of this._annotationProviders.values()) {
|
for (const provider of this._annotationProviders.values()) {
|
||||||
if (provider === undefined) continue;
|
if (provider === undefined) continue;
|
||||||
|
|
||||||
provider.reset(this._whitespaceController);
|
if (provider.annotationType === FileAnnotationType.RecentChanges) {
|
||||||
|
provider.reset(Decorations.recentChangesAnnotation, Decorations.recentChangesHighlight);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
provider.reset(Decorations.blameAnnotation, Decorations.blameHighlight, this._whitespaceController);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,10 +232,15 @@ export class AnnotationController extends Disposable {
|
|||||||
let provider: AnnotationProviderBase | undefined = undefined;
|
let provider: AnnotationProviderBase | undefined = undefined;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case FileAnnotationType.Gutter:
|
case FileAnnotationType.Gutter:
|
||||||
provider = new GutterBlameAnnotationProvider(this.context, editor, Decorations.annotation, Decorations.highlight, this._whitespaceController, this.git, gitUri);
|
provider = new GutterBlameAnnotationProvider(this.context, editor, Decorations.blameAnnotation, Decorations.blameHighlight, this._whitespaceController, this.git, gitUri);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileAnnotationType.Hover:
|
case FileAnnotationType.Hover:
|
||||||
provider = new HoverBlameAnnotationProvider(this.context, editor, Decorations.annotation, Decorations.highlight, this._whitespaceController, this.git, gitUri);
|
provider = new HoverBlameAnnotationProvider(this.context, editor, Decorations.blameAnnotation, Decorations.blameHighlight, this._whitespaceController, this.git, gitUri);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FileAnnotationType.RecentChanges:
|
||||||
|
provider = new RecentChangesAnnotationProvider(this.context, editor, undefined, Decorations.recentChangesHighlight!, this.git, gitUri);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (provider === undefined || !(await provider.validate())) return false;
|
if (provider === undefined || !(await provider.validate())) return false;
|
||||||
@@ -219,13 +272,17 @@ export class AnnotationController extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async toggleAnnotations(editor: TextEditor, type: FileAnnotationType, shaOrLine?: string | number): Promise<boolean> {
|
async toggleAnnotations(editor: TextEditor, type: FileAnnotationType, shaOrLine?: string | number): Promise<boolean> {
|
||||||
if (!editor || !editor.document || !this.git.isEditorBlameable(editor)) return false;
|
if (!editor || !editor.document || type === FileAnnotationType.RecentChanges ? !this.git.isTrackable(editor.document.uri) : !this.git.isEditorBlameable(editor)) return false;
|
||||||
|
|
||||||
const provider = this._annotationProviders.get(editor.viewColumn || -1);
|
const provider = this._annotationProviders.get(editor.viewColumn || -1);
|
||||||
if (provider === undefined) return this.showAnnotations(editor, type, shaOrLine);
|
if (provider === undefined) return this.showAnnotations(editor, type, shaOrLine);
|
||||||
|
|
||||||
|
const reopen = provider.annotationType !== type;
|
||||||
await this.clear(provider.editor.viewColumn || -1);
|
await this.clear(provider.editor.viewColumn || -1);
|
||||||
return false;
|
|
||||||
|
if (!reopen) return false;
|
||||||
|
|
||||||
|
return this.showAnnotations(editor, type, shaOrLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onBlameabilityChanged(e: BlameabilityChangeEvent) {
|
private _onBlameabilityChanged(e: BlameabilityChangeEvent) {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Functions } from '../system';
|
import { Functions } from '../system';
|
||||||
import { Disposable, ExtensionContext, TextDocument, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, window, workspace } from 'vscode';
|
import { Disposable, ExtensionContext, TextDocument, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, window, workspace } from 'vscode';
|
||||||
|
import { FileAnnotationType } from '../annotations/annotationController';
|
||||||
import { TextDocumentComparer } from '../comparers';
|
import { TextDocumentComparer } from '../comparers';
|
||||||
import { ExtensionKey, FileAnnotationType, IConfig } from '../configuration';
|
import { ExtensionKey, IConfig } from '../configuration';
|
||||||
import { WhitespaceController } from './whitespaceController';
|
import { WhitespaceController } from './whitespaceController';
|
||||||
|
|
||||||
export abstract class AnnotationProviderBase extends Disposable {
|
export abstract class AnnotationProviderBase extends Disposable {
|
||||||
@@ -13,7 +14,7 @@ import { WhitespaceController } from './whitespaceController';
|
|||||||
protected _config: IConfig;
|
protected _config: IConfig;
|
||||||
protected _disposable: Disposable;
|
protected _disposable: Disposable;
|
||||||
|
|
||||||
constructor(context: ExtensionContext, public editor: TextEditor, protected decoration: TextEditorDecorationType, protected highlightDecoration: TextEditorDecorationType | undefined, protected whitespaceController: WhitespaceController | undefined) {
|
constructor(context: ExtensionContext, public editor: TextEditor, protected decoration: TextEditorDecorationType | undefined, protected highlightDecoration: TextEditorDecorationType | undefined, protected whitespaceController: WhitespaceController | undefined) {
|
||||||
super(() => this.dispose());
|
super(() => this.dispose());
|
||||||
|
|
||||||
this.document = this.editor.document;
|
this.document = this.editor.document;
|
||||||
@@ -42,10 +43,14 @@ import { WhitespaceController } from './whitespaceController';
|
|||||||
async clear() {
|
async clear() {
|
||||||
if (this.editor !== undefined) {
|
if (this.editor !== undefined) {
|
||||||
try {
|
try {
|
||||||
this.editor.setDecorations(this.decoration, []);
|
if (this.decoration !== undefined) {
|
||||||
this.highlightDecoration && this.editor.setDecorations(this.highlightDecoration, []);
|
this.editor.setDecorations(this.decoration, []);
|
||||||
// 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) {
|
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);
|
await Functions.wait(1);
|
||||||
|
|
||||||
if (this.highlightDecoration === undefined) return;
|
if (this.highlightDecoration === undefined) return;
|
||||||
@@ -60,10 +65,12 @@ import { WhitespaceController } from './whitespaceController';
|
|||||||
this.whitespaceController && await this.whitespaceController.restore();
|
this.whitespaceController && await this.whitespaceController.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
async reset(whitespaceController: WhitespaceController | undefined) {
|
async reset(decoration: TextEditorDecorationType | undefined, highlightDecoration: TextEditorDecorationType | undefined, whitespaceController?: WhitespaceController) {
|
||||||
await this.clear();
|
await this.clear();
|
||||||
|
|
||||||
this._config = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
|
this._config = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
|
||||||
|
this.decoration = decoration;
|
||||||
|
this.highlightDecoration = highlightDecoration;
|
||||||
this.whitespaceController = whitespaceController;
|
this.whitespaceController = whitespaceController;
|
||||||
|
|
||||||
await this.provideAnnotation(this.editor === undefined ? undefined : this.editor.selection.active.line);
|
await this.provideAnnotation(this.editor === undefined ? undefined : this.editor.selection.active.line);
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ export class Annotations {
|
|||||||
} as DecorationOptions;
|
} as DecorationOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
static detailsHover(commit: GitCommit): DecorationOptions {
|
static detailsHover(commit: GitCommit, dateFormat: string | null): DecorationOptions {
|
||||||
const message = CommitFormatter.toHoverAnnotation(commit);
|
const message = CommitFormatter.toHoverAnnotation(commit, dateFormat);
|
||||||
return {
|
return {
|
||||||
hoverMessage: message
|
hoverMessage: message
|
||||||
} as DecorationOptions;
|
} as DecorationOptions;
|
||||||
@@ -127,9 +127,9 @@ export class Annotations {
|
|||||||
} as IRenderOptions;
|
} as IRenderOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
static hover(commit: GitCommit, renderOptions: IRenderOptions, heatmap: boolean): DecorationOptions {
|
static hover(commit: GitCommit, renderOptions: IRenderOptions, heatmap: boolean, dateFormat: string | null): DecorationOptions {
|
||||||
return {
|
return {
|
||||||
hoverMessage: CommitFormatter.toHoverAnnotation(commit),
|
hoverMessage: CommitFormatter.toHoverAnnotation(commit, dateFormat),
|
||||||
renderOptions: heatmap ? { before: { ...renderOptions.before } } : undefined
|
renderOptions: heatmap ? { before: { ...renderOptions.before } } : undefined
|
||||||
} as DecorationOptions;
|
} as DecorationOptions;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
|
|||||||
|
|
||||||
protected _blame: Promise<GitBlame>;
|
protected _blame: Promise<GitBlame>;
|
||||||
|
|
||||||
constructor(context: ExtensionContext, editor: TextEditor, decoration: TextEditorDecorationType, highlightDecoration: TextEditorDecorationType | undefined, whitespaceController: WhitespaceController | undefined, protected git: GitService, protected uri: GitUri) {
|
constructor(context: ExtensionContext, editor: TextEditor, decoration: TextEditorDecorationType | undefined, highlightDecoration: TextEditorDecorationType | undefined, whitespaceController: WhitespaceController | undefined, protected git: GitService, protected uri: GitUri) {
|
||||||
super(context, editor, decoration, highlightDecoration, whitespaceController);
|
super(context, editor, decoration, highlightDecoration, whitespaceController);
|
||||||
|
|
||||||
this._blame = this.git.getBlameForFile(this.uri);
|
this._blame = this.git.getBlameForFile(this.uri);
|
||||||
@@ -56,7 +56,6 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
|
|||||||
const blame = await this._blame;
|
const blame = await this._blame;
|
||||||
return blame !== undefined && blame.lines.length !== 0;
|
return blame !== undefined && blame.lines.length !== 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async getBlame(requiresWhitespaceHack: boolean): Promise<GitBlame | undefined> {
|
protected async getBlame(requiresWhitespaceHack: boolean): Promise<GitBlame | undefined> {
|
||||||
let whitespacePromise: Promise<void> | undefined;
|
let whitespacePromise: Promise<void> | undefined;
|
||||||
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- override whitespace (turn off)
|
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- override whitespace (turn off)
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
'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.currentPosition.start - 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Strings } from '../system';
|
import { Strings } from '../system';
|
||||||
import { DecorationOptions, Range } from 'vscode';
|
import { DecorationOptions, Range } from 'vscode';
|
||||||
|
import { FileAnnotationType } from './annotationController';
|
||||||
import { BlameAnnotationProviderBase } from './blameAnnotationProvider';
|
import { BlameAnnotationProviderBase } from './blameAnnotationProvider';
|
||||||
import { Annotations, endOfLineIndex } from './annotations';
|
import { Annotations, endOfLineIndex } from './annotations';
|
||||||
import { FileAnnotationType } from '../configuration';
|
|
||||||
import { ICommitFormatOptions } from '../gitService';
|
import { ICommitFormatOptions } from '../gitService';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
|||||||
}, {} as { [token: string]: ICommitFormatOptions });
|
}, {} as { [token: string]: ICommitFormatOptions });
|
||||||
|
|
||||||
const options: ICommitFormatOptions = {
|
const options: ICommitFormatOptions = {
|
||||||
dateFormat: cfg.dateFormat,
|
dateFormat: cfg.dateFormat === null ? this._config.defaultDateFormat : cfg.dateFormat,
|
||||||
tokenOptions: tokenOptions
|
tokenOptions: tokenOptions
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,6 +33,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
|||||||
const offset = this.uri.offset;
|
const offset = this.uri.offset;
|
||||||
let previousLine: string | undefined = undefined;
|
let previousLine: string | undefined = undefined;
|
||||||
const renderOptions = Annotations.gutterRenderOptions(this._config.theme, cfg.heatmap);
|
const renderOptions = Annotations.gutterRenderOptions(this._config.theme, cfg.heatmap);
|
||||||
|
const dateFormat = this._config.defaultDateFormat;
|
||||||
|
|
||||||
const decorations: DecorationOptions[] = [];
|
const decorations: DecorationOptions[] = [];
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
|||||||
decorations.push(gutter);
|
decorations.push(gutter);
|
||||||
|
|
||||||
if (cfg.hover.details) {
|
if (cfg.hover.details) {
|
||||||
const details = Annotations.detailsHover(commit);
|
const details = Annotations.detailsHover(commit, dateFormat);
|
||||||
details.range = cfg.hover.wholeLine
|
details.range = cfg.hover.wholeLine
|
||||||
? this.editor.document.validateRange(new Range(line, 0, line, endOfLineIndex))
|
? this.editor.document.validateRange(new Range(line, 0, line, endOfLineIndex))
|
||||||
: gutter.range;
|
: gutter.range;
|
||||||
@@ -67,7 +68,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (decorations.length) {
|
if (decorations.length) {
|
||||||
this.editor.setDecorations(this.decoration, decorations);
|
this.editor.setDecorations(this.decoration!, decorations);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selection(shaOrLine, blame);
|
this.selection(shaOrLine, blame);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { DecorationOptions, Range } from 'vscode';
|
import { DecorationOptions, Range } from 'vscode';
|
||||||
|
import { FileAnnotationType } from './annotationController';
|
||||||
import { BlameAnnotationProviderBase } from './blameAnnotationProvider';
|
import { BlameAnnotationProviderBase } from './blameAnnotationProvider';
|
||||||
import { Annotations, endOfLineIndex } from './annotations';
|
import { Annotations, endOfLineIndex } from './annotations';
|
||||||
import { FileAnnotationType } from '../configuration';
|
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
export class HoverBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
export class HoverBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
||||||
@@ -18,6 +18,7 @@ export class HoverBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
|||||||
const now = moment();
|
const now = moment();
|
||||||
const offset = this.uri.offset;
|
const offset = this.uri.offset;
|
||||||
const renderOptions = Annotations.hoverRenderOptions(this._config.theme, cfg.heatmap);
|
const renderOptions = Annotations.hoverRenderOptions(this._config.theme, cfg.heatmap);
|
||||||
|
const dateFormat = this._config.defaultDateFormat;
|
||||||
|
|
||||||
const decorations: DecorationOptions[] = [];
|
const decorations: DecorationOptions[] = [];
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ export class HoverBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
|||||||
|
|
||||||
const line = l.line + offset;
|
const line = l.line + offset;
|
||||||
|
|
||||||
const hover = Annotations.hover(commit, renderOptions, cfg.heatmap.enabled);
|
const hover = Annotations.hover(commit, renderOptions, cfg.heatmap.enabled, dateFormat);
|
||||||
|
|
||||||
const endIndex = cfg.wholeLine ? endOfLineIndex : this.editor.document.lineAt(line).firstNonWhitespaceCharacterIndex;
|
const endIndex = cfg.wholeLine ? endOfLineIndex : this.editor.document.lineAt(line).firstNonWhitespaceCharacterIndex;
|
||||||
hover.range = this.editor.document.validateRange(new Range(line, 0, line, endIndex));
|
hover.range = this.editor.document.validateRange(new Range(line, 0, line, endIndex));
|
||||||
@@ -40,7 +41,7 @@ export class HoverBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (decorations.length) {
|
if (decorations.length) {
|
||||||
this.editor.setDecorations(this.decoration, decorations);
|
this.editor.setDecorations(this.decoration!, decorations);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selection(shaOrLine, blame);
|
this.selection(shaOrLine, blame);
|
||||||
|
|||||||
74
src/annotations/recentChangesAnnotationProvider.ts
Normal file
74
src/annotations/recentChangesAnnotationProvider.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
'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 dateFormat = this._config.defaultDateFormat;
|
||||||
|
|
||||||
|
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;
|
||||||
|
if (cfg.hover.details || cfg.hover.changes) {
|
||||||
|
endingIndex = cfg.hover.wholeLine ? endOfLineIndex : this.editor.document.lineAt(count).firstNonWhitespaceCharacterIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
const range = this.editor.document.validateRange(new Range(new Position(count, 0), new Position(count, endingIndex)));
|
||||||
|
|
||||||
|
if (cfg.hover.details) {
|
||||||
|
decorators.push({
|
||||||
|
hoverMessage: CommitFormatter.toHoverAnnotation(commit, dateFormat),
|
||||||
|
range: range
|
||||||
|
} as DecorationOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
let message: string | undefined = undefined;
|
||||||
|
if (cfg.hover.changes) {
|
||||||
|
message = CommitFormatter.toHoverDiff(commit, chunk.previous[count], change);
|
||||||
|
}
|
||||||
|
|
||||||
|
decorators.push({
|
||||||
|
hoverMessage: message,
|
||||||
|
range: range
|
||||||
|
} as DecorationOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.editor.setDecorations(this.highlightDecoration!, decorators);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async selection(shaOrLine?: string | number): Promise<void> {
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,16 +21,16 @@ export * from './commands/openInRemote';
|
|||||||
export * from './commands/openRepoInRemote';
|
export * from './commands/openRepoInRemote';
|
||||||
export * from './commands/resetSuppressedWarnings';
|
export * from './commands/resetSuppressedWarnings';
|
||||||
export * from './commands/showBlameHistory';
|
export * from './commands/showBlameHistory';
|
||||||
|
export * from './commands/showCommitSearch';
|
||||||
export * from './commands/showFileBlame';
|
export * from './commands/showFileBlame';
|
||||||
export * from './commands/showFileHistory';
|
export * from './commands/showFileHistory';
|
||||||
export * from './commands/showLastQuickPick';
|
export * from './commands/showLastQuickPick';
|
||||||
export * from './commands/showLineBlame';
|
export * from './commands/showLineBlame';
|
||||||
|
export * from './commands/showQuickBranchHistory';
|
||||||
export * from './commands/showQuickCommitDetails';
|
export * from './commands/showQuickCommitDetails';
|
||||||
export * from './commands/showQuickCommitFileDetails';
|
export * from './commands/showQuickCommitFileDetails';
|
||||||
export * from './commands/showCommitSearch';
|
|
||||||
export * from './commands/showQuickFileHistory';
|
|
||||||
export * from './commands/showQuickBranchHistory';
|
|
||||||
export * from './commands/showQuickCurrentBranchHistory';
|
export * from './commands/showQuickCurrentBranchHistory';
|
||||||
|
export * from './commands/showQuickFileHistory';
|
||||||
export * from './commands/showQuickRepoStatus';
|
export * from './commands/showQuickRepoStatus';
|
||||||
export * from './commands/showQuickStashList';
|
export * from './commands/showQuickStashList';
|
||||||
export * from './commands/stashApply';
|
export * from './commands/stashApply';
|
||||||
@@ -38,4 +38,5 @@ export * from './commands/stashDelete';
|
|||||||
export * from './commands/stashSave';
|
export * from './commands/stashSave';
|
||||||
export * from './commands/toggleCodeLens';
|
export * from './commands/toggleCodeLens';
|
||||||
export * from './commands/toggleFileBlame';
|
export * from './commands/toggleFileBlame';
|
||||||
|
export * from './commands/toggleFileRecentChanges';
|
||||||
export * from './commands/toggleLineBlame';
|
export * from './commands/toggleLineBlame';
|
||||||
@@ -39,6 +39,7 @@ export type Commands = 'gitlens.closeUnchangedFiles' |
|
|||||||
'gitlens.stashSave' |
|
'gitlens.stashSave' |
|
||||||
'gitlens.toggleCodeLens' |
|
'gitlens.toggleCodeLens' |
|
||||||
'gitlens.toggleFileBlame' |
|
'gitlens.toggleFileBlame' |
|
||||||
|
'gitlens.toggleFileRecentChanges' |
|
||||||
'gitlens.toggleLineBlame';
|
'gitlens.toggleLineBlame';
|
||||||
export const Commands = {
|
export const Commands = {
|
||||||
CloseUnchangedFiles: 'gitlens.closeUnchangedFiles' as Commands,
|
CloseUnchangedFiles: 'gitlens.closeUnchangedFiles' as Commands,
|
||||||
@@ -76,6 +77,7 @@ export const Commands = {
|
|||||||
StashSave: 'gitlens.stashSave' as Commands,
|
StashSave: 'gitlens.stashSave' as Commands,
|
||||||
ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands,
|
ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands,
|
||||||
ToggleFileBlame: 'gitlens.toggleFileBlame' as Commands,
|
ToggleFileBlame: 'gitlens.toggleFileBlame' as Commands,
|
||||||
|
ToggleFileRecentChanges: 'gitlens.toggleFileRecentChanges' as Commands,
|
||||||
ToggleLineBlame: 'gitlens.toggleLineBlame' as Commands
|
ToggleLineBlame: 'gitlens.toggleLineBlame' as Commands
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
|
import { TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
|
||||||
import { AnnotationController } from '../annotations/annotationController';
|
import { AnnotationController, FileAnnotationType } from '../annotations/annotationController';
|
||||||
import { Commands, EditorCommand } from './common';
|
import { Commands, EditorCommand } from './common';
|
||||||
import { ExtensionKey, FileAnnotationType, IConfig } from '../configuration';
|
import { ExtensionKey, IConfig } from '../configuration';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
export interface ShowFileBlameCommandArgs {
|
export interface ShowFileBlameCommandArgs {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
|
import { TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
|
||||||
import { CurrentLineController } from '../currentLineController';
|
import { CurrentLineController, LineAnnotationType } from '../currentLineController';
|
||||||
import { Commands, EditorCommand } from './common';
|
import { Commands, EditorCommand } from './common';
|
||||||
import { ExtensionKey, IConfig, LineAnnotationType } from '../configuration';
|
import { ExtensionKey, IConfig } from '../configuration';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
export interface ShowLineBlameCommandArgs {
|
export interface ShowLineBlameCommandArgs {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
|
import { TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
|
||||||
import { AnnotationController } from '../annotations/annotationController';
|
import { AnnotationController, FileAnnotationType } from '../annotations/annotationController';
|
||||||
import { Commands, EditorCommand } from './common';
|
import { Commands, EditorCommand } from './common';
|
||||||
import { ExtensionKey, FileAnnotationType, IConfig } from '../configuration';
|
import { ExtensionKey, IConfig } from '../configuration';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
export interface ToggleFileBlameCommandArgs {
|
export interface ToggleFileBlameCommandArgs {
|
||||||
|
|||||||
24
src/commands/toggleFileRecentChanges.ts
Normal file
24
src/commands/toggleFileRecentChanges.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
'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`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
|
import { TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
|
||||||
import { CurrentLineController } from '../currentLineController';
|
import { CurrentLineController, LineAnnotationType } from '../currentLineController';
|
||||||
import { Commands, EditorCommand } from './common';
|
import { Commands, EditorCommand } from './common';
|
||||||
import { ExtensionKey, IConfig, LineAnnotationType } from '../configuration';
|
import { ExtensionKey, IConfig } from '../configuration';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
export interface ToggleLineBlameCommandArgs {
|
export interface ToggleLineBlameCommandArgs {
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
import { FileAnnotationType } from './annotations/annotationController';
|
||||||
import { Commands } from './commands';
|
import { Commands } from './commands';
|
||||||
|
import { LineAnnotationType } from './currentLineController';
|
||||||
import { OutputLevel } from './logger';
|
import { OutputLevel } from './logger';
|
||||||
|
|
||||||
export { ExtensionKey } from './constants';
|
export { ExtensionKey } from './constants';
|
||||||
|
|
||||||
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' |
|
export type CodeLensCommand = 'gitlens.toggleFileBlame' |
|
||||||
'gitlens.showBlameHistory' |
|
'gitlens.showBlameHistory' |
|
||||||
'gitlens.showFileHistory' |
|
'gitlens.showFileHistory' |
|
||||||
@@ -38,16 +33,11 @@ export const CodeLensLocations = {
|
|||||||
Custom: 'custom' as CodeLensLocations
|
Custom: 'custom' as CodeLensLocations
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FileAnnotationType = 'gutter' | 'hover';
|
export type LineHighlightLocations = 'gutter' | 'line' | 'overviewRuler';
|
||||||
export const FileAnnotationType = {
|
export const LineHighlightLocations = {
|
||||||
Gutter: 'gutter' as FileAnnotationType,
|
Gutter: 'gutter' as LineHighlightLocations,
|
||||||
Hover: 'hover' as FileAnnotationType
|
Line: 'line' as LineHighlightLocations,
|
||||||
};
|
OverviewRuler: 'overviewRuler' as LineHighlightLocations
|
||||||
|
|
||||||
export type LineAnnotationType = 'trailing' | 'hover';
|
|
||||||
export const LineAnnotationType = {
|
|
||||||
Trailing: 'trailing' as LineAnnotationType,
|
|
||||||
Hover: 'hover' as LineAnnotationType
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StatusBarCommand = 'gitlens.toggleFileBlame' |
|
export type StatusBarCommand = 'gitlens.toggleFileBlame' |
|
||||||
@@ -226,7 +216,7 @@ export interface IConfig {
|
|||||||
file: {
|
file: {
|
||||||
gutter: {
|
gutter: {
|
||||||
format: string;
|
format: string;
|
||||||
dateFormat: string;
|
dateFormat: string | null;
|
||||||
compact: boolean;
|
compact: boolean;
|
||||||
heatmap: {
|
heatmap: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
@@ -244,6 +234,14 @@ export interface IConfig {
|
|||||||
};
|
};
|
||||||
wholeLine: boolean;
|
wholeLine: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
recentChanges: {
|
||||||
|
hover: {
|
||||||
|
details: boolean;
|
||||||
|
changes: boolean;
|
||||||
|
wholeLine: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
line: {
|
line: {
|
||||||
@@ -254,7 +252,7 @@ export interface IConfig {
|
|||||||
|
|
||||||
trailing: {
|
trailing: {
|
||||||
format: string;
|
format: string;
|
||||||
dateFormat: string;
|
dateFormat: string | null;
|
||||||
hover: {
|
hover: {
|
||||||
changes: boolean;
|
changes: boolean;
|
||||||
details: boolean;
|
details: boolean;
|
||||||
@@ -269,7 +267,7 @@ export interface IConfig {
|
|||||||
annotationType: FileAnnotationType;
|
annotationType: FileAnnotationType;
|
||||||
lineHighlight: {
|
lineHighlight: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
locations: BlameLineHighlightLocations[];
|
locations: LineHighlightLocations[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -279,6 +277,14 @@ export interface IConfig {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
recentChanges: {
|
||||||
|
file: {
|
||||||
|
lineHighlight: {
|
||||||
|
locations: LineHighlightLocations[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
codeLens: {
|
codeLens: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
recentChange: {
|
recentChange: {
|
||||||
@@ -295,12 +301,14 @@ export interface IConfig {
|
|||||||
debug: boolean;
|
debug: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
defaultDateFormat: string | null;
|
||||||
|
|
||||||
statusBar: {
|
statusBar: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
alignment: 'left' | 'right';
|
alignment: 'left' | 'right';
|
||||||
command: StatusBarCommand;
|
command: StatusBarCommand;
|
||||||
format: string;
|
format: string;
|
||||||
dateFormat: string;
|
dateFormat: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
strings: {
|
strings: {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Functions, Objects } from './system';
|
import { Functions, Objects } from './system';
|
||||||
import { DecorationOptions, DecorationRenderOptions, Disposable, ExtensionContext, Range, StatusBarAlignment, StatusBarItem, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, window, workspace } from 'vscode';
|
import { DecorationOptions, DecorationRenderOptions, Disposable, ExtensionContext, Range, StatusBarAlignment, StatusBarItem, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, window, workspace } from 'vscode';
|
||||||
import { AnnotationController } from './annotations/annotationController';
|
import { AnnotationController, FileAnnotationType } from './annotations/annotationController';
|
||||||
import { Annotations, endOfLineIndex } from './annotations/annotations';
|
import { Annotations, endOfLineIndex } from './annotations/annotations';
|
||||||
import { Commands } from './commands';
|
import { Commands } from './commands';
|
||||||
import { TextEditorComparer } from './comparers';
|
import { TextEditorComparer } from './comparers';
|
||||||
import { FileAnnotationType, IConfig, LineAnnotationType, StatusBarCommand } from './configuration';
|
import { IConfig, StatusBarCommand } from './configuration';
|
||||||
import { DocumentSchemes, ExtensionKey } from './constants';
|
import { DocumentSchemes, ExtensionKey } from './constants';
|
||||||
import { BlameabilityChangeEvent, CommitFormatter, GitCommit, GitCommitLine, GitContextTracker, GitService, GitUri } from './gitService';
|
import { BlameabilityChangeEvent, CommitFormatter, GitCommit, GitCommitLine, GitContextTracker, GitService, GitUri } from './gitService';
|
||||||
|
|
||||||
@@ -16,6 +16,12 @@ const annotationDecoration: TextEditorDecorationType = window.createTextEditorDe
|
|||||||
}
|
}
|
||||||
} as DecorationRenderOptions);
|
} as DecorationRenderOptions);
|
||||||
|
|
||||||
|
export type LineAnnotationType = 'trailing' | 'hover';
|
||||||
|
export const LineAnnotationType = {
|
||||||
|
Trailing: 'trailing' as LineAnnotationType,
|
||||||
|
Hover: 'hover' as LineAnnotationType
|
||||||
|
};
|
||||||
|
|
||||||
export class CurrentLineController extends Disposable {
|
export class CurrentLineController extends Disposable {
|
||||||
|
|
||||||
private _activeEditorLineDisposable: Disposable | undefined;
|
private _activeEditorLineDisposable: Disposable | undefined;
|
||||||
@@ -285,7 +291,7 @@ export class CurrentLineController extends Disposable {
|
|||||||
showDetailsInStartingWhitespace = true;
|
showDetailsInStartingWhitespace = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const decoration = Annotations.trailing(commit, cfgAnnotations.format, cfgAnnotations.dateFormat, this._config.theme);
|
const decoration = Annotations.trailing(commit, cfgAnnotations.format, cfgAnnotations.dateFormat === null ? this._config.defaultDateFormat : cfgAnnotations.dateFormat, this._config.theme);
|
||||||
decoration.range = editor.document.validateRange(new Range(line, endOfLineIndex, line, endOfLineIndex));
|
decoration.range = editor.document.validateRange(new Range(line, endOfLineIndex, line, endOfLineIndex));
|
||||||
decorationOptions.push(decoration);
|
decorationOptions.push(decoration);
|
||||||
|
|
||||||
@@ -351,6 +357,30 @@ export class CurrentLineController extends Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FileAnnotationType.RecentChanges: {
|
||||||
|
const cfgChanges = this._config.annotations.file.recentChanges.hover;
|
||||||
|
if (cfgChanges.details) {
|
||||||
|
if (cfgChanges.wholeLine) {
|
||||||
|
// Avoid double annotations if we are showing the whole-file hover blame annotations
|
||||||
|
showDetails = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
showDetailsInStartingWhitespace = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,7 +395,7 @@ export class CurrentLineController extends Disposable {
|
|||||||
// I have no idea why I need this protection -- but it happens
|
// I have no idea why I need this protection -- but it happens
|
||||||
if (editor.document === undefined) return;
|
if (editor.document === undefined) return;
|
||||||
|
|
||||||
const decoration = Annotations.detailsHover(logCommit || commit);
|
const decoration = Annotations.detailsHover(logCommit || commit, this._config.defaultDateFormat);
|
||||||
decoration.range = editor.document.validateRange(new Range(line, showDetailsStartIndex, line, endOfLineIndex));
|
decoration.range = editor.document.validateRange(new Range(line, showDetailsStartIndex, line, endOfLineIndex));
|
||||||
decorationOptions.push(decoration);
|
decorationOptions.push(decoration);
|
||||||
|
|
||||||
@@ -399,7 +429,7 @@ export class CurrentLineController extends Disposable {
|
|||||||
const cfg = this._config.statusBar;
|
const cfg = this._config.statusBar;
|
||||||
if (!cfg.enabled || this._statusBarItem === undefined) return;
|
if (!cfg.enabled || this._statusBarItem === undefined) return;
|
||||||
|
|
||||||
this._statusBarItem.text = `$(git-commit) ${CommitFormatter.fromTemplate(cfg.format, commit, cfg.dateFormat)}`;
|
this._statusBarItem.text = `$(git-commit) ${CommitFormatter.fromTemplate(cfg.format, commit, cfg.dateFormat === null ? this._config.defaultDateFormat : cfg.dateFormat)}`;
|
||||||
|
|
||||||
switch (cfg.command) {
|
switch (cfg.command) {
|
||||||
case StatusBarCommand.BlameAnnotate:
|
case StatusBarCommand.BlameAnnotate:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { OpenBranchInRemoteCommand, OpenCommitInRemoteCommand, OpenFileInRemoteC
|
|||||||
import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands';
|
import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands';
|
||||||
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithWorkingCommand} from './commands';
|
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithWorkingCommand} from './commands';
|
||||||
import { ResetSuppressedWarningsCommand } from './commands';
|
import { ResetSuppressedWarningsCommand } from './commands';
|
||||||
import { ShowFileBlameCommand, ShowLineBlameCommand, ToggleFileBlameCommand, ToggleLineBlameCommand } from './commands';
|
import { ShowFileBlameCommand, ShowLineBlameCommand, ToggleFileBlameCommand, ToggleFileRecentChangesCommand, ToggleLineBlameCommand } from './commands';
|
||||||
import { ShowBlameHistoryCommand, ShowFileHistoryCommand } from './commands';
|
import { ShowBlameHistoryCommand, ShowFileHistoryCommand } from './commands';
|
||||||
import { ShowLastQuickPickCommand } from './commands';
|
import { ShowLastQuickPickCommand } from './commands';
|
||||||
import { ShowQuickBranchHistoryCommand, ShowQuickCurrentBranchHistoryCommand, ShowQuickFileHistoryCommand } from './commands';
|
import { ShowQuickBranchHistoryCommand, ShowQuickCurrentBranchHistoryCommand, ShowQuickFileHistoryCommand } from './commands';
|
||||||
@@ -17,12 +17,12 @@ import { ShowQuickRepoStatusCommand, ShowQuickStashListCommand } from './command
|
|||||||
import { StashApplyCommand, StashDeleteCommand, StashSaveCommand } from './commands';
|
import { StashApplyCommand, StashDeleteCommand, StashSaveCommand } from './commands';
|
||||||
import { ToggleCodeLensCommand } from './commands';
|
import { ToggleCodeLensCommand } from './commands';
|
||||||
import { Keyboard } from './commands';
|
import { Keyboard } from './commands';
|
||||||
import { BlameLineHighlightLocations, CodeLensLocations, IConfig, LineAnnotationType } from './configuration';
|
import { CodeLensLocations, IConfig, LineHighlightLocations } from './configuration';
|
||||||
import { ApplicationInsightsKey, ExtensionKey, QualifiedExtensionId, WorkspaceState } from './constants';
|
import { ApplicationInsightsKey, ExtensionKey, QualifiedExtensionId, WorkspaceState } from './constants';
|
||||||
import { CurrentLineController } from './currentLineController';
|
import { CurrentLineController, LineAnnotationType } from './currentLineController';
|
||||||
import { GitContentProvider } from './gitContentProvider';
|
import { GitContentProvider } from './gitContentProvider';
|
||||||
import { GitContextTracker, GitService } from './gitService';
|
|
||||||
import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider';
|
import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider';
|
||||||
|
import { GitContextTracker, GitService } from './gitService';
|
||||||
import { Logger } from './logger';
|
import { Logger } from './logger';
|
||||||
import { Messages, SuppressedKeys } from './messages';
|
import { Messages, SuppressedKeys } from './messages';
|
||||||
import { Telemetry } from './telemetry';
|
import { Telemetry } from './telemetry';
|
||||||
@@ -107,6 +107,7 @@ export async function activate(context: ExtensionContext) {
|
|||||||
context.subscriptions.push(new ShowFileBlameCommand(annotationController));
|
context.subscriptions.push(new ShowFileBlameCommand(annotationController));
|
||||||
context.subscriptions.push(new ShowLineBlameCommand(currentLineController));
|
context.subscriptions.push(new ShowLineBlameCommand(currentLineController));
|
||||||
context.subscriptions.push(new ToggleFileBlameCommand(annotationController));
|
context.subscriptions.push(new ToggleFileBlameCommand(annotationController));
|
||||||
|
context.subscriptions.push(new ToggleFileRecentChangesCommand(annotationController));
|
||||||
context.subscriptions.push(new ToggleLineBlameCommand(currentLineController));
|
context.subscriptions.push(new ToggleLineBlameCommand(currentLineController));
|
||||||
context.subscriptions.push(new ResetSuppressedWarningsCommand(context));
|
context.subscriptions.push(new ResetSuppressedWarningsCommand(context));
|
||||||
context.subscriptions.push(new ShowBlameHistoryCommand(git));
|
context.subscriptions.push(new ShowBlameHistoryCommand(git));
|
||||||
@@ -166,10 +167,10 @@ async function migrateSettings(context: ExtensionContext) {
|
|||||||
await cfg.update('blame.file.lineHighlight.enabled', false);
|
await cfg.update('blame.file.lineHighlight.enabled', false);
|
||||||
break;
|
break;
|
||||||
case 'gutter':
|
case 'gutter':
|
||||||
await cfg.update('blame.file.lineHighlight.locations', [BlameLineHighlightLocations.Gutter, BlameLineHighlightLocations.OverviewRuler], true);
|
await cfg.update('blame.file.lineHighlight.locations', [LineHighlightLocations.Gutter, LineHighlightLocations.OverviewRuler], true);
|
||||||
break;
|
break;
|
||||||
case 'line':
|
case 'line':
|
||||||
await cfg.update('blame.file.lineHighlight.locations', [BlameLineHighlightLocations.Line, BlameLineHighlightLocations.OverviewRuler], true);
|
await cfg.update('blame.file.lineHighlight.locations', [LineHighlightLocations.Line, LineHighlightLocations.OverviewRuler], true);
|
||||||
break;
|
break;
|
||||||
case 'both':
|
case 'both':
|
||||||
}
|
}
|
||||||
@@ -269,6 +270,8 @@ async function notifyOnNewGitLensVersion(context: ExtensionContext, version: str
|
|||||||
const [major, minor] = version.split('.');
|
const [major, minor] = version.split('.');
|
||||||
const [prevMajor, prevMinor] = previousVersion.split('.');
|
const [prevMajor, prevMinor] = previousVersion.split('.');
|
||||||
if (major === prevMajor && minor === prevMinor) return;
|
if (major === prevMajor && minor === prevMinor) return;
|
||||||
|
// Don't notify on downgrades
|
||||||
|
if (major < prevMajor || (major === prevMajor && minor < prevMinor)) return;
|
||||||
|
|
||||||
await Messages.showUpdateMessage(version);
|
await Messages.showUpdateMessage(version);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import { GitCommit } from '../models/commit';
|
|||||||
import { GitDiffLine } from '../models/diff';
|
import { GitDiffLine } from '../models/diff';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
const escapeMarkdownRegEx = /[`\>\#\*\_\-\+\.]/g;
|
||||||
|
// const sampleMarkdown = '## message `not code` *not important* _no underline_ \n> don\'t quote me \n- don\'t list me \n+ don\'t list me \n1. don\'t list me \nnot h1 \n=== \nnot h2 \n---\n***\n---\n___';
|
||||||
|
|
||||||
export interface ICommitFormatOptions {
|
export interface ICommitFormatOptions {
|
||||||
dateFormat?: string | null;
|
dateFormat?: string | null;
|
||||||
tokenOptions?: {
|
tokenOptions?: {
|
||||||
@@ -17,9 +20,18 @@ export interface ICommitFormatOptions {
|
|||||||
|
|
||||||
export class CommitFormatter {
|
export class CommitFormatter {
|
||||||
|
|
||||||
|
private _commit: GitCommit;
|
||||||
private _options: ICommitFormatOptions;
|
private _options: ICommitFormatOptions;
|
||||||
|
|
||||||
constructor(private commit: GitCommit, options?: ICommitFormatOptions) {
|
constructor(commit: GitCommit, options?: ICommitFormatOptions) {
|
||||||
|
this.reset(commit, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(commit: GitCommit, options?: ICommitFormatOptions) {
|
||||||
|
this._commit = commit;
|
||||||
|
|
||||||
|
if (options === undefined && this._options !== undefined) return;
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
if (options.tokenOptions == null) {
|
if (options.tokenOptions == null) {
|
||||||
options.tokenOptions = {};
|
options.tokenOptions = {};
|
||||||
@@ -33,31 +45,31 @@ export class CommitFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get ago() {
|
get ago() {
|
||||||
const ago = moment(this.commit.date).fromNow();
|
const ago = moment(this._commit.date).fromNow();
|
||||||
return this._padOrTruncate(ago, this._options.tokenOptions!.ago);
|
return this._padOrTruncate(ago, this._options.tokenOptions!.ago);
|
||||||
}
|
}
|
||||||
|
|
||||||
get author() {
|
get author() {
|
||||||
const author = this.commit.author;
|
const author = this._commit.author;
|
||||||
return this._padOrTruncate(author, this._options.tokenOptions!.author);
|
return this._padOrTruncate(author, this._options.tokenOptions!.author);
|
||||||
}
|
}
|
||||||
|
|
||||||
get authorAgo() {
|
get authorAgo() {
|
||||||
const authorAgo = `${this.commit.author}, ${moment(this.commit.date).fromNow()}`;
|
const authorAgo = `${this._commit.author}, ${moment(this._commit.date).fromNow()}`;
|
||||||
return this._padOrTruncate(authorAgo, this._options.tokenOptions!.authorAgo);
|
return this._padOrTruncate(authorAgo, this._options.tokenOptions!.authorAgo);
|
||||||
}
|
}
|
||||||
|
|
||||||
get date() {
|
get date() {
|
||||||
const date = moment(this.commit.date).format(this._options.dateFormat!);
|
const date = moment(this._commit.date).format(this._options.dateFormat!);
|
||||||
return this._padOrTruncate(date, this._options.tokenOptions!.date);
|
return this._padOrTruncate(date, this._options.tokenOptions!.date);
|
||||||
}
|
}
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
return this.commit.shortSha;
|
return this._commit.shortSha;
|
||||||
}
|
}
|
||||||
|
|
||||||
get message() {
|
get message() {
|
||||||
const message = this.commit.isUncommitted ? 'Uncommitted change' : this.commit.message;
|
const message = this._commit.isUncommitted ? 'Uncommitted change' : this._commit.message;
|
||||||
return this._padOrTruncate(message, this._options.tokenOptions!.message);
|
return this._padOrTruncate(message, this._options.tokenOptions!.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +125,18 @@ export class CommitFormatter {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static _formatter: CommitFormatter | undefined = undefined;
|
||||||
|
|
||||||
|
static fromCommit(commit: GitCommit, options?: ICommitFormatOptions): CommitFormatter {
|
||||||
|
if (CommitFormatter._formatter === undefined) {
|
||||||
|
CommitFormatter._formatter = new CommitFormatter(commit, options);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CommitFormatter._formatter.reset(commit, options);
|
||||||
|
}
|
||||||
|
return CommitFormatter._formatter;
|
||||||
|
}
|
||||||
|
|
||||||
static fromTemplate(template: string, commit: GitCommit, dateFormat: string | null): string;
|
static fromTemplate(template: string, commit: GitCommit, dateFormat: string | null): string;
|
||||||
static fromTemplate(template: string, commit: GitCommit, options?: ICommitFormatOptions): string;
|
static fromTemplate(template: string, commit: GitCommit, options?: ICommitFormatOptions): string;
|
||||||
static fromTemplate(template: string, commit: GitCommit, dateFormatOrOptions?: string | null | ICommitFormatOptions): string;
|
static fromTemplate(template: string, commit: GitCommit, dateFormatOrOptions?: string | null | ICommitFormatOptions): string;
|
||||||
@@ -137,8 +161,22 @@ export class CommitFormatter {
|
|||||||
return Strings.interpolate(template, new CommitFormatter(commit, options));
|
return Strings.interpolate(template, new CommitFormatter(commit, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
static toHoverAnnotation(commit: GitCommit, dateFormat: string = 'MMMM Do, YYYY h:MMa'): string | string[] {
|
static toHoverAnnotation(commit: GitCommit, dateFormat: string | null): string | string[] {
|
||||||
const message = commit.isUncommitted ? '' : `\n\n> ${commit.message.replace(/\n/g, '\n>\n> ')}`;
|
if (dateFormat === null) {
|
||||||
|
dateFormat = 'MMMM Do, YYYY h:MMa';
|
||||||
|
}
|
||||||
|
|
||||||
|
let message = '';
|
||||||
|
if (!commit.isUncommitted) {
|
||||||
|
message = commit.message
|
||||||
|
// Escape markdown
|
||||||
|
.replace(escapeMarkdownRegEx, '\\$&')
|
||||||
|
// Escape markdown header (since the above regex won't match it)
|
||||||
|
.replace(/^===/gm, '\u200b===')
|
||||||
|
// Keep under the same block-quote
|
||||||
|
.replace(/\n/g, ' \n');
|
||||||
|
message = `\n\n> ${message}`;
|
||||||
|
}
|
||||||
return `\`${commit.shortSha}\` __${commit.author}__, ${moment(commit.date).fromNow()} _(${moment(commit.date).format(dateFormat)})_${message}`;
|
return `\`${commit.shortSha}\` __${commit.author}__, ${moment(commit.date).fromNow()} _(${moment(commit.date).format(dateFormat)})_${message}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,28 @@ export class GitUri extends Uri {
|
|||||||
const uri = Uri.file(path.resolve(repoPath, original ? status.originalFileName || status.fileName : status.fileName));
|
const uri = Uri.file(path.resolve(repoPath, original ? status.originalFileName || status.fileName : status.fileName));
|
||||||
return new GitUri(uri, repoPathOrCommit);
|
return new GitUri(uri, repoPathOrCommit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getDirectory(fileName: string): string {
|
||||||
|
const directory: string | undefined = GitService.normalizePath(path.dirname(fileName));
|
||||||
|
return (!directory || directory === '.') ? '' : directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getFormattedPath(fileNameOrUri: string | Uri, separator: string = ' \u00a0\u2022\u00a0 '): string {
|
||||||
|
let fileName: string;
|
||||||
|
if (fileNameOrUri instanceof Uri) {
|
||||||
|
if (fileNameOrUri instanceof GitUri) return fileNameOrUri.getFormattedPath(separator);
|
||||||
|
|
||||||
|
fileName = fileNameOrUri.fsPath;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fileName = fileNameOrUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
const directory = GitUri.getDirectory(fileName);
|
||||||
|
return !directory
|
||||||
|
? path.basename(fileName)
|
||||||
|
: `${path.basename(fileName)}${separator}${directory}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IGitCommitInfo {
|
export interface IGitCommitInfo {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Uri } from 'vscode';
|
import { Uri } from 'vscode';
|
||||||
|
import { GitUri } from '../gitUri';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
export interface GitStatus {
|
export interface GitStatus {
|
||||||
@@ -32,6 +33,14 @@ export class GitStatusFile implements IGitStatusFile {
|
|||||||
this.originalFileName = originalFileName;
|
this.originalFileName = originalFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFormattedDirectory(includeOriginal: boolean = false): string {
|
||||||
|
return GitStatusFile.getFormattedDirectory(this, includeOriginal);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormattedPath(separator: string = ' \u00a0\u2022\u00a0 '): string {
|
||||||
|
return GitUri.getFormattedPath(this.fileName, separator);
|
||||||
|
}
|
||||||
|
|
||||||
getIcon() {
|
getIcon() {
|
||||||
return getGitStatusIcon(this.status);
|
return getGitStatusIcon(this.status);
|
||||||
}
|
}
|
||||||
@@ -39,6 +48,13 @@ export class GitStatusFile implements IGitStatusFile {
|
|||||||
get Uri(): Uri {
|
get Uri(): Uri {
|
||||||
return Uri.file(path.resolve(this.repoPath, this.fileName));
|
return Uri.file(path.resolve(this.repoPath, this.fileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getFormattedDirectory(status: IGitStatusFile, includeOriginal: boolean = false): string {
|
||||||
|
const directory = GitUri.getDirectory(status.fileName);
|
||||||
|
return (includeOriginal && status.status === 'R' && status.originalFileName)
|
||||||
|
? `${directory} \u00a0\u2190\u00a0 ${status.originalFileName}`
|
||||||
|
: directory;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusOcticonsMap = {
|
const statusOcticonsMap = {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
|
|||||||
let blame: GitBlame | undefined;
|
let blame: GitBlame | undefined;
|
||||||
if (languageLocations.locations.length === 1 && languageLocations.locations.includes(CodeLensLocations.Document)) {
|
if (languageLocations.locations.length === 1 && languageLocations.locations.includes(CodeLensLocations.Document)) {
|
||||||
blame = await blamePromise;
|
blame = await blamePromise;
|
||||||
if (blame === undefined || !blame.lines.length) return lenses;
|
if (blame === undefined || blame.lines.length === 0) return lenses;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const values = await Promise.all([
|
const values = await Promise.all([
|
||||||
@@ -82,7 +82,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
blame = values[0] as GitBlame;
|
blame = values[0] as GitBlame;
|
||||||
if (blame === undefined || !blame.lines.length) return lenses;
|
if (blame === undefined || blame.lines.length === 0) return lenses;
|
||||||
|
|
||||||
const symbols = values[1] as SymbolInformation[];
|
const symbols = values[1] as SymbolInformation[];
|
||||||
Logger.log('GitCodeLensProvider.provideCodeLenses:', `${symbols.length} symbol(s) found`);
|
Logger.log('GitCodeLensProvider.provideCodeLenses:', `${symbols.length} symbol(s) found`);
|
||||||
|
|||||||
@@ -486,7 +486,7 @@ export class GitService extends Disposable {
|
|||||||
const authors: Map<string, GitAuthor> = new Map();
|
const authors: Map<string, GitAuthor> = new Map();
|
||||||
const commits: Map<string, GitBlameCommit> = new Map();
|
const commits: Map<string, GitBlameCommit> = new Map();
|
||||||
for (const c of blame.commits.values()) {
|
for (const c of blame.commits.values()) {
|
||||||
if (!shas.has(c.sha)) return;
|
if (!shas.has(c.sha)) continue;
|
||||||
|
|
||||||
const commit = new GitBlameCommit(c.repoPath, c.sha, c.fileName, c.author, c.date, c.message,
|
const commit = new GitBlameCommit(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);
|
c.lines.filter(l => l.line >= range.start.line && l.line <= range.end.line), c.originalFileName, c.previousSha, c.previousFileName);
|
||||||
@@ -521,13 +521,14 @@ export class GitService extends Disposable {
|
|||||||
if (blame === undefined) return undefined;
|
if (blame === undefined) return undefined;
|
||||||
|
|
||||||
const commitCount = blame.commits.size;
|
const commitCount = blame.commits.size;
|
||||||
|
const dateFormat = this.config.defaultDateFormat === null ? 'MMMM Do, YYYY h:MMa' : this.config.defaultDateFormat;
|
||||||
|
|
||||||
const locations: Location[] = [];
|
const locations: Location[] = [];
|
||||||
Iterables.forEach(blame.commits.values(), (c, i) => {
|
Iterables.forEach(blame.commits.values(), (c, i) => {
|
||||||
if (c.isUncommitted) return;
|
if (c.isUncommitted) return;
|
||||||
|
|
||||||
const decoration = `\u2937 ${c.author}, ${moment(c.date).format('MMMM Do, YYYY h:MMa')}`;
|
const decoration = `\u2937 ${c.author}, ${moment(c.date).format(dateFormat)}`;
|
||||||
const uri = GitService.toReferenceGitContentUri(c, i + 1, commitCount, c.originalFileName, decoration);
|
const uri = GitService.toReferenceGitContentUri(c, i + 1, commitCount, c.originalFileName, decoration, dateFormat);
|
||||||
locations.push(new Location(uri, new Position(0, 0)));
|
locations.push(new Location(uri, new Position(0, 0)));
|
||||||
if (c.sha === selectedSha) {
|
if (c.sha === selectedSha) {
|
||||||
locations.push(new Location(uri, new Position((line || 0) + 1, 0)));
|
locations.push(new Location(uri, new Position((line || 0) + 1, 0)));
|
||||||
@@ -858,13 +859,14 @@ export class GitService extends Disposable {
|
|||||||
if (log === undefined) return undefined;
|
if (log === undefined) return undefined;
|
||||||
|
|
||||||
const commitCount = log.commits.size;
|
const commitCount = log.commits.size;
|
||||||
|
const dateFormat = this.config.defaultDateFormat === null ? 'MMMM Do, YYYY h:MMa' : this.config.defaultDateFormat;
|
||||||
|
|
||||||
const locations: Location[] = [];
|
const locations: Location[] = [];
|
||||||
Iterables.forEach(log.commits.values(), (c, i) => {
|
Iterables.forEach(log.commits.values(), (c, i) => {
|
||||||
if (c.isUncommitted) return;
|
if (c.isUncommitted) return;
|
||||||
|
|
||||||
const decoration = `\u2937 ${c.author}, ${moment(c.date).format('MMMM Do, YYYY h:MMa')}`;
|
const decoration = `\u2937 ${c.author}, ${moment(c.date).format(dateFormat)}`;
|
||||||
const uri = GitService.toReferenceGitContentUri(c, i + 1, commitCount, c.originalFileName, decoration);
|
const uri = GitService.toReferenceGitContentUri(c, i + 1, commitCount, c.originalFileName, decoration, dateFormat);
|
||||||
locations.push(new Location(uri, new Position(0, 0)));
|
locations.push(new Location(uri, new Position(0, 0)));
|
||||||
if (c.sha === selectedSha) {
|
if (c.sha === selectedSha) {
|
||||||
locations.push(new Location(uri, new Position((line || 0) + 1, 0)));
|
locations.push(new Location(uri, new Position((line || 0) + 1, 0)));
|
||||||
@@ -1086,11 +1088,11 @@ export class GitService extends Disposable {
|
|||||||
return Uri.parse(`${DocumentSchemes.GitLensGit}:${path.basename(fileName!, extension)}:${shortSha}${extension}?${JSON.stringify(data)}`);
|
return Uri.parse(`${DocumentSchemes.GitLensGit}:${path.basename(fileName!, extension)}:${shortSha}${extension}?${JSON.stringify(data)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static toReferenceGitContentUri(commit: GitCommit, index: number, commitCount: number, originalFileName?: string, decoration?: string): Uri {
|
static toReferenceGitContentUri(commit: GitCommit, index: number, commitCount: number, originalFileName: string | undefined, decoration: string, dateFormat: string | null): Uri {
|
||||||
return GitService._toReferenceGitContentUri(commit, DocumentSchemes.GitLensGit, commitCount, GitService._toGitUriData(commit, index, originalFileName, decoration));
|
return GitService._toReferenceGitContentUri(commit, DocumentSchemes.GitLensGit, commitCount, GitService._toGitUriData(commit, index, originalFileName, decoration), dateFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _toReferenceGitContentUri(commit: GitCommit, scheme: DocumentSchemes, commitCount: number, data: IGitUriData) {
|
private static _toReferenceGitContentUri(commit: GitCommit, scheme: DocumentSchemes, commitCount: number, data: IGitUriData, dateFormat: string | null) {
|
||||||
const pad = (n: number) => ('0000000' + n).slice(-('' + commitCount).length);
|
const pad = (n: number) => ('0000000' + n).slice(-('' + commitCount).length);
|
||||||
const ext = path.extname(data.fileName);
|
const ext = path.extname(data.fileName);
|
||||||
const uriPath = `${path.relative(commit.repoPath, data.fileName.slice(0, -ext.length))}/${commit.shortSha}${ext}`;
|
const uriPath = `${path.relative(commit.repoPath, data.fileName.slice(0, -ext.length))}/${commit.shortSha}${ext}`;
|
||||||
@@ -1100,8 +1102,12 @@ export class GitService extends Disposable {
|
|||||||
message = message.substring(0, 49) + '\u2026';
|
message = message.substring(0, 49) + '\u2026';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dateFormat === null) {
|
||||||
|
dateFormat = 'MMMM Do, YYYY h:MMa';
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: Need to specify an index here, since I can't control the sort order -- just alphabetic or by file location
|
// NOTE: Need to specify an index here, since I can't control the sort order -- just alphabetic or by file location
|
||||||
return Uri.parse(`${scheme}:${pad(data.index || 0)} \u2022 ${encodeURIComponent(message)} \u2022 ${moment(commit.date).format('MMM D, YYYY hh:MMa')} \u2022 ${encodeURIComponent(uriPath)}?${JSON.stringify(data)}`);
|
return Uri.parse(`${scheme}:${pad(data.index || 0)} \u2022 ${encodeURIComponent(message)} \u2022 ${moment(commit.date).format(dateFormat)} \u2022 ${encodeURIComponent(uriPath)}?${JSON.stringify(data)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _toGitUriData<T extends IGitUriData>(commit: IGitUriData, index?: number, originalFileName?: string, decoration?: string): T {
|
private static _toGitUriData<T extends IGitUriData>(commit: IGitUriData, index?: number, originalFileName?: string, decoration?: string): T {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Arrays, Iterables } from '../system';
|
|||||||
import { commands, QuickPickOptions, TextDocumentShowOptions, Uri, window } from 'vscode';
|
import { commands, QuickPickOptions, TextDocumentShowOptions, Uri, window } from 'vscode';
|
||||||
import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffDirectoryCommandCommandArgs, DiffWithPreviousCommandArgs, Keyboard, KeyNoopCommand, Keys, ShowQuickCommitDetailsCommandArgs, StashApplyCommandArgs, StashDeleteCommandArgs } from '../commands';
|
import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffDirectoryCommandCommandArgs, DiffWithPreviousCommandArgs, Keyboard, KeyNoopCommand, Keys, ShowQuickCommitDetailsCommandArgs, StashApplyCommandArgs, StashDeleteCommandArgs } from '../commands';
|
||||||
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem, QuickPickItem } from './common';
|
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, GitLog, GitLogCommit, GitService, GitStashCommit, GitStatusFile, GitStatusFileStatus, GitUri, IGitCommitInfo, IGitStatusFile, RemoteResource } from '../gitService';
|
||||||
import { OpenRemotesCommandQuickPickItem } from './remotes';
|
import { OpenRemotesCommandQuickPickItem } from './remotes';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
@@ -19,15 +19,7 @@ export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickI
|
|||||||
|
|
||||||
constructor(commit: GitCommit, status: IGitStatusFile) {
|
constructor(commit: GitCommit, status: IGitStatusFile) {
|
||||||
const icon = getGitStatusIcon(status.status);
|
const icon = getGitStatusIcon(status.status);
|
||||||
|
const description = GitStatusFile.getFormattedDirectory(status, true);
|
||||||
let directory: string | undefined = GitService.normalizePath(path.dirname(status.fileName));
|
|
||||||
if (!directory || directory === '.') {
|
|
||||||
directory = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const description = (status.status === 'R' && status.originalFileName)
|
|
||||||
? `${directory} \u00a0\u2190\u00a0 ${status.originalFileName}`
|
|
||||||
: directory;
|
|
||||||
|
|
||||||
let sha;
|
let sha;
|
||||||
let shortSha;
|
let shortSha;
|
||||||
|
|||||||
Reference in New Issue
Block a user