From 2fae22b4eb13d3d3c2e173d2d662d258948e19d6 Mon Sep 17 00:00:00 2001
From: takker99 <37929109+takker99@users.noreply.github.com>
Date: Thu, 1 Dec 2022 16:50:08 +0900
Subject: [PATCH 1/2] =?UTF-8?q?:fire:=20=E3=81=84=E3=82=89=E3=81=AA?=
=?UTF-8?q?=E3=81=84=E3=83=A9=E3=82=A4=E3=82=BB=E3=83=B3=E3=82=B9=E3=83=95?=
=?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92=E6=B6=88=E3=81=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
browser/websocket/LICENSE | 21 ---------------------
1 file changed, 21 deletions(-)
delete mode 100644 browser/websocket/LICENSE
diff --git a/browser/websocket/LICENSE b/browser/websocket/LICENSE
deleted file mode 100644
index 736c542..0000000
--- a/browser/websocket/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2021 takker
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
From 8a3e379a0a0d762e73ade0858c1f4493c87e42eb Mon Sep 17 00:00:00 2001
From: takker99 <37929109+takker99@users.noreply.github.com>
Date: Thu, 1 Dec 2022 16:55:21 +0900
Subject: [PATCH 2/2] =?UTF-8?q?:recycle:=20onp=20algorithm=E3=82=92?=
=?UTF-8?q?=E4=BB=96=E3=81=AErepo=E3=81=AB=E5=88=87=E3=82=8A=E5=87=BA?=
=?UTF-8?q?=E3=81=97=E3=81=9F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
browser/websocket/diff.test.ts | 184 -----------------------------
browser/websocket/diff.ts | 181 ----------------------------
browser/websocket/diffToChanges.ts | 2 +-
deps/onp.ts | 1 +
4 files changed, 2 insertions(+), 366 deletions(-)
delete mode 100644 browser/websocket/diff.test.ts
delete mode 100644 browser/websocket/diff.ts
create mode 100644 deps/onp.ts
diff --git a/browser/websocket/diff.test.ts b/browser/websocket/diff.test.ts
deleted file mode 100644
index f572a38..0000000
--- a/browser/websocket/diff.test.ts
+++ /dev/null
@@ -1,184 +0,0 @@
-///
-import { Change, diff, ExtendedChange, toExtendedChanges } from "./diff.ts";
-import { assertEquals, assertStrictEquals } from "../../deps/testing.ts";
-
-Deno.test("diff()", async (t) => {
- await t.step("check variables", async ({ step }) => {
- await step("return arguments", () => {
- assertEquals(diff("aaa", "bbbb").from, "aaa");
- assertEquals(diff("aaa", "bbbb").to, "bbbb");
- const left = ["aaa", "bbb", 111] as const;
- const right = ["ccc", "ddd", 222] as const;
- assertStrictEquals(diff(left, right).from, left);
- assertStrictEquals(diff(left, right).to, right);
- });
- });
- await t.step("string", async ({ step }) => {
- const diffData: [string, string, Change[]][] = [
- ["kitten", "sitting", [
- { value: "s", type: "added" },
- { value: "k", type: "deleted" },
- { value: "i", type: "common" },
- { value: "t", type: "common" },
- { value: "t", type: "common" },
- { value: "i", type: "added" },
- { value: "e", type: "deleted" },
- { value: "n", type: "common" },
- { value: "g", type: "added" },
- ]],
- ["sitting", "kitten", [
- { value: "s", type: "deleted" },
- { value: "k", type: "added" },
- { value: "i", type: "common" },
- { value: "t", type: "common" },
- { value: "t", type: "common" },
- { value: "i", type: "deleted" },
- { value: "e", type: "added" },
- { value: "n", type: "common" },
- { value: "g", type: "deleted" },
- ]],
- ];
- for (const [before, after, changes] of diffData) {
- await step(
- `${before}->${after}`,
- () => assertEquals([...diff(before, after).buildSES()], changes),
- );
- }
- });
-});
-
-Deno.test("toExtendedChanges()", async (t) => {
- await t.step("only", async ({ step }) => {
- await step("only added", () => {
- const before: Change[] = [
- { value: "aaa", type: "added" },
- { value: "bbb", type: "added" },
- { value: "ccc", type: "added" },
- ];
- const after: ExtendedChange[] = [
- { value: "aaa", type: "added" },
- { value: "bbb", type: "added" },
- { value: "ccc", type: "added" },
- ];
- assertEquals([...toExtendedChanges(before)], after);
- });
- await step("only deleted", () => {
- const before: Change[] = [
- { value: "aaa", type: "deleted" },
- { value: "bbb", type: "deleted" },
- { value: "ccc", type: "deleted" },
- ];
- const after: ExtendedChange[] = [
- { value: "aaa", type: "deleted" },
- { value: "bbb", type: "deleted" },
- { value: "ccc", type: "deleted" },
- ];
- assertEquals([...toExtendedChanges(before)], after);
- });
- await step("only common", () => {
- const before: Change[] = [
- { value: "aaa", type: "common" },
- { value: "bbb", type: "common" },
- { value: "ccc", type: "common" },
- ];
- const after: ExtendedChange[] = [
- { value: "aaa", type: "common" },
- { value: "bbb", type: "common" },
- { value: "ccc", type: "common" },
- ];
- assertEquals([...toExtendedChanges(before)], after);
- });
- });
-
- await t.step("mixed", async ({ step }) => {
- await step("added and deleted", () => {
- const before: Change[] = [
- { value: "111", type: "added" },
- { value: "aaa", type: "added" },
- { value: "bbb", type: "deleted" },
- { value: "222", type: "added" },
- { value: "eee", type: "added" },
- { value: "fff", type: "deleted" },
- { value: "ggg", type: "deleted" },
- { value: "222", type: "added" },
- { value: "eee", type: "added" },
- { value: "ggg", type: "deleted" },
- { value: "222", type: "added" },
- { value: "fff", type: "deleted" },
- { value: "ggg", type: "deleted" },
- { value: "222", type: "added" },
- { value: "eee", type: "added" },
- ];
- const after: ExtendedChange[] = [
- { value: "111", oldValue: "bbb", type: "replaced" },
- { value: "aaa", oldValue: "fff", type: "replaced" },
- { value: "222", oldValue: "ggg", type: "replaced" },
- { value: "eee", oldValue: "ggg", type: "replaced" },
- { value: "222", oldValue: "fff", type: "replaced" },
- { value: "eee", oldValue: "ggg", type: "replaced" },
- { value: "222", type: "added" },
- { value: "222", type: "added" },
- { value: "eee", type: "added" },
- ];
- assertEquals([...toExtendedChanges(before)], after);
- });
- await step("added and deleted and common", () => {
- const before: Change[] = [
- { value: "111", type: "added" },
- { value: "aaa", type: "added" },
- { value: "bbb", type: "deleted" },
- { value: "ccc", type: "common" },
- { value: "ddd", type: "common" },
- { value: "222", type: "added" },
- { value: "eee", type: "added" },
- { value: "fff", type: "deleted" },
- { value: "ggg", type: "deleted" },
- { value: "ddd", type: "common" },
- { value: "222", type: "added" },
- { value: "eee", type: "added" },
- { value: "ggg", type: "deleted" },
- { value: "ddd", type: "common" },
- { value: "222", type: "added" },
- { value: "fff", type: "deleted" },
- { value: "ggg", type: "deleted" },
- { value: "ddd", type: "common" },
- { value: "222", type: "added" },
- { value: "eee", type: "added" },
- { value: "ddd", type: "common" },
- { value: "fff", type: "deleted" },
- { value: "ggg", type: "deleted" },
- { value: "ddd", type: "common" },
- { value: "222", type: "added" },
- { value: "eee", type: "added" },
- { value: "fff", type: "deleted" },
- { value: "ggg", type: "deleted" },
- { value: "222", type: "added" },
- ];
- const after: ExtendedChange[] = [
- { value: "111", oldValue: "bbb", type: "replaced" },
- { value: "aaa", type: "added" },
- { value: "ccc", type: "common" },
- { value: "ddd", type: "common" },
- { value: "222", oldValue: "fff", type: "replaced" },
- { value: "eee", oldValue: "ggg", type: "replaced" },
- { value: "ddd", type: "common" },
- { value: "222", oldValue: "ggg", type: "replaced" },
- { value: "eee", type: "added" },
- { value: "ddd", type: "common" },
- { value: "222", oldValue: "fff", type: "replaced" },
- { value: "ggg", type: "deleted" },
- { value: "ddd", type: "common" },
- { value: "222", type: "added" },
- { value: "eee", type: "added" },
- { value: "ddd", type: "common" },
- { value: "fff", type: "deleted" },
- { value: "ggg", type: "deleted" },
- { value: "ddd", type: "common" },
- { value: "222", oldValue: "fff", type: "replaced" },
- { value: "eee", oldValue: "ggg", type: "replaced" },
- { value: "222", type: "added" },
- ];
- assertEquals([...toExtendedChanges(before)], after);
- });
- });
-});
diff --git a/browser/websocket/diff.ts b/browser/websocket/diff.ts
deleted file mode 100644
index a68780a..0000000
--- a/browser/websocket/diff.ts
+++ /dev/null
@@ -1,181 +0,0 @@
-/**
- * The algorithm implemented here is based on "An O(NP) Sequence Comparison Algorithm"
- * by described by Sun Wu, Udi Manber and Gene Myers */
-
-/** LICENSE: https://github.com/cubicdaiya/onp/blob/master/COPYING */
-
-type Position = {
- x: number;
- y: number;
-};
-
-export interface Added {
- value: T;
- type: "added";
-}
-export interface Deleted {
- value: T;
- type: "deleted";
-}
-export interface Common {
- value: T;
- type: "common";
-}
-export type Change = Added | Deleted | Common;
-export interface Replaced {
- value: T;
- oldValue: T;
- type: "replaced";
-}
-export type ExtendedChange = Change | Replaced;
-
-export interface DiffResult {
- from: ArrayLike;
- to: ArrayLike;
- editDistance: number;
- buildSES(): Generator, void, unknown>;
-}
-
-export const diff = (
- left: ArrayLike,
- right: ArrayLike,
-): DiffResult => {
- const reversed = left.length > right.length;
- const a = reversed ? right : left;
- const b = reversed ? left : right;
-
- const offset = a.length + 1;
- const MAXSIZE = a.length + b.length + 3;
- const path = new Array(MAXSIZE);
- path.fill(-1);
- const pathpos = [] as [Position, number][];
-
- function snake(k: number, p: number, pp: number) {
- let y = Math.max(p, pp);
- let x = y - k;
-
- while (x < a.length && y < b.length && a[x] === b[y]) {
- ++x;
- ++y;
- }
-
- path[k + offset] = pathpos.length;
- pathpos.push([{ x, y }, path[k + (p > pp ? -1 : +1) + offset]]);
- return y;
- }
-
- const fp = new Array(MAXSIZE);
- fp.fill(-1);
- let p = -1;
- const delta = b.length - a.length;
- do {
- ++p;
- for (let k = -p; k <= delta - 1; ++k) {
- fp[k + offset] = snake(k, fp[k - 1 + offset] + 1, fp[k + 1 + offset]);
- }
- for (let k = delta + p; k >= delta + 1; --k) {
- fp[k + offset] = snake(k, fp[k - 1 + offset] + 1, fp[k + 1 + offset]);
- }
- fp[delta + offset] = snake(
- delta,
- fp[delta - 1 + offset] + 1,
- fp[delta + 1 + offset],
- );
- } while (fp[delta + offset] !== b.length);
-
- const epc = [] as Position[];
- let r = path[delta + offset];
- while (r !== -1) {
- epc.push(pathpos[r][0]);
- r = pathpos[r][1];
- }
-
- return {
- from: left,
- to: right,
- editDistance: delta + p * 2,
- buildSES: function* () {
- let xIndex = 0;
- let yIndex = 0;
- for (const { x, y } of reverse(epc)) {
- while (xIndex < x || yIndex < y) {
- if (y - x > yIndex - xIndex) {
- yield { value: b[yIndex], type: reversed ? "deleted" : "added" };
- ++yIndex;
- } else if (y - x < yIndex - xIndex) {
- yield { value: a[xIndex], type: reversed ? "added" : "deleted" };
- ++xIndex;
- } else {
- yield { value: a[xIndex], type: "common" };
- ++xIndex;
- ++yIndex;
- }
- }
- }
- },
- };
-};
-
-export function* toExtendedChanges(
- changes: Iterable>,
-): Generator, void, unknown> {
- let addedList = [] as Added[];
- let deletedList = [] as Deleted[];
-
- function* flush() {
- if (addedList.length > deletedList.length) {
- for (let i = 0; i < deletedList.length; i++) {
- yield makeReplaced(
- addedList[i],
- deletedList[i],
- );
- }
- for (let i = deletedList.length; i < addedList.length; i++) {
- yield addedList[i];
- }
- } else {
- for (let i = 0; i < addedList.length; i++) {
- yield makeReplaced(
- addedList[i],
- deletedList[i],
- );
- }
- for (let i = addedList.length; i < deletedList.length; i++) {
- yield deletedList[i];
- }
- }
- addedList = [];
- deletedList = [];
- }
-
- for (const change of changes) {
- switch (change.type) {
- case "added":
- addedList.push(change);
- break;
- case "deleted":
- deletedList.push(change);
- break;
- case "common":
- yield* flush();
- yield change;
- break;
- }
- }
- yield* flush();
-}
-
-const makeReplaced = (
- left: Added,
- right: Deleted,
-): Replaced => ({
- value: left.value,
- oldValue: right.value,
- type: "replaced",
-});
-
-function* reverse(list: ArrayLike) {
- for (let i = list.length - 1; i >= 0; i--) {
- yield list[i];
- }
-}
diff --git a/browser/websocket/diffToChanges.ts b/browser/websocket/diffToChanges.ts
index 84f7f38..b4c02f3 100644
--- a/browser/websocket/diffToChanges.ts
+++ b/browser/websocket/diffToChanges.ts
@@ -1,4 +1,4 @@
-import { diff, toExtendedChanges } from "./diff.ts";
+import { diff, toExtendedChanges } from "../../deps/onp.ts";
import type { Line } from "../../deps/scrapbox.ts";
import type {
DeleteCommit,
diff --git a/deps/onp.ts b/deps/onp.ts
new file mode 100644
index 0000000..fe489c5
--- /dev/null
+++ b/deps/onp.ts
@@ -0,0 +1 @@
+export * from "https://raw.githubusercontent.com/takker99/onp/0.0.1/mod.ts";