Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/#109 crdt 클라이언트 서버 연동 #152

Merged
merged 10 commits into from
Nov 19, 2024
156 changes: 112 additions & 44 deletions @noctaCrdt/Crdt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
import { CharId, BlockId, NodeId } from "./NodeId";
import { Node, Char, Block } from "./Node";
import {
RemoteDeleteOperation,
RemoteInsertOperation,
RemoteBlockDeleteOperation,
RemoteCharDeleteOperation,
RemoteBlockInsertOperation,
RemoteCharInsertOperation,
SerializedProps,
RemoteReorderOperation,
} from "./Interfaces";
Expand All @@ -19,18 +21,50 @@
this.LinkedList = new LinkedList<T>();
}

localInsert(index: number, value: string): RemoteInsertOperation {
const id =
this instanceof BlockCRDT
? new CharId(this.clock + 1, this.client)
: new BlockId(this.clock + 1, this.client);
localInsert(index: number, value: string, blockId?: BlockId) {}

Check warning on line 24 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'index' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 24 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'value' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 24 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'blockId' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 24 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'index' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 24 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'value' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 24 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'blockId' is defined but never used. Allowed unused args must match /^_/u

localDelete(index: number, blockId?: BlockId) {}

Check warning on line 26 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'index' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 26 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'blockId' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 26 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'index' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 26 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'blockId' is defined but never used. Allowed unused args must match /^_/u

remoteInsert(operation: RemoteBlockInsertOperation | RemoteCharInsertOperation) {}

Check warning on line 28 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'operation' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 28 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'operation' is defined but never used. Allowed unused args must match /^_/u

remoteDelete(operation: RemoteBlockDeleteOperation | RemoteCharDeleteOperation) {}

Check warning on line 30 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'operation' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 30 in @noctaCrdt/Crdt.ts

View workflow job for this annotation

GitHub Actions / Lint and Unit Test

'operation' is defined but never used. Allowed unused args must match /^_/u

read(): string {
return this.LinkedList.stringify();
}

spread(): T[] {
return this.LinkedList.spread();
}

serialize(): SerializedProps<T> {
return {
clock: this.clock,
client: this.client,
LinkedList: {
head: this.LinkedList.head,
nodeMap: this.LinkedList.nodeMap || {},
},
};
}
}

