From 57b5c1b59a1d2033cb6360c8a5ee77a8a180e8a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Collonval?= Date: Thu, 21 Sep 2023 18:26:13 +0200 Subject: [PATCH] First working version for diff text file --- package.json | 7 +- src/commandsAndMenu.tsx | 19 ++- src/components/diff/PlainTextDiff.ts | 197 ++++++++++++++++++++------ src/index.ts | 4 + src/model.ts | 8 +- style/diff-common.css | 48 ++++--- style/diff-nb.css | 98 ++++++------- style/diff-text.css | 93 +++++++----- ui-tests/tests/merge-conflict.spec.ts | 2 +- yarn.lock | 40 +----- 10 files changed, 305 insertions(+), 211 deletions(-) diff --git a/package.json b/package.json index 998187a57..9c3aa8534 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,6 @@ "@lumino/polling": "^2.0.0", "@lumino/signaling": "^2.0.0", "@lumino/widgets": "^2.0.1", - "@mui/core": "^5.0.0-alpha.54", "@mui/icons-material": "^5.11.16", "@mui/lab": "^5.0.0-alpha.127", "@mui/material": "^5.12.1", @@ -175,13 +174,13 @@ "extension": true, "schemaDir": "schema", "sharedPackages": { - "@material-ui/core": { + "@mui/core": { "singleton": true }, - "@material-ui/icons": { + "@mui/icons": { "singleton": true }, - "@material-ui/lab": { + "@mui/lab": { "singleton": true }, "nbdime": { diff --git a/src/commandsAndMenu.tsx b/src/commandsAndMenu.tsx index 6dd5ea116..aa19a8503 100644 --- a/src/commandsAndMenu.tsx +++ b/src/commandsAndMenu.tsx @@ -56,6 +56,7 @@ import { AdvancedPushForm } from './widgets/AdvancedPushForm'; import { GitCredentialsForm } from './widgets/CredentialsBox'; import { discardAllChanges } from './widgets/discardAllChanges'; import { CheckboxForm } from './widgets/GitResetToRemoteForm'; +import { IEditorLanguageRegistry } from '@jupyterlab/codemirror'; export interface IGitCloneArgs { /** @@ -126,6 +127,7 @@ export function addCommands( app: JupyterFrontEnd, gitModel: GitExtension, editorFactory: CodeEditor.Factory, + languageRegistry: IEditorLanguageRegistry, fileBrowserModel: FileBrowserModel, settings: ISettingRegistry.ISettings, translator: ITranslator @@ -550,18 +552,23 @@ export function addCommands( const buildDiffWidget = getDiffProvider(fullPath) ?? (isText && - (options => createPlainTextDiff({ ...options, editorFactory }))); + (options => + createPlainTextDiff({ + ...options, + editorFactory, + languageRegistry + }))); if (buildDiffWidget) { const id = `git-diff-${fullPath}-${model.reference.label}-${model.challenger.label}`; const mainAreaItems = shell.widgets('main'); - let mainAreaItem = mainAreaItems.next().value; - while (mainAreaItem) { - if (mainAreaItem.id === id) { + let mainAreaItem: Widget | null = null; + for (const item of mainAreaItems) { + if (item.id === id) { shell.activateById(id); + mainAreaItem = item; break; } - mainAreaItem = mainAreaItems.next().value; } if (!mainAreaItem) { @@ -693,7 +700,7 @@ export function addCommands( ); if (!targetFile || targetFile.status === 'unmodified') { gitModel.statusChanged.disconnect(maybeClose); - mainAreaItem.dispose(); + mainAreaItem!.dispose(); } }; gitModel.statusChanged.connect(maybeClose); diff --git a/src/components/diff/PlainTextDiff.ts b/src/components/diff/PlainTextDiff.ts index 8bf626828..26ed89bc4 100644 --- a/src/components/diff/PlainTextDiff.ts +++ b/src/components/diff/PlainTextDiff.ts @@ -1,15 +1,23 @@ -import { CodeEditor } from '@jupyterlab/codeeditor'; +import { CodeEditor, IEditorMimeTypeService } from '@jupyterlab/codeeditor'; import { CodeMirrorEditorFactory, EditorExtensionRegistry, - EditorLanguageRegistry + EditorLanguageRegistry, + IEditorLanguageRegistry } from '@jupyterlab/codemirror'; import { Contents } from '@jupyterlab/services'; -import { nullTranslator, TranslationBundle } from '@jupyterlab/translation'; +import { TranslationBundle, nullTranslator } from '@jupyterlab/translation'; import { PromiseDelegate } from '@lumino/coreutils'; -import { Widget } from '@lumino/widgets'; -import { createNbdimeMergeView, MergeView } from 'nbdime/lib/common/mergeview'; -import { StringDiffModel } from 'nbdime/lib/diff/model'; +import { Panel, Widget } from '@lumino/widgets'; +import { + DIFF_DELETE, + DIFF_EQUAL, + DIFF_INSERT, + diff_match_patch +} from 'diff-match-patch'; +import { MergeView, createNbdimeMergeView } from 'nbdime/lib/common/mergeview'; +import { IStringDiffModel, StringDiffModel } from 'nbdime/lib/diff/model'; +import { DiffRangeRaw } from 'nbdime/lib/diff/range'; import { Git } from '../../tokens'; /** @@ -21,14 +29,17 @@ import { Git } from '../../tokens'; */ export const createPlainTextDiff = async ({ editorFactory, + languageRegistry, model, toolbar, translator }: Git.Diff.IFactoryOptions & { + languageRegistry: IEditorLanguageRegistry; editorFactory?: CodeEditor.Factory; }): Promise => { const widget = new PlainTextDiff({ model, + languageRegistry, editorFactory, trans: (translator ?? nullTranslator).load('jupyterlab_git') }); @@ -39,27 +50,34 @@ export const createPlainTextDiff = async ({ /** * Plain Text Diff widget */ -export class PlainTextDiff extends Widget implements Git.Diff.IDiffWidget { +export class PlainTextDiff extends Panel implements Git.Diff.IDiffWidget { constructor({ model, + languageRegistry, trans, editorFactory }: { model: Git.Diff.IModel; + languageRegistry: IEditorLanguageRegistry; editorFactory?: CodeEditor.Factory; trans?: TranslationBundle; }) { - super({ - node: PlainTextDiff.createNode( - model.reference.label, - model.base?.label ?? '', - model.challenger.label - ) - }); + super(); + this.addClass('jp-git-diff-root'); + this.addClass('nbdime-root'); + this.addWidget( + new Widget({ + node: PlainTextDiff.createHeader( + model.reference.label, + model.base?.label, + model.challenger.label + ) + }) + ); const getReady = new PromiseDelegate(); this._isReady = getReady.promise; - this._container = this.node.lastElementChild as HTMLElement; this._editorFactory = editorFactory ?? createEditorFactory(); + this._languageRegistry = languageRegistry; this._model = model; this._trans = trans ?? nullTranslator.load('jupyterlab_git'); @@ -140,7 +158,6 @@ export class PlainTextDiff extends Widget implements Git.Diff.IDiffWidget { this.createDiffView( this._challenger, this._reference, - this._languageRegistry, this._hasConflict ? this._base : null ); } @@ -150,13 +167,6 @@ export class PlainTextDiff extends Widget implements Git.Diff.IDiffWidget { }); } - /** - * Undo onAfterAttach - */ - onBeforeDetach(): void { - this._container.innerHTML = ''; - } - /** * Refresh diff * @@ -166,7 +176,6 @@ export class PlainTextDiff extends Widget implements Git.Diff.IDiffWidget { await this.ready; try { // Clear all - this._container.innerHTML = ''; this._mergeView.dispose(); // ENH request content only if it changed @@ -183,7 +192,6 @@ export class PlainTextDiff extends Widget implements Git.Diff.IDiffWidget { this.createDiffView( this._challenger!, this._reference!, - this._languageRegistry, this._hasConflict ? this._base : null ); @@ -198,19 +206,17 @@ export class PlainTextDiff extends Widget implements Git.Diff.IDiffWidget { /** * Create wrapper node */ - protected static createNode(...labels: string[]): HTMLElement { + protected static createHeader( + ...labels: (string | undefined)[] + ): HTMLElement { const bannerClass = labels[1] !== undefined ? 'jp-git-merge-banner' : 'jp-git-diff-banner'; const head = document.createElement('div'); - head.className = 'jp-git-diff-root'; - head.innerHTML = ` -
- ${labels - .filter(label => !!label) - .map(label => `${label}`) - .join('')} -
- `; + head.classList.add(bannerClass); + head.innerHTML = labels + .filter(label => !!label) + .map(label => `${label}`) + .join(''); return head; } @@ -223,21 +229,24 @@ export class PlainTextDiff extends Widget implements Git.Diff.IDiffWidget { protected async createDiffView( challengerContent: string, referenceContent: string, - languageRegistry: EditorLanguageRegistry | null = null, baseContent: string | null = null ): Promise { if (!this._mergeView) { - const remote = new StringDiffModel( - referenceContent, - challengerContent, - [], - [] - ); + const remote = createStringDiffModel(referenceContent, challengerContent); + + const mimetype = + this._languageRegistry.findByFileName(this._model.filename)?.mime ?? + this._languageRegistry.findBest(this._model.filename)?.mime ?? + IEditorMimeTypeService.defaultMimeType; + remote.mimetype = Array.isArray(mimetype) ? mimetype[0] : mimetype; this._mergeView = createNbdimeMergeView({ - remote, - factory: this._editorFactory + remote + // factory: this._editorFactory }); + this._mergeView.addClass('jp-git-PlainText-diff'); + + this.addWidget(this._mergeView); } return Promise.resolve(); @@ -255,13 +264,17 @@ export class PlainTextDiff extends Widget implements Git.Diff.IDiffWidget { (error as any)?.traceback ); const msg = ((error.message || error) as string).replace('\n', '
'); + while (this.widgets.length > 0) { + const w = this.widgets[0]; + this.layout?.removeWidget(w); + w.dispose(); + } this.node.innerHTML = ``; } - protected _container: HTMLElement; protected _editorFactory: CodeEditor.Factory; protected _isReady: Promise; // @ts-expect-error complex initialization @@ -271,10 +284,102 @@ export class PlainTextDiff extends Widget implements Git.Diff.IDiffWidget { private _reference: string | null = null; private _challenger: string | null = null; - private _languageRegistry: EditorLanguageRegistry | null = null; + private _languageRegistry: IEditorLanguageRegistry; private _base: string | null = null; } +/** + * Diff status + */ +enum DiffStatus { + Equal = DIFF_EQUAL, + Delete = DIFF_DELETE, + Insert = DIFF_INSERT +} + +/** + * Diff type + */ +type Diff = [DiffStatus, string]; + +/** + * Pointer to the diff algorithm + */ +let dmp: any; +/** + * Compute the diff between two strings. + * + * @param a Reference + * @param b Challenger + * @param ignoreWhitespace Whether to ignore white spaces or not + * @returns Diff list + */ +function getDiff(a: string, b: string, ignoreWhitespace?: boolean): Diff[] { + if (!dmp) { + dmp = new diff_match_patch(); + } + + const diff = dmp.diff_main(a, b); + dmp.diff_cleanupSemantic(diff); + // The library sometimes leaves in empty parts, which confuse the algorithm + for (let i = 0; i < diff.length; ++i) { + const part = diff[i]; + if (ignoreWhitespace ? !/[^ \t]/.test(part[1]) : !part[1]) { + diff.splice(i--, 1); + } else if (i && diff[i - 1][0] === part[0]) { + diff.splice(i--, 1); + diff[i][1] += part[1]; + } + } + return diff; +} + +/** + * Create nbdime diff model from two strings. + * + * @param reference Reference text + * @param challenger Challenger text + * @param ignoreWhitespace Whether to ignore white spaces or not + * @returns The nbdime diff model + */ +function createStringDiffModel( + reference: string, + challenger: string, + ignoreWhitespace?: boolean +): IStringDiffModel { + const diffs = getDiff(reference, challenger, ignoreWhitespace); + + const additions: DiffRangeRaw[] = []; + const deletions: DiffRangeRaw[] = []; + + let referencePos = 0; + let challengerPos = 0; + diffs.forEach(([status, str]) => { + switch (status) { + case DiffStatus.Delete: + deletions.push(new DiffRangeRaw(referencePos, str.length)); + referencePos += str.length; + break; + case DiffStatus.Insert: + additions.push(new DiffRangeRaw(challengerPos, str.length)); + challengerPos += str.length; + break; + // Equal is not represented in nbdime + case DiffStatus.Equal: + referencePos += str.length; + challengerPos += str.length; + break; + } + }); + + return new StringDiffModel(reference, challenger, additions, deletions); +} + +/** + * Create a default editor factory. + * + * @returns Editor factory + */ function createEditorFactory(): CodeEditor.Factory { const factory = new CodeMirrorEditorFactory({ extensions: new EditorExtensionRegistry(), diff --git a/src/index.ts b/src/index.ts index d97f7e0b4..01d9faec4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,7 @@ import { gitIcon } from './style/icons'; import { CommandIDs, Git, IGitExtension } from './tokens'; import { addCloneButton } from './widgets/gitClone'; import { GitWidget } from './widgets/GitWidget'; +import { IEditorLanguageRegistry } from '@jupyterlab/codemirror'; export { DiffModel } from './components/diff/model'; export { NotebookDiff } from './components/diff/NotebookDiff'; @@ -46,6 +47,7 @@ const plugin: JupyterFrontEndPlugin = { id: '@jupyterlab/git:plugin', requires: [ ILayoutRestorer, + IEditorLanguageRegistry, IEditorServices, IDefaultFileBrowser, IRenderMimeRegistry, @@ -69,6 +71,7 @@ export default [plugin, gitCloneCommandPlugin]; async function activate( app: JupyterFrontEnd, restorer: ILayoutRestorer, + languageRegistry: IEditorLanguageRegistry, editorServices: IEditorServices, // Get a reference to the default file browser extension // We don't use the current tracked browser because extension like jupyterlab-github @@ -183,6 +186,7 @@ async function activate( editorServices.factoryService.newInlineEditor.bind( editorServices.factoryService ), + languageRegistry, fileBrowser.model, settings, translator diff --git a/src/model.ts b/src/model.ts index 1895d6768..dfd017b02 100644 --- a/src/model.ts +++ b/src/model.ts @@ -344,9 +344,9 @@ export class GitExtension implements IGitExtension { * * Note: This makes sure it always returns non null value */ - protected get _currentMarker(): BranchMarker | null { + protected get _currentMarker(): BranchMarker { if (!this.pathRepository) { - return null; + return new BranchMarker(() => {}); } if (!this.__currentMarker) { @@ -2141,8 +2141,8 @@ export class GitExtension implements IGitExtension { private _docRegistry: DocumentRegistry | null; private _fetchPoll: Poll; private _isDisposed = false; - private _markerCache: Markers = new Markers(() => this._markChanged.emit()); - private __currentMarker: BranchMarker | null = null; + private _markerCache = new Markers(() => this._markChanged.emit()); + private __currentMarker: BranchMarker = new BranchMarker(() => {}); private _readyPromise: Promise = Promise.resolve(); private _pendingReadyPromise = 0; private _settings: ISettingRegistry.ISettings | null; diff --git a/style/diff-common.css b/style/diff-common.css index 57fd4d16e..de590ce69 100644 --- a/style/diff-common.css +++ b/style/diff-common.css @@ -20,6 +20,8 @@ outline: none; background: var(--jp-layout-color0); color: var(--jp-ui-font-color0); + padding: 0; + overflow: hidden; } button.jp-git-diff-refresh { @@ -73,7 +75,7 @@ button.jp-git-diff-resolve .jp-ToolbarButtonComponent-label { width: 3.5%; } -.jp-git-diff-root .CodeMirror-merge { +.jp-git-diff-root .cm-merge { position: relative; white-space: pre; border: var(--codemirror-border); @@ -81,83 +83,83 @@ button.jp-git-diff-resolve .jp-ToolbarButtonComponent-label { background: var(--jp-cell-editor-background); } -.jp-git-diff-root .CodeMirror { +.jp-git-diff-root .cm { background: var(--jp-layout-color0); } -.jp-git-diff-root .CodeMirror-merge, -.CodeMirror-merge .CodeMirror { +.jp-git-diff-root .cm-merge, +.cm-merge .cm { height: auto; } -.jp-git-diff-root .CodeMirror-merge-4pane .CodeMirror-merge-pane-deleted { +.jp-git-diff-root .cm-merge-4pane .cm-merge-pane-deleted { display: none; } -.jp-git-diff-root .CodeMirror-merge-pane-unchanged { +.jp-git-diff-root .cm-merge-pane-unchanged { width: 100%; } -.jp-git-diff-root .CodeMirror-merge-pane { +.jp-git-diff-root .cm-merge-pane { display: inline-block; white-space: normal; vertical-align: top; } -.jp-git-diff-root .CodeMirror-merge-1pane .CodeMirror-merge-gap { +.jp-git-diff-root .cm-merge-1pane .cm-merge-gap { width: 6%; } -.jp-git-diff-root .CodeMirror-merge-2pane .CodeMirror-merge-pane { +.jp-git-diff-root .cm-merge-2pane .cm-merge-pane { width: 47%; } -.jp-git-diff-root .CodeMirror-merge-2pane .CodeMirror-merge-gap { +.jp-git-diff-root .cm-merge-2pane .cm-merge-gap { width: 6%; } -.jp-git-diff-root .CodeMirror-merge-3pane .CodeMirror-merge-pane { +.jp-git-diff-root .cm-merge-3pane .cm-merge-pane { width: 31%; } -.jp-git-diff-root .CodeMirror-merge-3pane .CodeMirror-merge-gap { +.jp-git-diff-root .cm-merge-3pane .cm-merge-gap { width: 3.5%; } -.jp-git-diff-root .CodeMirror-merge-pane-rightmost { +.jp-git-diff-root .cm-merge-pane-rightmost { position: absolute; right: 0; z-index: 1; } -.jp-git-diff-root .CodeMirror-merge-scrolllock-wrap { +.jp-git-diff-root .cm-merge-scrolllock-wrap { position: absolute; bottom: 0; left: 50%; } -.jp-git-diff-root .CodeMirror-merge-scrolllock { +.jp-git-diff-root .cm-merge-scrolllock { position: relative; left: -50%; cursor: pointer; line-height: 1; } -.jp-git-diff-root .CodeMirror-merge-r-inserted, -.jp-git-diff-root .CodeMirror-merge-l-inserted { +.jp-git-diff-root .cm-merge-r-inserted, +.jp-git-diff-root .cm-merge-l-inserted { background-color: var(--jp-git-diff-added-color); } -.jp-git-diff-root .CodeMirror-merge-r-deleted, -.jp-git-diff-root .CodeMirror-merge-l-deleted { +.jp-git-diff-root .cm-merge-r-deleted, +.jp-git-diff-root .cm-merge-l-deleted { background-color: var(--jp-git-diff-deleted-color); } -.jp-git-diff-root .CodeMirror-merge-collapsed-widget::before { +.jp-git-diff-root .cm-merge-collapsed-widget::before { content: '(...)'; } -.jp-git-diff-root .CodeMirror-merge-collapsed-widget { +.jp-git-diff-root .cm-merge-collapsed-widget { cursor: pointer; color: var(--jp-ui-font-color1); background: var(--jp-layout-color2); @@ -167,11 +169,11 @@ button.jp-git-diff-resolve .jp-ToolbarButtonComponent-label { border-radius: 4px; } -.jp-git-diff-root .CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { +.jp-git-diff-root .cm-merge-collapsed-line .cm-gutter-elt { display: none; } -.jp-git-diff-root .CodeMirror-merge-spacer { +.jp-git-diff-root .cm-merge-spacer { background-image: repeating-linear-gradient( 145deg, transparent, diff --git a/style/diff-nb.css b/style/diff-nb.css index 66dc3c2e8..77e950752 100644 --- a/style/diff-nb.css +++ b/style/diff-nb.css @@ -16,57 +16,48 @@ border: none; } -.jp-git-diff-root .jp-Notebook-diff .CodeMirror-merge-r-chunk-end, -.jp-git-diff-root .jp-Notebook-diff .CodeMirror-merge-l-chunk-end, -.jp-git-diff-root .jp-Notebook-merge .CodeMirror-merge-l-chunk-end, -.jp-git-diff-root .jp-Notebook-merge .CodeMirror-merge-r-chunk-end, -.jp-git-diff-root .jp-Notebook-merge .CodeMirror-merge-m-chunk-end-local, -.jp-git-diff-root .jp-Notebook-merge .CodeMirror-merge-m-chunk-end-remote { +.jp-git-diff-root .jp-Notebook-diff .cm-merge-r-chunk-end, +.jp-git-diff-root .jp-Notebook-diff .cm-merge-l-chunk-end, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-l-chunk-end, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-r-chunk-end, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-m-chunk-end-local, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-m-chunk-end-remote { border-bottom: none; } -.jp-git-diff-root .jp-Notebook-diff .CodeMirror-merge-r-chunk-start, -.jp-git-diff-root .jp-Notebook-diff .CodeMirror-merge-l-chunk-start, -.jp-git-diff-root .jp-Notebook-merge .CodeMirror-merge-l-chunk-start, -.jp-git-diff-root .jp-Notebook-merge .CodeMirror-merge-r-chunk-start, -.jp-git-diff-root .jp-Notebook-merge .CodeMirror-merge-m-chunk-start-local, -.jp-git-diff-root .jp-Notebook-merge .CodeMirror-merge-m-chunk-start-remote { +.jp-git-diff-root .jp-Notebook-diff .cm-merge-r-chunk-start, +.jp-git-diff-root .jp-Notebook-diff .cm-merge-l-chunk-start, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-l-chunk-start, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-r-chunk-start, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-m-chunk-start-local, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-m-chunk-start-remote { border-top: none; } -.jp-git-diff-root .jp-Notebook-diff .CodeMirror-merge-r-chunk, -.jp-git-diff-root .jp-Notebook-merge .CodeMirror-merge-r-chunk { +.jp-git-diff-root .jp-Notebook-diff .cm-merge-r-chunk, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-r-chunk { background-color: var(--jp-git-diff-deleted-color); } -.jp-git-diff-root - .jp-Notebook-diff - .CodeMirror-merge-pane-remote - .CodeMirror-merge-r-chunk { +.jp-git-diff-root .jp-Notebook-diff .cm-merge-pane-remote .cm-merge-r-chunk { background-color: var(--jp-git-diff-added-color); } -.jp-git-diff-root .jp-Notebook-diff .CodeMirror-merge-spacer, -.jp-git-diff-root .jp-Notebook-merge .CodeMirror-merge-spacer, -.jp-git-diff-root - .jp-Notebook-diff - .CodeMirror-merge-pane-remote - .CodeMirror-merge-spacer, -.jp-git-diff-root - .jp-Notebook-merge - .CodeMirror-merge-pane-remote - .CodeMirror-merge-spacer { +.jp-git-diff-root .jp-Notebook-diff .cm-merge-spacer, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-spacer, +.jp-git-diff-root .jp-Notebook-diff .cm-merge-pane-remote .cm-merge-spacer, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-pane-remote .cm-merge-spacer { background-color: transparent; } -.jp-git-diff-root .jp-Diff-added .CodeMirror, +.jp-git-diff-root .jp-Diff-added .cm, .jp-git-diff-root .jp-Diff-added .jp-Diff-renderedOutput, .jp-git-diff-root .jp-Cellrow-outputs .jp-Diff-twoway .jp-Diff-remote { background-color: var(--jp-git-diff-added-color); border: none; } -.jp-git-diff-root .jp-Diff-deleted .CodeMirror, +.jp-git-diff-root .jp-Diff-deleted .cm, .jp-git-diff-root .jp-Diff-deleted .jp-Diff-renderedOutput, .jp-git-diff-root .jp-Cellrow-outputs .jp-Diff-twoway .jp-Diff-base { background-color: var(--jp-git-diff-deleted-color); @@ -75,10 +66,8 @@ /* Specific rules for the notebook merge conflict */ .jp-git-diff-root .jp-Notebook-merge .jp-Merge-conflict, -.jp-git-diff-root .jp-Notebook-merge .CodeMirror-merge-l-chunk, -.jp-git-diff-root - .jp-Notebook-merge - .CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-l-chunk, +.jp-git-diff-root .jp-Notebook-merge .cm-merge-l-chunk.cm-merge-r-chunk, .jp-git-diff-root .jp-Notebook-merge .jp-Cellrow-outputs.jp-conflicted-outputs @@ -86,47 +75,44 @@ background-color: var(--jp-git-diff-deleted-color); } -.jp-git-diff-root - .jp-Notebook-merge - .CodeMirror-line - .CodeMirror-merge-r-inserted { +.jp-git-diff-root .jp-Notebook-merge .cm-line .cm-merge-r-inserted { background-color: var(--jp-git-diff-deleted-color); } .jp-git-diff-root .jp-Notebook-merge - .CodeMirror-merge-pane-base - .CodeMirror-line - .CodeMirror-merge-m-deleted, + .cm-merge-pane-base + .cm-line + .cm-merge-m-deleted, .jp-git-diff-root .jp-Notebook-merge - .CodeMirror-merge-pane-base - .CodeMirror-line - .CodeMirror-merge-l-deleted, + .cm-merge-pane-base + .cm-line + .cm-merge-l-deleted, .jp-git-diff-root .jp-Notebook-merge - .CodeMirror-merge-pane-base - .CodeMirror-line - .CodeMirror-merge-r-deleted { + .cm-merge-pane-base + .cm-line + .cm-merge-r-deleted { background-color: var(--jp-git-diff-deleted-color); color: var(--jp-mirror-editor-string-color); } .jp-git-diff-root .jp-Notebook-merge - .CodeMirror-merge-pane-base - .CodeMirror-line - .CodeMirror-merge-m-added, + .cm-merge-pane-base + .cm-line + .cm-merge-m-added, .jp-git-diff-root .jp-Notebook-merge - .CodeMirror-merge-pane-base - .CodeMirror-line - .CodeMirror-merge-l-added, + .cm-merge-pane-base + .cm-line + .cm-merge-l-added, .jp-git-diff-root .jp-Notebook-merge - .CodeMirror-merge-pane-base - .CodeMirror-line - .CodeMirror-merge-r-added { + .cm-merge-pane-base + .cm-line + .cm-merge-r-added { background-color: var(--jp-git-diff-added-color); color: var(--jp-mirror-editor-string-color); } diff --git a/style/diff-text.css b/style/diff-text.css index d4d4067d6..b91d12831 100644 --- a/style/diff-text.css +++ b/style/diff-text.css @@ -1,15 +1,16 @@ .jp-git-PlainText-diff { width: 100%; max-height: calc(100% - 23px); + overflow-y: auto; } -.jp-git-PlainText-diff .CodeMirror-merge, -.jp-git-PlainText-diff .CodeMirror-merge .CodeMirror, -.jp-git-PlainText-diff .CodeMirror-merge .CodeMirror-merge-pane { +/* .jp-git-PlainText-diff .cm-merge, +.jp-git-PlainText-diff .cm-merge .cm, +.jp-git-PlainText-diff .cm-merge .cm-merge-pane { height: 100%; } -.jp-git-PlainText-diff .CodeMirror-merge-gap { +.jp-git-PlainText-diff .cm-merge-gap { z-index: 2; display: inline-block; height: 100%; @@ -21,91 +22,111 @@ position: relative; color: var(--jp-ui-font-color2); background: var(--jp-layout-color2); +} */ + +.jp-git-PlainText-diff .cm-merge-r-chunk { + background-color: var(--jp-diff-deleted-color2); } -.jp-git-PlainText-diff .CodeMirror-merge-r-inserted, -.jp-git-PlainText-diff .CodeMirror-merge-l-inserted { - background-position: bottom left; - background-repeat: repeat-x; +.jp-git-PlainText-diff .cm-merge-r-chunk-start { + border-top: 1px solid var(--jp-diff-deleted-color0); } -.jp-git-PlainText-diff .CodeMirror-merge-r-deleted, -.jp-git-PlainText-diff .CodeMirror-merge-l-deleted { - background-position: bottom left; - background-repeat: repeat-x; +.jp-git-PlainText-diff .cm-merge-r-chunk-end { + border-bottom: 1px solid var(--jp-diff-deleted-color0); } -.jp-git-PlainText-diff .CodeMirror-merge-l-chunk, -.jp-git-PlainText-diff .CodeMirror-merge-editor .CodeMirror-merge-l-chunk, -.jp-git-PlainText-diff .CodeMirror-merge-editor .CodeMirror-merge-r-chunk { - background-color: var(--jp-git-diff-added-color); +.jp-git-PlainText-diff .cm-merge-r-connect { + fill: var(--jp-diff-deleted-color2); + stroke: var(--jp-diff-deleted-color0); + stroke-width: 1px; } -.jp-git-PlainText-diff .CodeMirror-merge-left .CodeMirror-merge-l-chunk, -.jp-git-PlainText-diff .CodeMirror-merge-right .CodeMirror-merge-r-chunk { - background-color: var(--jp-git-diff-deleted-color); +.jp-git-PlainText-diff .cm-merge-spacer { + background-color: var(--jp-diff-deleted-color2); +} + +.jp-git-PlainText-diff .cm-line .cm-merge-r-inserted { + background-color: var(--jp-diff-added-color0); +} + +.jp-git-PlainText-diff .cm-diff-right-editor .cm-merge-r-chunk { + background-color: var(--jp-diff-added-color2); +} + +.jp-git-PlainText-diff .cm-diff-right-editor .cm-merge-r-chunk-start { + border-top: 1px solid var(--jp-diff-added-color0); +} + +.jp-git-PlainText-diff .cm-diff-right-editor .cm-merge-r-chunk-end { + border-bottom: 1px solid var(--jp-diff-added-color0); +} + +.jp-git-PlainText-diff .cm-diff-right-editor .cm-merge-r-connect { + fill: var(--jp-diff-added-color2); + stroke: var(--jp-diff-added-color0); + stroke-width: 1px; +} + +.jp-git-PlainText-diff .cm-diff-right-editor .cm-merge-spacer { + background-color: var(--jp-diff-added-color2); } -.CodeMirror-patchgutter { +.cm-patchgutter { width: 0.7em; } -.CodeMirror-patchgutter-insert::before { +.cm-patchgutter-insert::before { content: '+'; color: #999; background-color: var(--jp-git-diff-added-color); } -.CodeMirror-patchgutter-delete::before { +.cm-patchgutter-delete::before { content: '-'; color: #999; background-color: var(--jp-git-diff-deleted-color); } /* Styles for horizontal scroll lock */ -.jp-git-PlainText-diff .CodeMirror-merge-scrolllock { +.jp-git-PlainText-diff .cm-merge-scrolllock { color: var(--jp-inverse-layout-color2); } -.jp-git-PlainText-diff .CodeMirror-merge-scrolllock::after { +.jp-git-PlainText-diff .cm-merge-scrolllock::after { content: '\21db\00a0\00a0\21da'; } -.jp-git-PlainText-diff - .CodeMirror-merge-scrolllock.CodeMirror-merge-scrolllock-enabled::after { +.jp-git-PlainText-diff .cm-merge-scrolllock.cm-merge-scrolllock-enabled::after { content: '\21db\21da'; } /* Styles for revert buttons handling */ -.jp-git-PlainText-diff .CodeMirror-merge-copybuttons-left, -.jp-git-PlainText-diff .CodeMirror-merge-copybuttons-right { +.jp-git-PlainText-diff .cm-merge-copybuttons-left, +.jp-git-PlainText-diff .cm-merge-copybuttons-right { position: absolute; inset: 0; line-height: 1; } -.jp-git-PlainText-diff .CodeMirror-merge-copy { +.jp-git-PlainText-diff .cm-merge-copy { position: absolute; cursor: pointer; color: var(--jp-brand-color1); z-index: 3; } -.jp-git-PlainText-diff .CodeMirror-merge-copy-reverse { +.jp-git-PlainText-diff .cm-merge-copy-reverse { position: absolute; cursor: pointer; color: var(--jp-brand-color1); } -.jp-git-PlainText-diff - .CodeMirror-merge-copybuttons-left - .CodeMirror-merge-copy { +.jp-git-PlainText-diff .cm-merge-copybuttons-left .cm-merge-copy { left: 2px; } -.jp-git-PlainText-diff - .CodeMirror-merge-copybuttons-right - .CodeMirror-merge-copy { +.jp-git-PlainText-diff .cm-merge-copybuttons-right .cm-merge-copy { right: 2px; } diff --git a/ui-tests/tests/merge-conflict.spec.ts b/ui-tests/tests/merge-conflict.spec.ts index 4a2b9f6a0..b508ff5ba 100644 --- a/ui-tests/tests/merge-conflict.spec.ts +++ b/ui-tests/tests/merge-conflict.spec.ts @@ -56,7 +56,7 @@ test.describe('Merge conflict tests', () => { await expect(banner).toHaveText(/Result/); await expect(banner).toHaveText(/Incoming/); - const mergeDiff = page.locator('.CodeMirror-merge-3pane'); + const mergeDiff = page.locator('.cm-merge-3pane'); await expect(mergeDiff).toBeVisible(); }); diff --git a/yarn.lock b/yarn.lock index b69daec7a..0e0b98d56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1298,7 +1298,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.9, @babel/runtime@npm:^7.22.15, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.9, @babel/runtime@npm:^7.22.15, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": version: 7.22.15 resolution: "@babel/runtime@npm:7.22.15" dependencies: @@ -1688,7 +1688,7 @@ __metadata: languageName: node linkType: hard -"@emotion/is-prop-valid@npm:^1.1.0, @emotion/is-prop-valid@npm:^1.2.1": +"@emotion/is-prop-valid@npm:^1.2.1": version: 1.2.1 resolution: "@emotion/is-prop-valid@npm:1.2.1" dependencies: @@ -2632,7 +2632,6 @@ __metadata: "@lumino/polling": ^2.0.0 "@lumino/signaling": ^2.0.0 "@lumino/widgets": ^2.0.1 - "@mui/core": ^5.0.0-alpha.54 "@mui/icons-material": ^5.11.16 "@mui/lab": ^5.0.0-alpha.127 "@mui/material": ^5.12.1 @@ -3348,28 +3347,6 @@ __metadata: languageName: node linkType: hard -"@mui/core@npm:^5.0.0-alpha.54": - version: 5.0.0-alpha.54 - resolution: "@mui/core@npm:5.0.0-alpha.54" - dependencies: - "@babel/runtime": ^7.16.0 - "@emotion/is-prop-valid": ^1.1.0 - "@mui/utils": ^5.1.0 - "@popperjs/core": ^2.4.4 - clsx: ^1.1.1 - prop-types: ^15.7.2 - react-is: ^17.0.2 - peerDependencies: - "@types/react": ^16.8.6 || ^17.0.0 - react: ^17.0.2 - react-dom: ^17.0.2 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 1166fa0ea047da2f851ce87a11d568c631d7e61bf70619dd546b47b36c182a71fb1aceaf8eddfdf6d7ba116d7bc8a53978a925ee00347577b2fd0cf851c0d947 - languageName: node - linkType: hard - "@mui/icons-material@npm:^5.11.16": version: 5.14.9 resolution: "@mui/icons-material@npm:5.14.9" @@ -3558,7 +3535,7 @@ __metadata: languageName: node linkType: hard -"@mui/utils@npm:^5.1.0, @mui/utils@npm:^5.14.10, @mui/utils@npm:^5.14.3": +"@mui/utils@npm:^5.14.10, @mui/utils@npm:^5.14.3": version: 5.14.10 resolution: "@mui/utils@npm:5.14.10" dependencies: @@ -3655,7 +3632,7 @@ __metadata: languageName: node linkType: hard -"@popperjs/core@npm:^2.11.8, @popperjs/core@npm:^2.4.4": +"@popperjs/core@npm:^2.11.8": version: 2.11.8 resolution: "@popperjs/core@npm:2.11.8" checksum: e5c69fdebf52a4012f6a1f14817ca8e9599cb1be73dd1387e1785e2ed5e5f0862ff817f420a87c7fc532add1f88a12e25aeb010ffcbdc98eace3d55ce2139cf0 @@ -5282,13 +5259,6 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^1.1.1": - version: 1.2.1 - resolution: "clsx@npm:1.2.1" - checksum: 30befca8019b2eb7dbad38cff6266cf543091dae2825c856a62a8ccf2c3ab9c2907c4d12b288b73101196767f66812365400a227581484a05f968b0307cfaf12 - languageName: node - linkType: hard - "clsx@npm:^2.0.0": version: 2.0.0 resolution: "clsx@npm:2.0.0" @@ -10096,7 +10066,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.6.2, prop-types@npm:^15.7.0, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": +"prop-types@npm:^15.6.2, prop-types@npm:^15.7.0, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: