From d9e071178f225b8e83964de2302ea84d588f8894 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Thu, 19 Jul 2018 11:51:59 +0100 Subject: [PATCH 01/11] starting work on treeview for incoming changes --- package.json | 8 + src/extension.ts | 9 +- src/treeView/dependencyTreeItem.ts | 3 + src/treeView/incommingChangesDataProvider.ts | 149 +++++++++++++++++++ 4 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 src/treeView/dependencyTreeItem.ts create mode 100644 src/treeView/incommingChangesDataProvider.ts diff --git a/package.json b/package.json index a83b5656..1f207cc2 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,14 @@ "vscode": "^1.1.17" }, "contributes": { + "views": { + "explorer": [ + { + "id": "incommingChanges", + "name": "Incoming Changes" + } + ] + }, "commands": [ { "command": "svn.add", diff --git a/src/extension.ts b/src/extension.ts index 028208fd..ed02176c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -4,7 +4,8 @@ import { ExtensionContext, OutputChannel, Uri, - window + window, + workspace } from "vscode"; import { SvnCommands } from "./commands"; import SvnDecorations from "./decorations/svnDecorations"; @@ -18,6 +19,7 @@ import { hasSupportToRegisterDiffCommand, toDisposable } from "./util"; +import { DepNodeProvider } from "./treeView/incommingChangesDataProvider"; async function init( context: ExtensionContext, @@ -36,6 +38,11 @@ async function init( const svnCommands = new SvnCommands(model); disposables.push(model, contentProvider, svnCommands); + const rootPath = workspace.rootPath; + const incommingChangesProvider = new DepNodeProvider(rootPath); + + window.registerTreeDataProvider("incommingChanges", incommingChangesProvider); + // First, check the vscode has support to DecorationProvider if (hasSupportToDecorationProvider()) { const decoration = new SvnDecorations(model); diff --git a/src/treeView/dependencyTreeItem.ts b/src/treeView/dependencyTreeItem.ts new file mode 100644 index 00000000..949ca73a --- /dev/null +++ b/src/treeView/dependencyTreeItem.ts @@ -0,0 +1,3 @@ +import { TreeItem } from "vscode"; + +export default class Dependency extends TreeItem {} diff --git a/src/treeView/incommingChangesDataProvider.ts b/src/treeView/incommingChangesDataProvider.ts new file mode 100644 index 00000000..7718f571 --- /dev/null +++ b/src/treeView/incommingChangesDataProvider.ts @@ -0,0 +1,149 @@ +import * as fs from "fs"; +import * as path from "path"; +import * as vscode from "vscode"; + +export class DepNodeProvider implements vscode.TreeDataProvider { + private _onDidChangeTreeData: vscode.EventEmitter< + Dependency | undefined + > = new vscode.EventEmitter(); + public readonly onDidChangeTreeData: vscode.Event< + Dependency | undefined + > = this._onDidChangeTreeData.event; + + constructor(private workspaceRoot: string) {} + + public refresh(): void { + this._onDidChangeTreeData.fire(); + } + + public getTreeItem(element: Dependency): vscode.TreeItem { + return element; + } + + public getChildren(element?: Dependency): Thenable { + if (!this.workspaceRoot) { + vscode.window.showInformationMessage("No dependency in empty workspace"); + return Promise.resolve([]); + } + + return new Promise(resolve => { + if (element) { + resolve( + this.getDepsInPackageJson( + path.join( + this.workspaceRoot, + "node_modules", + element.label, + "package.json" + ) + ) + ); + } else { + const packageJsonPath = path.join(this.workspaceRoot, "package.json"); + console.log(packageJsonPath); + if (this.pathExists(packageJsonPath)) { + resolve(this.getDepsInPackageJson(packageJsonPath)); + } else { + vscode.window.showInformationMessage("Workspace has no package.json"); + resolve([]); + } + } + }); + } + + /** + * Given the path to package.json, read all its dependencies and devDependencies. + */ + private getDepsInPackageJson(packageJsonPath: string): Dependency[] { + if (this.pathExists(packageJsonPath)) { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")); + + const toDep = (moduleName: string, version: string): Dependency => { + if ( + this.pathExists( + path.join(this.workspaceRoot, "node_modules", moduleName) + ) + ) { + return new Dependency( + moduleName, + version, + vscode.TreeItemCollapsibleState.Collapsed + ); + } else { + return new Dependency( + moduleName, + version, + vscode.TreeItemCollapsibleState.None, + { + command: "extension.openPackageOnNpm", + title: "", + arguments: [moduleName] + } + ); + } + }; + + const deps = packageJson.dependencies + ? Object.keys(packageJson.dependencies).map(dep => + toDep(dep, packageJson.dependencies[dep]) + ) + : []; + const devDeps = packageJson.devDependencies + ? Object.keys(packageJson.devDependencies).map(dep => + toDep(dep, packageJson.devDependencies[dep]) + ) + : []; + return deps.concat(devDeps); + } else { + return []; + } + } + + private pathExists(p: string): boolean { + try { + fs.accessSync(p); + } catch (err) { + return false; + } + + return true; + } +} + +// class Dependency extends vscode.TreeItem { +// constructor( +// public readonly label: string, +// private version: string, +// public readonly collapsibleState: vscode.TreeItemCollapsibleState, +// public readonly command?: vscode.Command +// ) { +// super(label, collapsibleState); +// } + +// get tooltip(): string { +// return `${this.label}-${this.version}`; +// } + +// public iconPath = { +// light: path.join( +// __filename, +// "..", +// "..", +// "..", +// "resources", +// "light", +// "dependency.svg" +// ), +// dark: path.join( +// __filename, +// "..", +// "..", +// "..", +// "resources", +// "dark", +// "dependency.svg" +// ) +// }; + +// public contextValue = "dependency"; +// } From 9717f3a6c8f9164eb1559300690b1dd5735d1c05 Mon Sep 17 00:00:00 2001 From: chris johnston Date: Fri, 20 Jul 2018 13:34:11 +0100 Subject: [PATCH 02/11] treeview now shows repositories --- icons/dark/repo.svg | 4 + icons/light/repo.svg | 4 + package-lock.json | 20 ++- package.json | 7 +- src/extension.ts | 7 +- src/svn.ts | 4 +- src/treeView/dataProviders/svnProvider.ts | 35 +++++ src/treeView/dependencyTreeItem.ts | 3 - src/treeView/incommingChangesDataProvider.ts | 149 ------------------- src/treeView/nodes/baseNode.ts | 6 + src/treeView/nodes/repositoryNode.ts | 26 ++++ src/uri.ts | 6 + 12 files changed, 103 insertions(+), 168 deletions(-) create mode 100644 icons/dark/repo.svg create mode 100644 icons/light/repo.svg create mode 100644 src/treeView/dataProviders/svnProvider.ts delete mode 100644 src/treeView/dependencyTreeItem.ts delete mode 100644 src/treeView/incommingChangesDataProvider.ts create mode 100644 src/treeView/nodes/baseNode.ts create mode 100644 src/treeView/nodes/repositoryNode.ts diff --git a/icons/dark/repo.svg b/icons/dark/repo.svg new file mode 100644 index 00000000..b1f87469 --- /dev/null +++ b/icons/dark/repo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/icons/light/repo.svg b/icons/light/repo.svg new file mode 100644 index 00000000..4e2368ef --- /dev/null +++ b/icons/light/repo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index efc1f092..d5fa368c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svn-scm", - "version": "1.29.6", + "version": "1.30.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -161,7 +161,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "integrity": + "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, "arr-union": { @@ -1214,7 +1215,8 @@ "growl": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha1-GSa6kM8+3+KttJJ/WIC8IsZseQ8=", + "integrity": + "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", "dev": true }, "gulp-chmod": { @@ -1547,7 +1549,8 @@ "queue": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/queue/-/queue-4.4.2.tgz", - "integrity": "sha1-Wpcz2ai4vRs26TS8nFWribKOKcc=", + "integrity": + "sha512-fSMRXbwhMwipcDZ08enW2vl+YDmAmhcNcr43sCJL8DIg+CFOsoRLG23ctxA+fwNk1w55SePSiS7oqQQSgQoVJQ==", "dev": true, "requires": { "inherits": "2.0.3" @@ -2382,7 +2385,8 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha1-iD992rwWUUKyphQn8zUt7RldGj4=", + "integrity": + "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", "dev": true, "requires": { "has-flag": "2.0.0" @@ -2804,7 +2808,8 @@ "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", + "integrity": + "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { "is-equal-shallow": "0.1.3" @@ -3586,7 +3591,8 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha1-iD992rwWUUKyphQn8zUt7RldGj4=", + "integrity": + "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", "dev": true, "requires": { "has-flag": "2.0.0" diff --git a/package.json b/package.json index 1f207cc2..6e65eba4 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "prettier": "prettier --no-config --list-different 'src/**/*.ts'", "tools:genReadme": "node ./out/tools/generateConfigSectionForReadme.js", "precommit": "pretty-quick --staged && npm run lint", - "lint": "tslint -p ./" + "lint": "tslint -p ./", + "lint:fix": "tslint -p --fix ./" }, "dependencies": { "iconv-lite": "^0.4.23", @@ -59,8 +60,8 @@ "views": { "explorer": [ { - "id": "incommingChanges", - "name": "Incoming Changes" + "id": "svn", + "name": "SVN" } ] }, diff --git a/src/extension.ts b/src/extension.ts index ed02176c..e15b17b3 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -19,7 +19,7 @@ import { hasSupportToRegisterDiffCommand, toDisposable } from "./util"; -import { DepNodeProvider } from "./treeView/incommingChangesDataProvider"; +import SvnProvider from "./treeView/dataProviders/svnProvider"; async function init( context: ExtensionContext, @@ -38,10 +38,9 @@ async function init( const svnCommands = new SvnCommands(model); disposables.push(model, contentProvider, svnCommands); - const rootPath = workspace.rootPath; - const incommingChangesProvider = new DepNodeProvider(rootPath); + const svnProvider = new SvnProvider(model); - window.registerTreeDataProvider("incommingChanges", incommingChangesProvider); + window.registerTreeDataProvider("svn", svnProvider); // First, check the vscode has support to DecorationProvider if (hasSupportToDecorationProvider()) { diff --git a/src/svn.ts b/src/svn.ts index 408185e0..6a2b83ba 100644 --- a/src/svn.ts +++ b/src/svn.ts @@ -3,7 +3,7 @@ import { EventEmitter } from "events"; import * as iconv from "iconv-lite"; import isUtf8 = require("is-utf8"); import * as jschardet from "jschardet"; -import { workspace } from "vscode"; +import { workspace, Uri } from "vscode"; import { ICpOptions, IExecutionResult, ISvnOptions } from "./common/types"; import { configuration } from "./helpers/configuration"; import { parseInfoXml } from "./infoParser"; @@ -139,7 +139,7 @@ export class Svn { dispose(disposables); let encoding = workspace - .getConfiguration("files") + .getConfiguration("files", Uri.file(cwd)) .get("encoding", "utf8"); // SVN with '--xml' always return 'UTF-8', and jschardet detects this encoding: 'TIS-620' diff --git a/src/treeView/dataProviders/svnProvider.ts b/src/treeView/dataProviders/svnProvider.ts new file mode 100644 index 00000000..63d189e1 --- /dev/null +++ b/src/treeView/dataProviders/svnProvider.ts @@ -0,0 +1,35 @@ +import { + TreeDataProvider, + window, + TreeItem, + TreeItemCollapsibleState +} from "vscode"; +import { Model } from "../../model"; +import RepositoryNode from "../nodes/repositoryNode"; + +export default class SvnProvider implements TreeDataProvider { + constructor(private model: Model) {} + + public getTreeItem(element: RepositoryNode): TreeItem { + return element.getTreeItem(); + } + + public getChildren(element?: RepositoryNode): Thenable { + if (!this.model || this.model.openRepositories.length === 0) { + window.showInformationMessage("No Svn repositories open"); + return Promise.resolve([]); + } + + return new Promise(resolve => { + if (element) { + resolve([]); + } + + const repositories = this.model.openRepositories.map(repository => { + return new RepositoryNode(repository.repository.workspaceRoot); + }); + + resolve(repositories); + }); + } +} diff --git a/src/treeView/dependencyTreeItem.ts b/src/treeView/dependencyTreeItem.ts deleted file mode 100644 index 949ca73a..00000000 --- a/src/treeView/dependencyTreeItem.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { TreeItem } from "vscode"; - -export default class Dependency extends TreeItem {} diff --git a/src/treeView/incommingChangesDataProvider.ts b/src/treeView/incommingChangesDataProvider.ts deleted file mode 100644 index 7718f571..00000000 --- a/src/treeView/incommingChangesDataProvider.ts +++ /dev/null @@ -1,149 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as vscode from "vscode"; - -export class DepNodeProvider implements vscode.TreeDataProvider { - private _onDidChangeTreeData: vscode.EventEmitter< - Dependency | undefined - > = new vscode.EventEmitter(); - public readonly onDidChangeTreeData: vscode.Event< - Dependency | undefined - > = this._onDidChangeTreeData.event; - - constructor(private workspaceRoot: string) {} - - public refresh(): void { - this._onDidChangeTreeData.fire(); - } - - public getTreeItem(element: Dependency): vscode.TreeItem { - return element; - } - - public getChildren(element?: Dependency): Thenable { - if (!this.workspaceRoot) { - vscode.window.showInformationMessage("No dependency in empty workspace"); - return Promise.resolve([]); - } - - return new Promise(resolve => { - if (element) { - resolve( - this.getDepsInPackageJson( - path.join( - this.workspaceRoot, - "node_modules", - element.label, - "package.json" - ) - ) - ); - } else { - const packageJsonPath = path.join(this.workspaceRoot, "package.json"); - console.log(packageJsonPath); - if (this.pathExists(packageJsonPath)) { - resolve(this.getDepsInPackageJson(packageJsonPath)); - } else { - vscode.window.showInformationMessage("Workspace has no package.json"); - resolve([]); - } - } - }); - } - - /** - * Given the path to package.json, read all its dependencies and devDependencies. - */ - private getDepsInPackageJson(packageJsonPath: string): Dependency[] { - if (this.pathExists(packageJsonPath)) { - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")); - - const toDep = (moduleName: string, version: string): Dependency => { - if ( - this.pathExists( - path.join(this.workspaceRoot, "node_modules", moduleName) - ) - ) { - return new Dependency( - moduleName, - version, - vscode.TreeItemCollapsibleState.Collapsed - ); - } else { - return new Dependency( - moduleName, - version, - vscode.TreeItemCollapsibleState.None, - { - command: "extension.openPackageOnNpm", - title: "", - arguments: [moduleName] - } - ); - } - }; - - const deps = packageJson.dependencies - ? Object.keys(packageJson.dependencies).map(dep => - toDep(dep, packageJson.dependencies[dep]) - ) - : []; - const devDeps = packageJson.devDependencies - ? Object.keys(packageJson.devDependencies).map(dep => - toDep(dep, packageJson.devDependencies[dep]) - ) - : []; - return deps.concat(devDeps); - } else { - return []; - } - } - - private pathExists(p: string): boolean { - try { - fs.accessSync(p); - } catch (err) { - return false; - } - - return true; - } -} - -// class Dependency extends vscode.TreeItem { -// constructor( -// public readonly label: string, -// private version: string, -// public readonly collapsibleState: vscode.TreeItemCollapsibleState, -// public readonly command?: vscode.Command -// ) { -// super(label, collapsibleState); -// } - -// get tooltip(): string { -// return `${this.label}-${this.version}`; -// } - -// public iconPath = { -// light: path.join( -// __filename, -// "..", -// "..", -// "..", -// "resources", -// "light", -// "dependency.svg" -// ), -// dark: path.join( -// __filename, -// "..", -// "..", -// "..", -// "resources", -// "dark", -// "dependency.svg" -// ) -// }; - -// public contextValue = "dependency"; -// } diff --git a/src/treeView/nodes/baseNode.ts b/src/treeView/nodes/baseNode.ts new file mode 100644 index 00000000..99d0a9ff --- /dev/null +++ b/src/treeView/nodes/baseNode.ts @@ -0,0 +1,6 @@ +import { TreeItem } from "vscode"; + +export default abstract class BaseNode { + abstract getChildren(): BaseNode[] | Promise; + abstract getTreeItem(): TreeItem | Promise; +} diff --git a/src/treeView/nodes/repositoryNode.ts b/src/treeView/nodes/repositoryNode.ts new file mode 100644 index 00000000..06f5372f --- /dev/null +++ b/src/treeView/nodes/repositoryNode.ts @@ -0,0 +1,26 @@ +import * as path from "path"; +import { TreeItem, TreeDataProvider, TreeItemCollapsibleState } from "vscode"; +import BaseNode from "./baseNode"; +import { getIconUri } from "../../uri"; + +export default class RepositoryNode implements TreeDataProvider { + constructor(private _label: string) {} + + get label() { + return path.basename(this._label); + } + + public getTreeItem(): TreeItem { + const item = new TreeItem(this.label, TreeItemCollapsibleState.Collapsed); + item.iconPath = { + dark: getIconUri("repo", "dark"), + light: getIconUri("repo", "light") + }; + + return item; + } + + public getChildren(): Thenable { + return Promise.resolve([]); + } +} diff --git a/src/uri.ts b/src/uri.ts index 626145e2..91800723 100644 --- a/src/uri.ts +++ b/src/uri.ts @@ -1,3 +1,4 @@ +import * as path from "path"; import { Uri } from "vscode"; import { ISvnUriExtraParams, @@ -27,3 +28,8 @@ export function toSvnUri( query: JSON.stringify(params) }); } + +export function getIconUri(iconName: string, theme: string): Uri { + const iconsRootPath = path.join(__dirname, "..", "icons"); + return Uri.file(path.join(iconsRootPath, theme, `${iconName}.svg`)); +} From ebaeac480ad4c90ee66672d8b67d24b4b3d7c16a Mon Sep 17 00:00:00 2001 From: chris johnston Date: Fri, 20 Jul 2018 14:03:45 +0100 Subject: [PATCH 03/11] treeview now shows repositories --- package.json | 2 +- src/extension.ts | 2 +- src/svn.ts | 2 +- src/treeView/dataProviders/svnProvider.ts | 4 ++-- src/treeView/nodes/baseNode.ts | 4 ++-- src/treeView/nodes/repositoryNode.ts | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 6e65eba4..0eba8d53 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "tools:genReadme": "node ./out/tools/generateConfigSectionForReadme.js", "precommit": "pretty-quick --staged && npm run lint", "lint": "tslint -p ./", - "lint:fix": "tslint -p --fix ./" + "lint:fix": "tslint --fix -p ./" }, "dependencies": { "iconv-lite": "^0.4.23", diff --git a/src/extension.ts b/src/extension.ts index e15b17b3..4bd07a1c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -14,12 +14,12 @@ import { Model } from "./model"; import { Svn } from "./svn"; import { SvnContentProvider } from "./svnContentProvider"; import { SvnFinder } from "./svnFinder"; +import SvnProvider from "./treeView/dataProviders/svnProvider"; import { hasSupportToDecorationProvider, hasSupportToRegisterDiffCommand, toDisposable } from "./util"; -import SvnProvider from "./treeView/dataProviders/svnProvider"; async function init( context: ExtensionContext, diff --git a/src/svn.ts b/src/svn.ts index 6a2b83ba..76e584f3 100644 --- a/src/svn.ts +++ b/src/svn.ts @@ -3,7 +3,7 @@ import { EventEmitter } from "events"; import * as iconv from "iconv-lite"; import isUtf8 = require("is-utf8"); import * as jschardet from "jschardet"; -import { workspace, Uri } from "vscode"; +import { Uri, workspace } from "vscode"; import { ICpOptions, IExecutionResult, ISvnOptions } from "./common/types"; import { configuration } from "./helpers/configuration"; import { parseInfoXml } from "./infoParser"; diff --git a/src/treeView/dataProviders/svnProvider.ts b/src/treeView/dataProviders/svnProvider.ts index 63d189e1..3d7d0f7f 100644 --- a/src/treeView/dataProviders/svnProvider.ts +++ b/src/treeView/dataProviders/svnProvider.ts @@ -1,8 +1,8 @@ import { TreeDataProvider, - window, TreeItem, - TreeItemCollapsibleState + TreeItemCollapsibleState, + window } from "vscode"; import { Model } from "../../model"; import RepositoryNode from "../nodes/repositoryNode"; diff --git a/src/treeView/nodes/baseNode.ts b/src/treeView/nodes/baseNode.ts index 99d0a9ff..d82af366 100644 --- a/src/treeView/nodes/baseNode.ts +++ b/src/treeView/nodes/baseNode.ts @@ -1,6 +1,6 @@ import { TreeItem } from "vscode"; export default abstract class BaseNode { - abstract getChildren(): BaseNode[] | Promise; - abstract getTreeItem(): TreeItem | Promise; + public abstract getChildren(): BaseNode[] | Promise; + public abstract getTreeItem(): TreeItem | Promise; } diff --git a/src/treeView/nodes/repositoryNode.ts b/src/treeView/nodes/repositoryNode.ts index 06f5372f..b400e3e7 100644 --- a/src/treeView/nodes/repositoryNode.ts +++ b/src/treeView/nodes/repositoryNode.ts @@ -1,7 +1,7 @@ import * as path from "path"; -import { TreeItem, TreeDataProvider, TreeItemCollapsibleState } from "vscode"; -import BaseNode from "./baseNode"; +import { TreeDataProvider, TreeItem, TreeItemCollapsibleState } from "vscode"; import { getIconUri } from "../../uri"; +import BaseNode from "./baseNode"; export default class RepositoryNode implements TreeDataProvider { constructor(private _label: string) {} From 790e5d11f6b1c1cb1c2938aa27e3012529c22d00 Mon Sep 17 00:00:00 2001 From: chris johnston Date: Fri, 10 Aug 2018 15:51:23 +0100 Subject: [PATCH 04/11] incoming changes are now listed --- icons/dark/download.svg | 4 ++ icons/light/download.svg | 4 ++ package.json | 15 +++++++ src/repository.ts | 7 ++++ src/treeView/dataProviders/svnProvider.ts | 43 ++++++++++++++------- src/treeView/nodes/baseNode.ts | 2 +- src/treeView/nodes/incomingChangeNode.ts | 26 +++++++++++++ src/treeView/nodes/incomingChangesNode.ts | 39 +++++++++++++++++++ src/treeView/nodes/noIncomingChangesNode.ts | 17 ++++++++ src/treeView/nodes/repositoryNode.ts | 14 ++++--- 10 files changed, 150 insertions(+), 21 deletions(-) create mode 100644 icons/dark/download.svg create mode 100644 icons/light/download.svg create mode 100644 src/treeView/nodes/incomingChangeNode.ts create mode 100644 src/treeView/nodes/incomingChangesNode.ts create mode 100644 src/treeView/nodes/noIncomingChangesNode.ts diff --git a/icons/dark/download.svg b/icons/dark/download.svg new file mode 100644 index 00000000..12ad689a --- /dev/null +++ b/icons/dark/download.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/icons/light/download.svg b/icons/light/download.svg new file mode 100644 index 00000000..f271a008 --- /dev/null +++ b/icons/light/download.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/package.json b/package.json index 0eba8d53..4d4247fe 100644 --- a/package.json +++ b/package.json @@ -235,6 +235,14 @@ "command": "svn.renameExplorer", "title": "Rename with SVN", "category": "SVN" + }, + { + "command": "svn.treeview.refreshProvider", + "title": "Refresh", + "icon": { + "light": "icons/light/refresh.svg", + "dark": "icons/dark/refresh.svg" + } } ], "menus": { @@ -339,6 +347,13 @@ "when": "false" } ], + "view/title": [ + { + "command": "svn.treeview.refreshProvider", + "when": "view == svn", + "group": "navigation" + } + ], "scm/title": [ { "command": "svn.commitWithMessage", diff --git a/src/repository.ts b/src/repository.ts index 5d2356d1..a72cc785 100644 --- a/src/repository.ts +++ b/src/repository.ts @@ -68,6 +68,7 @@ export class Repository { public remoteChangedFiles: number = 0; public isIncomplete: boolean = false; public needCleanUp: boolean = false; + public remoteChanges: any[] = []; private lastPromptAuth?: Thenable; @@ -406,6 +407,12 @@ export class Repository { : undefined; if (status.reposStatus) { + const changes = { + uri, + type: status.reposStatus.item, + props: status.reposStatus.props + }; + this.remoteChanges.push(changes); remoteChanged.push( new Resource( uri, diff --git a/src/treeView/dataProviders/svnProvider.ts b/src/treeView/dataProviders/svnProvider.ts index 3d7d0f7f..9e932490 100644 --- a/src/treeView/dataProviders/svnProvider.ts +++ b/src/treeView/dataProviders/svnProvider.ts @@ -1,35 +1,50 @@ import { TreeDataProvider, TreeItem, - TreeItemCollapsibleState, - window + window, + Event, + EventEmitter, + commands } from "vscode"; import { Model } from "../../model"; import RepositoryNode from "../nodes/repositoryNode"; +import BaseNode from "../nodes/baseNode"; -export default class SvnProvider implements TreeDataProvider { - constructor(private model: Model) {} +export default class SvnProvider implements TreeDataProvider { + private _onDidChangeTreeData: EventEmitter< + BaseNode | undefined + > = new EventEmitter(); + public onDidChangeTreeData: Event = this + ._onDidChangeTreeData.event; + + constructor(private model: Model) { + commands.registerCommand("svn.treeview.refreshProvider", () => + this.refresh() + ); + } + + public refresh(): void { + this._onDidChangeTreeData.fire(); + } public getTreeItem(element: RepositoryNode): TreeItem { return element.getTreeItem(); } - public getChildren(element?: RepositoryNode): Thenable { + public async getChildren(element?: BaseNode): Promise { if (!this.model || this.model.openRepositories.length === 0) { window.showInformationMessage("No Svn repositories open"); return Promise.resolve([]); } - return new Promise(resolve => { - if (element) { - resolve([]); - } - - const repositories = this.model.openRepositories.map(repository => { - return new RepositoryNode(repository.repository.workspaceRoot); - }); + if (element) { + return element.getChildren(); + } - resolve(repositories); + const repositories = this.model.openRepositories.map(repository => { + return new RepositoryNode(repository.repository); }); + + return repositories; } } diff --git a/src/treeView/nodes/baseNode.ts b/src/treeView/nodes/baseNode.ts index d82af366..cfad9190 100644 --- a/src/treeView/nodes/baseNode.ts +++ b/src/treeView/nodes/baseNode.ts @@ -1,6 +1,6 @@ import { TreeItem } from "vscode"; export default abstract class BaseNode { - public abstract getChildren(): BaseNode[] | Promise; + public abstract getChildren(): BaseNode[] | Promise; public abstract getTreeItem(): TreeItem | Promise; } diff --git a/src/treeView/nodes/incomingChangeNode.ts b/src/treeView/nodes/incomingChangeNode.ts new file mode 100644 index 00000000..ccdb3893 --- /dev/null +++ b/src/treeView/nodes/incomingChangeNode.ts @@ -0,0 +1,26 @@ +import { TreeItem, TreeItemCollapsibleState, Uri } from "vscode"; +import BaseNode from "./baseNode"; +import * as path from "path"; +import { getIconUri } from "../../uri"; + +export default class IncomingChangeNode implements BaseNode { + constructor(private uri: Uri, private type: string, private props: string) {} + + get label() { + return path.basename(this.uri.path); + } + + public getTreeItem(): TreeItem { + const item = new TreeItem(this.label, TreeItemCollapsibleState.None); + item.iconPath = { + dark: getIconUri(`status-${this.type}`, "dark"), + light: getIconUri(`status-${this.type}`, "light") + }; + + return item; + } + + public getChildren(): Promise { + return Promise.resolve([]); + } +} diff --git a/src/treeView/nodes/incomingChangesNode.ts b/src/treeView/nodes/incomingChangesNode.ts new file mode 100644 index 00000000..af075853 --- /dev/null +++ b/src/treeView/nodes/incomingChangesNode.ts @@ -0,0 +1,39 @@ +import { TreeItem, TreeItemCollapsibleState } from "vscode"; +import BaseNode from "./baseNode"; +import { getIconUri } from "../../uri"; +import IncommingChangeNode from "./incomingChangeNode"; +import { Repository } from "../../repository"; +import NoIncomingChangesNode from "./noIncomingChangesNode"; + +export default class IncomingChangesNode implements BaseNode { + constructor(private repository: Repository) {} + + public getTreeItem(): TreeItem { + const item = new TreeItem( + "Incoming Changes", + TreeItemCollapsibleState.Collapsed + ); + item.iconPath = { + dark: getIconUri("download", "dark"), + light: getIconUri("download", "light") + }; + + return item; + } + + public async getChildren(): Promise { + const changes = this.repository.remoteChanges.map(remoteChange => { + return new IncommingChangeNode( + remoteChange.uri, + remoteChange.type, + remoteChange.props + ); + }); + + if (changes.length === 0) { + return [new NoIncomingChangesNode()]; + } + + return changes; + } +} diff --git a/src/treeView/nodes/noIncomingChangesNode.ts b/src/treeView/nodes/noIncomingChangesNode.ts new file mode 100644 index 00000000..5d365033 --- /dev/null +++ b/src/treeView/nodes/noIncomingChangesNode.ts @@ -0,0 +1,17 @@ +import { TreeItem, TreeItemCollapsibleState } from "vscode"; +import BaseNode from "./baseNode"; + +export default class NoIncomingChangesNode implements BaseNode { + public getTreeItem(): TreeItem { + const item = new TreeItem( + "No Incoming Changes", + TreeItemCollapsibleState.None + ); + + return item; + } + + public async getChildren(): Promise { + return []; + } +} diff --git a/src/treeView/nodes/repositoryNode.ts b/src/treeView/nodes/repositoryNode.ts index b400e3e7..3e228585 100644 --- a/src/treeView/nodes/repositoryNode.ts +++ b/src/treeView/nodes/repositoryNode.ts @@ -1,13 +1,15 @@ import * as path from "path"; -import { TreeDataProvider, TreeItem, TreeItemCollapsibleState } from "vscode"; +import { TreeItem, TreeItemCollapsibleState, window } from "vscode"; import { getIconUri } from "../../uri"; import BaseNode from "./baseNode"; +import IncomingChangesNode from "./incomingChangesNode"; +import { Repository } from "../../repository"; -export default class RepositoryNode implements TreeDataProvider { - constructor(private _label: string) {} +export default class RepositoryNode implements BaseNode { + constructor(private repository: Repository) {} get label() { - return path.basename(this._label); + return path.basename(this.repository.workspaceRoot); } public getTreeItem(): TreeItem { @@ -20,7 +22,7 @@ export default class RepositoryNode implements TreeDataProvider { return item; } - public getChildren(): Thenable { - return Promise.resolve([]); + public async getChildren(): Promise { + return [new IncomingChangesNode(this.repository)]; } } From 4f2d9e9cc8ee27ddadc1071227ab00b024f96efb Mon Sep 17 00:00:00 2001 From: chris johnston Date: Fri, 10 Aug 2018 15:59:08 +0100 Subject: [PATCH 05/11] linter changes --- src/treeView/dataProviders/svnProvider.ts | 10 +++++----- src/treeView/nodes/incomingChangeNode.ts | 4 ++-- src/treeView/nodes/incomingChangesNode.ts | 4 ++-- src/treeView/nodes/repositoryNode.ts | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/treeView/dataProviders/svnProvider.ts b/src/treeView/dataProviders/svnProvider.ts index 9e932490..63e7865c 100644 --- a/src/treeView/dataProviders/svnProvider.ts +++ b/src/treeView/dataProviders/svnProvider.ts @@ -1,14 +1,14 @@ import { - TreeDataProvider, - TreeItem, - window, + commands, Event, EventEmitter, - commands + TreeDataProvider, + TreeItem, + window } from "vscode"; import { Model } from "../../model"; -import RepositoryNode from "../nodes/repositoryNode"; import BaseNode from "../nodes/baseNode"; +import RepositoryNode from "../nodes/repositoryNode"; export default class SvnProvider implements TreeDataProvider { private _onDidChangeTreeData: EventEmitter< diff --git a/src/treeView/nodes/incomingChangeNode.ts b/src/treeView/nodes/incomingChangeNode.ts index ccdb3893..603e5057 100644 --- a/src/treeView/nodes/incomingChangeNode.ts +++ b/src/treeView/nodes/incomingChangeNode.ts @@ -1,7 +1,7 @@ -import { TreeItem, TreeItemCollapsibleState, Uri } from "vscode"; -import BaseNode from "./baseNode"; import * as path from "path"; +import { TreeItem, TreeItemCollapsibleState, Uri } from "vscode"; import { getIconUri } from "../../uri"; +import BaseNode from "./baseNode"; export default class IncomingChangeNode implements BaseNode { constructor(private uri: Uri, private type: string, private props: string) {} diff --git a/src/treeView/nodes/incomingChangesNode.ts b/src/treeView/nodes/incomingChangesNode.ts index af075853..3f61c65a 100644 --- a/src/treeView/nodes/incomingChangesNode.ts +++ b/src/treeView/nodes/incomingChangesNode.ts @@ -1,8 +1,8 @@ import { TreeItem, TreeItemCollapsibleState } from "vscode"; -import BaseNode from "./baseNode"; +import { Repository } from "../../repository"; import { getIconUri } from "../../uri"; +import BaseNode from "./baseNode"; import IncommingChangeNode from "./incomingChangeNode"; -import { Repository } from "../../repository"; import NoIncomingChangesNode from "./noIncomingChangesNode"; export default class IncomingChangesNode implements BaseNode { diff --git a/src/treeView/nodes/repositoryNode.ts b/src/treeView/nodes/repositoryNode.ts index 3e228585..c8c10f09 100644 --- a/src/treeView/nodes/repositoryNode.ts +++ b/src/treeView/nodes/repositoryNode.ts @@ -1,9 +1,9 @@ import * as path from "path"; import { TreeItem, TreeItemCollapsibleState, window } from "vscode"; +import { Repository } from "../../repository"; import { getIconUri } from "../../uri"; import BaseNode from "./baseNode"; import IncomingChangesNode from "./incomingChangesNode"; -import { Repository } from "../../repository"; export default class RepositoryNode implements BaseNode { constructor(private repository: Repository) {} From 676cd1e8ffd26f07af0d826c6a6b27291ee49599 Mon Sep 17 00:00:00 2001 From: chris johnston Date: Sun, 12 Aug 2018 09:26:12 +0100 Subject: [PATCH 06/11] added commands to open added and modified files --- package.json | 10 ++++++++++ src/commands.ts | 7 ++++++- src/repository.ts | 7 ------- src/treeView/nodes/incomingChangeNode.ts | 12 +++++++++++- src/treeView/nodes/incomingChangesNode.ts | 19 ++++++++++++------- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 4d4247fe..4ec77c7e 100644 --- a/package.json +++ b/package.json @@ -354,6 +354,16 @@ "group": "navigation" } ], + "view/item/context": [ + { + "command": "svn.openHEADFile", + "when": "viewItem == incomingChange:file:modified" + }, + { + "command": "svn.openHEADFile", + "when": "viewItem == incomingChange:file:added" + } + ], "scm/title": [ { "command": "svn.commitWithMessage", diff --git a/src/commands.ts b/src/commands.ts index 6b183c86..fe790fe3 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -35,6 +35,7 @@ import { inputCommitMessage } from "./messages"; import { Model } from "./model"; import { Repository } from "./repository"; import { Resource } from "./resource"; +import IncommingChangeNode from "./treeView/nodes/incomingChangeNode"; import { fromSvnUri, toSvnUri } from "./uri"; import { fixPathSeparator, @@ -426,13 +427,17 @@ export class SvnCommands implements IDisposable { } @command("svn.openHEADFile") - public async openHEADFile(arg?: Resource | Uri): Promise { + public async openHEADFile( + arg?: Resource | Uri | IncommingChangeNode + ): Promise { let resource: Resource | undefined; if (arg instanceof Resource) { resource = arg; } else if (arg instanceof Uri) { resource = this.getSCMResource(arg); + } else if (arg instanceof IncommingChangeNode) { + resource = new Resource(arg.uri, arg.type, undefined, arg.props, true); } else { resource = this.getSCMResource(); } diff --git a/src/repository.ts b/src/repository.ts index a72cc785..5d2356d1 100644 --- a/src/repository.ts +++ b/src/repository.ts @@ -68,7 +68,6 @@ export class Repository { public remoteChangedFiles: number = 0; public isIncomplete: boolean = false; public needCleanUp: boolean = false; - public remoteChanges: any[] = []; private lastPromptAuth?: Thenable; @@ -407,12 +406,6 @@ export class Repository { : undefined; if (status.reposStatus) { - const changes = { - uri, - type: status.reposStatus.item, - props: status.reposStatus.props - }; - this.remoteChanges.push(changes); remoteChanged.push( new Resource( uri, diff --git a/src/treeView/nodes/incomingChangeNode.ts b/src/treeView/nodes/incomingChangeNode.ts index 603e5057..b5c90eb5 100644 --- a/src/treeView/nodes/incomingChangeNode.ts +++ b/src/treeView/nodes/incomingChangeNode.ts @@ -1,10 +1,15 @@ +import * as fs from "fs"; import * as path from "path"; import { TreeItem, TreeItemCollapsibleState, Uri } from "vscode"; import { getIconUri } from "../../uri"; import BaseNode from "./baseNode"; export default class IncomingChangeNode implements BaseNode { - constructor(private uri: Uri, private type: string, private props: string) {} + constructor(public uri: Uri, public type: string) {} + + get props(): undefined { + return undefined; + } get label() { return path.basename(this.uri.path); @@ -16,6 +21,7 @@ export default class IncomingChangeNode implements BaseNode { dark: getIconUri(`status-${this.type}`, "dark"), light: getIconUri(`status-${this.type}`, "light") }; + item.contextValue = this.getContextValue(); return item; } @@ -23,4 +29,8 @@ export default class IncomingChangeNode implements BaseNode { public getChildren(): Promise { return Promise.resolve([]); } + + private getContextValue(): string { + return `incomingChange:file:${this.type}`; + } } diff --git a/src/treeView/nodes/incomingChangesNode.ts b/src/treeView/nodes/incomingChangesNode.ts index 3f61c65a..50f91ab8 100644 --- a/src/treeView/nodes/incomingChangesNode.ts +++ b/src/treeView/nodes/incomingChangesNode.ts @@ -22,13 +22,18 @@ export default class IncomingChangesNode implements BaseNode { } public async getChildren(): Promise { - const changes = this.repository.remoteChanges.map(remoteChange => { - return new IncommingChangeNode( - remoteChange.uri, - remoteChange.type, - remoteChange.props - ); - }); + if (!this.repository.remoteChanged) { + return []; + } + + const changes = this.repository.remoteChanged.resourceStates.map( + remoteChange => { + return new IncommingChangeNode( + remoteChange.resourceUri, + remoteChange.type + ); + } + ); if (changes.length === 0) { return [new NoIncomingChangesNode()]; From 302b4784df634501eea9a3535a815e84901ede51 Mon Sep 17 00:00:00 2001 From: chris johnston Date: Mon, 13 Aug 2018 10:57:48 +0100 Subject: [PATCH 07/11] added context options and hocked up to status change evenet --- package.json | 8 ++++++-- src/commands.ts | 12 +++++++++++- src/treeView/dataProviders/svnProvider.ts | 6 +++++- src/treeView/nodes/repositoryNode.ts | 10 +++++++++- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4ec77c7e..3a16e5f3 100644 --- a/package.json +++ b/package.json @@ -357,11 +357,15 @@ "view/item/context": [ { "command": "svn.openHEADFile", + "when": "viewItem != incomingChange:file:deleted" + }, + { + "command": "svn.openFile", "when": "viewItem == incomingChange:file:modified" }, { - "command": "svn.openHEADFile", - "when": "viewItem == incomingChange:file:added" + "command": "svn.openFile", + "when": "viewItem == incomingChange:file:deleted" } ], "scm/title": [ diff --git a/src/commands.ts b/src/commands.ts index fe790fe3..4d68e279 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -368,7 +368,7 @@ export class SvnCommands implements IDisposable { @command("svn.openFile") public async openFile( - arg?: Resource | Uri, + arg?: Resource | Uri | IncommingChangeNode, ...resourceStates: SourceControlResourceState[] ): Promise { const preserveFocus = arg instanceof Resource; @@ -381,6 +381,16 @@ export class SvnCommands implements IDisposable { } else if (arg.scheme === "file") { uris = [arg]; } + } else if (arg instanceof IncommingChangeNode) { + const resource = new Resource( + arg.uri, + arg.type, + undefined, + arg.props, + true + ); + + uris = [resource.resourceUri]; } else { let resource = arg; diff --git a/src/treeView/dataProviders/svnProvider.ts b/src/treeView/dataProviders/svnProvider.ts index 63e7865c..fccb27a3 100644 --- a/src/treeView/dataProviders/svnProvider.ts +++ b/src/treeView/dataProviders/svnProvider.ts @@ -42,9 +42,13 @@ export default class SvnProvider implements TreeDataProvider { } const repositories = this.model.openRepositories.map(repository => { - return new RepositoryNode(repository.repository); + return new RepositoryNode(repository.repository, this); }); return repositories; } + + public update(node: BaseNode): void { + this._onDidChangeTreeData.fire(node); + } } diff --git a/src/treeView/nodes/repositoryNode.ts b/src/treeView/nodes/repositoryNode.ts index c8c10f09..94a5b7ef 100644 --- a/src/treeView/nodes/repositoryNode.ts +++ b/src/treeView/nodes/repositoryNode.ts @@ -2,11 +2,19 @@ import * as path from "path"; import { TreeItem, TreeItemCollapsibleState, window } from "vscode"; import { Repository } from "../../repository"; import { getIconUri } from "../../uri"; +import SvnProvider from "../dataProviders/svnProvider"; import BaseNode from "./baseNode"; import IncomingChangesNode from "./incomingChangesNode"; export default class RepositoryNode implements BaseNode { - constructor(private repository: Repository) {} + constructor( + private repository: Repository, + private svnProvider: SvnProvider + ) { + repository.onDidChangeStatus(() => { + svnProvider.update(this); + }); + } get label() { return path.basename(this.repository.workspaceRoot); From 6c27e688e466acd888864cac351b5a2a33b4662a Mon Sep 17 00:00:00 2001 From: chris johnston Date: Mon, 13 Aug 2018 13:22:07 +0100 Subject: [PATCH 08/11] Added pull remote change command --- package.json | 13 ++++++++---- src/commands.ts | 24 +++++++++++++++++++++++ src/repository.ts | 8 ++++++++ src/svnRepository.ts | 18 +++++++++++++++++ src/treeView/nodes/incomingChangeNode.ts | 17 ++++++++++------ src/treeView/nodes/incomingChangesNode.ts | 3 ++- 6 files changed, 72 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 3a16e5f3..1b285094 100644 --- a/package.json +++ b/package.json @@ -243,6 +243,11 @@ "light": "icons/light/refresh.svg", "dark": "icons/dark/refresh.svg" } + }, + { + "command": "svn.treeview.pullIncomingChange", + "title": "Pull incoming change", + "category": "SVN" } ], "menus": { @@ -357,15 +362,15 @@ "view/item/context": [ { "command": "svn.openHEADFile", - "when": "viewItem != incomingChange:file:deleted" + "when": "viewItem =~ /incomingChange:(added|modified)/" }, { "command": "svn.openFile", - "when": "viewItem == incomingChange:file:modified" + "when": "viewItem =~ /incomingChange:(modified|deleted)/" }, { - "command": "svn.openFile", - "when": "viewItem == incomingChange:file:deleted" + "command": "svn.treeview.pullIncomingChange", + "when": "viewItem =~ /incomingChange:*/" } ], "scm/title": [ diff --git a/src/commands.ts b/src/commands.ts index 4d68e279..673aecbf 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -36,6 +36,7 @@ import { Model } from "./model"; import { Repository } from "./repository"; import { Resource } from "./resource"; import IncommingChangeNode from "./treeView/nodes/incomingChangeNode"; +import IncomingChangeNode from "./treeView/nodes/incomingChangeNode"; import { fromSvnUri, toSvnUri } from "./uri"; import { fixPathSeparator, @@ -771,6 +772,29 @@ export class SvnCommands implements IDisposable { } } + @command("svn.treeview.pullIncomingChange") + public async pullIncomingChange( + incomingChange: IncomingChangeNode + ): Promise { + try { + const showUpdateMessage = configuration.get( + "showUpdateMessage", + true + ); + + const result = await incomingChange.repository.pullIncomingChange( + incomingChange.uri.fsPath + ); + + if (showUpdateMessage) { + window.showInformationMessage(result); + } + } catch (error) { + console.error(error); + window.showErrorMessage("Unable to update"); + } + } + private async showDiffPath(repository: Repository, content: string) { try { const tempFile = path.join(repository.root, ".svn", "tmp", "svn.patch"); diff --git a/src/repository.ts b/src/repository.ts index 5d2356d1..5efd0737 100644 --- a/src/repository.ts +++ b/src/repository.ts @@ -644,6 +644,14 @@ export class Repository { }); } + public async pullIncomingChange(path: string) { + return this.run(Operation.Update, async () => { + const response = await this.repository.pullIncomingChange(path); + this.updateRemoteChangedFiles(); + return response; + }); + } + public async resolve(files: string[], action: string) { return this.run(Operation.Resolve, () => this.repository.resolve(files, action) diff --git a/src/svnRepository.ts b/src/svnRepository.ts index 83c76d71..cf94b6e5 100644 --- a/src/svnRepository.ts +++ b/src/svnRepository.ts @@ -360,6 +360,24 @@ export class Repository { return result.stdout; } + public async pullIncomingChange(path: string): Promise { + const args = ["update", path]; + + const result = await this.exec(args); + + this.resetInfo(); + + const message = result.stdout + .trim() + .split(/\r?\n/) + .pop(); + + if (message) { + return message; + } + return result.stdout; + } + public async patch(files: string[]) { files = files.map(file => this.removeAbsolutePath(file)); const result = await this.exec(["diff", ...files]); diff --git a/src/treeView/nodes/incomingChangeNode.ts b/src/treeView/nodes/incomingChangeNode.ts index b5c90eb5..a97e5fc4 100644 --- a/src/treeView/nodes/incomingChangeNode.ts +++ b/src/treeView/nodes/incomingChangeNode.ts @@ -1,11 +1,16 @@ import * as fs from "fs"; import * as path from "path"; import { TreeItem, TreeItemCollapsibleState, Uri } from "vscode"; +import { Repository } from "../../repository"; import { getIconUri } from "../../uri"; import BaseNode from "./baseNode"; export default class IncomingChangeNode implements BaseNode { - constructor(public uri: Uri, public type: string) {} + constructor( + public uri: Uri, + public type: string, + public repository: Repository + ) {} get props(): undefined { return undefined; @@ -15,13 +20,17 @@ export default class IncomingChangeNode implements BaseNode { return path.basename(this.uri.path); } + get contextValue() { + return `incomingChange:${this.type}`; + } + public getTreeItem(): TreeItem { const item = new TreeItem(this.label, TreeItemCollapsibleState.None); item.iconPath = { dark: getIconUri(`status-${this.type}`, "dark"), light: getIconUri(`status-${this.type}`, "light") }; - item.contextValue = this.getContextValue(); + item.contextValue = this.contextValue; return item; } @@ -29,8 +38,4 @@ export default class IncomingChangeNode implements BaseNode { public getChildren(): Promise { return Promise.resolve([]); } - - private getContextValue(): string { - return `incomingChange:file:${this.type}`; - } } diff --git a/src/treeView/nodes/incomingChangesNode.ts b/src/treeView/nodes/incomingChangesNode.ts index 50f91ab8..73fb70a4 100644 --- a/src/treeView/nodes/incomingChangesNode.ts +++ b/src/treeView/nodes/incomingChangesNode.ts @@ -30,7 +30,8 @@ export default class IncomingChangesNode implements BaseNode { remoteChange => { return new IncommingChangeNode( remoteChange.resourceUri, - remoteChange.type + remoteChange.type, + this.repository ); } ); From a055f7d7e2ac1012813f0468c8af8cee4b7c9ac4 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Wed, 15 Aug 2018 08:36:34 +0100 Subject: [PATCH 09/11] updated label to show relative path from root --- package-lock.json | 2 +- src/treeView/nodes/incomingChangeNode.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b6872048..49acc86c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1241,7 +1241,7 @@ "queue": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/queue/-/queue-4.4.2.tgz", - "integrity": "sha512-fSMRXbwhMwipcDZ08enW2vl+YDmAmhcNcr43sCJL8DIg+CFOsoRLG23ctxA+fwNk1w55SePSiS7oqQQSgQoVJQ==", + "integrity": "sha1-Wpcz2ai4vRs26TS8nFWribKOKcc=", "dev": true, "requires": { "inherits": "2.0.3" diff --git a/src/treeView/nodes/incomingChangeNode.ts b/src/treeView/nodes/incomingChangeNode.ts index a97e5fc4..57bfb4ee 100644 --- a/src/treeView/nodes/incomingChangeNode.ts +++ b/src/treeView/nodes/incomingChangeNode.ts @@ -17,7 +17,7 @@ export default class IncomingChangeNode implements BaseNode { } get label() { - return path.basename(this.uri.path); + return path.relative(this.repository.workspaceRoot, this.uri.fsPath); } get contextValue() { From 57da8f88fb8fc2c8e339ef94a305bf9ea58f7684 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Wed, 15 Aug 2018 11:12:44 +0100 Subject: [PATCH 10/11] added openChangeHead command to item context --- package.json | 4 ++++ src/commands.ts | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9fd79942..c6f4919f 100644 --- a/package.json +++ b/package.json @@ -377,6 +377,10 @@ { "command": "svn.treeview.pullIncomingChange", "when": "viewItem =~ /incomingChange:*/" + }, + { + "command": "svn.openChangeHead", + "when": "viewItem == incomingChange:modified" } ], "scm/title": [ diff --git a/src/commands.ts b/src/commands.ts index e6cb1dc9..dfa0d950 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -488,14 +488,14 @@ export class SvnCommands implements IDisposable { @command("svn.openChangeHead") public async openChangeHead( - arg?: Resource | Uri, + arg?: Resource | Uri | IncommingChangeNode, ...resourceStates: SourceControlResourceState[] ): Promise { return this.openChange(arg, "HEAD", resourceStates); } public async openChange( - arg?: Resource | Uri, + arg?: Resource | Uri | IncommingChangeNode, against?: string, resourceStates?: SourceControlResourceState[] ): Promise { @@ -508,6 +508,16 @@ export class SvnCommands implements IDisposable { if (resource !== undefined) { resources = [resource]; } + } else if (arg instanceof IncommingChangeNode) { + const resource = new Resource( + arg.uri, + arg.type, + undefined, + arg.props, + true + ); + + resources = [resource]; } else { let resource: Resource | undefined; From 58eaebe2b5c0ece549c24bc083c4f397714a6c90 Mon Sep 17 00:00:00 2001 From: chris johnston Date: Tue, 21 Aug 2018 18:16:39 +0100 Subject: [PATCH 11/11] fixed changed typo and removed incoming changed resourcegroup --- src/repository.ts | 28 ++++++++++------------- src/treeView/nodes/incomingChangesNode.ts | 18 +++++++-------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/repository.ts b/src/repository.ts index a79d1857..309f10f7 100644 --- a/src/repository.ts +++ b/src/repository.ts @@ -58,7 +58,7 @@ export class Repository { public statusBar: SvnStatusBar; public changes: ISvnResourceGroup; public unversioned: ISvnResourceGroup; - public remoteChanged?: ISvnResourceGroup; + public remoteChanges?: Resource[]; public changelists: Map = new Map(); public conflicts: ISvnResourceGroup; public statusIgnored: IFileStatus[] = []; @@ -122,8 +122,8 @@ export class Repository { group.resourceStates = []; }); - if (this.remoteChanged) { - this.remoteChanged.dispose(); + if (this.remoteChanges) { + this.remoteChanges = []; } this.isIncomplete = false; @@ -321,7 +321,7 @@ export class Repository { const external: any[] = []; const conflicts: any[] = []; const changelists: Map = new Map(); - const remoteChanged: any[] = []; + const remoteChanges: any[] = []; this.statusExternal = []; this.statusIgnored = []; @@ -406,7 +406,7 @@ export class Repository { : undefined; if (status.reposStatus) { - remoteChanged.push( + remoteChanges.push( new Resource( uri, status.reposStatus.item, @@ -515,18 +515,14 @@ export class Repository { /** * Destroy and create for keep at last position */ - if (this.remoteChanged) { - this.remoteChanged.dispose(); + if (this.remoteChanges) { + this.remoteChanges = []; } - this.remoteChanged = this.sourceControl.createResourceGroup( - "remotechanged", - "Remote Changes" - ) as ISvnResourceGroup; - this.remoteChanged.hideWhenEmpty = true; - this.remoteChanged.resourceStates = remoteChanged; - - if (remoteChanged.length !== this.remoteChangedFiles) { - this.remoteChangedFiles = remoteChanged.length; + + this.remoteChanges = remoteChanges; + + if (remoteChanges.length !== this.remoteChangedFiles) { + this.remoteChangedFiles = remoteChanges.length; this._onDidChangeRemoteChangedFiles.fire(); } } diff --git a/src/treeView/nodes/incomingChangesNode.ts b/src/treeView/nodes/incomingChangesNode.ts index 73fb70a4..98871017 100644 --- a/src/treeView/nodes/incomingChangesNode.ts +++ b/src/treeView/nodes/incomingChangesNode.ts @@ -22,19 +22,17 @@ export default class IncomingChangesNode implements BaseNode { } public async getChildren(): Promise { - if (!this.repository.remoteChanged) { + if (!this.repository.remoteChanges) { return []; } - const changes = this.repository.remoteChanged.resourceStates.map( - remoteChange => { - return new IncommingChangeNode( - remoteChange.resourceUri, - remoteChange.type, - this.repository - ); - } - ); + const changes = this.repository.remoteChanges.map(remoteChange => { + return new IncommingChangeNode( + remoteChange.resourceUri, + remoteChange.type, + this.repository + ); + }); if (changes.length === 0) { return [new NoIncomingChangesNode()];