From fa70ae05c858e7e9c5994a7b616457959133bd09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E4=B8=B0?= Date: Mon, 9 Dec 2024 14:30:15 +0800 Subject: [PATCH 1/4] fix: complete display time --- .../semi-json-viewer-core/src/view/complete/completeWidget.ts | 1 + packages/semi-json-viewer-core/src/view/view.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/semi-json-viewer-core/src/view/complete/completeWidget.ts b/packages/semi-json-viewer-core/src/view/complete/completeWidget.ts index 61f9aa94f6..8306413c1d 100644 --- a/packages/semi-json-viewer-core/src/view/complete/completeWidget.ts +++ b/packages/semi-json-viewer-core/src/view/complete/completeWidget.ts @@ -199,6 +199,7 @@ export class CompleteWidget { } public hide() { + if (!this.isVisible) return; this.isVisible = false; this._container.style.display = 'none'; this._suggestions = []; diff --git a/packages/semi-json-viewer-core/src/view/view.ts b/packages/semi-json-viewer-core/src/view/view.ts index 420bcd7def..4eb926d0cd 100644 --- a/packages/semi-json-viewer-core/src/view/view.ts +++ b/packages/semi-json-viewer-core/src/view/view.ts @@ -142,6 +142,7 @@ export class View { this._contentDom.addEventListener('click', e => { e.preventDefault(); + this._completeWidget.hide(); this._selectionModel.isSelectedAll = false; this._selectionModel.updateFromSelection(); }); From ea4344b944ec814911c558e9b7140e012120f04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E4=B8=B0?= Date: Mon, 9 Dec 2024 15:52:34 +0800 Subject: [PATCH 2/4] fix: support CN IME --- .../src/model/selectionModel.ts | 22 ++ .../src/view/edit/editWidget.ts | 206 ++++++++---------- 2 files changed, 107 insertions(+), 121 deletions(-) diff --git a/packages/semi-json-viewer-core/src/model/selectionModel.ts b/packages/semi-json-viewer-core/src/model/selectionModel.ts index c659c3869f..d25d1ce8c9 100644 --- a/packages/semi-json-viewer-core/src/model/selectionModel.ts +++ b/packages/semi-json-viewer-core/src/model/selectionModel.ts @@ -13,6 +13,10 @@ export class SelectionModel { public startCol: number; public endRow: number; public endCol: number; + public preStartRow: number; + public preStartCol: number; + public preEndRow: number; + public preEndCol: number; public isCollapsed: boolean; public isSelectedAll: boolean = false; private _view: View; @@ -148,4 +152,22 @@ export class SelectionModel { row = (lineElement as any).lineNumber || 1; return { row, col: col + 1 }; } + + public savePreviousSelection() { + this.preStartRow = this.startRow; + this.preStartCol = this.startCol; + this.preEndRow = this.endRow; + this.preEndCol = this.endCol; + } + + public restorePreviousSelection() { + this.startRow = this.preStartRow; + this.startCol = this.preStartCol; + this.endRow = this.preEndRow; + this.endCol = this.preEndCol; + this._jsonModel.lastChangeBufferPos = { + lineNumber: this.startRow, + column: this.startCol, + }; + } } diff --git a/packages/semi-json-viewer-core/src/view/edit/editWidget.ts b/packages/semi-json-viewer-core/src/view/edit/editWidget.ts index ddbbddc4a4..a11c0518b1 100644 --- a/packages/semi-json-viewer-core/src/view/edit/editWidget.ts +++ b/packages/semi-json-viewer-core/src/view/edit/editWidget.ts @@ -17,6 +17,7 @@ export class EditWidget { private _selectionModel: SelectionModel; private _jsonModel: JSONModel; private _foldingModel: FoldingModel; + private _isComposition: boolean = false; private _autoClosingPairs: Record = { '{': '}', '[': ']', @@ -51,14 +52,20 @@ export class EditWidget { this._handleBeforeInput(e); }); + this._view.contentDom.addEventListener('compositionstart', (e: CompositionEvent) => { + this._handleCompositionStart(e); + }); + + this._view.contentDom.addEventListener('compositionend', (e: CompositionEvent) => { + this._handleCompositionEnd(e); + }); + this._view.contentDom.addEventListener('keydown', (e: KeyboardEvent) => { this._handleKeyDown(e); }); } - private _handleBeforeInput(e: InputEvent) { - e.preventDefault(); - this._selectionModel.updateFromSelection(); + private buildBaseOperation(type: IModelContentChangeEvent['type'] = 'insert') { const startRow = this._selectionModel.startRow; const startCol = this._selectionModel.startCol; const endRow = this._selectionModel.endRow; @@ -66,7 +73,7 @@ export class EditWidget { const startOffset = this._jsonModel.getOffsetAt(startRow, startCol); const endOffset = this._jsonModel.getOffsetAt(endRow, endCol); const op: IModelContentChangeEvent = { - type: 'insert', + type, range: { startLineNumber: startRow, startColumn: startCol, @@ -75,9 +82,50 @@ export class EditWidget { }, rangeOffset: startOffset, rangeLength: endOffset - startOffset, - oldText: '', + oldText: this._jsonModel.getValueInRange({ + startLineNumber: startRow, + startColumn: startCol, + endLineNumber: endRow, + endColumn: endCol, + } as Range), newText: '', }; + if (this._selectionModel.isSelectedAll) { + op.range = { + startLineNumber: 1, + startColumn: 1, + endLineNumber: this._jsonModel.getLineCount(), + endColumn: this._jsonModel.getLineLength(this._jsonModel.getLineCount()) + 1, + }; + op.rangeOffset = 0; + op.rangeLength = this._jsonModel.getValue().length; + op.oldText = this._jsonModel.getValue(); + } + return op; + } + + private _handleCompositionStart(e: CompositionEvent) { + e.preventDefault(); + this._isComposition = true; + this._selectionModel.savePreviousSelection(); + } + + private _handleCompositionEnd(e: CompositionEvent) { + e.preventDefault(); + this._isComposition = false; + this._selectionModel.restorePreviousSelection(); + const op = this.buildBaseOperation('replace'); + op.newText = e.data || ''; + this._selectionModel.isSelectedAll = false; + this._jsonModel.applyOperation(op); + } + + private _handleBeforeInput(e: InputEvent) { + if (this._isComposition) return; + e.preventDefault(); + this._selectionModel.updateFromSelection(); + const op = this.buildBaseOperation(); + const { startLineNumber, startColumn, endLineNumber, endColumn } = op.range; switch (e.inputType) { case 'insertText': @@ -87,37 +135,31 @@ export class EditWidget { op.type = 'replace'; } op.newText = e.data || ''; - op.oldText = this._jsonModel.getValueInRange({ - startLineNumber: startRow, - startColumn: startCol, - endLineNumber: endRow, - endColumn: endCol, - } as Range); if (this._autoClosingPairs[op.newText]) { op.newText += this._autoClosingPairs[op.newText]; op.keepPosition = { - lineNumber: startRow, - column: endCol + 1, + lineNumber: startLineNumber, + column: startColumn + 1, }; } break; case 'insertParagraph': op.newText = '\n'; op.keepPosition = { - lineNumber: startRow + 1, + lineNumber: startLineNumber + 1, column: 1, }; const enterAction = processJsonEnterAction(this._jsonModel, { - startLineNumber: startRow, - startColumn: startCol, - endLineNumber: endRow, - endColumn: endCol, + startLineNumber: startLineNumber, + startColumn: startColumn, + endLineNumber: endLineNumber, + endColumn: endColumn, } as Range); if (enterAction) { if (enterAction.indentAction === IndentAction.Indent) { op.newText = '\n' + this.normalizeIndentation(enterAction.appendText + enterAction.indentation) || ''; op.keepPosition = { - lineNumber: startRow + 1, + lineNumber: startLineNumber + 1, column: enterAction.appendText.length + enterAction.indentation.length + 1, }; } else { @@ -125,65 +167,39 @@ export class EditWidget { const increasedIndent = this.normalizeIndentation(enterAction.indentation + enterAction.appendText); op.newText = '\n' + increasedIndent + '\n' + normalIndent; op.keepPosition = { - lineNumber: startRow + 1, + lineNumber: startLineNumber + 1, column: increasedIndent.length + 1, }; } } else { - const lineText = this._jsonModel.getLineContent(startRow); - const indentation = getLeadingWhitespace(lineText).substring(0, startCol - 1); + const lineText = this._jsonModel.getLineContent(startLineNumber); + const indentation = getLeadingWhitespace(lineText).substring(0, startColumn - 1); op.newText = '\n' + this.normalizeIndentation(indentation) || ''; op.keepPosition = { - lineNumber: startRow + 1, + lineNumber: startLineNumber + 1, column: indentation.length + 1, }; } break; case 'deleteContentBackward': - let oldText = ''; if (this._selectionModel.isCollapsed) { - op.rangeOffset = startOffset - 1; - oldText = this._jsonModel.getValueInRange({ - startLineNumber: startRow, - startColumn: startCol - 1, - endLineNumber: endRow, - endColumn: endCol, - } as Range); - } else { - oldText = this._jsonModel.getValueInRange({ - startLineNumber: startRow, - startColumn: startCol, - endLineNumber: endRow, - endColumn: endCol, + op.rangeOffset -= 1; + op.oldText = this._jsonModel.getValueInRange({ + startLineNumber: startLineNumber, + startColumn: startColumn - 1, + endLineNumber: endLineNumber, + endColumn: endColumn, } as Range); } - op.oldText = oldText; op.type = 'delete'; - op.rangeLength = oldText.length; + op.rangeLength = op.oldText.length; break; case 'insertFromPaste': const pasteData = e.dataTransfer?.getData('text/plain'); op.type = 'replace'; op.newText = pasteData || ''; - op.oldText = this._jsonModel.getValueInRange({ - startLineNumber: startRow, - startColumn: startCol, - endLineNumber: endRow, - endColumn: endCol, - } as Range); break; } - if (this._selectionModel.isSelectedAll) { - op.range = { - startLineNumber: 1, - startColumn: 1, - endLineNumber: this._jsonModel.getLineCount(), - endColumn: this._jsonModel.getLineLength(this._jsonModel.getLineCount()), - }; - op.rangeOffset = 0; - op.rangeLength = this._jsonModel.getValue().length; - op.oldText = this._jsonModel.getValue(); - } this._selectionModel.isSelectedAll = false; this._jsonModel.applyOperation(op); @@ -267,6 +283,7 @@ export class EditWidget { const endCol = this._selectionModel.endCol; const startOffset = this._jsonModel.getOffsetAt(startRow, startCol); const endOffset = this._jsonModel.getOffsetAt(endRow, endCol); + const op = this.buildBaseOperation(); switch (e.key) { case 'Tab': if (this._view.completeWidget.isVisible) { @@ -285,19 +302,7 @@ export class EditWidget { } else { insertText = '\t'; } - const op: IModelContentChangeEvent = { - type: 'insert', - range: { - startLineNumber: startRow, - startColumn: startCol, - endLineNumber: endRow, - endColumn: endCol, - }, - rangeOffset: startOffset, - rangeLength: endOffset - startOffset, - oldText: '', - newText: insertText, - }; + op.newText = insertText; this._jsonModel.applyOperation(op); break; case 'f': @@ -349,63 +354,22 @@ export class EditWidget { } private _cutHandler() { - const startRow = this._selectionModel.startRow; - const startCol = this._selectionModel.startCol; - const endRow = this._selectionModel.endRow; - const endCol = this._selectionModel.endCol; - let startOffset; - let oldText = ''; - const op: IModelContentChangeEvent = { - type: 'replace', - range: { - startLineNumber: startRow, - startColumn: startCol, - endLineNumber: endRow, - endColumn: endCol, - }, - rangeOffset: 0, - rangeLength: 0, - oldText: '', - newText: '', - }; - if (!this._selectionModel.isCollapsed) { - oldText = this._jsonModel.getValueInRange({ - startLineNumber: startRow, - startColumn: startCol, - endLineNumber: endRow, - endColumn: endCol, - } as Range); - startOffset = this._jsonModel.getOffsetAt(startRow, startCol); - } else { - oldText = this._jsonModel.getValueInRange({ - startLineNumber: startRow, + const op = this.buildBaseOperation('replace'); + if (this._selectionModel.isCollapsed) { + const { startLineNumber, endLineNumber } = op.range; + op.rangeOffset = this._jsonModel.getOffsetAt(startLineNumber, 1); + op.oldText = this._jsonModel.getValueInRange({ + startLineNumber, startColumn: 1, - endLineNumber: endRow, - endColumn: this._jsonModel.getLineLength(endRow) + 1, + endLineNumber, + endColumn: this._jsonModel.getLineLength(endLineNumber) + 1, } as Range); op.range = { - startLineNumber: startRow, - startColumn: 1, - endLineNumber: endRow, - endColumn: this._jsonModel.getLineLength(endRow) + 1, - }; - startOffset = this._jsonModel.getOffsetAt(startRow, 1); - } - - op.oldText = oldText; - op.rangeOffset = startOffset; - op.rangeLength = oldText.length; - - if (this._selectionModel.isSelectedAll) { - op.range = { - startLineNumber: 1, + startLineNumber, startColumn: 1, - endLineNumber: this._jsonModel.getLineCount(), - endColumn: this._jsonModel.getLineLength(this._jsonModel.getLineCount()) + 1, + endLineNumber, + endColumn: this._jsonModel.getLineLength(endLineNumber) + 1, }; - op.rangeOffset = 0; - op.rangeLength = this._jsonModel.getValue().length; - op.oldText = this._jsonModel.getValue(); } navigator.clipboard.writeText(op.oldText); this._jsonModel.applyOperation(op); From 126a06a273f8b7367ca787c9824cc765108dfa19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E4=B8=B0?= Date: Mon, 9 Dec 2024 15:55:58 +0800 Subject: [PATCH 3/4] fix: render bug caused enter --- .../semi-json-viewer-core/src/view/view.ts | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/packages/semi-json-viewer-core/src/view/view.ts b/packages/semi-json-viewer-core/src/view/view.ts index 4eb926d0cd..3e6c8bf33a 100644 --- a/packages/semi-json-viewer-core/src/view/view.ts +++ b/packages/semi-json-viewer-core/src/view/view.ts @@ -149,20 +149,23 @@ export class View { this.emitter.on('contentChanged', () => { this.resetScalingManagerConfigAndCell(0); + if ( + this._jsonModel.lastChangeBufferPos.lineNumber >= + this.visibleLineCount + this.startLineNumber + ) { + this.scrollToLine( + this._jsonModel.lastChangeBufferPos.lineNumber - + this.visibleLineCount + + 1 + ); + return; + } this.layout(); }); } public getLineElement(lineNumber: number): HTMLElement | null { - const visibleLineNumber = this._foldingModel.getVisibleLineNumber(lineNumber); - if ( - visibleLineNumber > this.visibleLineCount + this.startLineNumber || - visibleLineNumber < this.startLineNumber - ) - return null; - return this._scrollDom.children[ - visibleLineNumber - this._foldingModel.getVisibleLineNumber(this.startLineNumber) - ] as HTMLElement; + return this.scrollDom.querySelector(`[data-line-number="${lineNumber}"]`); } public updateVisibleRange(start: number, end: number) { @@ -385,20 +388,12 @@ export class View { } private renderLine(actualLineNumber: number, visibleLineNumber: number) { - // const cache = this._domCache.get(actualLineNumber); - // if (cache) { - // return; - // } const line = this._jsonModel.getLineContent(actualLineNumber); const tokens = this._tokenizationJsonModelPart.getLineTokens(actualLineNumber); const lineNumberElement = this.renderLineNumber(actualLineNumber, visibleLineNumber); const lineElement = this.renderLineContent(actualLineNumber, visibleLineNumber, tokens, line); - // this._domCache.set(actualLineNumber, { - // lineElement, - // lineNumberElement - // }); } @@ -413,7 +408,6 @@ export class View { const lineElement = this.createLineContentElement(lineContent, actualLineNumber, visibleLineNumber); this._scrollDom.appendChild(lineElement); - // this._options?.autoWrap && this._measureAndUpdateItemHeight(lineElement, visibleLineNumber); return lineElement; } From f956287994d310fcb76ee8706d9f146496313034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E4=B8=B0?= Date: Tue, 10 Dec 2024 17:44:52 +0800 Subject: [PATCH 4/4] fix: prevent multiple renders of foldIcon --- packages/semi-json-viewer-core/src/view/fold/foldWidget.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/semi-json-viewer-core/src/view/fold/foldWidget.ts b/packages/semi-json-viewer-core/src/view/fold/foldWidget.ts index faee0caca4..9d3523ffc9 100644 --- a/packages/semi-json-viewer-core/src/view/fold/foldWidget.ts +++ b/packages/semi-json-viewer-core/src/view/fold/foldWidget.ts @@ -8,6 +8,7 @@ import { FoldingModel } from '../../model/foldingModel'; export class FoldWidget { private _view: View; private _foldingModel: FoldingModel; + private _isMouseOver: boolean = false; constructor(view: View, foldingModel: FoldingModel) { this._view = view; @@ -26,13 +27,16 @@ export class FoldWidget { private _handleLineNumberHover(e: MouseEvent) { this._showFoldingIcon(); + this._isMouseOver = true; } private _handleLineNumberContainerLeave() { this.removeAllFoldingIcons(); + this._isMouseOver = false; } private _showFoldingIcon() { + if (this._isMouseOver) return; const lineNumberElement = this._view.lineScrollDom.children; for (let i = 0; i < lineNumberElement.length; i++) { const element: HTMLElement = lineNumberElement[i] as HTMLElement; @@ -86,6 +90,7 @@ export class FoldWidget { this._foldingModel.toggleFoldingRange(lineNumber); this._view.scalingCellSizeAndPositionManager.resetCell(0); this._view.layout(); + this._isMouseOver = false; }); return foldingIcon;