Skip to content

Commit

Permalink
Merge pull request #3122 from opral/pr3122
Browse files Browse the repository at this point in the history
dont override draft changes
  • Loading branch information
janfjohannes authored Sep 12, 2024
2 parents a864c0d + d49bfa9 commit e63b4e8
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 174 deletions.
4 changes: 0 additions & 4 deletions inlang/source-code/sdk-v2/src/env-variables/index.ts

This file was deleted.

92 changes: 82 additions & 10 deletions lix/packages/sdk/src/change-queue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,43 @@ import { newLixFile } from "./newLix.js";
import type { DiffReport, LixPlugin } from "./plugin.js";

test("should use queue and settled correctly", async () => {
const mockPlugin: LixPlugin = {
const mockPlugin: LixPlugin<{
text: { id: string; text: string };
}> = {
key: "mock-plugin",
glob: "*",
diff: {
file: async ({ old }) => {
file: async ({ old, neu }) => {
const dec = new TextDecoder();
// console.log("diff", neu, old?.data, neu?.data);
const newText = dec.decode(neu?.data);
const oldText = dec.decode(old?.data);

if (newText === oldText) {
return [];
}

return await mockPlugin.diff.text({
old: old
? {
id: "test",
text: oldText,
}
: undefined,
neu: neu
? {
id: "test",
text: newText,
}
: undefined,
});
},
text: async ({ old, neu }) => {
// console.log("text", old, neu);
if (old?.text === neu?.text) {
return [];
}

return [
!old
? {
Expand All @@ -18,25 +50,26 @@ test("should use queue and settled correctly", async () => {
old: undefined,
neu: {
id: "test",
text: "inserted text",
text: neu?.text,
},
}
: {
type: "text",
operation: "update",
old: {
id: "test",
text: "inserted text",
text: old?.text,
},
neu: {
id: "test",
text: "updated text",
text: neu?.text,
},
},
];
},
},
};

const lix = await openLixInMemory({
blob: await newLixFile(),
providePlugins: [mockPlugin],
Expand Down Expand Up @@ -98,7 +131,7 @@ test("should use queue and settled correctly", async () => {
plugin_key: "mock-plugin",
value: {
id: "test",
text: "inserted text",
text: "test",
},
meta: null,
commit_id: null,
Expand All @@ -113,6 +146,13 @@ test("should use queue and settled correctly", async () => {
.where("id", "=", "test")
.execute();

// re apply same change
await lix.db
.updateTable("file")
.set({ data: enc.encode("test updated text") })
.where("id", "=", "test")
.execute();

await lix.db
.updateTable("file")
.set({ data: enc.encode("test updated text second update") })
Expand All @@ -122,14 +162,14 @@ test("should use queue and settled correctly", async () => {
const queue2 = await lix.db.selectFrom("change_queue").selectAll().execute();
expect(queue2).toEqual([
{
id: 2,
id: 3,
file_id: "test",
path: "test.txt",
metadata: null,
data: queue2[0]?.data,
},
{
id: 3,
id: 4,
file_id: "test",
path: "test.txt",
metadata: null,
Expand All @@ -156,15 +196,47 @@ test("should use queue and settled correctly", async () => {
parent_id: null,
type: "text",
file_id: "test",
operation: "update",
operation: "create",
plugin_key: "mock-plugin",
value: {
id: "test",
text: "updated text",
text: "test",
},
meta: null,
commit_id: null,
},
{
author: null,
commit_id: null,
created_at: updatedChanges[1]?.created_at,
file_id: "test",
id: updatedChanges[1]?.id,
meta: null,
operation: "update",
parent_id: updatedChanges[0]?.id,
plugin_key: "mock-plugin",
type: "text",
value: {
id: "test",
text: "test updated text",
},
},
{
author: null,
commit_id: null,
created_at: updatedChanges[2]?.created_at,
file_id: "test",
id: updatedChanges[2]?.id,
meta: null,
operation: "update",
parent_id: updatedChanges[1]?.id,
plugin_key: "mock-plugin",
type: "text",
value: {
id: "test",
text: "test updated text second update",
},
},
]);

await lix.commit({ description: "test commit" });
Expand Down
178 changes: 48 additions & 130 deletions lix/packages/sdk/src/file-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { LixDatabaseSchema, LixFile } from "./database/schema.js";
import type { LixPlugin } from "./plugin.js";
import { minimatch } from "minimatch";
import { Kysely } from "kysely";
import { getLeafChange } from "./query-utilities/get-leaf-change.js";

// start a new normalize path function that has the absolute minimum implementation.
function normalizePath(path: string) {
Expand All @@ -12,62 +13,6 @@ function normalizePath(path: string) {
return path;
}

async function getChangeHistory({
atomId,
depth,
fileId,
pluginKey,
diffType,
db,
}: {
atomId: string;
depth: number;
fileId: string;
pluginKey: string;
diffType: string;
db: Kysely<LixDatabaseSchema>;
}): Promise<any[]> {
if (depth > 1) {
// TODO: walk change parents until depth
throw new Error("depth > 1 not supported yet");
}

const { commit_id } = await db
.selectFrom("ref")
.select("commit_id")
.where("name", "=", "current")
.executeTakeFirstOrThrow();

let nextCommitId = commit_id;
let firstChange;
while (!firstChange && nextCommitId) {
const commit = await db
.selectFrom("commit")
.selectAll()
.where("id", "=", nextCommitId)
.executeTakeFirst();

if (!commit) {
break;
}
nextCommitId = commit.parent_id;

firstChange = await db
.selectFrom("change")
.selectAll()
.where("commit_id", "=", commit.id)
.where((eb) => eb.ref("value", "->>").key("id"), "=", atomId)
.where("plugin_key", "=", pluginKey)
.where("file_id", "=", fileId)
.where("type", "=", diffType)
.executeTakeFirst();
}

const changes: any[] = [firstChange];

return changes;
}

// creates initial changes for new files
export async function handleFileInsert(args: {
neu: LixFile;
Expand Down Expand Up @@ -165,90 +110,63 @@ export async function handleFileChange(args: {

// TODO: save hash of changed fles in every commit to discover inconsistent commits with blob?

const previousUncomittedChange = await trx
const previousChanges = await trx
.selectFrom("change")
.selectAll()
.where((eb) => eb.ref("value", "->>").key("id"), "=", value.id)
.where("type", "=", diff.type)
.where("file_id", "=", fileId)
.where("plugin_key", "=", pluginKey)
.where("commit_id", "is", null)
.executeTakeFirst();

const previousCommittedChange = (
await getChangeHistory({
atomId: value.id,
depth: 1,
fileId,
pluginKey,
diffType: diff.type,
db: trx,
})
)[0];

if (previousUncomittedChange) {
// working change exists but is different from previously committed change
// -> update the working change or delete if it is the same as previous uncommitted change
// overwrite the (uncomitted) change
// to avoid (potentially) saving every keystroke change
let previousCommittedDiff = [];

// working change exists but is identical to previously committed change
if (previousCommittedChange) {
previousCommittedDiff = await pluginDiffFunction?.[diff.type]?.({
old: previousCommittedChange.value,
neu: diff.neu,
});
.where("type", "=", diff.type)
.where((eb) => eb.ref("value", "->>").key("id"), "=", value.id)
// TODO don't rely on created at. a plugin should report the parent id.
.execute();

if (previousCommittedDiff.length === 0) {
// drop the change because it's identical
await trx
.deleteFrom("change")
.where((eb) => eb.ref("value", "->>").key("id"), "=", value.id)
.where("type", "=", diff.type)
.where("file_id", "=", fileId)
.where("plugin_key", "=", pluginKey)
.where("commit_id", "is", null)
.execute();
continue;
// we need to finde the real leaf change as multiple changes can be set in the same created timestamp second or clockskew
let previousChange; // = previousChanges.at(-1);
for (let i = previousChanges.length - 1; i >= 0; i--) {
for (const change of previousChanges) {
if (change.parent_id === previousChanges[i]?.id) {
break;
}
}
previousChange = previousChanges[i];
break;
}

if (!previousCommittedChange || previousCommittedDiff.length) {
await trx
.updateTable("change")
.where((eb) => eb.ref("value", "->>").key("id"), "=", value.id)
.where("type", "=", diff.type)
.where("file_id", "=", fileId)
.where("plugin_key", "=", pluginKey)
.where("commit_id", "is", null)
.set({
// @ts-expect-error - database expects stringified json
value: JSON.stringify(value),
operation: diff.operation,
// @ts-expect-error - database expects stringified json
meta: JSON.stringify(diff.meta),
})
.execute();
// working change exists but is different from previously committed change
// -> update the working change or delete if it is the same as previous uncommitted change
// overwrite the (uncomitted) change
// to avoid (potentially) saving every keystroke change
let previousCommittedDiff = [];

// working change exists but is identical to previously committed change
if (previousChange) {
previousCommittedDiff = await pluginDiffFunction?.[diff.type]?.({
old: previousChange?.value,
neu: diff.neu,
});

if (previousCommittedDiff?.length === 0) {
// drop the change because it's identical
continue;
}
} else {
await trx
.insertInto("change")
.values({
id: v4(),
type: diff.type,
file_id: fileId,
plugin_key: pluginKey,
author: args.currentAuthor,
parent_id: previousCommittedChange?.id,
// @ts-expect-error - database expects stringified json
value: JSON.stringify(value),
// @ts-expect-error - database expects stringified json
meta: JSON.stringify(diff.meta),
operation: diff.operation,
})
.execute();
}

await trx
.insertInto("change")
.values({
id: v4(),
type: diff.type,
file_id: fileId,
plugin_key: pluginKey,
author: args.currentAuthor,
parent_id: previousChange?.id,
// @ts-expect-error - database expects stringified json
value: JSON.stringify(value),
// @ts-expect-error - database expects stringified json
meta: JSON.stringify(diff.meta),
operation: diff.operation,
})
.execute();
}
}

Expand Down
Loading

0 comments on commit e63b4e8

Please sign in to comment.