export class EditorCRDT extends CRDT<Block> {
currentBlock: Block | null;

constructor(client: number) {
super(client);
this.currentBlock = null;
}

localInsert(index: number, value: string): RemoteBlockInsertOperation {
const id = new BlockId(this.clock + 1, this.client);
const remoteInsertion = this.LinkedList.insertAtIndex(index, value, id);
this.clock += 1;
return { node: remoteInsertion.node };
return { node: remoteInsertion.node } as RemoteBlockInsertOperation;
}

localDelete(index: number): RemoteDeleteOperation {
localDelete(index: number): RemoteBlockDeleteOperation {
if (index < 0 || index >= this.LinkedList.spread().length) {
throw new Error(`Invalid index: ${index}`);
}
Expand All @@ -40,7 +74,7 @@
throw new Error(`Node not found at index: ${index}`);
}

const operation: RemoteDeleteOperation = {
const operation: RemoteBlockDeleteOperation = {
targetId: nodeToDelete.id,
clock: this.clock + 1,
};
Expand All @@ -51,12 +85,15 @@
return operation;
}

remoteInsert(operation: RemoteInsertOperation): void {
const NodeIdClass = this instanceof BlockCRDT ? CharId : BlockId;
const NodeClass = this instanceof BlockCRDT ? Char : Block;
remoteUpdate(block: Block) {
this.LinkedList.nodeMap[JSON.stringify(block.id)] = block;
return { remoteUpdateOperation: block };
}

remoteInsert(operation: RemoteBlockInsertOperation): void {
const newNodeId = new BlockId(operation.node.id.clock, operation.node.id.client);
const newNode = new Block(operation.node.value, newNodeId);

const newNodeId = new NodeIdClass(operation.node.id.clock, operation.node.id.client);
const newNode = new NodeClass(operation.node.value, newNodeId) as T;
newNode.next = operation.node.next;
newNode.prev = operation.node.prev;

Expand All @@ -67,7 +104,7 @@
}
}

remoteDelete(operation: RemoteDeleteOperation): void {
remoteDelete(operation: RemoteBlockDeleteOperation): void {
const { targetId, clock } = operation;
if (targetId) {
this.LinkedList.deleteNode(targetId);
Expand All @@ -78,9 +115,9 @@
}

localReorder(params: {
targetId: NodeId;
beforeId: NodeId | null;
afterId: NodeId | null;
targetId: BlockId;
beforeId: BlockId | null;
afterId: BlockId | null;
}): RemoteReorderOperation {
const operation: RemoteReorderOperation = {
...params,
Expand All @@ -107,41 +144,72 @@
this.clock = clock + 1;
}
}
}

read(): string {
return this.LinkedList.stringify();
}
export class BlockCRDT extends CRDT<Char> {
currentCaret: number;

spread(): T[] {
return this.LinkedList.spread();
constructor(client: number) {
super(client);
this.currentCaret = 0;
}

serialize(): SerializedProps<T> {
return {
clock: this.clock,
client: this.client,
LinkedList: {
head: this.LinkedList.head,
nodeMap: this.LinkedList.nodeMap || {},
},
localInsert(index: number, value: string, blockId: BlockId): RemoteCharInsertOperation {
const id = new CharId(this.clock + 1, this.client);
const { node } = this.LinkedList.insertAtIndex(index, value, id);
this.clock += 1;
const operation: RemoteCharInsertOperation = {
node,
blockId,
};

return operation;
}
}

export class EditorCRDT extends CRDT<Block> {
currentBlock: Block | null;
localDelete(index: number, blockId: BlockId): RemoteCharDeleteOperation {
if (index < 0 || index >= this.LinkedList.spread().length) {
throw new Error(`Invalid index: ${index}`);
}

constructor(client: number) {
super(client);
this.currentBlock = null;
const nodeToDelete = this.LinkedList.findByIndex(index);
if (!nodeToDelete) {
throw new Error(`Node not found at index: ${index}`);
}

const operation: RemoteCharDeleteOperation = {
targetId: nodeToDelete.id,
clock: this.clock + 1,
blockId,
};

this.LinkedList.deleteNode(nodeToDelete.id);
this.clock += 1;

return operation;
}
}

export class BlockCRDT extends CRDT<Char> {
currentCaret: number;
remoteInsert(operation: RemoteCharInsertOperation): void {
const newNodeId = new CharId(operation.node.id.clock, operation.node.id.client);
const newNode = new Char(operation.node.value, newNodeId);

constructor(client: number) {
super(client);
this.currentCaret = 0;
newNode.next = operation.node.next;
newNode.prev = operation.node.prev;

this.LinkedList.insertById(newNode);

if (this.clock <= newNode.id.clock) {
this.clock = newNode.id.clock + 1;
}
}

remoteDelete(operation: RemoteCharDeleteOperation): void {
const { targetId, clock } = operation;
if (targetId) {
const targetNodeId = new CharId(operation.targetId.clock, operation.targetId.client);
this.LinkedList.deleteNode(targetNodeId);
}
if (this.clock <= clock) {
this.clock = clock + 1;
}
}
}
28 changes: 21 additions & 7 deletions @noctaCrdt/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,29 @@ export interface DeleteOperation {
clock: number;
}

export interface RemoteInsertOperation {
node: Block | Char;
export interface RemoteBlockUpdateOperation {
node: Block;
}
export interface RemoteBlockInsertOperation {
node: Block;
}

export interface RemoteCharInsertOperation {
node: Char;
blockId: BlockId;
}

export interface RemoteDeleteOperation {
targetId: NodeId;
export interface RemoteBlockDeleteOperation {
targetId: BlockId;
clock: number;
}

export interface RemoteCharDeleteOperation {
targetId: CharId;
clock: number;
blockId?: BlockId;
}

export interface CursorPosition {
clientId: number;
position: number;
Expand All @@ -43,9 +57,9 @@ export interface ReorderNodesProps {
}

export interface RemoteReorderOperation {
targetId: NodeId;
beforeId: NodeId | null;
afterId: NodeId | null;
targetId: BlockId;
beforeId: BlockId | null;
afterId: BlockId | null;
clock: number;
client: number;
}
Loading
Loading