VOICEVOX / voicevox

無料で使える中品質なテキスト読み上げソフトウェア、VOICEVOXのエディター
https://voicevox.hiroshiba.jp/
Other
2.5k stars 305 forks source link

Commandにおいてundefined値を持つプロパティへの代入が不正なPatchとして記録される。 #310

Closed Segu-g closed 3 years ago

Segu-g commented 3 years ago

不具合の内容

undefinedを値として持つプロパティはimmerproduceWithPatches関数ではundefinedを持つプロパティとして判定されるためOperationはreplaceになる。しかしrfc6902ではundefined値を持つプロパティはプロパティが存在しないと判定される(obj.hasOwnProperty("a") && obj.a != undefined)為replace命令は不正となってしまう。

現象・ログ

undefined値のプロパティに対して代入するようなコマンドを実行すると、undoCommandsには操作が記録されるがstateではundefined値は更新されていない。

再現手順

const { enablePatches, Immer } = require("immer");
const { applyPatch } = require("rfc6902");

enablePatches();
const immer = new Immer();
immer.setAutoFreeze(false);

const obj1 = { a: "a", b: undefined };
const obj2 = { a: "a" };

const [nobj1, redo1Patches, undo1Patches] = immer.produceWithPatches(
  obj1,
  (obj) => {
    obj.a = "A";
    obj.b = "B";
  }
);

console.log(nobj1);
// { a: 'A', b: 'B' }
console.log(redo1Patches);
// [
//   { op: 'replace', path: [ 'a' ], value: 'A' },
//   { op: 'replace', path: [ 'b' ], value: 'B' }
// ]
console.log(undo1Patches);
// [
//   { op: 'replace', path: [ 'a' ], value: 'a' },
//   { op: 'replace', path: [ 'b' ], value: undefined }
// ]

const [nobj2, redo2Patches, undo2Patches] = immer.produceWithPatches(
  obj2,
  (obj) => {
    obj.a = "A";
    obj.b = "B";
  }
);

console.log(nobj2);
// { a: 'A', b: 'B' }
console.log(redo2Patches);
// [
//   { op: 'replace', path: [ 'a' ], value: 'A' },
//   { op: 'add', path: [ 'b' ], value: 'B' }
// ]
console.log(undo2Patches);
// [
//   { op: 'replace', path: [ 'a' ], value: 'a' },
//   { op: 'remove', path: [ 'b' ] }
// ]

const patchToOperation = (patch) => ({
  op: patch.op,
  path: `/${patch.path.join("/")}`,
  value: patch.value,
});

const redo1Operations = redo1Patches.map((patch) => patchToOperation(patch));

console.log(obj1);
// { a: 'a', b: undefined }
applyPatch(obj1, redo1Operations);
console.log(obj1);
// { a: 'A', b: undefined }

const redo2Operations = redo2Patches.map((patch) => patchToOperation(patch));

console.log(obj2);
// { a: 'a' }
applyPatch(obj2, redo2Operations);
console.log(obj2);
// { a: 'A', b: 'B' }

期待動作

undefined値に対しても正しく記録が適応される。

VOICEVOXのバージョン

main最新 c7275738c6eef347c493f8beab19162424dde160

OSの種類/ディストリ/バージョン

その他

immerのapplyPatches_という内部関数が使えれば良さそう

Hiroshiba commented 3 years ago

issueと詳細、ありがとうございます!面白い現象だと思いました。

immerを使ってpatchを作っているので、patchを使う部分もimmerのものを使うのが良いかなと思いました。 applyPatches_はexportされていないとのことなので、(あまりやりたくないですが)該当コードを抜き出して使うとかでしょうか。 immer側に理由を伝えて、exportできないか相談しても良いかもです。 もしくはrft~にissueを建てるとかでしょうか・・・。