Skip to content

hrushi2003/code-editor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

91 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸš€ Real-Time Collaborative Code Editor

A Google Docs–style collaborative code editor built with React + Monaco Editor, WebSockets, and MongoDB.
Supports real-time cursor tracking, batched updates, and efficient compaction of operational logs.


✨ Features

  • πŸ–ŠοΈ Live Code Editing – Multiple users can edit the same file simultaneously.
  • πŸ‘₯ Remote Cursor Tracking – See collaborators' cursors and names in real-time.
  • ⚑ Low-Latency Updates –
    • Cursor positions are emitted instantly.
    • Text updates are sent per keystroke for responsiveness.
    • Backend updates are batched for efficiency.
  • πŸ“¦ Operational Log Storage (CRDT-inspired) – Every edit is stored as a payload with position and timestamp.
  • 🧹 Automatic Compaction –
    • Old operations are periodically merged into snapshots.
    • Keeps database size manageable and lookup efficient.
  • πŸ”„ Diff/Patch Algorithm – Efficiently applies incoming changes with near O(1) updates.

βš™οΈ Tech Stack

  • Frontend: React, Monaco Editor, TailwindCSS
  • Backend: Node.js, Express.js, WebSocket
  • Database: MongoDB (Ops log + snapshots)

πŸ—οΈ How It Works

  1. Payload Generation

    • Each edit generates a payload:
      {
        "startIndx": 0,
        "endIndx": 0,
        "newLines": ["e"],
        "startColumn": 0,
        "endColumn": 0,
        "deleteCount": 1,
        "timeStamp": 1694567890000
      }
  2. Frontend β†’ Backend

    • Cursor updates: sent instantly.
    • Text updates: emitted live but batched before DB writes.
  3. Backend Storage

    • Stores operations like a CRDT log.
    • Runs compaction every 10 minutes or when a file is opened.
    • Operations update in DB takes O(1) in general.
  4. Diff/Patch Algorithm

    • Applies ops in timestamp order.
    • Achieves O(1) for most updates, O(n) worst case during compaction.

πŸ“Š Efficiency

  • Cursor emission latency: ~30–80 ms
  • Text update latency: ~100–200 ms
  • DB writes: Batching reduces ops from 1000s β†’ 10s
  • Compaction complexity: amortized ~O(1)–O(n), runs in background
  • This project implements a custom text buffer inspired by ropes and CRDTs, using a LinkedList + Map hybrid for O(1) line access and update.
  • Keystroke operations are batched (coalesced) into larger diffs to reduce overhead.

🚧 Future Improvements

  • πŸ” Access control (roles: leader, member).
  • πŸ“ Syntax highlighting with Monaco’s language services.
  • 🌍 Support for cross-region low-latency replication.

πŸ”„ System Flow

sequenceDiagram
    participant UserA as User A (Editor)
    participant FE as Frontend
    participant WS as WebSocket Server
    participant BE as Backend (Express)
    participant DB as MongoDB
    participant UserB as User B (Editor)

    UserA->>FE: Type code / Move cursor
    FE->>WS: Emit payload (cursor or text)
    WS->>BE: Forward event
    BE->>DB: Store operation (batched)
    BE-->>WS: Broadcast to other users
    WS-->>UserB: Send update
    UserB->>FE: Apply diff/patch
Loading

Diff Match Patch Flow

sequenceDiagram
  participant U as User (Monaco)
  participant D as Diff Engine
  participant RT as Real-time Server
  participant DB as Ops Store
  participant P as Peers

  U->>D: Local edit (range, text)
  D->>D: Compute minimal diff (start,end,newText)
  D-->>RT: Patch payload
  RT->>DB: Append op (O(1))
  RT-->>P: Broadcast patch
  P->>P: Apply patch β†’ update editor
Loading

Compaction Flow

sequenceDiagram
  participant Worker as Compaction Worker
  participant DB as MongoDB
  participant Snap as Snapshots

  Worker->>DB: load latest snapshot
  Worker->>DB: fetch ops after snapshot.version
  Worker->>Worker: apply ops in-memory
  Worker->>Snap: write new snapshot
  Worker->>DB: delete archived ops
Loading

Edit and Broadcast

sequenceDiagram
  participant U as User (Monaco)
  participant WS as WebSocket/RT service
  participant DB as Ops DB
  participant P as Peer(s)

  U->>WS: code-change (range, text, ts)
  WS->>DB: append op (O(1))
  WS-->>P: broadcast remote-change
  P->>PeerEditor: applyEdits()
Loading

Operation Coalescing

flowchart TD

    subgraph YourAlgo["Hybrid Approach (LinkedMapBuffer)"]
        C1["LinkedList β†’ Maintains line order (O(1) insert/delete)"]
        C2["HashMap β†’ Direct access by lineId (O(1))"]
        C3["Ops Batching β†’ Merge keystrokes into single op"]
        C4["Diff-Patch β†’ Store transformations instead of whole text"]
        C1 --> C2
        C2 --> C3
        C3 --> C4
    end
Loading

When working on real-time editors, every keystroke usually triggers a payload that’s sent to the backend.
This can quickly flood the network with redundant updates β€” especially when a user types fast or deletes characters continuously.

To tackle this, I implemented an operation coalescing algorithm that intelligently merges multiple nearby operations into a single optimized payload before synchronization.


🧩 How It Works

  • Each edit (insert or delete) is captured as an operation with metadata:
    • startIndex
    • startColumn
    • endColumn
    • deleteCount
    • newString
  • Before sending updates, operations on the same line or in close proximity are merged using the mergeOps() function.
  • Both insertions and deletions are handled gracefully to maintain data integrity and consistent cursor behavior.
  • The result: fewer network calls, less load on the database, and a smoother collaborative experience.

✨ Before vs After

Case Without Coalescing With Coalescing
Typing 24 characters 24 payloads sent 4 payloads sent
Reduction β€” β‰ˆ83.3% fewer payloads πŸš€

🧠 Example

Before Coalescing

[
  { startColumn: 1, newString: "H" },
  { startColumn: 2, newString: "e" },
  { startColumn: 3, newString: "l" },
  { startColumn: 4, newString: "l" },
  { startColumn: 5, newString: "o" }
]

After Coalescing

[
  { startColumn : 1, newString : "Hello", endColumn : 5 }
]

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published