Skip to content

Editor#601

Open
ije wants to merge 135 commits into
pierrecomputer:mainfrom
ije:editor
Open

Editor#601
ije wants to merge 135 commits into
pierrecomputer:mainfrom
ije:editor

Conversation

@ije
Copy link
Copy Markdown

@ije ije commented Apr 23, 2026

Architecture

The editor is a plugable system for the File and FileDiff components. It provides:

  • Text Editing
  • Selection management
  • History (undo, redo)

The editor is using a hidden textarea element as the input controller, which allows you to use Emacs-style keyboard binding(control + F/B/A/E/etc...), emoji/non-ascii input without implement them from scratch.

const textarea = document.createElement("textarea")
// make the textarea element invisible
textarea.style.opacity = '0'
// when change text
textarea.addEventListener("input", syncTextareState)
// moving caret
textarea.addEventListener("selectionchange", syncTextareState)

Selections are created via the native web selection API, multiple cursors has been supported.

document.addEventListener('selectionchange', () => {
  const selectionRaw = document.getSelection();
  const composedRanges = selectionRaw?.getComposedRanges({
    shadowRoots: [fileContainerShadowRoot],
  });
  const selection = convertSelection(composedRanges);
  if (selection) {
    // Text in the editor has been selected.
    // You can now get the `start.line`, `start.column`, `end.line` and `end.column` from the selection
  }
})

API

Vanilla JS:

import { Editor, File } from "@pierre/diffs"

// render a file
const fileInstance = new File({ ... });
fileInstance.render({ ... });

// make the file editable
const editor = new Editor()
const dispose  = editor.edit(fileInstance, (file, lineAnnotations) => console.log("change", file.name, file.contents))
dispose() // or call `editor.cleanUp()`

lazy import (recommended):

import { File } from "@pierre/diffs"

// render a file
const fileInstance = new File({ ... });
fileInstance.render({ ... });

const edit = async (file: File) => {
  const { edit } = await import("@pierre/diffs/editor")
  return edit(file, (file, lineAnnotations) => console.log("change", file.name, file.contents))
}
edit(fileInstance)

React:

import { Editor } from "@pierre/diffs"
import {
  type FileContents,
  type LineAnnotation,
  File,
  EditorProvider,
} from '@pierre/diffs/react';

const file: FileContents = {
  name: 'example.ts',
  contents: `function greet(name: string) {
  console.log(\`Hello, \${name}!\`);
}

export { greet };`,
};

export function EditCodeFile() {
  const [editable, setEditable] = useState(true)
  return (
    <EditorProvider editor={new Editor()}>
      <File
        file={file}
        options={{
          theme: { dark: 'pierre-dark', light: 'pierre-light' },
        }}
        editable={editable}
        onChange={(file, lineAnnotations) => console.log("change", file.name, file.contents)}
      />
    </EditorProvider>
  );
}

TODOS

  • Basic editing
  • Selection & cursor
  • Multiple cursors (Pressing ⌘)
  • History(undo/redo)
  • Shortcuts
    • 'copy' ⌘ + C
    • 'cut' ⌘ + X
    • 'paste' ⌘ + V/Y
    • 'extendSelection' ⌘ + D
    • 'indent' Tab
    • 'outdent' Shift + Tab
    • 'documentStart' ⌘ + ↑
    • 'documentEnd' ⌘ + ↓
    • 'undo' ⌘ + Z
    • 'redo' ⌘ + Shift + Z
    • 'selectAll' ⌘ + A
  • Browser compatibility
    • Chrome
    • Firefox
    • Safari
    • Mobile phone
  • LSP next version

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

@ije is attempting to deploy a commit to the Pierre Computer Company Team on Vercel.

A member of the Team first needs to authorize it.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pierrejs-diff-demo Ready Ready Preview May 10, 2026 1:42pm
pierrejs-docs Ready Ready Preview May 10, 2026 1:42pm

Request Review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new editable “Editor” subsystem for File/FileDiff rendering, including text editing, selection management (multi-cursor), undo/redo history, and background tokenization. In parallel, it refactors file windowing/rendering utilities by replacing line-splitting iteration with offset-based slicing, and adds supporting utilities/tests.

Changes:

  • Added a new editor implementation (piece table + TextDocument, selections, textarea syncing, edit history, commands, tokenizer) and exposed it via both vanilla and React APIs.
  • Reworked windowed file rendering to use computeLineOffsets + string slicing (removing iterateOverFile) and added hasVisibleLineAnnotation.
  • Added substantial Bun test coverage for the new editor primitives and updated renderer snapshots/fixtures.

Reviewed changes

