Skip to content

Commit c9556c6

Browse files
committed
Show suggestion
1 parent 5b44c01 commit c9556c6

File tree

6 files changed

+152
-20
lines changed

6 files changed

+152
-20
lines changed

packages/base/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@
4747
"@lumino/disposable": "^2.0.0",
4848
"@lumino/signaling": "^2.0.0",
4949
"@lumino/widgets": "^2.0.0",
50+
"diff": "^7.0.0",
5051
"typestyle": "^2.4.0"
5152
},
5253
"devDependencies": {
54+
"@types/diff": "^6.0.0",
5355
"rimraf": "^3.0.2",
5456
"typescript": "^5"
5557
},

packages/base/src/suggestionsPanel/cellWidget.ts

+14-20
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1+
import { IYText } from '@jupyter/ydoc';
12
import { Cell, CodeCell, CodeCellModel } from '@jupyterlab/cells';
2-
import { ICell } from '@jupyterlab/nbformat';
3-
import {
4-
RenderMimeRegistry,
5-
standardRendererFactories as initialFactories
6-
} from '@jupyterlab/rendermime';
7-
import { Panel } from '@lumino/widgets';
8-
import { suggestionCellStyle } from './style';
93
import {
104
CodeMirrorEditorFactory,
115
EditorExtensionRegistry,
126
EditorLanguageRegistry,
137
EditorThemeRegistry,
148
ybinding
159
} from '@jupyterlab/codemirror';
16-
import { IYText } from '@jupyter/ydoc';
10+
import { ICell } from '@jupyterlab/nbformat';
11+
import {
12+
RenderMimeRegistry,
13+
standardRendererFactories as initialFactories
14+
} from '@jupyterlab/rendermime';
15+
import { Panel } from '@lumino/widgets';
16+
17+
import { highlightTextExtension } from './cmExtension';
18+
import { suggestionCellStyle } from './style';
1719

18-
import { EditorView, ViewPlugin } from '@codemirror/view';
1920
export class CellWidget extends Panel {
2021
constructor(options: CellWidget.IOptions) {
2122
super(options);
@@ -40,24 +41,17 @@ export class CellWidget extends Panel {
4041
const sharedModel = options.model.sharedModel as IYText;
4142
return EditorExtensionRegistry.createImmutableExtension(
4243
ybinding({
43-
ytext: sharedModel.ysource,
44-
undoManager: sharedModel.undoManager ?? undefined
44+
ytext: sharedModel.ysource
4545
})
4646
);
4747
}
4848
});
4949
registry.addExtension({
5050
name: 'suggestion-view',
5151
factory: options => {
52-
const ext = ViewPlugin.fromClass(
53-
class {
54-
constructor(view: EditorView) {
55-
console.log('3333333', options, view);
56-
}
57-
}
58-
);
59-
60-
return EditorExtensionRegistry.createImmutableExtension([ext]);
52+
return EditorExtensionRegistry.createImmutableExtension([
53+
highlightTextExtension
54+
]);
6155
}
6256
});
6357
return registry;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import {
2+
ViewPlugin,
3+
Decoration,
4+
DecorationSet,
5+
ViewUpdate,
6+
WidgetType,
7+
EditorView
8+
} from '@codemirror/view';
9+
import * as Diff from 'diff';
10+
class HighlightDiff {
11+
constructor(view: EditorView) {
12+
this._view = view;
13+
this.decorations = Decoration.none;
14+
this._originalText = this._view.state.doc.toString();
15+
this.updateDecorations();
16+
}
17+
18+
update(update: ViewUpdate) {
19+
if (update.docChanged || update.selectionSet) {
20+
this.updateDecorations();
21+
}
22+
}
23+
24+
updateDecorations() {
25+
const currentText = this._view.state.doc.toString();
26+
// Compare words with spaces
27+
const diffs = Diff.diffWordsWithSpace(this._originalText, currentText);
28+
29+
const decorations = [];
30+
let pos = 0;
31+
32+
for (const diff of diffs) {
33+
const length = diff.value.length;
34+
35+
if (diff.added) {
36+
// Highlight added text
37+
decorations.push(
38+
Decoration.mark({
39+
class: 'cm-diff-added'
40+
}).range(pos, pos + length)
41+
);
42+
pos += length; // Move the position for added text
43+
} else if (diff.removed) {
44+
// Keep removed text and display it inline as strikethrough
45+
decorations.push(
46+
Decoration.widget({
47+
widget: new RemovedTextWidget(diff.value),
48+
side: -1
49+
}).range(pos, pos) // Insert before current position
50+
);
51+
} else {
52+
pos += length; // Unchanged text
53+
}
54+
}
55+
56+
this.decorations = Decoration.set(decorations);
57+
}
58+
59+
destroy() {
60+
// TODO
61+
}
62+
63+
decorations: DecorationSet;
64+
private _originalText: string;
65+
private _view: EditorView;
66+
}
67+
/**
68+
* Widget to show removed text
69+
*
70+
* @class RemovedTextWidget
71+
* @extends {WidgetType}
72+
*/
73+
class RemovedTextWidget extends WidgetType {
74+
constructor(text: string) {
75+
super();
76+
this._text = text;
77+
}
78+
79+
get text(): string {
80+
return this._text;
81+
}
82+
83+
toDOM() {
84+
const span = document.createElement('span');
85+
span.textContent = this._text;
86+
span.className = 'cm-diff-removed';
87+
return span;
88+
}
89+
90+
eq(other: WidgetType) {
91+
return other instanceof RemovedTextWidget && other.text === this.text;
92+
}
93+
94+
updateDOM() {
95+
return false;
96+
}
97+
98+
private _text: string;
99+
}
100+
101+
export const highlightTextExtension = [
102+
ViewPlugin.fromClass(HighlightDiff, {
103+
decorations: (v: HighlightDiff) => v.decorations
104+
})
105+
];

