vscode-zap
Advanced tools
Comparing version
@@ -37,2 +37,12 @@ import * as vscode from "vscode"; | ||
export declare function editOpsFromContentChanges(doc: vscode.TextDocument, changes: vscode.TextDocumentContentChangeEvent[], extraWideCharsInOldDocument: vscode.Position[]): EditOps; | ||
/** | ||
* workspaceEditorFromOp converts a given zap workspace op to a vscode workspace edit. | ||
* | ||
* params: | ||
* - docs are the open documents in the current workspace. | ||
* - isDirty is a map of file names to their current dirty status. | ||
* - env is the environment for the current workspace. | ||
* - op is the zap workspace op to be applied. | ||
* - currentPending contains generated edits that have not been applied to a vscode document yet. | ||
*/ | ||
export declare function workspaceEditFromOp(docs: vscode.TextDocument[], isDirty: Map<string, boolean>, env: IEnvironment, op: WorkspaceOp, currentPending: FileEdits): { | ||
@@ -39,0 +49,0 @@ edit: vscode.WorkspaceEdit | null; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const vscode = require("vscode"); | ||
const lodash_1 = require("lodash"); | ||
const textDoc_1 = require("libzap/lib/ot/textDoc"); | ||
@@ -219,2 +220,12 @@ const workspace_1 = require("libzap/lib/ot/workspace"); | ||
exports.editOpsFromContentChanges = editOpsFromContentChanges; | ||
/** | ||
* workspaceEditorFromOp converts a given zap workspace op to a vscode workspace edit. | ||
* | ||
* params: | ||
* - docs are the open documents in the current workspace. | ||
* - isDirty is a map of file names to their current dirty status. | ||
* - env is the environment for the current workspace. | ||
* - op is the zap workspace op to be applied. | ||
* - currentPending contains generated edits that have not been applied to a vscode document yet. | ||
*/ | ||
function workspaceEditFromOp(docs, isDirty, env, op, currentPending) { | ||
@@ -246,3 +257,2 @@ if (!op.edit) { | ||
edits = textDoc_1.mergeEdits(combined); | ||
delete currentPending[path]; | ||
} | ||
@@ -256,3 +266,7 @@ const isOrWillBecomeDirty = Boolean(doc.isDirty || (workspace_1.isFilePath(path) && op.copy && op.copy[workspace_1.fileToBufferPath(path)] === path)); | ||
} | ||
const bufferFileExists = doc.isDirty || isDirty.get(doc.uri.toString()) || (op.copy && op.copy[`#${strippedPath}`] === `/${strippedPath}`); | ||
if (workspace_1.isFilePath(path) && op.edit[`#${strippedPath}`]) { | ||
// Don't apply file system edits to the doc if we have pending buffer changes (i.e. a dirty file). | ||
continue; | ||
} | ||
const bufferFileExists = doc.isDirty || isDirty.get(doc.uri.toString()) || (op.copy && op.copy[`#${strippedPath}`] === `/${strippedPath}`) || (op.create && lodash_1.includes(op.create, `#${strippedPath}`)); | ||
if (workspace_1.isBufferPath(path) && !bufferFileExists) { | ||
@@ -264,2 +278,3 @@ throw new Error(`edit to nonexistent buffer file ${path} (either the file does not exist at all, or it is not dirty and therefore only has a disk/non-buffer file)`); | ||
} | ||
delete currentPending[path]; | ||
} | ||
@@ -266,0 +281,0 @@ } |
@@ -210,3 +210,3 @@ "use strict"; | ||
const rootURI = this.environment.rootURI; | ||
const uri = rootURI.with({ fragment: workspace_1.stripFileOrBufferPathPrefix(path) }); | ||
const uri = rootURI.with({ fragment: workspace_1.stripFileOrBufferPathPrefix(path), scheme: "git" }); | ||
const doc = yield vscode.workspace.openTextDocument(uri); | ||
@@ -468,4 +468,11 @@ yield this.environment.deleteTextDocument(doc); | ||
// Only apply ops to "modifiable" content (i.e. not the left side of a diff). | ||
const changes = this.pendingWorkspaceFileEdits[`/${fileName}`]; | ||
let changes = this.pendingWorkspaceFileEdits[`/${fileName}`]; | ||
delete this.pendingWorkspaceFileEdits[`/${fileName}`]; | ||
// Check for changes in the saved version of the file on opening | ||
// and diff them with the changes that are buffered. | ||
const diffChanges = this.pendingWorkspaceFileEdits[`#${fileName}`]; | ||
if (diffChanges) { | ||
this.pendingWorkspaceFileEdits[`/${fileName}`] = diffChanges; | ||
changes = diffChanges; | ||
} | ||
if (changes) { | ||
@@ -472,0 +479,0 @@ const edit = op_1.resolvePendingEditOpsToWorkspaceEdit(doc, changes); |
@@ -5,7 +5,7 @@ { | ||
"description": "Real-time code synchronization", | ||
"version": "1.0.1", | ||
"version": "1.0.2", | ||
"publisher": "sqs", | ||
"preview": true, | ||
"engines": { | ||
"vscode": "^1.9.1", | ||
"vscode": "^1.11.0", | ||
"node": ">=4.0.0" | ||
@@ -43,7 +43,6 @@ }, | ||
"dependencies": { | ||
"libzap": "^1.0.1", | ||
"libzap": "^1.0.2", | ||
"lodash": "^4.17.4", | ||
"vscode-jsonrpc": "3.0.1-alpha.7" | ||
}, | ||
"enableProposedApi": false, | ||
"contributes": { | ||
@@ -50,0 +49,0 @@ "configuration": { |
import * as vscode from "vscode"; | ||
import { includes } from "lodash"; | ||
@@ -230,2 +231,12 @@ import { EditOp, EditOps, mergeEdits } from "libzap/lib/ot/textDoc"; | ||
/** | ||
* workspaceEditorFromOp converts a given zap workspace op to a vscode workspace edit. | ||
* | ||
* params: | ||
* - docs are the open documents in the current workspace. | ||
* - isDirty is a map of file names to their current dirty status. | ||
* - env is the environment for the current workspace. | ||
* - op is the zap workspace op to be applied. | ||
* - currentPending contains generated edits that have not been applied to a vscode document yet. | ||
*/ | ||
export function workspaceEditFromOp(docs: vscode.TextDocument[], isDirty: Map<string, boolean>, env: IEnvironment, op: WorkspaceOp, currentPending: FileEdits): { edit: vscode.WorkspaceEdit | null; pendingFileEdits: FileEdits } { | ||
@@ -258,3 +269,2 @@ if (!op.edit) { | ||
edits = mergeEdits(combined); | ||
delete currentPending[path]; | ||
} | ||
@@ -269,4 +279,7 @@ | ||
} | ||
const bufferFileExists = doc.isDirty || isDirty.get(doc.uri.toString()) || (op.copy && op.copy[`#${strippedPath}`] === `/${strippedPath}`); | ||
if (isFilePath(path) && op.edit[`#${strippedPath}`]) { | ||
// Don't apply file system edits to the doc if we have pending buffer changes (i.e. a dirty file). | ||
continue; | ||
} | ||
const bufferFileExists = doc.isDirty || isDirty.get(doc.uri.toString()) || (op.copy && op.copy[`#${strippedPath}`] === `/${strippedPath}`) || (op.create && includes(op.create, `#${strippedPath}`)); | ||
if (isBufferPath(path) && !bufferFileExists) { | ||
@@ -278,2 +291,4 @@ throw new Error(`edit to nonexistent buffer file ${path} (either the file does not exist at all, or it is not dirty and therefore only has a disk/non-buffer file)`); | ||
} | ||
delete currentPending[path]; | ||
} | ||
@@ -482,2 +497,2 @@ } | ||
); | ||
} | ||
} |
140
src/scm.ts
@@ -1,43 +0,34 @@ | ||
import { scm, commands, workspace, Uri, Disposable, SCMProvider, SCMResourceGroup, Event, EventEmitter, SCMResource, SCMResourceDecorations, ProviderResult } from "vscode"; | ||
import * as path from "path"; | ||
import * as vscode from "vscode"; | ||
import { WorkspaceOp, stripFileOrBufferPathPrefix as stripPathPrefix } from "libzap/lib/ot/workspace"; | ||
import { WorkspaceState } from "./extension.common"; | ||
export class ZapSCMProvider implements SCMProvider { | ||
export class ZapSourceControlProvider { | ||
private disposables: Disposable[] = []; | ||
private _resources: Map<string, Resource> = new Map(); | ||
private disposables: vscode.Disposable[] = []; | ||
private _onDidChangeResources = new EventEmitter<SCMResourceGroup[]>(); | ||
private sourceControl: vscode.SourceControl; | ||
private resources = new Map<string, vscode.SourceControlResourceState>(); | ||
private changedGroup: vscode.SourceControlResourceGroup; | ||
get resources(): SCMResourceGroup[] { | ||
const resources = Array.from(this._resources.values()); | ||
resources.sort((a, b) => { | ||
// Case-insensitive lexicographic ordering by file path. | ||
const aPathParts = a.uri.path.toLowerCase().split("/"); | ||
const bPathParts = b.uri.path.toLowerCase().split("/"); | ||
const aFile = aPathParts[aPathParts.length - 1]; | ||
const bFile = bPathParts[bPathParts.length - 1]; | ||
if (aFile < bFile) { | ||
return -1; | ||
constructor(private workspaceState?: WorkspaceState) { | ||
this.sourceControl = vscode.scm.createSourceControl("zap", "Zap"); | ||
this.disposables.push(this.sourceControl); | ||
this.changedGroup = this.sourceControl.createResourceGroup("changed", "Zap Changes"); | ||
this.disposables.push(this.changedGroup); | ||
const openCommand = vscode.commands.registerCommand("zap.openResource", (uri: vscode.Uri): Thenable<void> => { | ||
const originalResource = this.provideOriginalResource(uri); | ||
if (!originalResource) { | ||
throw new Error(`could not determine base git resource for resource: ${uri.toString()}`); | ||
} | ||
if (aFile > bFile) { | ||
return 1; | ||
} | ||
return 0; | ||
return vscode.commands.executeCommand<void>("vscode.diff", originalResource, uri); | ||
}); | ||
return [new ResourceGroup("", "Zap Changes", resources)]; | ||
} | ||
this.disposables.push(openCommand); | ||
get onDidChange(): Event<SCMResourceGroup[]> { | ||
return this._onDidChangeResources.event; | ||
} | ||
get label(): string { return "Zap"; } | ||
constructor(private workspaceState?: WorkspaceState) { | ||
scm.registerSCMProvider("zap", this); | ||
if ((workspace as any).onDidUpdateWorkspace) { | ||
if ((vscode.workspace as any).onDidUpdateWorkspace) { | ||
// Sourcegraph communicates workspace rev state changes | ||
// to the zap extension (e.g. when zap ref or git base commit changes). | ||
(workspace as any).onDidUpdateWorkspace((workspace: any) => { | ||
(vscode.workspace as any).onDidUpdateWorkspace((workspace: any) => { | ||
this.workspaceState = { | ||
@@ -51,11 +42,3 @@ workspace: workspace.resource.toString(), | ||
open(resource: Resource): ProviderResult<void> { | ||
const originalResource = this.getOriginalResource(resource.uri); | ||
if (!originalResource) { | ||
throw new Error(`could not determine base git resource for resource: ${resource.uri}`); | ||
} | ||
return commands.executeCommand<void>("vscode.diff", originalResource, resource.uri); | ||
} | ||
getOriginalResource(uri: Uri): Uri | undefined { | ||
provideOriginalResource(uri: vscode.Uri, _token?: vscode.CancellationToken): vscode.ProviderResult<vscode.Uri> { | ||
if (uri.scheme !== "file") { | ||
@@ -69,3 +52,3 @@ return; | ||
} | ||
const workspaceUri = Uri.parse(this.workspaceState.workspace); | ||
const workspaceUri = vscode.Uri.parse(this.workspaceState.workspace); | ||
let filePath = uri.toString().substr(this.workspaceState.workspace.length + 1); // remove leading "/" | ||
@@ -80,8 +63,37 @@ return uri.with({ scheme: "git", path: workspaceUri.path, query: this.workspaceState.revState!.commitID, fragment: filePath }); | ||
private setResourceState(relativePath: string, exists: boolean): void { | ||
const uri = vscode.Uri.file(path.join(vscode.workspace.rootPath, relativePath)); | ||
this.resources.set(relativePath, { | ||
resourceUri: uri, | ||
command: { | ||
command: "zap.openResource", | ||
title: "Open", | ||
arguments: [uri], | ||
}, | ||
decorations: { strikeThrough: !exists, light: {}, dark: {} }, | ||
}); | ||
} | ||
private update(): void { | ||
this.changedGroup.resourceStates = Array.from(this.resources.values()).sort((a, b) => { | ||
// Case-insensitive lexicographic ordering by file path. | ||
const aPathParts = a.resourceUri.path.toLowerCase().split("/"); | ||
const bPathParts = b.resourceUri.path.toLowerCase().split("/"); | ||
const aFile = aPathParts[aPathParts.length - 1]; | ||
const bFile = bPathParts[bPathParts.length - 1]; | ||
if (aFile < bFile) { | ||
return -1; | ||
} | ||
if (aFile > bFile) { | ||
return 1; | ||
} | ||
return 0; | ||
}); | ||
this.sourceControl.count = this.changedGroup.resourceStates.length; | ||
} | ||
reset(op?: WorkspaceOp) { | ||
this._resources.clear(); | ||
this.resources.clear(); | ||
if (op) { | ||
this.updateFromOp(op); | ||
} else { | ||
this._onDidChangeResources.fire(this.resources); | ||
} | ||
@@ -93,3 +105,3 @@ } | ||
for (const f of op.create) { | ||
this._resources.set(stripPathPrefix(f), new Resource(this.resourcePath(f), false)); | ||
this.setResourceState(stripPathPrefix(f), false); | ||
} | ||
@@ -99,3 +111,3 @@ } | ||
for (const f of op.delete) { | ||
this._resources.set(stripPathPrefix(f), new Resource(this.resourcePath(f), true)); | ||
this.setResourceState(stripPathPrefix(f), true); | ||
} | ||
@@ -105,3 +117,3 @@ } | ||
for (const f of Object.keys(op.edit)) { | ||
this._resources.set(stripPathPrefix(f), new Resource(this.resourcePath(f), false)); | ||
this.setResourceState(stripPathPrefix(f), false); | ||
} | ||
@@ -111,37 +123,11 @@ } | ||
for (const f of op.truncate) { | ||
this._resources.set(stripPathPrefix(f), new Resource(this.resourcePath(f), false)); | ||
this.setResourceState(stripPathPrefix(f), false); | ||
} | ||
} | ||
this._onDidChangeResources.fire(this.resources); | ||
this.update(); | ||
} | ||
resourcePath(f: string): Uri { | ||
return Uri.parse(`${workspace.rootPath}/${stripPathPrefix(f)}`); | ||
resourcePath(f: string): vscode.Uri { | ||
return vscode.Uri.parse(`${vscode.workspace.rootPath}/${stripPathPrefix(f)}`); | ||
} | ||
} | ||
export class ResourceGroup implements SCMResourceGroup { | ||
get id(): string { return this._id; } | ||
get label(): string { return this._label; } | ||
get resources(): Resource[] { return this._resources; } | ||
constructor(private _id: string, private _label: string, private _resources: Resource[]) { | ||
} | ||
} | ||
export class Resource implements SCMResource { | ||
get uri(): Uri { return this._uri; } | ||
// TODO(renfred) actually implement | ||
get decorations(): SCMResourceDecorations { | ||
const light = {}; | ||
const dark = {}; | ||
return { strikeThrough: this._strikeThrough, light, dark }; | ||
} | ||
constructor(private _uri: Uri, private _strikeThrough: boolean) { | ||
} | ||
} |
@@ -220,3 +220,3 @@ import * as vscode from "vscode"; | ||
const rootURI = this.environment.rootURI!; | ||
const uri = rootURI.with({ fragment: stripFileOrBufferPathPrefix(path) }); | ||
const uri = rootURI.with({ fragment: stripFileOrBufferPathPrefix(path), scheme: "git" }); | ||
const doc = await vscode.workspace.openTextDocument(uri); | ||
@@ -503,4 +503,12 @@ await this.environment.deleteTextDocument(doc); | ||
// Only apply ops to "modifiable" content (i.e. not the left side of a diff). | ||
const changes = this.pendingWorkspaceFileEdits[`/${fileName}`]; | ||
let changes = this.pendingWorkspaceFileEdits[`/${fileName}`]; | ||
delete this.pendingWorkspaceFileEdits[`/${fileName}`]; | ||
// Check for changes in the saved version of the file on opening | ||
// and diff them with the changes that are buffered. | ||
const diffChanges = this.pendingWorkspaceFileEdits[`#${fileName}`]; | ||
if (diffChanges) { | ||
this.pendingWorkspaceFileEdits[`/${fileName}`] = diffChanges; | ||
changes = diffChanges; | ||
} | ||
if (changes) { | ||
@@ -507,0 +515,0 @@ const edit = resolvePendingEditOpsToWorkspaceEdit(doc, changes); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
518956
0.56%7526
0.64%Updated