Skip to content

Commit 3ee837d

Browse files
committed
Allow diffing with @codemirror/merge
1 parent 03cbfe4 commit 3ee837d

File tree

14 files changed

+1203
-520
lines changed

14 files changed

+1203
-520
lines changed

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ node_modules
44
**/package.json
55
!/package.json
66
jupyterlab_cell_diff
7+
.venv

README.md

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,59 @@
1-
# JupyterLab Plugin: Show a Cell Diff
1+
# jupyterlab-cell-diff
22

33
[![Github Actions Status](https://github.com/Zsailer/jupyterlab-cell-diff/workflows/Build/badge.svg)](https://github.com/Zsailer/jupyterlab-cell-diff/actions/workflows/build.yml)
4+
[![PyPI version](https://badge.fury.io/py/jupyterlab-cell-diff.svg)](https://badge.fury.io/py/jupyterlab-cell-diff)
45

5-
A JupyterLab Extension for showing cell (git) diffs.
6-
7-
This extension is composed of a Python package named `jupyterlab_cell_diff`
8-
for the server extension and a NPM package named `jupyterlab-cell-diff`
9-
for the frontend extension.
6+
A JupyterLab extension for showing cell diffs with multiple diffing strategies.
107

118
## Requirements
129

1310
- JupyterLab >= 4.0.0
1411

15-
## Install
12+
## Installation
1613

17-
To install the extension, execute:
14+
### PyPI Installation
1815

1916
```bash
2017
pip install jupyterlab_cell_diff
2118
```
2219

20+
### Development Installation
21+
22+
```bash
23+
# Clone the repository
24+
git clone https://github.com/jupyter-ai-contrib/jupyterlab-cell-diff.git
25+
cd jupyterlab-cell-diff
26+
27+
# Install the extension in development mode
28+
pip install -e .
29+
jupyter labextension develop . --overwrite
30+
```
31+
32+
## Usage
33+
34+
### Commands
35+
36+
The extension provides several commands:
37+
38+
- `jupyterlab-cell-diff:show-codemirror` - Show diff using `@codemirror/merge`
39+
- `jupyterlab-cell-diff:show-nbdime` - Show diff using NBDime
40+
41+
### Programmatic Usage
42+
43+
```typescript
44+
app.commands.execute('jupyterlab-cell-diff:show-codemirror', {
45+
cellId: 'cell-id',
46+
originalSource: 'print("Hello")',
47+
newSource: 'print("Hello, World!")'
48+
});
49+
50+
app.commands.execute('jupyterlab-cell-diff:show-nbdime', {
51+
cellId: 'cell-id',
52+
originalSource: 'print("Hello")',
53+
newSource: 'print("Hello, World!")'
54+
});
55+
```
56+
2357
## Uninstall
2458

2559
To remove the extension, execute:

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"files": [
2020
"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
2121
"style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
22-
"src/**/*.{ts,tsx}"
22+
"src/**/*.{ts,tsx}",
23+
"schema/*.json"
2324
],
2425
"main": "lib/index.js",
2526
"types": "lib/index.d.ts",
@@ -55,14 +56,19 @@
5556
"watch:labextension": "jupyter labextension watch ."
5657
},
5758
"dependencies": {
59+
"@codemirror/lang-python": "^6.2.1",
60+
"@codemirror/merge": "^6.10.2",
61+
"@codemirror/view": "^6.38.2",
5862
"@jupyterlab/application": "^4.0.0",
5963
"@jupyterlab/cells": "^4.0.0",
6064
"@jupyterlab/codeeditor": "^4.0.0",
65+
"@jupyterlab/codemirror": "^4.4.7",
6166
"@jupyterlab/coreutils": "^6.0.0",
6267
"@jupyterlab/notebook": "^4.0.0",
6368
"@jupyterlab/services": "^7.0.0",
6469
"@jupyterlab/ui-components": "^4.0.0",
6570
"@lumino/widgets": "^2.0.0",
71+
"codemirror": "^6.0.2",
6672
"jupyterlab-cell-input-footer": "^0.3.0",
6773
"nbdime": "^7.0.1"
6874
},

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ classifiers = [
2525
dependencies = [
2626
"nbdime",
2727
"jupyter_server>=2.4.0,<3",
28-
"jupyterlab-eventlistener>=0.4.0",
29-
"jupyterlab-cell-input-footer>=0.2.0"
28+
"jupyterlab-eventlistener>=0.4.0,<0.5",
29+
"jupyterlab-cell-input-footer>=0.2.0,<0.3"
3030
]
3131
dynamic = ["version", "description", "authors", "urls", "keywords"]
3232

src/command.ts

Lines changed: 0 additions & 97 deletions
This file was deleted.

src/diff/codemirror.ts

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { python } from '@codemirror/lang-python';
2+
import { MergeView } from '@codemirror/merge';
3+
import { EditorView } from '@codemirror/view';
4+
import { jupyterTheme } from '@jupyterlab/codemirror';
5+
import { Message } from '@lumino/messaging';
6+
import { Widget } from '@lumino/widgets';
7+
import { basicSetup } from 'codemirror';
8+
import { IDiffStrategy, IDiffWidgetOptions } from '../interfaces';
9+
import { BaseDiffWidget } from '../widget';
10+
11+
/**
12+
* CodeMirror-based diff strategy that performs client-side diffing
13+
*/
14+
export class CodeMirrorStrategy implements IDiffStrategy {
15+
readonly id = 'codemirror';
16+
readonly name = 'CodeMirror (Client-side)';
17+
18+
/**
19+
* Create a diff widget using CodeMirror merge view
20+
*/
21+
async createDiffWidget(options: IDiffWidgetOptions): Promise<Widget> {
22+
const {
23+
cell,
24+
cellFooterTracker,
25+
originalSource,
26+
newSource,
27+
showActionButtons = true,
28+
openDiff = true
29+
} = options;
30+
31+
const diffWidget = new CodeMirrorDiffWidget({
32+
originalSource,
33+
newSource,
34+
cell,
35+
cellFooterTracker,
36+
showActionButtons,
37+
openDiff
38+
});
39+
40+
diffWidget.addClass('jupyterlab-cell-diff');
41+
diffWidget.addToFooter();
42+
43+
return diffWidget;
44+
}
45+
}
46+
47+
/**
48+
* A Lumino widget that contains a CodeMirror diff view
49+
*/
50+
class CodeMirrorDiffWidget extends BaseDiffWidget {
51+
/**
52+
* Construct a new CodeMirrorDiffWidget.
53+
*/
54+
constructor(options: IDiffWidgetOptions) {
55+
super(options);
56+
this._originalCode = options.originalSource;
57+
this._modifiedCode = options.newSource;
58+
this.addClass('jp-DiffView');
59+
}
60+
61+
/**
62+
* Handle after-attach messages for the widget.
63+
*/
64+
protected onAfterAttach(msg: Message): void {
65+
super.onAfterAttach(msg);
66+
this._createMergeView();
67+
}
68+
69+
/**
70+
* Handle before-detach messages for the widget.
71+
*/
72+
protected onBeforeDetach(msg: Message): void {
73+
this._destroyMergeView();
74+
super.onBeforeDetach(msg);
75+
}
76+
77+
/**
78+
* Create the merge view with CodeMirror diff functionality.
79+
*/
80+
private _createMergeView(): void {
81+
if (this._mergeView) {
82+
return;
83+
}
84+
85+
this._mergeView = new MergeView({
86+
a: {
87+
doc: this._originalCode,
88+
extensions: [
89+
basicSetup,
90+
python(),
91+
EditorView.editable.of(false),
92+
jupyterTheme
93+
]
94+
},
95+
b: {
96+
doc: this._modifiedCode,
97+
extensions: [
98+
basicSetup,
99+
python(),
100+
EditorView.editable.of(false),
101+
jupyterTheme
102+
]
103+
},
104+
parent: this.node
105+
});
106+
}
107+
108+
/**
109+
* Destroy the merge view and clean up resources.
110+
*/
111+
private _destroyMergeView(): void {
112+
if (this._mergeView) {
113+
this._mergeView.destroy();
114+
this._mergeView = null;
115+
}
116+
}
117+
118+
private _originalCode: string;
119+
private _modifiedCode: string;
120+
private _mergeView: MergeView | null = null;
121+
}

0 commit comments

Comments
 (0)