Stop using jsdiff. Use diff-match-patch.

This commit is contained in:
johnfn 2016-08-14 12:03:45 -07:00
parent cba507db79
commit 3352a05859
6 changed files with 310 additions and 233 deletions

View File

@ -1,233 +1,233 @@
{
"name": "vim",
"displayName": "Vim",
"description": "Vim emulation for Visual Studio Code",
"icon": "images/icon.png",
"version": "0.1.6",
"publisher": "vscodevim",
"galleryBanner": {
"color": "#a5c9a2",
"theme": "light"
},
"license": "MIT",
"keywords": [
"vim",
"vi",
"vscodevim",
"vsc-vim"
"name": "vim",
"displayName": "Vim",
"description": "Vim emulation for Visual Studio Code",
"icon": "images/icon.png",
"version": "0.1.6",
"publisher": "vscodevim",
"galleryBanner": {
"color": "#a5c9a2",
"theme": "light"
},
"license": "MIT",
"keywords": [
"vim",
"vi",
"vscodevim",
"vsc-vim"
],
"repository": {
"type": "git",
"url": "https://github.com/VSCodeVim/Vim.git"
},
"homepage": "https://github.com/VSCodeVim/Vim",
"bugs": {
"url": "https://github.com/VSCodeVim/Vim/issues"
},
"engines": {
"vscode": "^1.0.0"
},
"categories": [
"Other"
],
"activationEvents": [
"*"
],
"main": "./out/extension",
"contributes": {
"commands": [
{
"command": "extension.showCmdLine",
"title": "Vim: Show Command Line"
}
],
"repository": {
"type": "git",
"url": "https://github.com/VSCodeVim/Vim.git"
},
"homepage": "https://github.com/VSCodeVim/Vim",
"bugs": {
"url": "https://github.com/VSCodeVim/Vim/issues"
},
"engines": {
"vscode": "^1.0.0"
},
"categories": [
"Other"
"keybindings": [
{
"key": "Escape",
"command": "extension.vim_escape",
"when": "editorTextFocus"
},
{
"key": "Backspace",
"command": "extension.vim_backspace",
"when": "editorTextFocus"
},
{
"key": "Delete",
"command": "extension.vim_delete",
"when": "editorTextFocus && vim.mode == 'Normal Mode'"
},
{
"key": "ctrl+r",
"command": "extension.vim_ctrl+r",
"when": "editorTextFocus"
},
{
"key": "ctrl+f",
"command": "extension.vim_ctrl+f",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "ctrl+b",
"command": "extension.vim_ctrl+b",
"when": "editorTextFocus && vim.mode != 'Insert Mode'"
},
{
"key": "ctrl+e",
"command": "extension.vim_ctrl+e",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "ctrl+y",
"command": "extension.vim_ctrl+y",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "ctrl+u",
"command": "extension.vim_ctrl+u",
"when": "editorTextFocus"
},
{
"key": "ctrl+v",
"command": "extension.vim_ctrl+v",
"when": "editorTextFocus && vim.mode != 'Insert Mode'"
},
{
"key": "ctrl+d",
"command": "extension.vim_ctrl+d",
"when": "editorTextFocus"
},
{
"key": "ctrl+[",
"command": "extension.vim_ctrl+[",
"when": "editorTextFocus"
},
{
"key": "ctrl+w",
"command": "extension.vim_ctrl+w",
"when": "editorTextFocus"
},
{
"key": "ctrl+c",
"command": "extension.vim_ctrl+c",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "ctrl+a",
"command": "extension.vim_ctrl+a",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "ctrl+x",
"command": "extension.vim_ctrl+x",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "left",
"command": "extension.vim_left",
"when": "editorTextFocus && vim.mode != 'Insert Mode'"
},
{
"key": "right",
"command": "extension.vim_right",
"when": "editorTextFocus && vim.mode != 'Insert Mode'"
},
{
"key": "up",
"command": "extension.vim_up",
"when": "editorTextFocus && vim.mode != 'Insert Mode'"
},
{
"key": "down",
"command": "extension.vim_down",
"when": "editorTextFocus && vim.mode != 'Insert Mode'"
}
],
"activationEvents": [
"*"
],
"main": "./out/extension",
"contributes": {
"commands": [
{
"command": "extension.showCmdLine",
"title": "Vim: Show Command Line"
}
],
"keybindings": [
{
"key": "Escape",
"command": "extension.vim_escape",
"when": "editorTextFocus"
},
{
"key": "Backspace",
"command": "extension.vim_backspace",
"when": "editorTextFocus"
},
{
"key": "Delete",
"command": "extension.vim_delete",
"when": "editorTextFocus && vim.mode == 'Normal Mode'"
},
{
"key": "ctrl+r",
"command": "extension.vim_ctrl+r",
"when": "editorTextFocus"
},
{
"key": "ctrl+f",
"command": "extension.vim_ctrl+f",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "ctrl+b",
"command": "extension.vim_ctrl+b",
"when": "editorTextFocus && vim.mode != 'Insert Mode'"
},
{
"key": "ctrl+e",
"command": "extension.vim_ctrl+e",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "ctrl+y",
"command": "extension.vim_ctrl+y",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "ctrl+u",
"command": "extension.vim_ctrl+u",
"when": "editorTextFocus"
},
{
"key": "ctrl+v",
"command": "extension.vim_ctrl+v",
"when": "editorTextFocus && vim.mode != 'Insert Mode'"
},
{
"key": "ctrl+d",
"command": "extension.vim_ctrl+d",
"when": "editorTextFocus"
},
{
"key": "ctrl+[",
"command": "extension.vim_ctrl+[",
"when": "editorTextFocus"
},
{
"key": "ctrl+w",
"command": "extension.vim_ctrl+w",
"when": "editorTextFocus"
},
{
"key": "ctrl+c",
"command": "extension.vim_ctrl+c",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "ctrl+a",
"command": "extension.vim_ctrl+a",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "ctrl+x",
"command": "extension.vim_ctrl+x",
"when": "editorTextFocus && vim.useCtrlKeys"
},
{
"key": "left",
"command": "extension.vim_left",
"when": "editorTextFocus && vim.mode != 'Insert Mode'"
},
{
"key": "right",
"command": "extension.vim_right",
"when": "editorTextFocus && vim.mode != 'Insert Mode'"
},
{
"key": "up",
"command": "extension.vim_up",
"when": "editorTextFocus && vim.mode != 'Insert Mode'"
},
{
"key": "down",
"command": "extension.vim_down",
"when": "editorTextFocus && vim.mode != 'Insert Mode'"
}
],
"configuration": {
"title": "Vim Configuration",
"type": "object",
"properties": {
"vim.otherModesKeyBindings": {
"type": "array",
"description": "Keybinding overrides to use for normal mode."
},
"vim.otherModesKeyBindingsNonRecursive": {
"type": "array",
"description": "Non-recursive keybinding overrides to use for normal mode."
},
"vim.useCtrlKeys": {
"type": "boolean",
"description": "Enable some vim ctrl key commands that override otherwise common operations, like ctrl+c"
},
"vim.insertModeKeyBindings": {
"type": "array",
"description": "Keybinding overrides to use for insert mode."
},
"vim.insertModeKeyBindingsNonRecursive": {
"type": "array",
"description": "Non-recursive keybinding overrides to use for insert mode."
},
"vim.useSolidBlockCursor": {
"type": "boolean",
"description": "Use a non blinking block cursor.",
"default": false
},
"vim.scroll": {
"type": "number",
"description": "Number of lines to scroll with CTRL-U and CTRL-D commands.",
"default": 20
},
"vim.iskeyword": {
"type": "string",
"description": "keywords contain alphanumeric characters and '_'",
"default": "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
},
"vim.ignorecase": {
"type": "boolean",
"description": "Ignore case in search patterns.",
"default": true
},
"vim.smartcase": {
"type": "boolean",
"description": "Override the 'ignorecase' option if the search pattern contains upper case characters.",
"default": true
},
"vim.hlsearch": {
"type": "boolean",
"description": "When there is a previous search pattern, highlight all its matches.",
"default": true
}
}
"configuration": {
"title": "Vim Configuration",
"type": "object",
"properties": {
"vim.otherModesKeyBindings": {
"type": "array",
"description": "Keybinding overrides to use for normal mode."
},
"vim.otherModesKeyBindingsNonRecursive": {
"type": "array",
"description": "Non-recursive keybinding overrides to use for normal mode."
},
"vim.useCtrlKeys": {
"type": "boolean",
"description": "Enable some vim ctrl key commands that override otherwise common operations, like ctrl+c"
},
"vim.insertModeKeyBindings": {
"type": "array",
"description": "Keybinding overrides to use for insert mode."
},
"vim.insertModeKeyBindingsNonRecursive": {
"type": "array",
"description": "Non-recursive keybinding overrides to use for insert mode."
},
"vim.useSolidBlockCursor": {
"type": "boolean",
"description": "Use a non blinking block cursor.",
"default": false
},
"vim.scroll": {
"type": "number",
"description": "Number of lines to scroll with CTRL-U and CTRL-D commands.",
"default": 20
},
"vim.iskeyword": {
"type": "string",
"description": "keywords contain alphanumeric characters and '_'",
"default": "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
},
"vim.ignorecase": {
"type": "boolean",
"description": "Ignore case in search patterns.",
"default": true
},
"vim.smartcase": {
"type": "boolean",
"description": "Override the 'ignorecase' option if the search pattern contains upper case characters.",
"default": true
},
"vim.hlsearch": {
"type": "boolean",
"description": "When there is a previous search pattern, highlight all its matches.",
"default": true
}
},
"scripts": {
"vscode:prepublish": "node ./node_modules/vscode/bin/compile -p ./",
"compile": "node ./node_modules/vscode/bin/compile -watch -p ./",
"test": "node ./node_modules/vscode/bin/test",
"postinstall": "node ./node_modules/vscode/bin/install && gulp init"
},
"dependencies": {
"copy-paste": "^1.3.0",
"diff": "^2.2.3",
"lodash": "^4.12.0"
},
"devDependencies": {
"gulp": "^3.9.1",
"gulp-bump": "^2.1.0",
"gulp-filter": "^4.0.0",
"gulp-git": "^1.7.1",
"gulp-inject-string": "^1.1.0",
"gulp-shell": "^0.5.2",
"gulp-soften": "^0.0.1",
"gulp-tag-version": "^1.3.0",
"gulp-trimlines": "^1.0.0",
"gulp-tslint": "^5.0.0",
"gulp-typescript": "^2.13.4",
"gulp-typings": "^2.0.0",
"merge-stream": "^1.0.0",
"tslint": "^3.10.2",
"typescript": "2.0.0",
"typings": "^1.0.4",
"vscode": "^0.11.13"
}
}
},
"scripts": {
"vscode:prepublish": "node ./node_modules/vscode/bin/compile -p ./",
"compile": "node ./node_modules/vscode/bin/compile -watch -p ./",
"test": "node ./node_modules/vscode/bin/test",
"postinstall": "node ./node_modules/vscode/bin/install && gulp init"
},
"dependencies": {
"copy-paste": "^1.3.0",
"diff-match-patch": "^1.0.0",
"lodash": "^4.12.0"
},
"devDependencies": {
"gulp": "^3.9.1",
"gulp-bump": "^2.1.0",
"gulp-filter": "^4.0.0",
"gulp-git": "^1.7.1",
"gulp-inject-string": "^1.1.0",
"gulp-shell": "^0.5.2",
"gulp-soften": "^0.0.1",
"gulp-tag-version": "^1.3.0",
"gulp-trimlines": "^1.0.0",
"gulp-tslint": "^5.0.0",
"gulp-typescript": "^2.13.4",
"gulp-typings": "^2.0.0",
"merge-stream": "^1.0.0",
"tslint": "^3.10.2",
"typescript": "2.0.0",
"typings": "^1.0.4",
"vscode": "^0.11.13"
}
}

View File

@ -16,7 +16,10 @@ import * as _ from "lodash";
import { Position } from './../motion/position';
import { TextEditor } from './../textEditor';
import jsdiff = require('diff');
import DiffMatchPatch = require("diff-match-patch");
const diffEngine = new DiffMatchPatch.diff_match_patch();
diffEngine.Diff_Timeout = 1000;
export class DocumentChange {
start : Position;
@ -339,17 +342,30 @@ export class HistoryTracker {
// multiple changes in different places simultaneously. For those, we could require
// them to call addChange manually, I guess...
const diffs = jsdiff.diffChars(this.oldText, newText);
const diffs = diffEngine.diff_main(this.oldText, newText);
/*
this.historySteps.push(new HistoryStep({
changes : [new DocumentChange(new Position(0, 0), TextEditor.getAllText(), true)],
isFinished : true,
cursorStart: new Position(0, 0)
}));
*/
let currentPosition = new Position(0, 0);
for (const diff of diffs) {
const [whatHappened, text] = diff;
const added = whatHappened === DiffMatchPatch.DIFF_INSERT;
const removed = whatHappened === DiffMatchPatch.DIFF_DELETE;
const same = whatHappened === DiffMatchPatch.DIFF_EQUAL;
let change: DocumentChange;
// let lastChange = this.currentHistoryStep.changes.length > 1 &&
// this.currentHistoryStep.changes[this.currentHistoryStep.changes.length - 2];
if (diff.added || diff.removed) {
change = new DocumentChange(currentPosition, diff.value, !!diff.added);
if (added || removed) {
change = new DocumentChange(currentPosition, text, !!added);
this.currentHistoryStep.changes.push(change);
@ -358,8 +374,8 @@ export class HistoryTracker {
}
}
if (!diff.removed) {
currentPosition = currentPosition.advancePositionByText(diff.value);
if (!removed) {
currentPosition = currentPosition.advancePositionByText(text);
}
}

9
testing Normal file
View File

@ -0,0 +1,9 @@
one two blah
one two blah
one two blah
one two blah two blah
one two blah
one two blah
one two blah
one two blah
one two blah

12
tsd.json Normal file
View File

@ -0,0 +1,12 @@
{
"version": "v4",
"repo": "borisyankov/DefinitelyTyped",
"ref": "master",
"path": "typings",
"bundle": "typings/tsd.d.ts",
"installed": {
"diff-match-patch/diff-match-patch.d.ts": {
"commit": "67557f18a898462614ed14063a54ca33a1cfab12"
}
}
}

View File

@ -0,0 +1,39 @@
// Type definitions for diff-match-patch v1.0.0
// Project: https://www.npmjs.com/package/diff-match-patch
// Definitions by: Asana <https://asana.com>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
declare module "diff-match-patch" {
type Diff = [number, string];
export class diff_match_patch {
static new (): diff_match_patch;
Diff_Timeout: number;
Diff_EditCost: number;
Match_Threshold: number;
Match_Distance: number;
Patch_DeleteThreshold: number;
Patch_Margin: number;
Match_MaxBits: number;
diff_main(text1: string, text2: string, opt_checklines?: boolean, opt_deadline?: number): Diff[];
diff_commonPrefix(text1: string, text2: string): number;
diff_commonSuffix(text1: string, text2: string): number;
diff_cleanupSemantic(diffs: Diff[]): void;
diff_cleanupSemanticLossless(diffs: Diff[]): void;
diff_cleanupEfficiency(diffs: Diff[]): void;
diff_cleanupMerge(diffs: Diff[]): void;
diff_xIndex(diffs: Diff[], loc: number): number;
diff_prettyHtml(diffs: Diff[]): string;
diff_text1(diffs: Diff[]): string;
diff_text2(diffs: Diff[]): string;
diff_levenshtein(diffs: Diff[]): number;
diff_toDelta(diffs: Diff[]): string;
diff_fromDelta(text1: string, delta: string): Diff[];
}
export var DIFF_DELETE: number;
export var DIFF_INSERT: number;
export var DIFF_EQUAL: number;
}

1
typings/tsd.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference path="diff-match-patch/diff-match-patch.d.ts" />