python/jupyter_suggestions_core/style/base.css

+14
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,17 @@
33
44
https://jupyterlab.readthedocs.io/en/stable/developer/css.html
55
*/
6+
7+
/* Added text: Highlighted with green */
8+
.cm-diff-added {
9+
background-color: rgb(0 255 0 / 30%); /* Light green */
10+
border-radius: 3px;
11+
}
12+
13+
/* Removed text: Strikethrough with red */
14+
.cm-diff-removed {
15+
background-color: rgb(255 0 0 / 10%); /* Light red */
16+
text-decoration: line-through;
17+
color: gray;
18+
font-style: italic;
19+
}

tsconfigbase.json

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"declaration": true,
66
"esModuleInterop": true,
77
"incremental": true,
8+
"skipLibCheck": true,
89
"jsx": "react",
910
"module": "esnext",
1011
"moduleResolution": "node",

yarn.lock

+16
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,8 @@ __metadata:
503503
"@lumino/disposable": ^2.0.0
504504
"@lumino/signaling": ^2.0.0
505505
"@lumino/widgets": ^2.0.0
506+
"@types/diff": ^6.0.0
507+
diff: ^7.0.0
506508
rimraf: ^3.0.2
507509
typescript: ^5
508510
typestyle: ^2.4.0
@@ -2130,6 +2132,13 @@ __metadata:
21302132
languageName: node
21312133
linkType: hard
21322134

2135+
"@types/diff@npm:^6.0.0":
2136+
version: 6.0.0
2137+
resolution: "@types/diff@npm:6.0.0"
2138+
checksum: e29e3c3e36dc8de159794a4695be5a643257ed2ca209758b558ed3b11143c1857b8397ef507795c8067ac25429bc72033295b8ceac4d8fbff6b214fc9b74ae96
2139+
languageName: node
2140+
linkType: hard
2141+
21332142
"@types/eslint-scope@npm:^3.7.7":
21342143
version: 3.7.7
21352144
resolution: "@types/eslint-scope@npm:3.7.7"
@@ -3907,6 +3916,13 @@ __metadata:
39073916
languageName: node
39083917
linkType: hard
39093918

3919+
"diff@npm:^7.0.0":
3920+
version: 7.0.0
3921+
resolution: "diff@npm:7.0.0"
3922+
checksum: 5db0d339476b18dfbc8a08a7504fbcc74789eec626c8d20cf2cdd1871f1448962888128f4447c8f50a1e41a80decfe5e8489c375843b8cf1d42b7c2b611da4e1
3923+
languageName: node
3924+
linkType: hard
3925+
39103926
"dir-glob@npm:^3.0.1":
39113927
version: 3.0.1
39123928
resolution: "dir-glob@npm:3.0.1"

0 commit comments

Comments
 (0)