7 Commits

Author SHA1 Message Date
Eric Amodio
6ed0778a02 Preps v4.5.0-beta 2017-08-27 15:44:04 -04:00
Eric Amodio
68c17206e5 Adds a file history explorer view 2017-08-27 04:22:49 -04:00
Eric Amodio
d6c84061f9 Defaults stashes format to ${filePath}
Adds message when there are no stashes
Cleans up the stash explorer
2017-08-27 04:15:21 -04:00
Eric Amodio
208d549c1c Reworks ExplorerNode base class
Adds TextExplorerNode for messages
2017-08-27 04:10:02 -04:00
Eric Amodio
6518279937 Adds ${filePath} support to status file formatting 2017-08-27 03:58:00 -04:00
Eric Amodio
c03634fafc Splits code lens out of GitService 2017-08-26 15:36:32 -04:00
Eric Amodio
3ba1fe18ee Updates dependencies 2017-08-26 15:08:19 -04:00
25 changed files with 477 additions and 146 deletions

View File

@@ -4,6 +4,18 @@ 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.5.0-beta] - 2017-08-27
### Added
- Adds an all-new `Git File History` explorer to the Explorer activity -- enabled via `"gitlens.insiders": true`
- Shows the commit history of the active file -- automatically tracks the active editor
- Provides toolbar buttons to `Refresh`
- Provides a context menu with `Open Changes`, `Compare File with Working Tree`, `Open File`, `Open File Revision`, `Open File in Remote`, `Open File Revision in Remote`, and `Show Commit Details` commands
- Adds a `No stashed changes` message to the `Git Stashes` explorer when there are no stashes
- Adds `${filePath}` token to file formatting
### Changed
- Changes `gitlens.stashExplorer.stashFileFormat` setting to defaults to `${filePath}` for better separator handling
## [4.4.1] - 2017-08-23 ## [4.4.1] - 2017-08-23
## Fixed ## Fixed
- Fixes [#114](https://github.com/eamodio/vscode-gitlens/issues/114) - Stylus files makes code lens freak out - Fixes [#114](https://github.com/eamodio/vscode-gitlens/issues/114) - Stylus files makes code lens freak out
@@ -21,7 +33,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
## Removed ## Removed
- Removes unneeded `gitlens.stashExplorer.enabled` configuration setting since users can add or remove custom views natively now - Removes unneeded `gitlens.stashExplorer.enabled` configuration setting since users can add or remove custom views natively now
- Removes unneeded `Toggle Git Stashed Explorer` command (`gitlens.stashExplorer.toggle`) since users can add or remove custom views natively now - Removes unneeded `Toggle Git Stashes Explorer` command (`gitlens.stashExplorer.toggle`) since users can add or remove custom views natively now
- Removes the `gitlens.theme.annotations.file.hover.separateLines` configuration setting - Removes the `gitlens.theme.annotations.file.hover.separateLines` configuration setting
## Fixed ## Fixed
@@ -44,7 +56,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
## [4.3.1] - 2017-07-03 ## [4.3.1] - 2017-07-03
## Added ## Added
- Adds `gitlens.stashExplorer.enabled` setting to specify whether or not to show the `Git Stashes` explorer - Adds `gitlens.stashExplorer.enabled` setting to specify whether or not to show the `Git Stashes` explorer
- Adds `Toggle Git Stashed Explorer` command (`gitlens.stashExplorer.toggle`) - toggles the `Git Stashes` explorer on and off - Adds `Toggle Git Stashes Explorer` command (`gitlens.stashExplorer.toggle`) - toggles the `Git Stashes` explorer on and off
## Changed ## Changed
- Hides the `Git Stashes` explorer by default - Hides the `Git Stashes` explorer by default

View File

@@ -111,7 +111,13 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
### Navigate and Explore ### Navigate and Explore
- Adds a `Git Stashes` explorer to the Explorer activity ([optional](#git-stashes-explorer-settings), off by default) - Adds a [customizable](#git-file-history-explorer-settings) `Git File History` explorer to the Explorer activity -- currently [insiders](#insiders) only
- Shows the commit history of the active file -- automatically tracks the active editor
- Provides toolbar buttons to `Refresh`
- Provides a context menu with `Open Changes`, `Compare File with Working Tree`, `Open File`, `Open File Revision`, `Open File in Remote`, `Open File Revision in Remote`, and `Show Commit Details` commands
- Adds a [customizable](#git-stashes-explorer-settings) `Git Stashes` explorer to the Explorer activity
![Git Stashes explorer](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-git-stashes.png) ![Git Stashes explorer](https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/screenshot-git-stashes.png)
@@ -289,12 +295,18 @@ GitLens is highly customizable and provides many configuration settings to allow
|`gitlens.codeLens.customLocationSymbols`|Specifies the set of document symbols where Git code lens will be shown in the document |`gitlens.codeLens.customLocationSymbols`|Specifies the set of document symbols where Git code lens will be shown in the document
|`gitlens.codeLens.perLanguageLocations`|Specifies where Git code lens will be shown in the document for the specified languages |`gitlens.codeLens.perLanguageLocations`|Specifies where Git code lens will be shown in the document for the specified languages
### Git File History Explorer Settings
|Name | Description
|-----|------------
|`gitlens.fileHistoryExplorer.commitFormat`|Specifies the format of committed changes in the `Git File History` explorer <br />Available tokens<br /> ${id} - commit id<br /> ${author} - commit author<br /> ${message} - commit message<br /> ${ago} - relative commit date (e.g. 1 day ago)<br /> ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)<br /> ${authorAgo} - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
### Git Stashes Explorer Settings ### Git Stashes Explorer Settings
|Name | Description |Name | Description
|-----|------------ |-----|------------
|`gitlens.stashExplorer.stashFormat`|Specifies the format of stashed changes in the `Git Stashes` explorer <br />Available tokens<br /> ${id} - commit id<br /> ${author} - commit author<br /> ${message} - commit message<br /> ${ago} - relative commit date (e.g. 1 day ago)<br /> ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)<br /> ${authorAgo} - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting |`gitlens.stashExplorer.stashFormat`|Specifies the format of stashed changes in the `Git Stashes` explorer <br />Available tokens<br /> ${id} - commit id<br /> ${author} - commit author<br /> ${message} - commit message<br /> ${ago} - relative commit date (e.g. 1 day ago)<br /> ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)<br /> ${authorAgo} - commit author, relative commit date<br />See https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting
|`gitlens.stashExplorer.stashFileFormat`|Specifies the format of a stashed file in the `Git Stashes` explorer <br />Available tokens<br /> ${file} - file name<br /> ${path} - file path |`gitlens.stashExplorer.stashFileFormat`|Specifies the format of a stashed file in the `Git Stashes` explorer <br />Available tokens<br /> ${file} - file name<br /> ${filePath} - file name and path<br /> ${path} - file path
### Status Bar Settings ### Status Bar Settings

20
package-lock.json generated
View File

@@ -16,7 +16,7 @@
"integrity": "sha1-qjuL2ivlErGuCgV7lC6GnDcKVWk=", "integrity": "sha1-qjuL2ivlErGuCgV7lC6GnDcKVWk=",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/node": "8.0.24" "@types/node": "8.0.25"
} }
}, },
"@types/mocha": { "@types/mocha": {
@@ -26,9 +26,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "8.0.24", "version": "8.0.25",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.24.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.25.tgz",
"integrity": "sha512-c3Npme+2JGqxW8+B+aXdN5SPIlCf1C8WxQC6Ea39rO/ASPosnMkWVR16mDJtRE+2dr2xwOQ7DiLxb+wO/TWuPg==", "integrity": "sha512-zT+t9841g1HsjLtPMCYxmb1U4pcZ2TOegAKiomlmj6bIziuaEYHUavxLE9NRwdntY0vOCrgHho6OXjDX7fm/Kw==",
"dev": true "dev": true
}, },
"@types/tmp": { "@types/tmp": {
@@ -1271,9 +1271,9 @@
"integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA=="
}, },
"ignore": { "ignore": {
"version": "3.3.3", "version": "3.3.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.4.tgz",
"integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=" "integrity": "sha512-KjHyHxUgicfgFiTJaIA9DoeY3TIQz5thaKqm35re7RTVVB7zjF1fTMIDMXM4GUUBipR4FW8BvGnA115pZ/AxQQ=="
}, },
"inflight": { "inflight": {
"version": "1.0.6", "version": "1.0.6",
@@ -2475,9 +2475,9 @@
"dev": true "dev": true
}, },
"tslint": { "tslint": {
"version": "5.6.0", "version": "5.7.0",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.6.0.tgz", "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.7.0.tgz",
"integrity": "sha1-CIqmxgJmIzOGULKQCCirPt9Z9s8=", "integrity": "sha1-wl4NDJL6EgHCvDDoROCOaCtPNVI=",
"dev": true, "dev": true,
"requires": { "requires": {
"babel-code-frame": "6.26.0", "babel-code-frame": "6.26.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "gitlens", "name": "gitlens",
"version": "4.4.1", "version": "4.5.0-beta",
"author": { "author": {
"name": "Eric Amodio", "name": "Eric Amodio",
"email": "eamodio@gmail.com" "email": "eamodio@gmail.com"
@@ -413,6 +413,11 @@
"default": null, "default": null,
"description": "Specifies how all absolute dates will be formatted by default\nSee https://momentjs.com/docs/#/displaying/format/ for valid formats" "description": "Specifies how all absolute dates will be formatted by default\nSee https://momentjs.com/docs/#/displaying/format/ for valid formats"
}, },
"gitlens.fileHistoryExplorer.commitFormat": {
"type": "string",
"default": "${message} \u00a0\u2022\u00a0 ${authorAgo} \u00a0\u2022\u00a0 ${id}",
"description": "Specifies the format of committed changes in the `Git File History` explorer\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
},
"gitlens.stashExplorer.stashFormat": { "gitlens.stashExplorer.stashFormat": {
"type": "string", "type": "string",
"default": "${message}", "default": "${message}",
@@ -420,8 +425,8 @@
}, },
"gitlens.stashExplorer.stashFileFormat": { "gitlens.stashExplorer.stashFileFormat": {
"type": "string", "type": "string",
"default": "${file} \u00a0\u2022\u00a0 ${path}", "default": "${filePath}",
"description": "Specifies the format of a stashed file in the `Git Stashes` explorer\nAvailable tokens\n ${file} - file name\n ${path} - file path" "description": "Specifies the format of a stashed file in the `Git Stashes` explorer\nAvailable tokens\n ${file} - file name\n ${filePath} - file name and path\n ${path} - file path"
}, },
"gitlens.statusBar.enabled": { "gitlens.statusBar.enabled": {
"type": "boolean", "type": "boolean",
@@ -979,6 +984,40 @@
"light": "images/light/icon-refresh.svg" "light": "images/light/icon-refresh.svg"
} }
}, },
{
"command": "gitlens.fileHistoryExplorer.refresh",
"title": "Refresh",
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-refresh.svg",
"light": "images/light/icon-refresh.svg"
}
},
{
"command": "gitlens.fileHistoryExplorer.openChanges",
"title": "Open Changes",
"category": "GitLens"
},
{
"command": "gitlens.fileHistoryExplorer.openFile",
"title": "Open File",
"category": "GitLens"
},
{
"command": "gitlens.fileHistoryExplorer.openFileRevision",
"title": "Open File Revision",
"category": "GitLens"
},
{
"command": "gitlens.fileHistoryExplorer.openFileInRemote",
"title": "Open File in Remote",
"category": "GitLens"
},
{
"command": "gitlens.fileHistoryExplorer.openFileRevisionInRemote",
"title": "Open File Revision in Remote",
"category": "GitLens"
},
{ {
"command": "gitlens.stashExplorer.refresh", "command": "gitlens.stashExplorer.refresh",
"title": "Refresh", "title": "Refresh",
@@ -1161,11 +1200,35 @@
}, },
{ {
"command": "gitlens.gitExplorer.refresh", "command": "gitlens.gitExplorer.refresh",
"when": "gitlens:enabled" "when": "false"
},
{
"command": "gitlens.fileHistoryExplorer.refresh",
"when": "false"
},
{
"command": "gitlens.fileHistoryExplorer.openChanges",
"when": "false"
},
{
"command": "gitlens.fileHistoryExplorer.openFile",
"when": "false"
},
{
"command": "gitlens.fileHistoryExplorer.openFileRevision",
"when": "false"
},
{
"command": "gitlens.fileHistoryExplorer.openFileInRemote",
"when": "false"
},
{
"command": "gitlens.fileHistoryExplorer.openFileRevisionInRemote",
"when": "false"
}, },
{ {
"command": "gitlens.stashExplorer.refresh", "command": "gitlens.stashExplorer.refresh",
"when": "gitlens:enabled" "when": "false"
}, },
{ {
"command": "gitlens.stashExplorer.openChanges", "command": "gitlens.stashExplorer.openChanges",
@@ -1373,6 +1436,11 @@
"when": "gitlens:enabled && view == gitlens.gitExplorer", "when": "gitlens:enabled && view == gitlens.gitExplorer",
"group": "navigation" "group": "navigation"
}, },
{
"command": "gitlens.fileHistoryExplorer.refresh",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer",
"group": "navigation"
},
{ {
"command": "gitlens.stashSave", "command": "gitlens.stashSave",
"when": "gitlens:enabled && view == gitlens.stashExplorer", "when": "gitlens:enabled && view == gitlens.stashExplorer",
@@ -1405,6 +1473,41 @@
"when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == commit-file", "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == commit-file",
"group": "2_gitlens@2" "group": "2_gitlens@2"
}, },
{
"command": "gitlens.fileHistoryExplorer.openChanges",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
"group": "1_gitlens@1"
},
{
"command": "gitlens.diffWithWorking",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
"group": "1_gitlens@2"
},
{
"command": "gitlens.fileHistoryExplorer.openFile",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
"group": "2_gitlens@1"
},
{
"command": "gitlens.fileHistoryExplorer.openFileRevision",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
"group": "2_gitlens@2"
},
{
"command": "gitlens.fileHistoryExplorer.openFileInRemote",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
"group": "2_gitlens@3"
},
{
"command": "gitlens.fileHistoryExplorer.openFileRevisionInRemote",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
"group": "2_gitlens@4"
},
{
"command": "gitlens.showQuickCommitDetails",
"when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
"group": "3_gitlens@1"
},
{ {
"command": "gitlens.stashApply", "command": "gitlens.stashApply",
"when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == stash-commit", "when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == stash-commit",
@@ -1536,6 +1639,11 @@
], ],
"views": { "views": {
"explorer": [ "explorer": [
{
"id": "gitlens.fileHistoryExplorer",
"name": "Git File History",
"when": "gitlens:enabled && config.gitlens.insiders"
},
{ {
"id": "gitlens.stashExplorer", "id": "gitlens.stashExplorer",
"name": "Git Stashes", "name": "Git Stashes",
@@ -1562,7 +1670,7 @@
"applicationinsights": "0.21.0", "applicationinsights": "0.21.0",
"copy-paste": "1.3.0", "copy-paste": "1.3.0",
"iconv-lite": "0.4.18", "iconv-lite": "0.4.18",
"ignore": "3.3.3", "ignore": "3.3.4",
"lodash.debounce": "4.0.8", "lodash.debounce": "4.0.8",
"lodash.escaperegexp": "4.1.2", "lodash.escaperegexp": "4.1.2",
"lodash.isequal": "4.5.0", "lodash.isequal": "4.5.0",
@@ -1576,10 +1684,10 @@
"@types/copy-paste": "1.1.30", "@types/copy-paste": "1.1.30",
"@types/iconv-lite": "0.0.1", "@types/iconv-lite": "0.0.1",
"@types/mocha": "2.2.42", "@types/mocha": "2.2.42",
"@types/node": "8.0.24", "@types/node": "8.0.25",
"@types/tmp": "0.0.33", "@types/tmp": "0.0.33",
"mocha": "3.5.0", "mocha": "3.5.0",
"tslint": "5.6.0", "tslint": "5.7.0",
"typescript": "2.4.2", "typescript": "2.4.2",
"vscode": "1.1.5" "vscode": "1.1.5"
} }

81
src/codeLensController.ts Normal file
View File

@@ -0,0 +1,81 @@
'use strict';
import { Objects } from './system';
import { Disposable, ExtensionContext, languages, TextEditor, workspace } from 'vscode';
import { IConfig } from './configuration';
import { CommandContext, ExtensionKey, setCommandContext } from './constants';
import { GitCodeLensProvider } from './gitCodeLensProvider';
import { GitService } from './gitService';
import { Logger } from './logger';
export class CodeLensController extends Disposable {
private _codeLensProvider: GitCodeLensProvider | undefined;
private _codeLensProviderDisposable: Disposable | undefined;
private _config: IConfig;
private _disposable: Disposable | undefined;
constructor(private context: ExtensionContext, private git: GitService) {
super(() => this.dispose());
this._onConfigurationChanged();
const subscriptions: Disposable[] = [];
subscriptions.push(workspace.onDidChangeConfiguration(this._onConfigurationChanged, this));
subscriptions.push(git.onDidChangeGitCache(this._onGitCacheChanged, this));
this._disposable = Disposable.from(...subscriptions);
}
dispose() {
this._disposable && this._disposable.dispose();
this._codeLensProviderDisposable && this._codeLensProviderDisposable.dispose();
this._codeLensProviderDisposable = undefined;
this._codeLensProvider = undefined;
}
private _onConfigurationChanged() {
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
if (!Objects.areEquivalent(cfg.codeLens, this._config && this._config.codeLens)) {
Logger.log('CodeLens config changed; resetting CodeLens provider');
if (cfg.codeLens.enabled && (cfg.codeLens.recentChange.enabled || cfg.codeLens.authors.enabled)) {
if (this._codeLensProvider) {
this._codeLensProvider.reset();
}
else {
this._codeLensProvider = new GitCodeLensProvider(this.context, this.git);
this._codeLensProviderDisposable = languages.registerCodeLensProvider(GitCodeLensProvider.selector, this._codeLensProvider);
}
}
else {
this._codeLensProviderDisposable && this._codeLensProviderDisposable.dispose();
this._codeLensProviderDisposable = undefined;
this._codeLensProvider = undefined;
}
setCommandContext(CommandContext.CanToggleCodeLens, cfg.codeLens.recentChange.enabled || cfg.codeLens.authors.enabled);
}
this._config = cfg;
}
private _onGitCacheChanged() {
Logger.log('Git cache changed; resetting CodeLens provider');
this._codeLensProvider && this._codeLensProvider.reset();
}
toggleCodeLens(editor: TextEditor) {
if (!this._config.codeLens.recentChange.enabled && !this._config.codeLens.authors.enabled) return;
Logger.log(`toggleCodeLens()`);
if (this._codeLensProviderDisposable) {
this._codeLensProviderDisposable.dispose();
this._codeLensProviderDisposable = undefined;
return;
}
this._codeLensProviderDisposable = languages.registerCodeLensProvider(GitCodeLensProvider.selector, new GitCodeLensProvider(this.context, this.git));
}
}

View File

@@ -6,13 +6,17 @@ import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { OpenInRemoteCommandArgs } from './openInRemote'; import { OpenInRemoteCommandArgs } from './openInRemote';
export interface OpenFileInRemoteCommandArgs {
range?: boolean;
}
export class OpenFileInRemoteCommand extends ActiveEditorCommand { export class OpenFileInRemoteCommand extends ActiveEditorCommand {
constructor(private git: GitService) { constructor(private git: GitService) {
super(Commands.OpenFileInRemote); super(Commands.OpenFileInRemote);
} }
async execute(editor?: TextEditor, uri?: Uri) { async execute(editor?: TextEditor, uri?: Uri, args: OpenFileInRemoteCommandArgs = { range: true }) {
uri = getCommandUri(uri, editor); uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined; if (uri === undefined) return undefined;
@@ -23,7 +27,9 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand {
try { try {
const remotes = Arrays.uniqueBy(await this.git.getRemotes(gitUri.repoPath), _ => _.url, _ => !!_.provider); const remotes = Arrays.uniqueBy(await this.git.getRemotes(gitUri.repoPath), _ => _.url, _ => !!_.provider);
const range = editor === undefined ? undefined : new Range(editor.selection.start.with({ line: editor.selection.start.line + 1 }), editor.selection.end.with({ line: editor.selection.end.line + 1 })); const range = (args.range && editor !== undefined)
? new Range(editor.selection.start.with({ line: editor.selection.start.line + 1 }), editor.selection.end.with({ line: editor.selection.end.line + 1 }))
: undefined;
return commands.executeCommand(Commands.OpenInRemote, uri, { return commands.executeCommand(Commands.OpenInRemote, uri, {
resource: { resource: {

View File

@@ -1,8 +1,9 @@
'use strict'; 'use strict';
import { Strings } from '../system'; import { Strings } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode'; import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common'; import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri } from './common';
import { GlyphChars } from '../constants'; import { GlyphChars } from '../constants';
import { CommitNode } from '../views/explorerNodes';
import { GitCommit, GitLog, GitLogCommit, GitService, GitUri } from '../gitService'; import { GitCommit, GitLog, GitLogCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger'; import { Logger } from '../logger';
import { CommandQuickPickItem, CommitDetailsQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks'; import { CommandQuickPickItem, CommitDetailsQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks';
@@ -24,6 +25,18 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
super(Commands.ShowQuickCommitDetails); super(Commands.ShowQuickCommitDetails);
} }
protected async preExecute(context: CommandContext, ...args: any[]): Promise<any> {
if (context.type === 'view') {
if (context.node instanceof CommitNode) {
args = [{ sha: context.node.uri.sha, commit: context.node.commit }];
}
else {
args = [{ sha: context.node.uri.sha }];
}
}
return this.execute(context.editor, context.uri, ...args);
}
async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickCommitDetailsCommandArgs = {}) { async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickCommitDetailsCommandArgs = {}) {
uri = getCommandUri(uri, editor); uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined; if (uri === undefined) return undefined;

View File

@@ -1,15 +1,15 @@
'use strict'; 'use strict';
import { TextEditor, TextEditorEdit } from 'vscode'; import { TextEditor, TextEditorEdit } from 'vscode';
import { CodeLensController } from '../codeLensController';
import { Commands, EditorCommand } from './common'; import { Commands, EditorCommand } from './common';
import { GitService } from '../gitService';
export class ToggleCodeLensCommand extends EditorCommand { export class ToggleCodeLensCommand extends EditorCommand {
constructor(private git: GitService) { constructor(private codeLensController: CodeLensController) {
super(Commands.ToggleCodeLens); super(Commands.ToggleCodeLens);
} }
execute(editor: TextEditor, edit: TextEditorEdit) { execute(editor: TextEditor, edit: TextEditorEdit) {
return this.git.toggleCodeLens(editor); return this.codeLensController.toggleCodeLens(editor);
} }
} }

View File

@@ -296,15 +296,19 @@ export interface IConfig {
defaultDateFormat: string | null; defaultDateFormat: string | null;
fileHistoryExplorer: {
commitFormat: string;
// commitFileFormat: string;
// dateFormat: string | null;
};
gitExplorer: { gitExplorer: {
enabled: boolean;
commitFormat: string; commitFormat: string;
commitFileFormat: string; commitFileFormat: string;
// dateFormat: string | null; // dateFormat: string | null;
}; };
stashExplorer: { stashExplorer: {
enabled: boolean;
stashFormat: string; stashFormat: string;
stashFileFormat: string; stashFileFormat: string;
// dateFormat: string | null; // dateFormat: string | null;

View File

@@ -17,9 +17,11 @@ import { StashApplyCommand, StashDeleteCommand, StashSaveCommand } from './comma
import { ToggleCodeLensCommand } from './commands'; import { ToggleCodeLensCommand } from './commands';
import { CodeLensLocations, IConfig, LineHighlightLocations } from './configuration'; import { CodeLensLocations, IConfig, LineHighlightLocations } from './configuration';
import { ApplicationInsightsKey, CommandContext, ExtensionKey, QualifiedExtensionId, setCommandContext, WorkspaceState } from './constants'; import { ApplicationInsightsKey, CommandContext, ExtensionKey, QualifiedExtensionId, setCommandContext, WorkspaceState } from './constants';
import { CodeLensController } from './codeLensController';
import { CurrentLineController, LineAnnotationType } from './currentLineController'; import { CurrentLineController, LineAnnotationType } from './currentLineController';
import { GitContentProvider } from './gitContentProvider'; import { GitContentProvider } from './gitContentProvider';
// import { GitExplorer } from './views/gitExplorer'; // import { GitExplorer } from './views/gitExplorer';
import { FileHistoryExplorer } from './views/fileHistoryExplorer';
import { StashExplorer } from './views/stashExplorer'; import { StashExplorer } from './views/stashExplorer';
import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider'; import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider';
import { GitContextTracker, GitService } from './gitService'; import { GitContextTracker, GitService } from './gitService';
@@ -71,7 +73,7 @@ export async function activate(context: ExtensionContext) {
await context.globalState.update(WorkspaceState.GitLensVersion, gitlensVersion); await context.globalState.update(WorkspaceState.GitLensVersion, gitlensVersion);
const git = new GitService(context, repoPath); const git = new GitService(repoPath);
context.subscriptions.push(git); context.subscriptions.push(git);
const gitContextTracker = new GitContextTracker(git); const gitContextTracker = new GitContextTracker(git);
@@ -84,6 +86,9 @@ export async function activate(context: ExtensionContext) {
const annotationController = new AnnotationController(context, git, gitContextTracker); const annotationController = new AnnotationController(context, git, gitContextTracker);
context.subscriptions.push(annotationController); context.subscriptions.push(annotationController);
const codeLensController = new CodeLensController(context, git);
context.subscriptions.push(codeLensController);
const currentLineController = new CurrentLineController(context, git, gitContextTracker, annotationController); const currentLineController = new CurrentLineController(context, git, gitContextTracker, annotationController);
context.subscriptions.push(currentLineController); context.subscriptions.push(currentLineController);
@@ -92,6 +97,7 @@ export async function activate(context: ExtensionContext) {
// const explorer = new GitExplorer(context, git); // const explorer = new GitExplorer(context, git);
// context.subscriptions.push(window.registerTreeDataProvider('gitlens.gitExplorer', explorer)); // context.subscriptions.push(window.registerTreeDataProvider('gitlens.gitExplorer', explorer));
context.subscriptions.push(window.registerTreeDataProvider('gitlens.fileHistoryExplorer', new FileHistoryExplorer(context, git)));
context.subscriptions.push(window.registerTreeDataProvider('gitlens.stashExplorer', new StashExplorer(context, git))); context.subscriptions.push(window.registerTreeDataProvider('gitlens.stashExplorer', new StashExplorer(context, git)));
context.subscriptions.push(commands.registerTextEditorCommand('gitlens.computingFileAnnotations', () => { })); context.subscriptions.push(commands.registerTextEditorCommand('gitlens.computingFileAnnotations', () => { }));
@@ -134,7 +140,7 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new StashApplyCommand(git)); context.subscriptions.push(new StashApplyCommand(git));
context.subscriptions.push(new StashDeleteCommand(git)); context.subscriptions.push(new StashDeleteCommand(git));
context.subscriptions.push(new StashSaveCommand(git)); context.subscriptions.push(new StashSaveCommand(git));
context.subscriptions.push(new ToggleCodeLensCommand(git)); context.subscriptions.push(new ToggleCodeLensCommand(codeLensController));
// Constantly over my data cap so stop collecting initialized event // Constantly over my data cap so stop collecting initialized event
// Telemetry.trackEvent('initialized', Objects.flatten(cfg, 'config', true)); // Telemetry.trackEvent('initialized', Objects.flatten(cfg, 'config', true));

View File

@@ -7,6 +7,7 @@ import * as path from 'path';
export interface IStatusFormatOptions extends IFormatOptions { export interface IStatusFormatOptions extends IFormatOptions {
tokenOptions?: { tokenOptions?: {
file?: Strings.ITokenOptions; file?: Strings.ITokenOptions;
filePath?: Strings.ITokenOptions;
path?: Strings.ITokenOptions; path?: Strings.ITokenOptions;
}; };
} }
@@ -18,6 +19,11 @@ export class StatusFileFormatter extends Formatter<IGitStatusFile, IStatusFormat
return this._padOrTruncate(file, this._options.tokenOptions!.file); return this._padOrTruncate(file, this._options.tokenOptions!.file);
} }
get filePath() {
const filePath = GitStatusFile.getFormattedPath(this._item);
return this._padOrTruncate(filePath, this._options.tokenOptions!.filePath);
}
get path() { get path() {
const directory = GitStatusFile.getFormattedDirectory(this._item, false); const directory = GitStatusFile.getFormattedDirectory(this._item, false);
return this._padOrTruncate(directory, this._options.tokenOptions!.file); return this._padOrTruncate(directory, this._options.tokenOptions!.file);

View File

@@ -40,7 +40,7 @@ export class GitStatusFile implements IGitStatusFile {
} }
getFormattedPath(separator: string = Strings.pad(GlyphChars.Dot, 2, 2)): string { getFormattedPath(separator: string = Strings.pad(GlyphChars.Dot, 2, 2)): string {
return GitUri.getFormattedPath(this.fileName, separator); return GitStatusFile.getFormattedPath(this, separator);
} }
getOcticon() { getOcticon() {
@@ -57,6 +57,10 @@ export class GitStatusFile implements IGitStatusFile {
? `${directory} ${Strings.pad(GlyphChars.ArrowLeft, 1, 1)} ${status.originalFileName}` ? `${directory} ${Strings.pad(GlyphChars.ArrowLeft, 1, 1)} ${status.originalFileName}`
: directory; : directory;
} }
static getFormattedPath(status: IGitStatusFile, separator: string = Strings.pad(GlyphChars.Dot, 2, 2)): string {
return GitUri.getFormattedPath(status.fileName, separator);
}
} }
const statusOcticonsMap = { const statusOcticonsMap = {

View File

@@ -1,11 +1,10 @@
'use strict'; 'use strict';
import { Functions, Iterables, Objects } from './system'; import { Functions, Iterables, Objects } from './system';
import { Disposable, Event, EventEmitter, ExtensionContext, FileSystemWatcher, languages, Location, Position, Range, TextDocument, TextDocumentChangeEvent, TextEditor, Uri, workspace } from 'vscode'; import { Disposable, Event, EventEmitter, FileSystemWatcher, Location, Position, Range, TextDocument, TextDocumentChangeEvent, TextEditor, Uri, workspace } from 'vscode';
import { IConfig } from './configuration'; import { IConfig } from './configuration';
import { CommandContext, DocumentSchemes, ExtensionKey, GlyphChars, setCommandContext } from './constants'; import { DocumentSchemes, ExtensionKey, GlyphChars } from './constants';
import { Git, GitAuthor, GitBlame, GitBlameCommit, GitBlameLine, GitBlameLines, GitBlameParser, GitBranch, GitCommit, GitDiff, GitDiffChunkLine, GitDiffParser, GitLog, GitLogCommit, GitLogParser, GitRemote, GitStash, GitStashParser, GitStatus, GitStatusFile, GitStatusParser, IGit, setDefaultEncoding } from './git/git'; import { Git, GitAuthor, GitBlame, GitBlameCommit, GitBlameLine, GitBlameLines, GitBlameParser, GitBranch, GitCommit, GitDiff, GitDiffChunkLine, GitDiffParser, GitLog, GitLogCommit, GitLogParser, GitRemote, GitStash, GitStashParser, GitStatus, GitStatusFile, GitStatusParser, IGit, setDefaultEncoding } from './git/git';
import { GitUri, IGitCommitInfo, IGitUriData } from './git/gitUri'; import { GitUri, IGitCommitInfo, IGitUriData } from './git/gitUri';
import { GitCodeLensProvider } from './gitCodeLensProvider';
import { Logger } from './logger'; import { Logger } from './logger';
import * as fs from 'fs'; import * as fs from 'fs';
import * as ignore from 'ignore'; import * as ignore from 'ignore';
@@ -90,8 +89,6 @@ export class GitService extends Disposable {
private _uriCache: Map<string, UriCacheEntry>; private _uriCache: Map<string, UriCacheEntry>;
config: IConfig; config: IConfig;
private _codeLensProvider: GitCodeLensProvider | undefined;
private _codeLensProviderDisposable: Disposable | undefined;
private _disposable: Disposable | undefined; private _disposable: Disposable | undefined;
private _gitignore: Promise<ignore.Ignore | undefined>; private _gitignore: Promise<ignore.Ignore | undefined>;
private _repoWatcher: FileSystemWatcher | undefined; private _repoWatcher: FileSystemWatcher | undefined;
@@ -99,7 +96,7 @@ export class GitService extends Disposable {
static EmptyPromise: Promise<GitBlame | GitDiff | GitLog | undefined> = Promise.resolve(undefined); static EmptyPromise: Promise<GitBlame | GitDiff | GitLog | undefined> = Promise.resolve(undefined);
constructor(private context: ExtensionContext, public repoPath: string) { constructor(public repoPath: string) {
super(() => this.dispose()); super(() => this.dispose());
this._gitCache = new Map(); this._gitCache = new Map();
@@ -118,10 +115,6 @@ export class GitService extends Disposable {
dispose() { dispose() {
this._disposable && this._disposable.dispose(); this._disposable && this._disposable.dispose();
this._codeLensProviderDisposable && this._codeLensProviderDisposable.dispose();
this._codeLensProviderDisposable = undefined;
this._codeLensProvider = undefined;
this._cacheDisposable && this._cacheDisposable.dispose(); this._cacheDisposable && this._cacheDisposable.dispose();
this._cacheDisposable = undefined; this._cacheDisposable = undefined;
@@ -146,30 +139,7 @@ export class GitService extends Disposable {
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!; const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
const codeLensChanged = !Objects.areEquivalent(cfg.codeLens, this.config && this.config.codeLens); if (!Objects.areEquivalent(cfg.advanced, this.config && this.config.advanced)) {
const advancedChanged = !Objects.areEquivalent(cfg.advanced, this.config && this.config.advanced);
if (codeLensChanged) {
Logger.log('CodeLens config changed; resetting CodeLens provider');
if (cfg.codeLens.enabled && (cfg.codeLens.recentChange.enabled || cfg.codeLens.authors.enabled)) {
if (this._codeLensProvider) {
this._codeLensProvider.reset();
}
else {
this._codeLensProvider = new GitCodeLensProvider(this.context, this);
this._codeLensProviderDisposable = languages.registerCodeLensProvider(GitCodeLensProvider.selector, this._codeLensProvider);
}
}
else {
this._codeLensProviderDisposable && this._codeLensProviderDisposable.dispose();
this._codeLensProviderDisposable = undefined;
this._codeLensProvider = undefined;
}
setCommandContext(CommandContext.CanToggleCodeLens, cfg.codeLens.recentChange.enabled || cfg.codeLens.authors.enabled);
}
if (advancedChanged) {
if (cfg.advanced.caching.enabled) { if (cfg.advanced.caching.enabled) {
this._cacheDisposable && this._cacheDisposable.dispose(); this._cacheDisposable && this._cacheDisposable.dispose();
@@ -264,9 +234,6 @@ export class GitService extends Disposable {
} }
private _fireGitCacheChangeCore() { private _fireGitCacheChangeCore() {
// Refresh the code lenses
this._codeLensProvider && this._codeLensProvider.reset();
this._onDidChangeGitCache.fire(); this._onDidChangeGitCache.fire();
} }
@@ -1053,19 +1020,6 @@ export class GitService extends Disposable {
return Git.stash_save(repoPath, message, unstagedOnly); return Git.stash_save(repoPath, message, unstagedOnly);
} }
toggleCodeLens(editor: TextEditor) {
if (!this.config.codeLens.recentChange.enabled && !this.config.codeLens.authors.enabled) return;
Logger.log(`toggleCodeLens()`);
if (this._codeLensProviderDisposable) {
this._codeLensProviderDisposable.dispose();
this._codeLensProviderDisposable = undefined;
return;
}
this._codeLensProviderDisposable = languages.registerCodeLensProvider(GitCodeLensProvider.selector, new GitCodeLensProvider(this.context, this));
}
static getGitPath(gitPath?: string): Promise<IGit> { static getGitPath(gitPath?: string): Promise<IGit> {
return Git.getGitPath(gitPath); return Git.getGitPath(gitPath);
} }

View File

@@ -10,15 +10,15 @@ export class BranchHistoryNode extends ExplorerNode {
readonly resourceType: ResourceType = 'branch-history'; readonly resourceType: ResourceType = 'branch-history';
constructor(public readonly branch: GitBranch, uri: GitUri, context: ExtensionContext, git: GitService) { constructor(public readonly branch: GitBranch, uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri, context, git); super(uri);
} }
async getChildren(): Promise<CommitNode[]> { async getChildren(): Promise<CommitNode[]> {
const log = await this.git.getLogForRepo(this.uri.repoPath!, this.branch.name); const log = await this.git.getLogForRepo(this.uri.repoPath!, this.branch.name);
if (log === undefined) return []; if (log === undefined) return [];
return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.context, this.git))]; return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
} }
getTreeItem(): TreeItem { getTreeItem(): TreeItem {

View File

@@ -9,9 +9,9 @@ export class BranchesNode extends ExplorerNode {
readonly resourceType: ResourceType = 'branches'; readonly resourceType: ResourceType = 'branches';
constructor(uri: GitUri, context: ExtensionContext, git: GitService) { constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri, context, git); super(uri);
} }
async getChildren(): Promise<BranchHistoryNode[]> { async getChildren(): Promise<BranchHistoryNode[]> {
const branches = await this.git.getBranches(this.uri.repoPath!); const branches = await this.git.getBranches(this.uri.repoPath!);

View File

@@ -9,8 +9,8 @@ export class CommitFileNode extends ExplorerNode {
readonly resourceType: ResourceType = 'commit-file'; readonly resourceType: ResourceType = 'commit-file';
constructor(public readonly status: IGitStatusFile, public commit: GitCommit, private template: string, context: ExtensionContext, git: GitService) { constructor(public readonly status: IGitStatusFile, public commit: GitCommit, private template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(new GitUri(Uri.file(path.resolve(commit.repoPath, status.fileName)), { repoPath: commit.repoPath, fileName: status.fileName, sha: commit.sha }), context, git); super(new GitUri(Uri.file(path.resolve(commit.repoPath, status.fileName)), { repoPath: commit.repoPath, fileName: status.fileName, sha: commit.sha }));
} }
getChildren(): Promise<ExplorerNode[]> { getChildren(): Promise<ExplorerNode[]> {

View File

@@ -1,6 +1,7 @@
'use strict'; 'use strict';
import { Iterables } from '../system'; import { Iterables } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
import { CommitFileNode } from './commitFileNode'; import { CommitFileNode } from './commitFileNode';
import { ExplorerNode, ResourceType } from './explorerNode'; import { ExplorerNode, ResourceType } from './explorerNode';
import { CommitFormatter, GitCommit, GitService, GitUri } from '../gitService'; import { CommitFormatter, GitCommit, GitService, GitUri } from '../gitService';
@@ -9,12 +10,13 @@ export class CommitNode extends ExplorerNode {
readonly resourceType: ResourceType = 'commit'; readonly resourceType: ResourceType = 'commit';
constructor(public readonly commit: GitCommit, context: ExtensionContext, git: GitService) { constructor(public readonly commit: GitCommit, private template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(new GitUri(commit.uri, commit), context, git); super(new GitUri(commit.uri, commit));
this.commit = commit;
} }
async getChildren(): Promise<ExplorerNode[]> { async getChildren(): Promise<ExplorerNode[]> {
if (this.commit.type === 'file') Promise.resolve([]);
const log = await this.git.getLogForRepo(this.commit.repoPath, this.commit.sha, 1); const log = await this.git.getLogForRepo(this.commit.repoPath, this.commit.sha, 1);
if (log === undefined) return []; if (log === undefined) return [];
@@ -25,14 +27,40 @@ export class CommitNode extends ExplorerNode {
} }
getTreeItem(): TreeItem { getTreeItem(): TreeItem {
const label = CommitFormatter.fromTemplate(this.git.config.gitExplorer.commitFormat, this.commit, this.git.config.defaultDateFormat); const item = new TreeItem(CommitFormatter.fromTemplate(this.template, this.commit, this.git.config.defaultDateFormat));
if (this.commit.type === 'file') {
item.collapsibleState = TreeItemCollapsibleState.None;
item.command = this.getCommand();
item.contextValue = 'commit-file';
}
else {
item.collapsibleState = TreeItemCollapsibleState.Collapsed;
item.contextValue = this.resourceType;
}
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
item.iconPath = { item.iconPath = {
dark: this.context.asAbsolutePath('images/dark/icon-commit.svg'), dark: this.context.asAbsolutePath('images/dark/icon-commit.svg'),
light: this.context.asAbsolutePath('images/light/icon-commit.svg') light: this.context.asAbsolutePath('images/light/icon-commit.svg')
}; };
return item; return item;
} }
getCommand(): Command | undefined {
return {
title: 'Compare File with Previous',
command: Commands.DiffWithPrevious,
arguments: [
new GitUri(this.uri, this.commit),
{
commit: this.commit,
line: 0,
showOptions: {
preserveFocus: true,
preview: true
}
} as DiffWithPreviousCommandArgs
]
};
}
} }

View File

@@ -1,14 +1,14 @@
'use strict'; 'use strict';
import { Command, Event, ExtensionContext, TreeItem } from 'vscode'; import { Command, Event, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { GitService, GitUri } from '../gitService'; import { GitUri } from '../gitService';
export declare type ResourceType = 'status' | 'branches' | 'repository' | 'branch-history' | 'file-history' | 'stash-history' | 'commit' | 'stash-commit' | 'commit-file'; export declare type ResourceType = 'text' | 'status' | 'branches' | 'repository' | 'branch-history' | 'file-history' | 'stash-history' | 'commit' | 'stash-commit' | 'commit-file';
export abstract class ExplorerNode { export abstract class ExplorerNode {
abstract readonly resourceType: ResourceType; abstract readonly resourceType: ResourceType;
constructor(public readonly uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) { } constructor(public readonly uri: GitUri) { }
abstract getChildren(): ExplorerNode[] | Promise<ExplorerNode[]>; abstract getChildren(): ExplorerNode[] | Promise<ExplorerNode[]>;
abstract getTreeItem(): TreeItem | Promise<TreeItem>; abstract getTreeItem(): TreeItem | Promise<TreeItem>;
@@ -20,4 +20,23 @@ export abstract class ExplorerNode {
onDidChangeTreeData?: Event<ExplorerNode>; onDidChangeTreeData?: Event<ExplorerNode>;
refresh?(): void; refresh?(): void;
}
export class TextExplorerNode extends ExplorerNode {
readonly resourceType: ResourceType = 'text';
constructor(private text: string) {
super(new GitUri());
}
getChildren(): ExplorerNode[] | Promise<ExplorerNode[]> {
return [];
}
getTreeItem(): TreeItem | Promise<TreeItem> {
const item = new TreeItem(this.text, TreeItemCollapsibleState.None);
item.contextValue = this.resourceType;
return item;
}
} }

View File

@@ -0,0 +1,89 @@
'use strict';
// import { Arrays } from '../system';
import { commands, Event, EventEmitter, ExtensionContext, TextEditor, TreeDataProvider, TreeItem, Uri, window } from 'vscode';
import { Commands, DiffWithPreviousCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
import { UriComparer } from '../comparers';
import { CommitNode, ExplorerNode, FileHistoryNode, TextExplorerNode } from './explorerNodes';
import { GitService, GitUri } from '../gitService';
export * from './explorerNodes';
export class FileHistoryExplorer implements TreeDataProvider<ExplorerNode> {
private _node?: ExplorerNode;
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
public get onDidChangeTreeData(): Event<ExplorerNode> {
return this._onDidChangeTreeData.event;
}
constructor(private context: ExtensionContext, private git: GitService) {
commands.registerCommand('gitlens.fileHistoryExplorer.refresh', this.refresh, this);
commands.registerCommand('gitlens.fileHistoryExplorer.openChanges', this.openChanges, this);
commands.registerCommand('gitlens.fileHistoryExplorer.openFile', this.openFile, this);
commands.registerCommand('gitlens.fileHistoryExplorer.openFileRevision', this.openFileRevision, this);
commands.registerCommand('gitlens.fileHistoryExplorer.openFileInRemote', this.openFileInRemote, this);
commands.registerCommand('gitlens.fileHistoryExplorer.openFileRevisionInRemote', this.openFileRevisionInRemote, this);
context.subscriptions.push(window.onDidChangeActiveTextEditor(this.onActiveEditorChanged, this));
this._node = this.getRootNode(window.activeTextEditor);
}
async getTreeItem(node: ExplorerNode): Promise<TreeItem> {
return node.getTreeItem();
}
async getChildren(node?: ExplorerNode): Promise<ExplorerNode[]> {
if (this._node === undefined) return [new TextExplorerNode('No active file')];
if (node === undefined) return this._node.getChildren();
return node.getChildren();
}
private getRootNode(editor: TextEditor | undefined): ExplorerNode | undefined {
if (window.visibleTextEditors.length === 0) return undefined;
if (editor === undefined) return this._node;
const uri = this.git.getGitUriForFile(editor.document.uri) || new GitUri(editor.document.uri, { repoPath: this.git.repoPath, fileName: editor.document.uri.fsPath });
if (UriComparer.equals(uri, this._node && this._node.uri)) return this._node;
return new FileHistoryNode(uri, this.context, this.git);
}
private onActiveEditorChanged(editor: TextEditor | undefined) {
const node = this.getRootNode(editor);
if (node === this._node) return;
this.refresh();
}
refresh(node?: ExplorerNode) {
this._node = node || this.getRootNode(window.activeTextEditor);
this._onDidChangeTreeData.fire();
}
private openChanges(node: CommitNode) {
const command = node.getCommand();
if (command === undefined || command.arguments === undefined) return;
const [uri, args] = command.arguments as [Uri, DiffWithPreviousCommandArgs];
args.showOptions!.preview = false;
return commands.executeCommand(command.command, uri, args);
}
private openFile(node: CommitNode) {
return openEditor(node.uri, { preserveFocus: true, preview: false });
}
private openFileRevision(node: CommitNode) {
return openEditor(GitService.toGitContentUri(node.uri), { preserveFocus: true, preview: false });
}
private async openFileInRemote(node: CommitNode) {
return commands.executeCommand(Commands.OpenFileInRemote, node.commit.uri, { range: false } as OpenFileInRemoteCommandArgs);
}
private async openFileRevisionInRemote(node: CommitNode) {
return commands.executeCommand(Commands.OpenFileInRemote, new GitUri(node.commit.uri, node.commit), { range: false } as OpenFileInRemoteCommandArgs);
}
}

View File

@@ -2,7 +2,7 @@
import { Iterables } from '../system'; import { Iterables } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { CommitNode } from './commitNode'; import { CommitNode } from './commitNode';
import { ExplorerNode, ResourceType } from './explorerNode'; import { ExplorerNode, ResourceType, TextExplorerNode } from './explorerNode';
import { GitService, GitUri } from '../gitService'; import { GitService, GitUri } from '../gitService';
export class FileHistoryNode extends ExplorerNode { export class FileHistoryNode extends ExplorerNode {
@@ -10,15 +10,15 @@ export class FileHistoryNode extends ExplorerNode {
static readonly rootType: ResourceType = 'file-history'; static readonly rootType: ResourceType = 'file-history';
readonly resourceType: ResourceType = 'file-history'; readonly resourceType: ResourceType = 'file-history';
constructor(uri: GitUri, context: ExtensionContext, git: GitService) { constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri, context, git); super(uri);
} }
async getChildren(): Promise<CommitNode[]> { async getChildren(): Promise<ExplorerNode[]> {
const log = await this.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, this.uri.sha); const log = await this.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, this.uri.sha);
if (log === undefined) return []; if (log === undefined) return [new TextExplorerNode('No file history')];
return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.context, this.git))]; return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.git.config.fileHistoryExplorer.commitFormat, this.context, this.git))];
} }
getTreeItem(): TreeItem { getTreeItem(): TreeItem {

View File

@@ -11,8 +11,8 @@ export class RepositoryNode extends ExplorerNode {
static readonly rootType: ResourceType = 'repository'; static readonly rootType: ResourceType = 'repository';
readonly resourceType: ResourceType = 'repository'; readonly resourceType: ResourceType = 'repository';
constructor(uri: GitUri, context: ExtensionContext, git: GitService) { constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri, context, git); super(uri);
} }
async getChildren(): Promise<ExplorerNode[]> { async getChildren(): Promise<ExplorerNode[]> {

View File

@@ -13,8 +13,8 @@ export class StashCommitNode extends ExplorerNode {
return this._onDidChangeTreeData.event; return this._onDidChangeTreeData.event;
} }
constructor(public readonly commit: GitStashCommit, context: ExtensionContext, git: GitService) { constructor(public readonly commit: GitStashCommit, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(new GitUri(commit.uri, commit), context, git); super(new GitUri(commit.uri, commit));
} }
async getChildren(): Promise<CommitFileNode[]> { async getChildren(): Promise<CommitFileNode[]> {

View File

@@ -1,7 +1,7 @@
'use strict'; 'use strict';
// import { Functions } from '../system'; // import { Functions } from '../system';
import { commands, Event, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, Uri } from 'vscode'; import { commands, Event, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, Uri } from 'vscode';
import { Commands, DiffWithPreviousCommandArgs, openEditor } from '../commands'; import { Commands, DiffWithPreviousCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
import { ExplorerNode, StashCommitNode, StashNode } from './explorerNodes'; import { ExplorerNode, StashCommitNode, StashNode } from './explorerNodes';
import { GitService, GitUri } from '../gitService'; import { GitService, GitUri } from '../gitService';
@@ -10,7 +10,6 @@ export * from './explorerNodes';
export class StashExplorer implements TreeDataProvider<ExplorerNode> { export class StashExplorer implements TreeDataProvider<ExplorerNode> {
private _node: ExplorerNode; private _node: ExplorerNode;
// private _refreshDebounced: () => void;
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>(); private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
public get onDidChangeTreeData(): Event<ExplorerNode> { public get onDidChangeTreeData(): Event<ExplorerNode> {
@@ -24,28 +23,12 @@ export class StashExplorer implements TreeDataProvider<ExplorerNode> {
commands.registerCommand('gitlens.stashExplorer.openStashedFile', this.openStashedFile, this); commands.registerCommand('gitlens.stashExplorer.openStashedFile', this.openStashedFile, this);
commands.registerCommand('gitlens.stashExplorer.openFileInRemote', this.openFileInRemote, this); commands.registerCommand('gitlens.stashExplorer.openFileInRemote', this.openFileInRemote, this);
context.subscriptions.push(this.git.onDidChangeRepo(reasons => { context.subscriptions.push(this.git.onDidChangeRepo(this.onRepoChanged, this));
if (!reasons.includes('stash')) return;
this.refresh(); this._node = this.getRootNode();
}, this));
// this._refreshDebounced = Functions.debounce(this.refresh.bind(this), 250);
// const editor = window.activeTextEditor;
// const uri = (editor !== undefined && editor.document !== undefined)
// ? new GitUri(editor.document.uri, { repoPath: git.repoPath, fileName: editor.document.uri.fsPath })
// : new GitUri(Uri.file(git.repoPath), { repoPath: git.repoPath, fileName: git.repoPath });
const uri = new GitUri(Uri.file(git.repoPath), { repoPath: git.repoPath, fileName: git.repoPath });
this._node = new StashNode(uri, this.context, this.git);
} }
async getTreeItem(node: ExplorerNode): Promise<TreeItem> { async getTreeItem(node: ExplorerNode): Promise<TreeItem> {
// if (node.onDidChangeTreeData !== undefined) {
// node.onDidChangeTreeData(() => setTimeout(this._refreshDebounced, 1));
// }
return node.getTreeItem(); return node.getTreeItem();
} }
@@ -54,10 +37,16 @@ export class StashExplorer implements TreeDataProvider<ExplorerNode> {
return node.getChildren(); return node.getChildren();
} }
// update(uri: GitUri) { private getRootNode(): ExplorerNode {
// this._node = new StashNode(uri, this.context, this.git); const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
// this.refresh(); return new StashNode(uri, this.context, this.git);
// } }
private onRepoChanged(reasons: ('stash' | 'unknown')[]) {
if (!reasons.includes('stash')) return;
this.refresh();
}
refresh() { refresh() {
this._onDidChangeTreeData.fire(); this._onDidChangeTreeData.fire();
@@ -81,6 +70,6 @@ export class StashExplorer implements TreeDataProvider<ExplorerNode> {
} }
private openFileInRemote(node: StashCommitNode) { private openFileInRemote(node: StashCommitNode) {
return commands.executeCommand(Commands.OpenFileInRemote, node.commit.previousUri); return commands.executeCommand(Commands.OpenFileInRemote, node.commit.uri, { range: false } as OpenFileInRemoteCommandArgs);
} }
} }

View File

@@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { Iterables } from '../system'; import { Iterables } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ExplorerNode, ResourceType } from './explorerNode'; import { ExplorerNode, ResourceType, TextExplorerNode } from './explorerNode';
import { GitService, GitUri } from '../gitService'; import { GitService, GitUri } from '../gitService';
import { StashCommitNode } from './stashCommitNode'; import { StashCommitNode } from './stashCommitNode';
@@ -10,13 +10,13 @@ export class StashNode extends ExplorerNode {
static readonly rootType: ResourceType = 'stash-history'; static readonly rootType: ResourceType = 'stash-history';
readonly resourceType: ResourceType = 'stash-history'; readonly resourceType: ResourceType = 'stash-history';
constructor(uri: GitUri, context: ExtensionContext, git: GitService) { constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri, context, git); super(uri);
} }
async getChildren(): Promise<StashCommitNode[]> { async getChildren(): Promise<ExplorerNode[]> {
const stash = await this.git.getStashList(this.uri.repoPath!); const stash = await this.git.getStashList(this.uri.repoPath!);
if (stash === undefined) return []; if (stash === undefined) return [new TextExplorerNode('No stashed changes')];
return [...Iterables.map(stash.commits.values(), c => new StashCommitNode(c, this.context, this.git))]; return [...Iterables.map(stash.commits.values(), c => new StashCommitNode(c, this.context, this.git))];
} }

View File

@@ -8,9 +8,9 @@ export class StatusNode extends ExplorerNode {
readonly resourceType: ResourceType = 'status'; readonly resourceType: ResourceType = 'status';
constructor(uri: GitUri, context: ExtensionContext, git: GitService) { constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri, context, git); super(uri);
} }
async getChildren(): Promise<ExplorerNode[]> { async getChildren(): Promise<ExplorerNode[]> {
return []; return [];