ide-assi 0.356.0 → 0.358.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.cjs.js +32 -31
- package/dist/bundle.esm.js +32 -31
- package/dist/components/ideDiff.js +50 -17
- package/package.json +1 -1
- package/src/components/ideDiff.js +50 -17
package/dist/bundle.cjs.js
CHANGED
|
@@ -234805,7 +234805,6 @@ const tobeDiffDecorations = StateField.define({
|
|
|
234805
234805
|
});
|
|
234806
234806
|
|
|
234807
234807
|
// 데코레이션 업데이트를 위한 Effect (트랜잭션에 포함시켜 상태 변경 알림)
|
|
234808
|
-
// StateEffect.define()을 사용하여 이펙트를 정의합니다.
|
|
234809
234808
|
const setAsisDecorationsEffect = StateEffect.define();
|
|
234810
234809
|
const setTobeDecorationsEffect = StateEffect.define();
|
|
234811
234810
|
|
|
@@ -234818,6 +234817,12 @@ class IdeDiff extends HTMLElement {
|
|
|
234818
234817
|
|
|
234819
234818
|
#languageCompartment = new Compartment();
|
|
234820
234819
|
|
|
234820
|
+
// ⭐️ 스크롤 동기화 활성/비활성화 플래그 및 핸들러 참조
|
|
234821
|
+
#isScrollSyncActive = false;
|
|
234822
|
+
_asisScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
234823
|
+
_tobeScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
234824
|
+
|
|
234825
|
+
|
|
234821
234826
|
constructor() {
|
|
234822
234827
|
super();
|
|
234823
234828
|
this.attachShadow({ mode: 'open' });
|
|
@@ -234936,33 +234941,16 @@ class IdeDiff extends HTMLElement {
|
|
|
234936
234941
|
parent: this.#tobeEditorEl
|
|
234937
234942
|
});
|
|
234938
234943
|
|
|
234944
|
+
// ⭐️ 초기 스크롤 동기화는 여기서 설정만 하고, initialize에서 활성화 제어
|
|
234939
234945
|
this.#setupScrollSync();
|
|
234940
234946
|
};
|
|
234941
234947
|
|
|
234948
|
+
// ⭐️ 스크롤 리스너를 제어할 수 있도록 플래그 및 클래스 메서드 사용
|
|
234942
234949
|
#setupScrollSync = () => {
|
|
234943
|
-
|
|
234944
|
-
let scrollingB = false;
|
|
234945
|
-
|
|
234946
|
-
this.#asisEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
234947
|
-
if (!scrollingB) {
|
|
234948
|
-
scrollingA = true;
|
|
234949
|
-
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
234950
|
-
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
234951
|
-
}
|
|
234952
|
-
scrollingB = false;
|
|
234953
|
-
});
|
|
234954
|
-
|
|
234955
|
-
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
234956
|
-
if (!scrollingA) {
|
|
234957
|
-
scrollingB = true;
|
|
234958
|
-
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
234959
|
-
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
234960
|
-
}
|
|
234961
|
-
scrollingA = false;
|
|
234962
|
-
});
|
|
234950
|
+
return;
|
|
234963
234951
|
};
|
|
234964
234952
|
|
|
234965
|
-
//
|
|
234953
|
+
// #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
|
|
234966
234954
|
#applyDiffDecorations = (asisSrc, tobeSrc) => {
|
|
234967
234955
|
const dmp = new diffMatchPatchExports.diff_match_patch();
|
|
234968
234956
|
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
@@ -234976,8 +234964,6 @@ class IdeDiff extends HTMLElement {
|
|
|
234976
234964
|
let asisCursor = 0;
|
|
234977
234965
|
let tobeCursor = 0;
|
|
234978
234966
|
|
|
234979
|
-
// ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
|
|
234980
|
-
// 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
|
|
234981
234967
|
const asisDoc = Text.of(asisSrc.split('\n'));
|
|
234982
234968
|
const tobeDoc = Text.of(tobeSrc.split('\n'));
|
|
234983
234969
|
|
|
@@ -234995,7 +234981,6 @@ class IdeDiff extends HTMLElement {
|
|
|
234995
234981
|
let currentTobeLineOffset = tobeCursor;
|
|
234996
234982
|
const tobeLines = text.split('\n');
|
|
234997
234983
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
234998
|
-
// ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
|
|
234999
234984
|
if (currentTobeLineOffset < tobeDoc.length) {
|
|
235000
234985
|
const line = tobeDoc.lineAt(currentTobeLineOffset);
|
|
235001
234986
|
tobeLineDecos.push({
|
|
@@ -235023,7 +235008,6 @@ class IdeDiff extends HTMLElement {
|
|
|
235023
235008
|
let currentAsisLineOffset = asisCursor;
|
|
235024
235009
|
const asisLines = text.split('\n');
|
|
235025
235010
|
for (let i = 0; i < asisLines.length; i++) {
|
|
235026
|
-
// ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
|
|
235027
235011
|
if (currentAsisLineOffset < asisDoc.length) {
|
|
235028
235012
|
const line = asisDoc.lineAt(currentAsisLineOffset);
|
|
235029
235013
|
asisLineDecos.push({
|
|
@@ -235083,13 +235067,15 @@ class IdeDiff extends HTMLElement {
|
|
|
235083
235067
|
};
|
|
235084
235068
|
};
|
|
235085
235069
|
|
|
235086
|
-
// ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
235087
235070
|
initialize = (src1, src2, language = 'javascript') => {
|
|
235088
235071
|
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
235089
235072
|
console.warn('CodeMirror Editors not initialized yet.');
|
|
235090
235073
|
return;
|
|
235091
235074
|
}
|
|
235092
235075
|
|
|
235076
|
+
// ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
|
|
235077
|
+
this.#isScrollSyncActive = false;
|
|
235078
|
+
|
|
235093
235079
|
let langExtension;
|
|
235094
235080
|
switch(language) {
|
|
235095
235081
|
case 'javascript':
|
|
@@ -235102,13 +235088,12 @@ class IdeDiff extends HTMLElement {
|
|
|
235102
235088
|
// 먼저 데코레이션 효과를 계산하여 가져옵니다.
|
|
235103
235089
|
const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
|
|
235104
235090
|
|
|
235105
|
-
console.log(asisEffect, tobeEffect);
|
|
235106
235091
|
// asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
235107
235092
|
this.#asisEditorView.dispatch({
|
|
235108
235093
|
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
|
|
235109
235094
|
effects: [
|
|
235110
235095
|
this.#languageCompartment.reconfigure(langExtension),
|
|
235111
|
-
asisEffect
|
|
235096
|
+
asisEffect
|
|
235112
235097
|
]
|
|
235113
235098
|
});
|
|
235114
235099
|
|
|
@@ -235116,13 +235101,29 @@ class IdeDiff extends HTMLElement {
|
|
|
235116
235101
|
this.#tobeEditorView.dispatch({
|
|
235117
235102
|
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
|
|
235118
235103
|
effects: [
|
|
235119
|
-
|
|
235120
|
-
|
|
235104
|
+
this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
|
|
235105
|
+
tobeEffect // <-- 이 주석을 풀어주세요!
|
|
235121
235106
|
]
|
|
235122
235107
|
});
|
|
235108
|
+
|
|
235109
|
+
// ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
|
|
235110
|
+
// requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
|
|
235111
|
+
requestAnimationFrame(() => {
|
|
235112
|
+
this.#isScrollSyncActive = true;
|
|
235113
|
+
// 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
|
|
235114
|
+
// this.#asisEditorView.scrollDOM.scrollTop = 0;
|
|
235115
|
+
// this.#tobeEditorView.scrollDOM.scrollTop = 0;
|
|
235116
|
+
});
|
|
235123
235117
|
};
|
|
235124
235118
|
|
|
235125
235119
|
disconnectedCallback() {
|
|
235120
|
+
// 컴포넌트 해제 시 스크롤 리스너 제거
|
|
235121
|
+
if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
|
|
235122
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
235123
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
235124
|
+
this._asisScrollHandler = null; // 참조 제거
|
|
235125
|
+
this._tobeScrollHandler = null; // 참조 제거
|
|
235126
|
+
}
|
|
235126
235127
|
if (this.#asisEditorView) {
|
|
235127
235128
|
this.#asisEditorView.destroy();
|
|
235128
235129
|
}
|
package/dist/bundle.esm.js
CHANGED
|
@@ -234801,7 +234801,6 @@ const tobeDiffDecorations = StateField.define({
|
|
|
234801
234801
|
});
|
|
234802
234802
|
|
|
234803
234803
|
// 데코레이션 업데이트를 위한 Effect (트랜잭션에 포함시켜 상태 변경 알림)
|
|
234804
|
-
// StateEffect.define()을 사용하여 이펙트를 정의합니다.
|
|
234805
234804
|
const setAsisDecorationsEffect = StateEffect.define();
|
|
234806
234805
|
const setTobeDecorationsEffect = StateEffect.define();
|
|
234807
234806
|
|
|
@@ -234814,6 +234813,12 @@ class IdeDiff extends HTMLElement {
|
|
|
234814
234813
|
|
|
234815
234814
|
#languageCompartment = new Compartment();
|
|
234816
234815
|
|
|
234816
|
+
// ⭐️ 스크롤 동기화 활성/비활성화 플래그 및 핸들러 참조
|
|
234817
|
+
#isScrollSyncActive = false;
|
|
234818
|
+
_asisScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
234819
|
+
_tobeScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
234820
|
+
|
|
234821
|
+
|
|
234817
234822
|
constructor() {
|
|
234818
234823
|
super();
|
|
234819
234824
|
this.attachShadow({ mode: 'open' });
|
|
@@ -234932,33 +234937,16 @@ class IdeDiff extends HTMLElement {
|
|
|
234932
234937
|
parent: this.#tobeEditorEl
|
|
234933
234938
|
});
|
|
234934
234939
|
|
|
234940
|
+
// ⭐️ 초기 스크롤 동기화는 여기서 설정만 하고, initialize에서 활성화 제어
|
|
234935
234941
|
this.#setupScrollSync();
|
|
234936
234942
|
};
|
|
234937
234943
|
|
|
234944
|
+
// ⭐️ 스크롤 리스너를 제어할 수 있도록 플래그 및 클래스 메서드 사용
|
|
234938
234945
|
#setupScrollSync = () => {
|
|
234939
|
-
|
|
234940
|
-
let scrollingB = false;
|
|
234941
|
-
|
|
234942
|
-
this.#asisEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
234943
|
-
if (!scrollingB) {
|
|
234944
|
-
scrollingA = true;
|
|
234945
|
-
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
234946
|
-
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
234947
|
-
}
|
|
234948
|
-
scrollingB = false;
|
|
234949
|
-
});
|
|
234950
|
-
|
|
234951
|
-
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
234952
|
-
if (!scrollingA) {
|
|
234953
|
-
scrollingB = true;
|
|
234954
|
-
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
234955
|
-
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
234956
|
-
}
|
|
234957
|
-
scrollingA = false;
|
|
234958
|
-
});
|
|
234946
|
+
return;
|
|
234959
234947
|
};
|
|
234960
234948
|
|
|
234961
|
-
//
|
|
234949
|
+
// #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
|
|
234962
234950
|
#applyDiffDecorations = (asisSrc, tobeSrc) => {
|
|
234963
234951
|
const dmp = new diffMatchPatchExports.diff_match_patch();
|
|
234964
234952
|
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
@@ -234972,8 +234960,6 @@ class IdeDiff extends HTMLElement {
|
|
|
234972
234960
|
let asisCursor = 0;
|
|
234973
234961
|
let tobeCursor = 0;
|
|
234974
234962
|
|
|
234975
|
-
// ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
|
|
234976
|
-
// 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
|
|
234977
234963
|
const asisDoc = Text.of(asisSrc.split('\n'));
|
|
234978
234964
|
const tobeDoc = Text.of(tobeSrc.split('\n'));
|
|
234979
234965
|
|
|
@@ -234991,7 +234977,6 @@ class IdeDiff extends HTMLElement {
|
|
|
234991
234977
|
let currentTobeLineOffset = tobeCursor;
|
|
234992
234978
|
const tobeLines = text.split('\n');
|
|
234993
234979
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
234994
|
-
// ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
|
|
234995
234980
|
if (currentTobeLineOffset < tobeDoc.length) {
|
|
234996
234981
|
const line = tobeDoc.lineAt(currentTobeLineOffset);
|
|
234997
234982
|
tobeLineDecos.push({
|
|
@@ -235019,7 +235004,6 @@ class IdeDiff extends HTMLElement {
|
|
|
235019
235004
|
let currentAsisLineOffset = asisCursor;
|
|
235020
235005
|
const asisLines = text.split('\n');
|
|
235021
235006
|
for (let i = 0; i < asisLines.length; i++) {
|
|
235022
|
-
// ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
|
|
235023
235007
|
if (currentAsisLineOffset < asisDoc.length) {
|
|
235024
235008
|
const line = asisDoc.lineAt(currentAsisLineOffset);
|
|
235025
235009
|
asisLineDecos.push({
|
|
@@ -235079,13 +235063,15 @@ class IdeDiff extends HTMLElement {
|
|
|
235079
235063
|
};
|
|
235080
235064
|
};
|
|
235081
235065
|
|
|
235082
|
-
// ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
235083
235066
|
initialize = (src1, src2, language = 'javascript') => {
|
|
235084
235067
|
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
235085
235068
|
console.warn('CodeMirror Editors not initialized yet.');
|
|
235086
235069
|
return;
|
|
235087
235070
|
}
|
|
235088
235071
|
|
|
235072
|
+
// ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
|
|
235073
|
+
this.#isScrollSyncActive = false;
|
|
235074
|
+
|
|
235089
235075
|
let langExtension;
|
|
235090
235076
|
switch(language) {
|
|
235091
235077
|
case 'javascript':
|
|
@@ -235098,13 +235084,12 @@ class IdeDiff extends HTMLElement {
|
|
|
235098
235084
|
// 먼저 데코레이션 효과를 계산하여 가져옵니다.
|
|
235099
235085
|
const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
|
|
235100
235086
|
|
|
235101
|
-
console.log(asisEffect, tobeEffect);
|
|
235102
235087
|
// asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
235103
235088
|
this.#asisEditorView.dispatch({
|
|
235104
235089
|
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
|
|
235105
235090
|
effects: [
|
|
235106
235091
|
this.#languageCompartment.reconfigure(langExtension),
|
|
235107
|
-
asisEffect
|
|
235092
|
+
asisEffect
|
|
235108
235093
|
]
|
|
235109
235094
|
});
|
|
235110
235095
|
|
|
@@ -235112,13 +235097,29 @@ class IdeDiff extends HTMLElement {
|
|
|
235112
235097
|
this.#tobeEditorView.dispatch({
|
|
235113
235098
|
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
|
|
235114
235099
|
effects: [
|
|
235115
|
-
|
|
235116
|
-
|
|
235100
|
+
this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
|
|
235101
|
+
tobeEffect // <-- 이 주석을 풀어주세요!
|
|
235117
235102
|
]
|
|
235118
235103
|
});
|
|
235104
|
+
|
|
235105
|
+
// ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
|
|
235106
|
+
// requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
|
|
235107
|
+
requestAnimationFrame(() => {
|
|
235108
|
+
this.#isScrollSyncActive = true;
|
|
235109
|
+
// 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
|
|
235110
|
+
// this.#asisEditorView.scrollDOM.scrollTop = 0;
|
|
235111
|
+
// this.#tobeEditorView.scrollDOM.scrollTop = 0;
|
|
235112
|
+
});
|
|
235119
235113
|
};
|
|
235120
235114
|
|
|
235121
235115
|
disconnectedCallback() {
|
|
235116
|
+
// 컴포넌트 해제 시 스크롤 리스너 제거
|
|
235117
|
+
if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
|
|
235118
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
235119
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
235120
|
+
this._asisScrollHandler = null; // 참조 제거
|
|
235121
|
+
this._tobeScrollHandler = null; // 참조 제거
|
|
235122
|
+
}
|
|
235122
235123
|
if (this.#asisEditorView) {
|
|
235123
235124
|
this.#asisEditorView.destroy();
|
|
235124
235125
|
}
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
import { history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
|
16
16
|
import { defaultKeymap, selectAll } from "@codemirror/commands";
|
|
17
17
|
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
|
|
18
|
-
// 올바른 import 구문: '=>' 대신 'from'
|
|
19
18
|
import { bracketMatching } from "@codemirror/language";
|
|
20
19
|
import { javascript } from "@codemirror/lang-javascript";
|
|
21
20
|
import { indentOnInput, syntaxHighlighting, defaultHighlightStyle } from "@codemirror/language";
|
|
@@ -23,7 +22,7 @@ import { lintKeymap } from "@codemirror/lint";
|
|
|
23
22
|
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
24
23
|
|
|
25
24
|
// Diff 로직을 위해 diff-match-patch 사용
|
|
26
|
-
import { diff_match_patch } from 'diff-match-patch';
|
|
25
|
+
import { diff_match_patch } from 'diff-match-patch';
|
|
27
26
|
|
|
28
27
|
// Diff 데코레이션을 위한 StateField 정의 (asis 에디터용)
|
|
29
28
|
const asisDiffDecorations = StateField.define({
|
|
@@ -54,7 +53,6 @@ const tobeDiffDecorations = StateField.define({
|
|
|
54
53
|
});
|
|
55
54
|
|
|
56
55
|
// 데코레이션 업데이트를 위한 Effect (트랜잭션에 포함시켜 상태 변경 알림)
|
|
57
|
-
// StateEffect.define()을 사용하여 이펙트를 정의합니다.
|
|
58
56
|
const setAsisDecorationsEffect = StateEffect.define();
|
|
59
57
|
const setTobeDecorationsEffect = StateEffect.define();
|
|
60
58
|
|
|
@@ -67,6 +65,12 @@ export class IdeDiff extends HTMLElement {
|
|
|
67
65
|
|
|
68
66
|
#languageCompartment = new Compartment();
|
|
69
67
|
|
|
68
|
+
// ⭐️ 스크롤 동기화 활성/비활성화 플래그 및 핸들러 참조
|
|
69
|
+
#isScrollSyncActive = false;
|
|
70
|
+
_asisScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
71
|
+
_tobeScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
72
|
+
|
|
73
|
+
|
|
70
74
|
constructor() {
|
|
71
75
|
super();
|
|
72
76
|
this.attachShadow({ mode: 'open' });
|
|
@@ -185,33 +189,49 @@ export class IdeDiff extends HTMLElement {
|
|
|
185
189
|
parent: this.#tobeEditorEl
|
|
186
190
|
});
|
|
187
191
|
|
|
192
|
+
// ⭐️ 초기 스크롤 동기화는 여기서 설정만 하고, initialize에서 활성화 제어
|
|
188
193
|
this.#setupScrollSync();
|
|
189
194
|
};
|
|
190
195
|
|
|
196
|
+
// ⭐️ 스크롤 리스너를 제어할 수 있도록 플래그 및 클래스 메서드 사용
|
|
191
197
|
#setupScrollSync = () => {
|
|
198
|
+
return;
|
|
199
|
+
// 기존 리스너가 있다면 제거 (중복 방지 및 disconnectedCallback에서 제거 용이)
|
|
200
|
+
if (this._asisScrollHandler) { // 핸들러가 이미 할당되어 있다면
|
|
201
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
202
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
203
|
+
}
|
|
204
|
+
|
|
192
205
|
let scrollingA = false;
|
|
193
206
|
let scrollingB = false;
|
|
194
207
|
|
|
195
|
-
this
|
|
208
|
+
this._asisScrollHandler = () => {
|
|
209
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
210
|
+
|
|
196
211
|
if (!scrollingB) {
|
|
197
212
|
scrollingA = true;
|
|
198
213
|
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
199
214
|
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
200
215
|
}
|
|
201
216
|
scrollingB = false;
|
|
202
|
-
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
this._tobeScrollHandler = () => {
|
|
220
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
203
221
|
|
|
204
|
-
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
205
222
|
if (!scrollingA) {
|
|
206
223
|
scrollingB = true;
|
|
207
224
|
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
208
225
|
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
209
226
|
}
|
|
210
227
|
scrollingA = false;
|
|
211
|
-
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
this.#asisEditorView.scrollDOM.addEventListener('scroll', this._asisScrollHandler);
|
|
231
|
+
this.#tobeEditorView.scrollDOM.addEventListener('scroll', this._tobeScrollHandler);
|
|
212
232
|
};
|
|
213
233
|
|
|
214
|
-
//
|
|
234
|
+
// #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
|
|
215
235
|
#applyDiffDecorations = (asisSrc, tobeSrc) => {
|
|
216
236
|
const dmp = new diff_match_patch();
|
|
217
237
|
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
@@ -225,8 +245,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
225
245
|
let asisCursor = 0;
|
|
226
246
|
let tobeCursor = 0;
|
|
227
247
|
|
|
228
|
-
// ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
|
|
229
|
-
// 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
|
|
230
248
|
const asisDoc = Text.of(asisSrc.split('\n'));
|
|
231
249
|
const tobeDoc = Text.of(tobeSrc.split('\n'));
|
|
232
250
|
|
|
@@ -244,7 +262,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
244
262
|
let currentTobeLineOffset = tobeCursor;
|
|
245
263
|
const tobeLines = text.split('\n');
|
|
246
264
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
247
|
-
// ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
|
|
248
265
|
if (currentTobeLineOffset < tobeDoc.length) {
|
|
249
266
|
const line = tobeDoc.lineAt(currentTobeLineOffset);
|
|
250
267
|
tobeLineDecos.push({
|
|
@@ -272,7 +289,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
272
289
|
let currentAsisLineOffset = asisCursor;
|
|
273
290
|
const asisLines = text.split('\n');
|
|
274
291
|
for (let i = 0; i < asisLines.length; i++) {
|
|
275
|
-
// ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
|
|
276
292
|
if (currentAsisLineOffset < asisDoc.length) {
|
|
277
293
|
const line = asisDoc.lineAt(currentAsisLineOffset);
|
|
278
294
|
asisLineDecos.push({
|
|
@@ -332,13 +348,15 @@ export class IdeDiff extends HTMLElement {
|
|
|
332
348
|
};
|
|
333
349
|
};
|
|
334
350
|
|
|
335
|
-
// ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
336
351
|
initialize = (src1, src2, language = 'javascript') => {
|
|
337
352
|
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
338
353
|
console.warn('CodeMirror Editors not initialized yet.');
|
|
339
354
|
return;
|
|
340
355
|
}
|
|
341
356
|
|
|
357
|
+
// ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
|
|
358
|
+
this.#isScrollSyncActive = false;
|
|
359
|
+
|
|
342
360
|
let langExtension;
|
|
343
361
|
switch(language) {
|
|
344
362
|
case 'javascript':
|
|
@@ -351,13 +369,12 @@ export class IdeDiff extends HTMLElement {
|
|
|
351
369
|
// 먼저 데코레이션 효과를 계산하여 가져옵니다.
|
|
352
370
|
const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
|
|
353
371
|
|
|
354
|
-
console.log(asisEffect, tobeEffect);
|
|
355
372
|
// asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
356
373
|
this.#asisEditorView.dispatch({
|
|
357
374
|
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
|
|
358
375
|
effects: [
|
|
359
376
|
this.#languageCompartment.reconfigure(langExtension),
|
|
360
|
-
asisEffect
|
|
377
|
+
asisEffect
|
|
361
378
|
]
|
|
362
379
|
});
|
|
363
380
|
|
|
@@ -365,13 +382,29 @@ export class IdeDiff extends HTMLElement {
|
|
|
365
382
|
this.#tobeEditorView.dispatch({
|
|
366
383
|
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
|
|
367
384
|
effects: [
|
|
368
|
-
|
|
369
|
-
|
|
385
|
+
this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
|
|
386
|
+
tobeEffect // <-- 이 주석을 풀어주세요!
|
|
370
387
|
]
|
|
371
388
|
});
|
|
389
|
+
|
|
390
|
+
// ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
|
|
391
|
+
// requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
|
|
392
|
+
requestAnimationFrame(() => {
|
|
393
|
+
this.#isScrollSyncActive = true;
|
|
394
|
+
// 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
|
|
395
|
+
// this.#asisEditorView.scrollDOM.scrollTop = 0;
|
|
396
|
+
// this.#tobeEditorView.scrollDOM.scrollTop = 0;
|
|
397
|
+
});
|
|
372
398
|
};
|
|
373
399
|
|
|
374
400
|
disconnectedCallback() {
|
|
401
|
+
// 컴포넌트 해제 시 스크롤 리스너 제거
|
|
402
|
+
if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
|
|
403
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
404
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
405
|
+
this._asisScrollHandler = null; // 참조 제거
|
|
406
|
+
this._tobeScrollHandler = null; // 참조 제거
|
|
407
|
+
}
|
|
375
408
|
if (this.#asisEditorView) {
|
|
376
409
|
this.#asisEditorView.destroy();
|
|
377
410
|
}
|
package/package.json
CHANGED
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
import { history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
|
16
16
|
import { defaultKeymap, selectAll } from "@codemirror/commands";
|
|
17
17
|
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
|
|
18
|
-
// 올바른 import 구문: '=>' 대신 'from'
|
|
19
18
|
import { bracketMatching } from "@codemirror/language";
|
|
20
19
|
import { javascript } from "@codemirror/lang-javascript";
|
|
21
20
|
import { indentOnInput, syntaxHighlighting, defaultHighlightStyle } from "@codemirror/language";
|
|
@@ -23,7 +22,7 @@ import { lintKeymap } from "@codemirror/lint";
|
|
|
23
22
|
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
24
23
|
|
|
25
24
|
// Diff 로직을 위해 diff-match-patch 사용
|
|
26
|
-
import { diff_match_patch } from 'diff-match-patch';
|
|
25
|
+
import { diff_match_patch } from 'diff-match-patch';
|
|
27
26
|
|
|
28
27
|
// Diff 데코레이션을 위한 StateField 정의 (asis 에디터용)
|
|
29
28
|
const asisDiffDecorations = StateField.define({
|
|
@@ -54,7 +53,6 @@ const tobeDiffDecorations = StateField.define({
|
|
|
54
53
|
});
|
|
55
54
|
|
|
56
55
|
// 데코레이션 업데이트를 위한 Effect (트랜잭션에 포함시켜 상태 변경 알림)
|
|
57
|
-
// StateEffect.define()을 사용하여 이펙트를 정의합니다.
|
|
58
56
|
const setAsisDecorationsEffect = StateEffect.define();
|
|
59
57
|
const setTobeDecorationsEffect = StateEffect.define();
|
|
60
58
|
|
|
@@ -67,6 +65,12 @@ export class IdeDiff extends HTMLElement {
|
|
|
67
65
|
|
|
68
66
|
#languageCompartment = new Compartment();
|
|
69
67
|
|
|
68
|
+
// ⭐️ 스크롤 동기화 활성/비활성화 플래그 및 핸들러 참조
|
|
69
|
+
#isScrollSyncActive = false;
|
|
70
|
+
_asisScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
71
|
+
_tobeScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
72
|
+
|
|
73
|
+
|
|
70
74
|
constructor() {
|
|
71
75
|
super();
|
|
72
76
|
this.attachShadow({ mode: 'open' });
|
|
@@ -185,33 +189,49 @@ export class IdeDiff extends HTMLElement {
|
|
|
185
189
|
parent: this.#tobeEditorEl
|
|
186
190
|
});
|
|
187
191
|
|
|
192
|
+
// ⭐️ 초기 스크롤 동기화는 여기서 설정만 하고, initialize에서 활성화 제어
|
|
188
193
|
this.#setupScrollSync();
|
|
189
194
|
};
|
|
190
195
|
|
|
196
|
+
// ⭐️ 스크롤 리스너를 제어할 수 있도록 플래그 및 클래스 메서드 사용
|
|
191
197
|
#setupScrollSync = () => {
|
|
198
|
+
return;
|
|
199
|
+
// 기존 리스너가 있다면 제거 (중복 방지 및 disconnectedCallback에서 제거 용이)
|
|
200
|
+
if (this._asisScrollHandler) { // 핸들러가 이미 할당되어 있다면
|
|
201
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
202
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
203
|
+
}
|
|
204
|
+
|
|
192
205
|
let scrollingA = false;
|
|
193
206
|
let scrollingB = false;
|
|
194
207
|
|
|
195
|
-
this
|
|
208
|
+
this._asisScrollHandler = () => {
|
|
209
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
210
|
+
|
|
196
211
|
if (!scrollingB) {
|
|
197
212
|
scrollingA = true;
|
|
198
213
|
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
199
214
|
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
200
215
|
}
|
|
201
216
|
scrollingB = false;
|
|
202
|
-
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
this._tobeScrollHandler = () => {
|
|
220
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
203
221
|
|
|
204
|
-
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
205
222
|
if (!scrollingA) {
|
|
206
223
|
scrollingB = true;
|
|
207
224
|
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
208
225
|
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
209
226
|
}
|
|
210
227
|
scrollingA = false;
|
|
211
|
-
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
this.#asisEditorView.scrollDOM.addEventListener('scroll', this._asisScrollHandler);
|
|
231
|
+
this.#tobeEditorView.scrollDOM.addEventListener('scroll', this._tobeScrollHandler);
|
|
212
232
|
};
|
|
213
233
|
|
|
214
|
-
//
|
|
234
|
+
// #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
|
|
215
235
|
#applyDiffDecorations = (asisSrc, tobeSrc) => {
|
|
216
236
|
const dmp = new diff_match_patch();
|
|
217
237
|
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
@@ -225,8 +245,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
225
245
|
let asisCursor = 0;
|
|
226
246
|
let tobeCursor = 0;
|
|
227
247
|
|
|
228
|
-
// ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
|
|
229
|
-
// 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
|
|
230
248
|
const asisDoc = Text.of(asisSrc.split('\n'));
|
|
231
249
|
const tobeDoc = Text.of(tobeSrc.split('\n'));
|
|
232
250
|
|
|
@@ -244,7 +262,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
244
262
|
let currentTobeLineOffset = tobeCursor;
|
|
245
263
|
const tobeLines = text.split('\n');
|
|
246
264
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
247
|
-
// ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
|
|
248
265
|
if (currentTobeLineOffset < tobeDoc.length) {
|
|
249
266
|
const line = tobeDoc.lineAt(currentTobeLineOffset);
|
|
250
267
|
tobeLineDecos.push({
|
|
@@ -272,7 +289,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
272
289
|
let currentAsisLineOffset = asisCursor;
|
|
273
290
|
const asisLines = text.split('\n');
|
|
274
291
|
for (let i = 0; i < asisLines.length; i++) {
|
|
275
|
-
// ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
|
|
276
292
|
if (currentAsisLineOffset < asisDoc.length) {
|
|
277
293
|
const line = asisDoc.lineAt(currentAsisLineOffset);
|
|
278
294
|
asisLineDecos.push({
|
|
@@ -332,13 +348,15 @@ export class IdeDiff extends HTMLElement {
|
|
|
332
348
|
};
|
|
333
349
|
};
|
|
334
350
|
|
|
335
|
-
// ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
336
351
|
initialize = (src1, src2, language = 'javascript') => {
|
|
337
352
|
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
338
353
|
console.warn('CodeMirror Editors not initialized yet.');
|
|
339
354
|
return;
|
|
340
355
|
}
|
|
341
356
|
|
|
357
|
+
// ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
|
|
358
|
+
this.#isScrollSyncActive = false;
|
|
359
|
+
|
|
342
360
|
let langExtension;
|
|
343
361
|
switch(language) {
|
|
344
362
|
case 'javascript':
|
|
@@ -351,13 +369,12 @@ export class IdeDiff extends HTMLElement {
|
|
|
351
369
|
// 먼저 데코레이션 효과를 계산하여 가져옵니다.
|
|
352
370
|
const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
|
|
353
371
|
|
|
354
|
-
console.log(asisEffect, tobeEffect);
|
|
355
372
|
// asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
356
373
|
this.#asisEditorView.dispatch({
|
|
357
374
|
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
|
|
358
375
|
effects: [
|
|
359
376
|
this.#languageCompartment.reconfigure(langExtension),
|
|
360
|
-
asisEffect
|
|
377
|
+
asisEffect
|
|
361
378
|
]
|
|
362
379
|
});
|
|
363
380
|
|
|
@@ -365,13 +382,29 @@ export class IdeDiff extends HTMLElement {
|
|
|
365
382
|
this.#tobeEditorView.dispatch({
|
|
366
383
|
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
|
|
367
384
|
effects: [
|
|
368
|
-
|
|
369
|
-
|
|
385
|
+
this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
|
|
386
|
+
tobeEffect // <-- 이 주석을 풀어주세요!
|
|
370
387
|
]
|
|
371
388
|
});
|
|
389
|
+
|
|
390
|
+
// ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
|
|
391
|
+
// requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
|
|
392
|
+
requestAnimationFrame(() => {
|
|
393
|
+
this.#isScrollSyncActive = true;
|
|
394
|
+
// 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
|
|
395
|
+
// this.#asisEditorView.scrollDOM.scrollTop = 0;
|
|
396
|
+
// this.#tobeEditorView.scrollDOM.scrollTop = 0;
|
|
397
|
+
});
|
|
372
398
|
};
|
|
373
399
|
|
|
374
400
|
disconnectedCallback() {
|
|
401
|
+
// 컴포넌트 해제 시 스크롤 리스너 제거
|
|
402
|
+
if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
|
|
403
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
404
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
405
|
+
this._asisScrollHandler = null; // 참조 제거
|
|
406
|
+
this._tobeScrollHandler = null; // 참조 제거
|
|
407
|
+
}
|
|
375
408
|
if (this.#asisEditorView) {
|
|
376
409
|
this.#asisEditorView.destroy();
|
|
377
410
|
}
|