Copilot reviewed 43 out of 43 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/diffs/test/textDocument.test.ts Tests for TextDocument editing, line handling, and undo/redo behavior.
packages/diffs/test/pieceTable.test.ts Tests for piece-table correctness across inserts/deletes, offsets, and line behavior.
packages/diffs/test/mocks.ts Adjusts mock file strings (notably trailing newlines) for updated rendering behavior.
packages/diffs/test/iterateOverFile.test.ts Removes tests for deleted iterateOverFile utility.
packages/diffs/test/hasVisibleLineAnnotation.test.ts Adds tests for new visible-annotation utility.
packages/diffs/test/FileRenderer.ast.test.ts Adds a test for rendering content that ends with a newline.
packages/diffs/test/fileLineUtils.test.ts Adds tests for the new line-offset computation helper.
packages/diffs/test/editStack.test.ts Adds tests for undo/redo stack entry behavior.
packages/diffs/test/editorTextareaSnapshot.test.ts Adds tests for textarea snapshot/change resolution behavior.
packages/diffs/test/editorSelection.test.ts Adds tests for selection conversion/mapping/edit application.
packages/diffs/test/editorLineAnnotations.test.ts Adds tests for line annotation updates on document changes.
packages/diffs/test/editorCommand.test.ts Adds tests for shortcut/command resolution.
packages/diffs/test/snapshots/FileRenderer.test.ts.snap Updates snapshots to match new line rendering behavior.
packages/diffs/src/worker/WorkerPoolManager.ts Updates plain-file AST rendering API to accept lineOffsets.
packages/diffs/src/utils/renderFileWithHighlighter.ts Switches window extraction to offset slicing; removes old split/iterate helpers.
packages/diffs/src/utils/iterateOverFile.ts Deletes iterateOverFile utility.
packages/diffs/src/utils/hasVisibleLineAnnotation.ts Adds helper to detect whether annotations intersect a render range.
packages/diffs/src/utils/computeFileOffsets.ts Adds computeLineOffsets implementation for offset-based windowing.
packages/diffs/src/utils/cleanLastNewline.ts Refactors newline trimming logic.
packages/diffs/src/types.ts Adds editor/highlighting types; renames ForceFilePlainTextOptions.lineslineOffsets.
packages/diffs/src/style.css Adds caret background CSS variable.
packages/diffs/src/renderers/FileRenderer.ts Replaces per-file split-line cache with offset caching; adds incremental update hooks.
packages/diffs/src/react/utils/useFileInstance.ts Wires React File to the editor via context when editable is enabled.
packages/diffs/src/react/types.ts Adds editable + onChange props to React File.
packages/diffs/src/react/index.ts Exports EditorContext.
packages/diffs/src/react/File.tsx Plumbs editable + onChange into useFileInstance.
packages/diffs/src/react/EditorContext.tsx Introduces EditorProvider/useEditor React context.
packages/diffs/src/managers/InteractionManager.ts Comments out a gutter/content length invariant check during selection rendering.
packages/diffs/src/index.ts Exports editor module + hasVisibleLineAnnotation.
packages/diffs/src/editor/tokenzier.ts Adds background tokenization utilities (new file).
packages/diffs/src/editor/textDocument.ts Adds TextDocument abstraction (new file).
packages/diffs/src/editor/pieceTable.ts Adds piece-table text buffer implementation (new file).
packages/diffs/src/editor/editStack.ts Adds undo/redo edit stack implementation (new file).
packages/diffs/src/editor/editorUtils.ts Adds DOM/util helpers used by the editor (new file).
packages/diffs/src/editor/editorTextarea.ts Adds textarea snapshot + diff resolution logic (new file).
packages/diffs/src/editor/editorSelection.ts Adds selection conversion/mapping/edit application logic (new file).
packages/diffs/src/editor/editorLineAnnotations.ts Adds line-annotation adjustment logic for edits (new file).
packages/diffs/src/editor/editorCommand.ts Adds shortcut→command mapping (new file).
packages/diffs/src/editor/constants.ts Adds editor/tokenization constants + editor CSS (new file).
packages/diffs/src/components/VirtualizedFile.ts Updates virtualization sizing/iteration to use line counts (no iterateOverFile).
packages/diffs/src/components/File.ts Adds editor hooks and incremental update APIs; introduces line-offset accessor.
apps/demo/src/main.ts Updates demo toggles and adds an “Editable” toggle; flips CRAZY_FILE default.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/diffs/src/react/utils/useFileInstance.ts Outdated
Comment thread packages/diffs/src/renderers/FileRenderer.ts
Comment on lines +179 to +186
public getOrCreateLineOffsets(file: FileContents): number[] {
let offsets = this.lineOffsets.get(file);
if (offsets == null) {
offsets = computeLineOffsets(file.contents);
this.lineOffsets.set(file, offsets);
}
return offsets;
}
Comment thread packages/diffs/src/managers/InteractionManager.ts Outdated
Comment thread packages/diffs/src/editor/editorSelection.ts
Comment thread packages/diffs/src/components/File.ts
Comment thread apps/demo/src/main.ts Outdated
Comment thread apps/demo/src/main.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants