ide-assi 0.356.0 → 0.357.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 +48 -15
- package/dist/bundle.esm.js +48 -15
- package/dist/components/ideDiff.js +49 -17
- package/package.json +1 -1
- package/src/components/ideDiff.js +49 -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,48 @@ 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 = () => {
|
|
234950
|
+
// 기존 리스너가 있다면 제거 (중복 방지 및 disconnectedCallback에서 제거 용이)
|
|
234951
|
+
if (this._asisScrollHandler) { // 핸들러가 이미 할당되어 있다면
|
|
234952
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
234953
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
234954
|
+
}
|
|
234955
|
+
|
|
234943
234956
|
let scrollingA = false;
|
|
234944
234957
|
let scrollingB = false;
|
|
234945
234958
|
|
|
234946
|
-
this
|
|
234959
|
+
this._asisScrollHandler = () => {
|
|
234960
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
234961
|
+
|
|
234947
234962
|
if (!scrollingB) {
|
|
234948
234963
|
scrollingA = true;
|
|
234949
234964
|
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
234950
234965
|
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
234951
234966
|
}
|
|
234952
234967
|
scrollingB = false;
|
|
234953
|
-
}
|
|
234968
|
+
};
|
|
234969
|
+
|
|
234970
|
+
this._tobeScrollHandler = () => {
|
|
234971
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
234954
234972
|
|
|
234955
|
-
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
234956
234973
|
if (!scrollingA) {
|
|
234957
234974
|
scrollingB = true;
|
|
234958
234975
|
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
234959
234976
|
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
234960
234977
|
}
|
|
234961
234978
|
scrollingA = false;
|
|
234962
|
-
}
|
|
234979
|
+
};
|
|
234980
|
+
|
|
234981
|
+
this.#asisEditorView.scrollDOM.addEventListener('scroll', this._asisScrollHandler);
|
|
234982
|
+
this.#tobeEditorView.scrollDOM.addEventListener('scroll', this._tobeScrollHandler);
|
|
234963
234983
|
};
|
|
234964
234984
|
|
|
234965
|
-
//
|
|
234985
|
+
// #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
|
|
234966
234986
|
#applyDiffDecorations = (asisSrc, tobeSrc) => {
|
|
234967
234987
|
const dmp = new diffMatchPatchExports.diff_match_patch();
|
|
234968
234988
|
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
@@ -234976,8 +234996,6 @@ class IdeDiff extends HTMLElement {
|
|
|
234976
234996
|
let asisCursor = 0;
|
|
234977
234997
|
let tobeCursor = 0;
|
|
234978
234998
|
|
|
234979
|
-
// ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
|
|
234980
|
-
// 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
|
|
234981
234999
|
const asisDoc = Text.of(asisSrc.split('\n'));
|
|
234982
235000
|
const tobeDoc = Text.of(tobeSrc.split('\n'));
|
|
234983
235001
|
|
|
@@ -234995,7 +235013,6 @@ class IdeDiff extends HTMLElement {
|
|
|
234995
235013
|
let currentTobeLineOffset = tobeCursor;
|
|
234996
235014
|
const tobeLines = text.split('\n');
|
|
234997
235015
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
234998
|
-
// ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
|
|
234999
235016
|
if (currentTobeLineOffset < tobeDoc.length) {
|
|
235000
235017
|
const line = tobeDoc.lineAt(currentTobeLineOffset);
|
|
235001
235018
|
tobeLineDecos.push({
|
|
@@ -235023,7 +235040,6 @@ class IdeDiff extends HTMLElement {
|
|
|
235023
235040
|
let currentAsisLineOffset = asisCursor;
|
|
235024
235041
|
const asisLines = text.split('\n');
|
|
235025
235042
|
for (let i = 0; i < asisLines.length; i++) {
|
|
235026
|
-
// ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
|
|
235027
235043
|
if (currentAsisLineOffset < asisDoc.length) {
|
|
235028
235044
|
const line = asisDoc.lineAt(currentAsisLineOffset);
|
|
235029
235045
|
asisLineDecos.push({
|
|
@@ -235083,13 +235099,15 @@ class IdeDiff extends HTMLElement {
|
|
|
235083
235099
|
};
|
|
235084
235100
|
};
|
|
235085
235101
|
|
|
235086
|
-
// ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
235087
235102
|
initialize = (src1, src2, language = 'javascript') => {
|
|
235088
235103
|
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
235089
235104
|
console.warn('CodeMirror Editors not initialized yet.');
|
|
235090
235105
|
return;
|
|
235091
235106
|
}
|
|
235092
235107
|
|
|
235108
|
+
// ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
|
|
235109
|
+
this.#isScrollSyncActive = false;
|
|
235110
|
+
|
|
235093
235111
|
let langExtension;
|
|
235094
235112
|
switch(language) {
|
|
235095
235113
|
case 'javascript':
|
|
@@ -235102,13 +235120,12 @@ class IdeDiff extends HTMLElement {
|
|
|
235102
235120
|
// 먼저 데코레이션 효과를 계산하여 가져옵니다.
|
|
235103
235121
|
const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
|
|
235104
235122
|
|
|
235105
|
-
console.log(asisEffect, tobeEffect);
|
|
235106
235123
|
// asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
235107
235124
|
this.#asisEditorView.dispatch({
|
|
235108
235125
|
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
|
|
235109
235126
|
effects: [
|
|
235110
235127
|
this.#languageCompartment.reconfigure(langExtension),
|
|
235111
|
-
asisEffect
|
|
235128
|
+
asisEffect
|
|
235112
235129
|
]
|
|
235113
235130
|
});
|
|
235114
235131
|
|
|
@@ -235116,13 +235133,29 @@ class IdeDiff extends HTMLElement {
|
|
|
235116
235133
|
this.#tobeEditorView.dispatch({
|
|
235117
235134
|
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
|
|
235118
235135
|
effects: [
|
|
235119
|
-
|
|
235120
|
-
|
|
235136
|
+
this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
|
|
235137
|
+
tobeEffect // <-- 이 주석을 풀어주세요!
|
|
235121
235138
|
]
|
|
235122
235139
|
});
|
|
235140
|
+
|
|
235141
|
+
// ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
|
|
235142
|
+
// requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
|
|
235143
|
+
requestAnimationFrame(() => {
|
|
235144
|
+
this.#isScrollSyncActive = true;
|
|
235145
|
+
// 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
|
|
235146
|
+
// this.#asisEditorView.scrollDOM.scrollTop = 0;
|
|
235147
|
+
// this.#tobeEditorView.scrollDOM.scrollTop = 0;
|
|
235148
|
+
});
|
|
235123
235149
|
};
|
|
235124
235150
|
|
|
235125
235151
|
disconnectedCallback() {
|
|
235152
|
+
// 컴포넌트 해제 시 스크롤 리스너 제거
|
|
235153
|
+
if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
|
|
235154
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
235155
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
235156
|
+
this._asisScrollHandler = null; // 참조 제거
|
|
235157
|
+
this._tobeScrollHandler = null; // 참조 제거
|
|
235158
|
+
}
|
|
235126
235159
|
if (this.#asisEditorView) {
|
|
235127
235160
|
this.#asisEditorView.destroy();
|
|
235128
235161
|
}
|
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,48 @@ 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 = () => {
|
|
234946
|
+
// 기존 리스너가 있다면 제거 (중복 방지 및 disconnectedCallback에서 제거 용이)
|
|
234947
|
+
if (this._asisScrollHandler) { // 핸들러가 이미 할당되어 있다면
|
|
234948
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
234949
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
234950
|
+
}
|
|
234951
|
+
|
|
234939
234952
|
let scrollingA = false;
|
|
234940
234953
|
let scrollingB = false;
|
|
234941
234954
|
|
|
234942
|
-
this
|
|
234955
|
+
this._asisScrollHandler = () => {
|
|
234956
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
234957
|
+
|
|
234943
234958
|
if (!scrollingB) {
|
|
234944
234959
|
scrollingA = true;
|
|
234945
234960
|
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
234946
234961
|
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
234947
234962
|
}
|
|
234948
234963
|
scrollingB = false;
|
|
234949
|
-
}
|
|
234964
|
+
};
|
|
234965
|
+
|
|
234966
|
+
this._tobeScrollHandler = () => {
|
|
234967
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
234950
234968
|
|
|
234951
|
-
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
234952
234969
|
if (!scrollingA) {
|
|
234953
234970
|
scrollingB = true;
|
|
234954
234971
|
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
234955
234972
|
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
234956
234973
|
}
|
|
234957
234974
|
scrollingA = false;
|
|
234958
|
-
}
|
|
234975
|
+
};
|
|
234976
|
+
|
|
234977
|
+
this.#asisEditorView.scrollDOM.addEventListener('scroll', this._asisScrollHandler);
|
|
234978
|
+
this.#tobeEditorView.scrollDOM.addEventListener('scroll', this._tobeScrollHandler);
|
|
234959
234979
|
};
|
|
234960
234980
|
|
|
234961
|
-
//
|
|
234981
|
+
// #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
|
|
234962
234982
|
#applyDiffDecorations = (asisSrc, tobeSrc) => {
|
|
234963
234983
|
const dmp = new diffMatchPatchExports.diff_match_patch();
|
|
234964
234984
|
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
@@ -234972,8 +234992,6 @@ class IdeDiff extends HTMLElement {
|
|
|
234972
234992
|
let asisCursor = 0;
|
|
234973
234993
|
let tobeCursor = 0;
|
|
234974
234994
|
|
|
234975
|
-
// ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
|
|
234976
|
-
// 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
|
|
234977
234995
|
const asisDoc = Text.of(asisSrc.split('\n'));
|
|
234978
234996
|
const tobeDoc = Text.of(tobeSrc.split('\n'));
|
|
234979
234997
|
|
|
@@ -234991,7 +235009,6 @@ class IdeDiff extends HTMLElement {
|
|
|
234991
235009
|
let currentTobeLineOffset = tobeCursor;
|
|
234992
235010
|
const tobeLines = text.split('\n');
|
|
234993
235011
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
234994
|
-
// ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
|
|
234995
235012
|
if (currentTobeLineOffset < tobeDoc.length) {
|
|
234996
235013
|
const line = tobeDoc.lineAt(currentTobeLineOffset);
|
|
234997
235014
|
tobeLineDecos.push({
|
|
@@ -235019,7 +235036,6 @@ class IdeDiff extends HTMLElement {
|
|
|
235019
235036
|
let currentAsisLineOffset = asisCursor;
|
|
235020
235037
|
const asisLines = text.split('\n');
|
|
235021
235038
|
for (let i = 0; i < asisLines.length; i++) {
|
|
235022
|
-
// ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
|
|
235023
235039
|
if (currentAsisLineOffset < asisDoc.length) {
|
|
235024
235040
|
const line = asisDoc.lineAt(currentAsisLineOffset);
|
|
235025
235041
|
asisLineDecos.push({
|
|
@@ -235079,13 +235095,15 @@ class IdeDiff extends HTMLElement {
|
|
|
235079
235095
|
};
|
|
235080
235096
|
};
|
|
235081
235097
|
|
|
235082
|
-
// ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
235083
235098
|
initialize = (src1, src2, language = 'javascript') => {
|
|
235084
235099
|
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
235085
235100
|
console.warn('CodeMirror Editors not initialized yet.');
|
|
235086
235101
|
return;
|
|
235087
235102
|
}
|
|
235088
235103
|
|
|
235104
|
+
// ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
|
|
235105
|
+
this.#isScrollSyncActive = false;
|
|
235106
|
+
|
|
235089
235107
|
let langExtension;
|
|
235090
235108
|
switch(language) {
|
|
235091
235109
|
case 'javascript':
|
|
@@ -235098,13 +235116,12 @@ class IdeDiff extends HTMLElement {
|
|
|
235098
235116
|
// 먼저 데코레이션 효과를 계산하여 가져옵니다.
|
|
235099
235117
|
const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
|
|
235100
235118
|
|
|
235101
|
-
console.log(asisEffect, tobeEffect);
|
|
235102
235119
|
// asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
235103
235120
|
this.#asisEditorView.dispatch({
|
|
235104
235121
|
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
|
|
235105
235122
|
effects: [
|
|
235106
235123
|
this.#languageCompartment.reconfigure(langExtension),
|
|
235107
|
-
asisEffect
|
|
235124
|
+
asisEffect
|
|
235108
235125
|
]
|
|
235109
235126
|
});
|
|
235110
235127
|
|
|
@@ -235112,13 +235129,29 @@ class IdeDiff extends HTMLElement {
|
|
|
235112
235129
|
this.#tobeEditorView.dispatch({
|
|
235113
235130
|
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
|
|
235114
235131
|
effects: [
|
|
235115
|
-
|
|
235116
|
-
|
|
235132
|
+
this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
|
|
235133
|
+
tobeEffect // <-- 이 주석을 풀어주세요!
|
|
235117
235134
|
]
|
|
235118
235135
|
});
|
|
235136
|
+
|
|
235137
|
+
// ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
|
|
235138
|
+
// requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
|
|
235139
|
+
requestAnimationFrame(() => {
|
|
235140
|
+
this.#isScrollSyncActive = true;
|
|
235141
|
+
// 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
|
|
235142
|
+
// this.#asisEditorView.scrollDOM.scrollTop = 0;
|
|
235143
|
+
// this.#tobeEditorView.scrollDOM.scrollTop = 0;
|
|
235144
|
+
});
|
|
235119
235145
|
};
|
|
235120
235146
|
|
|
235121
235147
|
disconnectedCallback() {
|
|
235148
|
+
// 컴포넌트 해제 시 스크롤 리스너 제거
|
|
235149
|
+
if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
|
|
235150
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
235151
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
235152
|
+
this._asisScrollHandler = null; // 참조 제거
|
|
235153
|
+
this._tobeScrollHandler = null; // 참조 제거
|
|
235154
|
+
}
|
|
235122
235155
|
if (this.#asisEditorView) {
|
|
235123
235156
|
this.#asisEditorView.destroy();
|
|
235124
235157
|
}
|
|
@@ -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,48 @@ 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
|
+
// 기존 리스너가 있다면 제거 (중복 방지 및 disconnectedCallback에서 제거 용이)
|
|
199
|
+
if (this._asisScrollHandler) { // 핸들러가 이미 할당되어 있다면
|
|
200
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
201
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
202
|
+
}
|
|
203
|
+
|
|
192
204
|
let scrollingA = false;
|
|
193
205
|
let scrollingB = false;
|
|
194
206
|
|
|
195
|
-
this
|
|
207
|
+
this._asisScrollHandler = () => {
|
|
208
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
209
|
+
|
|
196
210
|
if (!scrollingB) {
|
|
197
211
|
scrollingA = true;
|
|
198
212
|
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
199
213
|
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
200
214
|
}
|
|
201
215
|
scrollingB = false;
|
|
202
|
-
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
this._tobeScrollHandler = () => {
|
|
219
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
203
220
|
|
|
204
|
-
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
205
221
|
if (!scrollingA) {
|
|
206
222
|
scrollingB = true;
|
|
207
223
|
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
208
224
|
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
209
225
|
}
|
|
210
226
|
scrollingA = false;
|
|
211
|
-
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
this.#asisEditorView.scrollDOM.addEventListener('scroll', this._asisScrollHandler);
|
|
230
|
+
this.#tobeEditorView.scrollDOM.addEventListener('scroll', this._tobeScrollHandler);
|
|
212
231
|
};
|
|
213
232
|
|
|
214
|
-
//
|
|
233
|
+
// #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
|
|
215
234
|
#applyDiffDecorations = (asisSrc, tobeSrc) => {
|
|
216
235
|
const dmp = new diff_match_patch();
|
|
217
236
|
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
@@ -225,8 +244,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
225
244
|
let asisCursor = 0;
|
|
226
245
|
let tobeCursor = 0;
|
|
227
246
|
|
|
228
|
-
// ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
|
|
229
|
-
// 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
|
|
230
247
|
const asisDoc = Text.of(asisSrc.split('\n'));
|
|
231
248
|
const tobeDoc = Text.of(tobeSrc.split('\n'));
|
|
232
249
|
|
|
@@ -244,7 +261,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
244
261
|
let currentTobeLineOffset = tobeCursor;
|
|
245
262
|
const tobeLines = text.split('\n');
|
|
246
263
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
247
|
-
// ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
|
|
248
264
|
if (currentTobeLineOffset < tobeDoc.length) {
|
|
249
265
|
const line = tobeDoc.lineAt(currentTobeLineOffset);
|
|
250
266
|
tobeLineDecos.push({
|
|
@@ -272,7 +288,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
272
288
|
let currentAsisLineOffset = asisCursor;
|
|
273
289
|
const asisLines = text.split('\n');
|
|
274
290
|
for (let i = 0; i < asisLines.length; i++) {
|
|
275
|
-
// ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
|
|
276
291
|
if (currentAsisLineOffset < asisDoc.length) {
|
|
277
292
|
const line = asisDoc.lineAt(currentAsisLineOffset);
|
|
278
293
|
asisLineDecos.push({
|
|
@@ -332,13 +347,15 @@ export class IdeDiff extends HTMLElement {
|
|
|
332
347
|
};
|
|
333
348
|
};
|
|
334
349
|
|
|
335
|
-
// ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
336
350
|
initialize = (src1, src2, language = 'javascript') => {
|
|
337
351
|
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
338
352
|
console.warn('CodeMirror Editors not initialized yet.');
|
|
339
353
|
return;
|
|
340
354
|
}
|
|
341
355
|
|
|
356
|
+
// ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
|
|
357
|
+
this.#isScrollSyncActive = false;
|
|
358
|
+
|
|
342
359
|
let langExtension;
|
|
343
360
|
switch(language) {
|
|
344
361
|
case 'javascript':
|
|
@@ -351,13 +368,12 @@ export class IdeDiff extends HTMLElement {
|
|
|
351
368
|
// 먼저 데코레이션 효과를 계산하여 가져옵니다.
|
|
352
369
|
const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
|
|
353
370
|
|
|
354
|
-
console.log(asisEffect, tobeEffect);
|
|
355
371
|
// asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
356
372
|
this.#asisEditorView.dispatch({
|
|
357
373
|
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
|
|
358
374
|
effects: [
|
|
359
375
|
this.#languageCompartment.reconfigure(langExtension),
|
|
360
|
-
asisEffect
|
|
376
|
+
asisEffect
|
|
361
377
|
]
|
|
362
378
|
});
|
|
363
379
|
|
|
@@ -365,13 +381,29 @@ export class IdeDiff extends HTMLElement {
|
|
|
365
381
|
this.#tobeEditorView.dispatch({
|
|
366
382
|
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
|
|
367
383
|
effects: [
|
|
368
|
-
|
|
369
|
-
|
|
384
|
+
this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
|
|
385
|
+
tobeEffect // <-- 이 주석을 풀어주세요!
|
|
370
386
|
]
|
|
371
387
|
});
|
|
388
|
+
|
|
389
|
+
// ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
|
|
390
|
+
// requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
|
|
391
|
+
requestAnimationFrame(() => {
|
|
392
|
+
this.#isScrollSyncActive = true;
|
|
393
|
+
// 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
|
|
394
|
+
// this.#asisEditorView.scrollDOM.scrollTop = 0;
|
|
395
|
+
// this.#tobeEditorView.scrollDOM.scrollTop = 0;
|
|
396
|
+
});
|
|
372
397
|
};
|
|
373
398
|
|
|
374
399
|
disconnectedCallback() {
|
|
400
|
+
// 컴포넌트 해제 시 스크롤 리스너 제거
|
|
401
|
+
if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
|
|
402
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
403
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
404
|
+
this._asisScrollHandler = null; // 참조 제거
|
|
405
|
+
this._tobeScrollHandler = null; // 참조 제거
|
|
406
|
+
}
|
|
375
407
|
if (this.#asisEditorView) {
|
|
376
408
|
this.#asisEditorView.destroy();
|
|
377
409
|
}
|
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,48 @@ 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
|
+
// 기존 리스너가 있다면 제거 (중복 방지 및 disconnectedCallback에서 제거 용이)
|
|
199
|
+
if (this._asisScrollHandler) { // 핸들러가 이미 할당되어 있다면
|
|
200
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
201
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
202
|
+
}
|
|
203
|
+
|
|
192
204
|
let scrollingA = false;
|
|
193
205
|
let scrollingB = false;
|
|
194
206
|
|
|
195
|
-
this
|
|
207
|
+
this._asisScrollHandler = () => {
|
|
208
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
209
|
+
|
|
196
210
|
if (!scrollingB) {
|
|
197
211
|
scrollingA = true;
|
|
198
212
|
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
199
213
|
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
200
214
|
}
|
|
201
215
|
scrollingB = false;
|
|
202
|
-
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
this._tobeScrollHandler = () => {
|
|
219
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
203
220
|
|
|
204
|
-
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
205
221
|
if (!scrollingA) {
|
|
206
222
|
scrollingB = true;
|
|
207
223
|
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
208
224
|
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
209
225
|
}
|
|
210
226
|
scrollingA = false;
|
|
211
|
-
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
this.#asisEditorView.scrollDOM.addEventListener('scroll', this._asisScrollHandler);
|
|
230
|
+
this.#tobeEditorView.scrollDOM.addEventListener('scroll', this._tobeScrollHandler);
|
|
212
231
|
};
|
|
213
232
|
|
|
214
|
-
//
|
|
233
|
+
// #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
|
|
215
234
|
#applyDiffDecorations = (asisSrc, tobeSrc) => {
|
|
216
235
|
const dmp = new diff_match_patch();
|
|
217
236
|
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
@@ -225,8 +244,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
225
244
|
let asisCursor = 0;
|
|
226
245
|
let tobeCursor = 0;
|
|
227
246
|
|
|
228
|
-
// ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
|
|
229
|
-
// 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
|
|
230
247
|
const asisDoc = Text.of(asisSrc.split('\n'));
|
|
231
248
|
const tobeDoc = Text.of(tobeSrc.split('\n'));
|
|
232
249
|
|
|
@@ -244,7 +261,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
244
261
|
let currentTobeLineOffset = tobeCursor;
|
|
245
262
|
const tobeLines = text.split('\n');
|
|
246
263
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
247
|
-
// ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
|
|
248
264
|
if (currentTobeLineOffset < tobeDoc.length) {
|
|
249
265
|
const line = tobeDoc.lineAt(currentTobeLineOffset);
|
|
250
266
|
tobeLineDecos.push({
|
|
@@ -272,7 +288,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
272
288
|
let currentAsisLineOffset = asisCursor;
|
|
273
289
|
const asisLines = text.split('\n');
|
|
274
290
|
for (let i = 0; i < asisLines.length; i++) {
|
|
275
|
-
// ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
|
|
276
291
|
if (currentAsisLineOffset < asisDoc.length) {
|
|
277
292
|
const line = asisDoc.lineAt(currentAsisLineOffset);
|
|
278
293
|
asisLineDecos.push({
|
|
@@ -332,13 +347,15 @@ export class IdeDiff extends HTMLElement {
|
|
|
332
347
|
};
|
|
333
348
|
};
|
|
334
349
|
|
|
335
|
-
// ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
336
350
|
initialize = (src1, src2, language = 'javascript') => {
|
|
337
351
|
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
338
352
|
console.warn('CodeMirror Editors not initialized yet.');
|
|
339
353
|
return;
|
|
340
354
|
}
|
|
341
355
|
|
|
356
|
+
// ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
|
|
357
|
+
this.#isScrollSyncActive = false;
|
|
358
|
+
|
|
342
359
|
let langExtension;
|
|
343
360
|
switch(language) {
|
|
344
361
|
case 'javascript':
|
|
@@ -351,13 +368,12 @@ export class IdeDiff extends HTMLElement {
|
|
|
351
368
|
// 먼저 데코레이션 효과를 계산하여 가져옵니다.
|
|
352
369
|
const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
|
|
353
370
|
|
|
354
|
-
console.log(asisEffect, tobeEffect);
|
|
355
371
|
// asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
356
372
|
this.#asisEditorView.dispatch({
|
|
357
373
|
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
|
|
358
374
|
effects: [
|
|
359
375
|
this.#languageCompartment.reconfigure(langExtension),
|
|
360
|
-
asisEffect
|
|
376
|
+
asisEffect
|
|
361
377
|
]
|
|
362
378
|
});
|
|
363
379
|
|
|
@@ -365,13 +381,29 @@ export class IdeDiff extends HTMLElement {
|
|
|
365
381
|
this.#tobeEditorView.dispatch({
|
|
366
382
|
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
|
|
367
383
|
effects: [
|
|
368
|
-
|
|
369
|
-
|
|
384
|
+
this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
|
|
385
|
+
tobeEffect // <-- 이 주석을 풀어주세요!
|
|
370
386
|
]
|
|
371
387
|
});
|
|
388
|
+
|
|
389
|
+
// ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
|
|
390
|
+
// requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
|
|
391
|
+
requestAnimationFrame(() => {
|
|
392
|
+
this.#isScrollSyncActive = true;
|
|
393
|
+
// 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
|
|
394
|
+
// this.#asisEditorView.scrollDOM.scrollTop = 0;
|
|
395
|
+
// this.#tobeEditorView.scrollDOM.scrollTop = 0;
|
|
396
|
+
});
|
|
372
397
|
};
|
|
373
398
|
|
|
374
399
|
disconnectedCallback() {
|
|
400
|
+
// 컴포넌트 해제 시 스크롤 리스너 제거
|
|
401
|
+
if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
|
|
402
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
403
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
404
|
+
this._asisScrollHandler = null; // 참조 제거
|
|
405
|
+
this._tobeScrollHandler = null; // 참조 제거
|
|
406
|
+
}
|
|
375
407
|
if (this.#asisEditorView) {
|
|
376
408
|
this.#asisEditorView.destroy();
|
|
377
409
|
}
|