ide-assi 0.343.0 → 0.345.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.
@@ -234948,96 +234948,126 @@ class IdeDiff extends HTMLElement {
234948
234948
  const diffs = dmp.diff_main(asisSrc, tobeSrc);
234949
234949
  dmp.diff_cleanupSemantic(diffs);
234950
234950
 
234951
- // 모든 데코레이션을 builder에 추가하기 전에 수집합니다.
234952
- const asisDecorationsToAdd = [];
234953
- const tobeDecorationsToAdd = [];
234951
+ const asisLineDecos = [];
234952
+ const asisMarkDecos = [];
234953
+ const tobeLineDecos = [];
234954
+ const tobeMarkDecos = [];
234954
234955
 
234955
- let asisCursor = 0; // asis 에디터에서의 현재 텍스트 오프셋
234956
- let tobeCursor = 0; // tobe 에디터에서의 현재 텍스트 오프셋
234956
+ let asisCursor = 0;
234957
+ let tobeCursor = 0;
234957
234958
 
234958
234959
  for (const [op, text] of diffs) {
234959
234960
  const len = text.length;
234960
234961
 
234961
234962
  switch (op) {
234962
- case diffMatchPatchExports.diff_match_patch.DIFF_INSERT: // 추가된 텍스트
234963
- // tobe 쪽에 인라인 데코레이션 추가
234964
- tobeDecorationsToAdd.push({
234963
+ case diffMatchPatchExports.diff_match_patch.DIFF_INSERT: // Added text
234964
+ tobeMarkDecos.push({
234965
234965
  from: tobeCursor,
234966
234966
  to: tobeCursor + len,
234967
234967
  deco: Decoration.mark({ class: "cm-inserted-inline" })
234968
234968
  });
234969
234969
 
234970
- // tobe 쪽에 줄 배경 데코레이션 추가 (삽입된 줄)
234971
- // text가 여러 줄일 수 있으므로 각 줄에 적용
234972
234970
  let currentTobeLineOffset = tobeCursor;
234973
234971
  const tobeLines = text.split('\n');
234974
234972
  for (let i = 0; i < tobeLines.length; i++) {
234975
- // 현재 에디터 뷰의 문서에서 정보를 가져옵니다.
234976
- // 이전에 `tobeEditorView`가 전역 변수처럼 사용되었을 가능성을 위해 `this.#tobeEditorView`로 명시
234977
- const line = this.#tobeEditorView.state.doc.lineAt(currentTobeLineOffset);
234978
- tobeDecorationsToAdd.push({
234979
- from: line.from,
234980
- to: line.to,
234981
- deco: Decoration.line({ class: "cm-inserted-line-bg" })
234982
- });
234983
- currentTobeLineOffset += tobeLines[i].length + 1; // 다음 줄 시작 위치로 이동
234973
+ // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
234974
+ if (currentTobeLineOffset < this.#tobeEditorView.state.doc.length) {
234975
+ const line = this.#tobeEditorView.state.doc.lineAt(currentTobeLineOffset);
234976
+ tobeLineDecos.push({
234977
+ from: line.from,
234978
+ to: line.to,
234979
+ deco: Decoration.line({ class: "cm-inserted-line-bg" })
234980
+ });
234981
+ }
234982
+ // 다음 줄의 시작 위치 계산. 마지막 줄이 아니면 +1 (개행문자)
234983
+ currentTobeLineOffset += tobeLines[i].length + (i < tobeLines.length -1 ? 1 : 0);
234984
+ // 만약 마지막 줄이 비어있다면, 더 이상 진행하지 않도록 방지
234985
+ if (i === tobeLines.length - 1 && tobeLines[i].length === 0 && text.endsWith('\n') === false) {
234986
+ // 마지막 줄이 빈 줄이고 텍스트가 개행으로 끝나지 않으면 (오프셋이 더 이상 증가할 필요 없음)
234987
+ break;
234988
+ }
234984
234989
  }
234985
234990
 
234986
234991
  tobeCursor += len;
234987
234992
  break;
234988
234993
 
234989
- case diffMatchPatchExports.diff_match_patch.DIFF_DELETE: // 삭제된 텍스트
234990
- // asis 쪽에 인라인 데코레이션 추가
234991
- asisDecorationsToAdd.push({
234994
+ case diffMatchPatchExports.diff_match_patch.DIFF_DELETE: // Deleted text
234995
+ asisMarkDecos.push({
234992
234996
  from: asisCursor,
234993
234997
  to: asisCursor + len,
234994
234998
  deco: Decoration.mark({ class: "cm-deleted-inline" })
234995
234999
  });
234996
235000
 
234997
- // asis 쪽에 줄 배경 데코레이션 추가 (삭제된 줄)
234998
235001
  let currentAsisLineOffset = asisCursor;
234999
235002
  const asisLines = text.split('\n');
235000
235003
  for (let i = 0; i < asisLines.length; i++) {
235001
- const line = this.#asisEditorView.state.doc.lineAt(currentAsisLineOffset);
235002
- asisDecorationsToAdd.push({
235003
- from: line.from,
235004
- to: line.to,
235005
- deco: Decoration.line({ class: "cm-deleted-line-bg" })
235006
- });
235007
- currentAsisLineOffset += asisLines[i].length + 1;
235004
+ // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
235005
+ if (currentAsisLineOffset < this.#asisEditorView.state.doc.length) {
235006
+ const line = this.#asisEditorView.state.doc.lineAt(currentAsisLineOffset);
235007
+ asisLineDecos.push({
235008
+ from: line.from,
235009
+ to: line.to,
235010
+ deco: Decoration.line({ class: "cm-deleted-line-bg" })
235011
+ });
235012
+ }
235013
+ currentAsisLineOffset += asisLines[i].length + (i < asisLines.length -1 ? 1 : 0);
235014
+ if (i === asisLines.length - 1 && asisLines[i].length === 0 && text.endsWith('\n') === false) {
235015
+ break;
235016
+ }
235008
235017
  }
235009
235018
 
235010
235019
  asisCursor += len;
235011
235020
  break;
235012
235021
 
235013
- case diffMatchPatchExports.diff_match_patch.DIFF_EQUAL: // 변경되지 않은 텍스트
235022
+ case diffMatchPatchExports.diff_match_patch.DIFF_EQUAL: // Unchanged text
235014
235023
  asisCursor += len;
235015
235024
  tobeCursor += len;
235016
235025
  break;
235017
235026
  }
235018
235027
  }
235019
235028
 
235020
- // ⭐️ 중요: 데코레이션들을 'from' 위치를 기준으로 정렬합니다.
235021
- asisDecorationsToAdd.sort((a, b) => a.from - b.from);
235022
- tobeDecorationsToAdd.sort((a, b) => a.from - b.from);
235029
+ // Sort all collections by 'from' position (항상 좋은 습관)
235030
+ asisLineDecos.sort((a, b) => a.from - b.from);
235031
+ asisMarkDecos.sort((a, b) => a.from - b.from);
235032
+ tobeLineDecos.sort((a, b) => a.from - b.from);
235033
+ tobeMarkDecos.sort((a, b) => a.from - b.from);
235023
235034
 
235024
- // 정렬된 데코레이션들을 RangeSetBuilder 추가합니다.
235025
- const asisBuilder = new RangeSetBuilder();
235026
- for (const { from, to, deco } of asisDecorationsToAdd) {
235027
- asisBuilder.add(from, to, deco);
235035
+ // ASIS 에디터용 줄 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
235036
+ const asisLineBuilder = new RangeSetBuilder();
235037
+ for (const { from, to, deco } of asisLineDecos) {
235038
+ asisLineBuilder.add(from, to, deco);
235028
235039
  }
235040
+ const asisMarkBuilder = new RangeSetBuilder();
235041
+ for (const { from, to, deco } of asisMarkDecos) {
235042
+ asisMarkBuilder.add(from, to, deco);
235043
+ }
235044
+
235045
+ // ASIS 에디터용 줄 및 마크 RangeSet 결합
235046
+ const finalAsisDecorations = asisLineBuilder.finish().update({
235047
+ add: asisMarkBuilder.finish().children
235048
+ });
235029
235049
 
235030
- const tobeBuilder = new RangeSetBuilder();
235031
- for (const { from, to, deco } of tobeDecorationsToAdd) {
235032
- tobeBuilder.add(from, to, deco);
235050
+ // TOBE 에디터용 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
235051
+ const tobeLineBuilder = new RangeSetBuilder();
235052
+ for (const { from, to, deco } of tobeLineDecos) {
235053
+ tobeLineBuilder.add(from, to, deco);
235054
+ }
235055
+ const tobeMarkBuilder = new RangeSetBuilder();
235056
+ for (const { from, to, deco } of tobeMarkDecos) {
235057
+ tobeMarkBuilder.add(from, to, deco);
235033
235058
  }
235034
235059
 
235035
- // 데코레이션 업데이트를 위한 Effect를 디스패치합니다.
235060
+ // TOBE 에디터용 마크 RangeSet 결합
235061
+ const finalTobeDecorations = tobeLineBuilder.finish().update({
235062
+ add: tobeMarkBuilder.finish().children
235063
+ });
235064
+
235065
+ // 데코레이션 업데이트를 위한 Effect 디스패치
235036
235066
  this.#asisEditorView.dispatch({
235037
- effects: setAsisDecorationsEffect.of(asisBuilder.finish())
235067
+ effects: setAsisDecorationsEffect.of(finalAsisDecorations)
235038
235068
  });
235039
235069
  this.#tobeEditorView.dispatch({
235040
- effects: setTobeDecorationsEffect.of(tobeBuilder.finish())
235070
+ effects: setTobeDecorationsEffect.of(finalTobeDecorations)
235041
235071
  });
235042
235072
  };
235043
235073
 
@@ -234944,96 +234944,126 @@ class IdeDiff extends HTMLElement {
234944
234944
  const diffs = dmp.diff_main(asisSrc, tobeSrc);
234945
234945
  dmp.diff_cleanupSemantic(diffs);
234946
234946
 
234947
- // 모든 데코레이션을 builder에 추가하기 전에 수집합니다.
234948
- const asisDecorationsToAdd = [];
234949
- const tobeDecorationsToAdd = [];
234947
+ const asisLineDecos = [];
234948
+ const asisMarkDecos = [];
234949
+ const tobeLineDecos = [];
234950
+ const tobeMarkDecos = [];
234950
234951
 
234951
- let asisCursor = 0; // asis 에디터에서의 현재 텍스트 오프셋
234952
- let tobeCursor = 0; // tobe 에디터에서의 현재 텍스트 오프셋
234952
+ let asisCursor = 0;
234953
+ let tobeCursor = 0;
234953
234954
 
234954
234955
  for (const [op, text] of diffs) {
234955
234956
  const len = text.length;
234956
234957
 
234957
234958
  switch (op) {
234958
- case diffMatchPatchExports.diff_match_patch.DIFF_INSERT: // 추가된 텍스트
234959
- // tobe 쪽에 인라인 데코레이션 추가
234960
- tobeDecorationsToAdd.push({
234959
+ case diffMatchPatchExports.diff_match_patch.DIFF_INSERT: // Added text
234960
+ tobeMarkDecos.push({
234961
234961
  from: tobeCursor,
234962
234962
  to: tobeCursor + len,
234963
234963
  deco: Decoration.mark({ class: "cm-inserted-inline" })
234964
234964
  });
234965
234965
 
234966
- // tobe 쪽에 줄 배경 데코레이션 추가 (삽입된 줄)
234967
- // text가 여러 줄일 수 있으므로 각 줄에 적용
234968
234966
  let currentTobeLineOffset = tobeCursor;
234969
234967
  const tobeLines = text.split('\n');
234970
234968
  for (let i = 0; i < tobeLines.length; i++) {
234971
- // 현재 에디터 뷰의 문서에서 정보를 가져옵니다.
234972
- // 이전에 `tobeEditorView`가 전역 변수처럼 사용되었을 가능성을 위해 `this.#tobeEditorView`로 명시
234973
- const line = this.#tobeEditorView.state.doc.lineAt(currentTobeLineOffset);
234974
- tobeDecorationsToAdd.push({
234975
- from: line.from,
234976
- to: line.to,
234977
- deco: Decoration.line({ class: "cm-inserted-line-bg" })
234978
- });
234979
- currentTobeLineOffset += tobeLines[i].length + 1; // 다음 줄 시작 위치로 이동
234969
+ // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
234970
+ if (currentTobeLineOffset < this.#tobeEditorView.state.doc.length) {
234971
+ const line = this.#tobeEditorView.state.doc.lineAt(currentTobeLineOffset);
234972
+ tobeLineDecos.push({
234973
+ from: line.from,
234974
+ to: line.to,
234975
+ deco: Decoration.line({ class: "cm-inserted-line-bg" })
234976
+ });
234977
+ }
234978
+ // 다음 줄의 시작 위치 계산. 마지막 줄이 아니면 +1 (개행문자)
234979
+ currentTobeLineOffset += tobeLines[i].length + (i < tobeLines.length -1 ? 1 : 0);
234980
+ // 만약 마지막 줄이 비어있다면, 더 이상 진행하지 않도록 방지
234981
+ if (i === tobeLines.length - 1 && tobeLines[i].length === 0 && text.endsWith('\n') === false) {
234982
+ // 마지막 줄이 빈 줄이고 텍스트가 개행으로 끝나지 않으면 (오프셋이 더 이상 증가할 필요 없음)
234983
+ break;
234984
+ }
234980
234985
  }
234981
234986
 
234982
234987
  tobeCursor += len;
234983
234988
  break;
234984
234989
 
234985
- case diffMatchPatchExports.diff_match_patch.DIFF_DELETE: // 삭제된 텍스트
234986
- // asis 쪽에 인라인 데코레이션 추가
234987
- asisDecorationsToAdd.push({
234990
+ case diffMatchPatchExports.diff_match_patch.DIFF_DELETE: // Deleted text
234991
+ asisMarkDecos.push({
234988
234992
  from: asisCursor,
234989
234993
  to: asisCursor + len,
234990
234994
  deco: Decoration.mark({ class: "cm-deleted-inline" })
234991
234995
  });
234992
234996
 
234993
- // asis 쪽에 줄 배경 데코레이션 추가 (삭제된 줄)
234994
234997
  let currentAsisLineOffset = asisCursor;
234995
234998
  const asisLines = text.split('\n');
234996
234999
  for (let i = 0; i < asisLines.length; i++) {
234997
- const line = this.#asisEditorView.state.doc.lineAt(currentAsisLineOffset);
234998
- asisDecorationsToAdd.push({
234999
- from: line.from,
235000
- to: line.to,
235001
- deco: Decoration.line({ class: "cm-deleted-line-bg" })
235002
- });
235003
- currentAsisLineOffset += asisLines[i].length + 1;
235000
+ // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
235001
+ if (currentAsisLineOffset < this.#asisEditorView.state.doc.length) {
235002
+ const line = this.#asisEditorView.state.doc.lineAt(currentAsisLineOffset);
235003
+ asisLineDecos.push({
235004
+ from: line.from,
235005
+ to: line.to,
235006
+ deco: Decoration.line({ class: "cm-deleted-line-bg" })
235007
+ });
235008
+ }
235009
+ currentAsisLineOffset += asisLines[i].length + (i < asisLines.length -1 ? 1 : 0);
235010
+ if (i === asisLines.length - 1 && asisLines[i].length === 0 && text.endsWith('\n') === false) {
235011
+ break;
235012
+ }
235004
235013
  }
235005
235014
 
235006
235015
  asisCursor += len;
235007
235016
  break;
235008
235017
 
235009
- case diffMatchPatchExports.diff_match_patch.DIFF_EQUAL: // 변경되지 않은 텍스트
235018
+ case diffMatchPatchExports.diff_match_patch.DIFF_EQUAL: // Unchanged text
235010
235019
  asisCursor += len;
235011
235020
  tobeCursor += len;
235012
235021
  break;
235013
235022
  }
235014
235023
  }
235015
235024
 
235016
- // ⭐️ 중요: 데코레이션들을 'from' 위치를 기준으로 정렬합니다.
235017
- asisDecorationsToAdd.sort((a, b) => a.from - b.from);
235018
- tobeDecorationsToAdd.sort((a, b) => a.from - b.from);
235025
+ // Sort all collections by 'from' position (항상 좋은 습관)
235026
+ asisLineDecos.sort((a, b) => a.from - b.from);
235027
+ asisMarkDecos.sort((a, b) => a.from - b.from);
235028
+ tobeLineDecos.sort((a, b) => a.from - b.from);
235029
+ tobeMarkDecos.sort((a, b) => a.from - b.from);
235019
235030
 
235020
- // 정렬된 데코레이션들을 RangeSetBuilder 추가합니다.
235021
- const asisBuilder = new RangeSetBuilder();
235022
- for (const { from, to, deco } of asisDecorationsToAdd) {
235023
- asisBuilder.add(from, to, deco);
235031
+ // ASIS 에디터용 줄 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
235032
+ const asisLineBuilder = new RangeSetBuilder();
235033
+ for (const { from, to, deco } of asisLineDecos) {
235034
+ asisLineBuilder.add(from, to, deco);
235024
235035
  }
235036
+ const asisMarkBuilder = new RangeSetBuilder();
235037
+ for (const { from, to, deco } of asisMarkDecos) {
235038
+ asisMarkBuilder.add(from, to, deco);
235039
+ }
235040
+
235041
+ // ASIS 에디터용 줄 및 마크 RangeSet 결합
235042
+ const finalAsisDecorations = asisLineBuilder.finish().update({
235043
+ add: asisMarkBuilder.finish().children
235044
+ });
235025
235045
 
235026
- const tobeBuilder = new RangeSetBuilder();
235027
- for (const { from, to, deco } of tobeDecorationsToAdd) {
235028
- tobeBuilder.add(from, to, deco);
235046
+ // TOBE 에디터용 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
235047
+ const tobeLineBuilder = new RangeSetBuilder();
235048
+ for (const { from, to, deco } of tobeLineDecos) {
235049
+ tobeLineBuilder.add(from, to, deco);
235050
+ }
235051
+ const tobeMarkBuilder = new RangeSetBuilder();
235052
+ for (const { from, to, deco } of tobeMarkDecos) {
235053
+ tobeMarkBuilder.add(from, to, deco);
235029
235054
  }
235030
235055
 
235031
- // 데코레이션 업데이트를 위한 Effect를 디스패치합니다.
235056
+ // TOBE 에디터용 마크 RangeSet 결합
235057
+ const finalTobeDecorations = tobeLineBuilder.finish().update({
235058
+ add: tobeMarkBuilder.finish().children
235059
+ });
235060
+
235061
+ // 데코레이션 업데이트를 위한 Effect 디스패치
235032
235062
  this.#asisEditorView.dispatch({
235033
- effects: setAsisDecorationsEffect.of(asisBuilder.finish())
235063
+ effects: setAsisDecorationsEffect.of(finalAsisDecorations)
235034
235064
  });
235035
235065
  this.#tobeEditorView.dispatch({
235036
- effects: setTobeDecorationsEffect.of(tobeBuilder.finish())
235066
+ effects: setTobeDecorationsEffect.of(finalTobeDecorations)
235037
235067
  });
235038
235068
  };
235039
235069
 
@@ -221,96 +221,126 @@ export class IdeDiff extends HTMLElement {
221
221
  const diffs = dmp.diff_main(asisSrc, tobeSrc);
222
222
  dmp.diff_cleanupSemantic(diffs);
223
223
 
224
- // 모든 데코레이션을 builder에 추가하기 전에 수집합니다.
225
- const asisDecorationsToAdd = [];
226
- const tobeDecorationsToAdd = [];
224
+ const asisLineDecos = [];
225
+ const asisMarkDecos = [];
226
+ const tobeLineDecos = [];
227
+ const tobeMarkDecos = [];
227
228
 
228
- let asisCursor = 0; // asis 에디터에서의 현재 텍스트 오프셋
229
- let tobeCursor = 0; // tobe 에디터에서의 현재 텍스트 오프셋
229
+ let asisCursor = 0;
230
+ let tobeCursor = 0;
230
231
 
231
232
  for (const [op, text] of diffs) {
232
233
  const len = text.length;
233
234
 
234
235
  switch (op) {
235
- case diff_match_patch.DIFF_INSERT: // 추가된 텍스트
236
- // tobe 쪽에 인라인 데코레이션 추가
237
- tobeDecorationsToAdd.push({
236
+ case diff_match_patch.DIFF_INSERT: // Added text
237
+ tobeMarkDecos.push({
238
238
  from: tobeCursor,
239
239
  to: tobeCursor + len,
240
240
  deco: Decoration.mark({ class: "cm-inserted-inline" })
241
241
  });
242
242
 
243
- // tobe 쪽에 줄 배경 데코레이션 추가 (삽입된 줄)
244
- // text가 여러 줄일 수 있으므로 각 줄에 적용
245
243
  let currentTobeLineOffset = tobeCursor;
246
244
  const tobeLines = text.split('\n');
247
245
  for (let i = 0; i < tobeLines.length; i++) {
248
- // 현재 에디터 뷰의 문서에서 정보를 가져옵니다.
249
- // 이전에 `tobeEditorView`가 전역 변수처럼 사용되었을 가능성을 위해 `this.#tobeEditorView`로 명시
250
- const line = this.#tobeEditorView.state.doc.lineAt(currentTobeLineOffset);
251
- tobeDecorationsToAdd.push({
252
- from: line.from,
253
- to: line.to,
254
- deco: Decoration.line({ class: "cm-inserted-line-bg" })
255
- });
256
- currentTobeLineOffset += tobeLines[i].length + 1; // 다음 줄 시작 위치로 이동
246
+ // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
247
+ if (currentTobeLineOffset < this.#tobeEditorView.state.doc.length) {
248
+ const line = this.#tobeEditorView.state.doc.lineAt(currentTobeLineOffset);
249
+ tobeLineDecos.push({
250
+ from: line.from,
251
+ to: line.to,
252
+ deco: Decoration.line({ class: "cm-inserted-line-bg" })
253
+ });
254
+ }
255
+ // 다음 줄의 시작 위치 계산. 마지막 줄이 아니면 +1 (개행문자)
256
+ currentTobeLineOffset += tobeLines[i].length + (i < tobeLines.length -1 ? 1 : 0);
257
+ // 만약 마지막 줄이 비어있다면, 더 이상 진행하지 않도록 방지
258
+ if (i === tobeLines.length - 1 && tobeLines[i].length === 0 && text.endsWith('\n') === false) {
259
+ // 마지막 줄이 빈 줄이고 텍스트가 개행으로 끝나지 않으면 (오프셋이 더 이상 증가할 필요 없음)
260
+ break;
261
+ }
257
262
  }
258
263
 
259
264
  tobeCursor += len;
260
265
  break;
261
266
 
262
- case diff_match_patch.DIFF_DELETE: // 삭제된 텍스트
263
- // asis 쪽에 인라인 데코레이션 추가
264
- asisDecorationsToAdd.push({
267
+ case diff_match_patch.DIFF_DELETE: // Deleted text
268
+ asisMarkDecos.push({
265
269
  from: asisCursor,
266
270
  to: asisCursor + len,
267
271
  deco: Decoration.mark({ class: "cm-deleted-inline" })
268
272
  });
269
273
 
270
- // asis 쪽에 줄 배경 데코레이션 추가 (삭제된 줄)
271
274
  let currentAsisLineOffset = asisCursor;
272
275
  const asisLines = text.split('\n');
273
276
  for (let i = 0; i < asisLines.length; i++) {
274
- const line = this.#asisEditorView.state.doc.lineAt(currentAsisLineOffset);
275
- asisDecorationsToAdd.push({
276
- from: line.from,
277
- to: line.to,
278
- deco: Decoration.line({ class: "cm-deleted-line-bg" })
279
- });
280
- currentAsisLineOffset += asisLines[i].length + 1;
277
+ // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
278
+ if (currentAsisLineOffset < this.#asisEditorView.state.doc.length) {
279
+ const line = this.#asisEditorView.state.doc.lineAt(currentAsisLineOffset);
280
+ asisLineDecos.push({
281
+ from: line.from,
282
+ to: line.to,
283
+ deco: Decoration.line({ class: "cm-deleted-line-bg" })
284
+ });
285
+ }
286
+ currentAsisLineOffset += asisLines[i].length + (i < asisLines.length -1 ? 1 : 0);
287
+ if (i === asisLines.length - 1 && asisLines[i].length === 0 && text.endsWith('\n') === false) {
288
+ break;
289
+ }
281
290
  }
282
291
 
283
292
  asisCursor += len;
284
293
  break;
285
294
 
286
- case diff_match_patch.DIFF_EQUAL: // 변경되지 않은 텍스트
295
+ case diff_match_patch.DIFF_EQUAL: // Unchanged text
287
296
  asisCursor += len;
288
297
  tobeCursor += len;
289
298
  break;
290
299
  }
291
300
  }
292
301
 
293
- // ⭐️ 중요: 데코레이션들을 'from' 위치를 기준으로 정렬합니다.
294
- asisDecorationsToAdd.sort((a, b) => a.from - b.from);
295
- tobeDecorationsToAdd.sort((a, b) => a.from - b.from);
302
+ // Sort all collections by 'from' position (항상 좋은 습관)
303
+ asisLineDecos.sort((a, b) => a.from - b.from);
304
+ asisMarkDecos.sort((a, b) => a.from - b.from);
305
+ tobeLineDecos.sort((a, b) => a.from - b.from);
306
+ tobeMarkDecos.sort((a, b) => a.from - b.from);
296
307
 
297
- // 정렬된 데코레이션들을 RangeSetBuilder 추가합니다.
298
- const asisBuilder = new RangeSetBuilder();
299
- for (const { from, to, deco } of asisDecorationsToAdd) {
300
- asisBuilder.add(from, to, deco);
308
+ // ASIS 에디터용 줄 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
309
+ const asisLineBuilder = new RangeSetBuilder();
310
+ for (const { from, to, deco } of asisLineDecos) {
311
+ asisLineBuilder.add(from, to, deco);
301
312
  }
313
+ const asisMarkBuilder = new RangeSetBuilder();
314
+ for (const { from, to, deco } of asisMarkDecos) {
315
+ asisMarkBuilder.add(from, to, deco);
316
+ }
317
+
318
+ // ASIS 에디터용 줄 및 마크 RangeSet 결합
319
+ const finalAsisDecorations = asisLineBuilder.finish().update({
320
+ add: asisMarkBuilder.finish().children
321
+ });
302
322
 
303
- const tobeBuilder = new RangeSetBuilder();
304
- for (const { from, to, deco } of tobeDecorationsToAdd) {
305
- tobeBuilder.add(from, to, deco);
323
+ // TOBE 에디터용 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
324
+ const tobeLineBuilder = new RangeSetBuilder();
325
+ for (const { from, to, deco } of tobeLineDecos) {
326
+ tobeLineBuilder.add(from, to, deco);
306
327
  }
328
+ const tobeMarkBuilder = new RangeSetBuilder();
329
+ for (const { from, to, deco } of tobeMarkDecos) {
330
+ tobeMarkBuilder.add(from, to, deco);
331
+ }
332
+
333
+ // TOBE 에디터용 줄 및 마크 RangeSet 결합
334
+ const finalTobeDecorations = tobeLineBuilder.finish().update({
335
+ add: tobeMarkBuilder.finish().children
336
+ });
307
337
 
308
- // 데코레이션 업데이트를 위한 Effect 디스패치합니다.
338
+ // 데코레이션 업데이트를 위한 Effect 디스패치
309
339
  this.#asisEditorView.dispatch({
310
- effects: setAsisDecorationsEffect.of(asisBuilder.finish())
340
+ effects: setAsisDecorationsEffect.of(finalAsisDecorations)
311
341
  });
312
342
  this.#tobeEditorView.dispatch({
313
- effects: setTobeDecorationsEffect.of(tobeBuilder.finish())
343
+ effects: setTobeDecorationsEffect.of(finalTobeDecorations)
314
344
  });
315
345
  };
316
346
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ide-assi",
3
3
  "type": "module",
4
- "version": "0.343.0",
4
+ "version": "0.345.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
@@ -221,96 +221,126 @@ export class IdeDiff extends HTMLElement {
221
221
  const diffs = dmp.diff_main(asisSrc, tobeSrc);
222
222
  dmp.diff_cleanupSemantic(diffs);
223
223
 
224
- // 모든 데코레이션을 builder에 추가하기 전에 수집합니다.
225
- const asisDecorationsToAdd = [];
226
- const tobeDecorationsToAdd = [];
224
+ const asisLineDecos = [];
225
+ const asisMarkDecos = [];
226
+ const tobeLineDecos = [];
227
+ const tobeMarkDecos = [];
227
228
 
228
- let asisCursor = 0; // asis 에디터에서의 현재 텍스트 오프셋
229
- let tobeCursor = 0; // tobe 에디터에서의 현재 텍스트 오프셋
229
+ let asisCursor = 0;
230
+ let tobeCursor = 0;
230
231
 
231
232
  for (const [op, text] of diffs) {
232
233
  const len = text.length;
233
234
 
234
235
  switch (op) {
235
- case diff_match_patch.DIFF_INSERT: // 추가된 텍스트
236
- // tobe 쪽에 인라인 데코레이션 추가
237
- tobeDecorationsToAdd.push({
236
+ case diff_match_patch.DIFF_INSERT: // Added text
237
+ tobeMarkDecos.push({
238
238
  from: tobeCursor,
239
239
  to: tobeCursor + len,
240
240
  deco: Decoration.mark({ class: "cm-inserted-inline" })
241
241
  });
242
242
 
243
- // tobe 쪽에 줄 배경 데코레이션 추가 (삽입된 줄)
244
- // text가 여러 줄일 수 있으므로 각 줄에 적용
245
243
  let currentTobeLineOffset = tobeCursor;
246
244
  const tobeLines = text.split('\n');
247
245
  for (let i = 0; i < tobeLines.length; i++) {
248
- // 현재 에디터 뷰의 문서에서 정보를 가져옵니다.
249
- // 이전에 `tobeEditorView`가 전역 변수처럼 사용되었을 가능성을 위해 `this.#tobeEditorView`로 명시
250
- const line = this.#tobeEditorView.state.doc.lineAt(currentTobeLineOffset);
251
- tobeDecorationsToAdd.push({
252
- from: line.from,
253
- to: line.to,
254
- deco: Decoration.line({ class: "cm-inserted-line-bg" })
255
- });
256
- currentTobeLineOffset += tobeLines[i].length + 1; // 다음 줄 시작 위치로 이동
246
+ // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
247
+ if (currentTobeLineOffset < this.#tobeEditorView.state.doc.length) {
248
+ const line = this.#tobeEditorView.state.doc.lineAt(currentTobeLineOffset);
249
+ tobeLineDecos.push({
250
+ from: line.from,
251
+ to: line.to,
252
+ deco: Decoration.line({ class: "cm-inserted-line-bg" })
253
+ });
254
+ }
255
+ // 다음 줄의 시작 위치 계산. 마지막 줄이 아니면 +1 (개행문자)
256
+ currentTobeLineOffset += tobeLines[i].length + (i < tobeLines.length -1 ? 1 : 0);
257
+ // 만약 마지막 줄이 비어있다면, 더 이상 진행하지 않도록 방지
258
+ if (i === tobeLines.length - 1 && tobeLines[i].length === 0 && text.endsWith('\n') === false) {
259
+ // 마지막 줄이 빈 줄이고 텍스트가 개행으로 끝나지 않으면 (오프셋이 더 이상 증가할 필요 없음)
260
+ break;
261
+ }
257
262
  }
258
263
 
259
264
  tobeCursor += len;
260
265
  break;
261
266
 
262
- case diff_match_patch.DIFF_DELETE: // 삭제된 텍스트
263
- // asis 쪽에 인라인 데코레이션 추가
264
- asisDecorationsToAdd.push({
267
+ case diff_match_patch.DIFF_DELETE: // Deleted text
268
+ asisMarkDecos.push({
265
269
  from: asisCursor,
266
270
  to: asisCursor + len,
267
271
  deco: Decoration.mark({ class: "cm-deleted-inline" })
268
272
  });
269
273
 
270
- // asis 쪽에 줄 배경 데코레이션 추가 (삭제된 줄)
271
274
  let currentAsisLineOffset = asisCursor;
272
275
  const asisLines = text.split('\n');
273
276
  for (let i = 0; i < asisLines.length; i++) {
274
- const line = this.#asisEditorView.state.doc.lineAt(currentAsisLineOffset);
275
- asisDecorationsToAdd.push({
276
- from: line.from,
277
- to: line.to,
278
- deco: Decoration.line({ class: "cm-deleted-line-bg" })
279
- });
280
- currentAsisLineOffset += asisLines[i].length + 1;
277
+ // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
278
+ if (currentAsisLineOffset < this.#asisEditorView.state.doc.length) {
279
+ const line = this.#asisEditorView.state.doc.lineAt(currentAsisLineOffset);
280
+ asisLineDecos.push({
281
+ from: line.from,
282
+ to: line.to,
283
+ deco: Decoration.line({ class: "cm-deleted-line-bg" })
284
+ });
285
+ }
286
+ currentAsisLineOffset += asisLines[i].length + (i < asisLines.length -1 ? 1 : 0);
287
+ if (i === asisLines.length - 1 && asisLines[i].length === 0 && text.endsWith('\n') === false) {
288
+ break;
289
+ }
281
290
  }
282
291
 
283
292
  asisCursor += len;
284
293
  break;
285
294
 
286
- case diff_match_patch.DIFF_EQUAL: // 변경되지 않은 텍스트
295
+ case diff_match_patch.DIFF_EQUAL: // Unchanged text
287
296
  asisCursor += len;
288
297
  tobeCursor += len;
289
298
  break;
290
299
  }
291
300
  }
292
301
 
293
- // ⭐️ 중요: 데코레이션들을 'from' 위치를 기준으로 정렬합니다.
294
- asisDecorationsToAdd.sort((a, b) => a.from - b.from);
295
- tobeDecorationsToAdd.sort((a, b) => a.from - b.from);
302
+ // Sort all collections by 'from' position (항상 좋은 습관)
303
+ asisLineDecos.sort((a, b) => a.from - b.from);
304
+ asisMarkDecos.sort((a, b) => a.from - b.from);
305
+ tobeLineDecos.sort((a, b) => a.from - b.from);
306
+ tobeMarkDecos.sort((a, b) => a.from - b.from);
296
307
 
297
- // 정렬된 데코레이션들을 RangeSetBuilder 추가합니다.
298
- const asisBuilder = new RangeSetBuilder();
299
- for (const { from, to, deco } of asisDecorationsToAdd) {
300
- asisBuilder.add(from, to, deco);
308
+ // ASIS 에디터용 줄 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
309
+ const asisLineBuilder = new RangeSetBuilder();
310
+ for (const { from, to, deco } of asisLineDecos) {
311
+ asisLineBuilder.add(from, to, deco);
301
312
  }
313
+ const asisMarkBuilder = new RangeSetBuilder();
314
+ for (const { from, to, deco } of asisMarkDecos) {
315
+ asisMarkBuilder.add(from, to, deco);
316
+ }
317
+
318
+ // ASIS 에디터용 줄 및 마크 RangeSet 결합
319
+ const finalAsisDecorations = asisLineBuilder.finish().update({
320
+ add: asisMarkBuilder.finish().children
321
+ });
302
322
 
303
- const tobeBuilder = new RangeSetBuilder();
304
- for (const { from, to, deco } of tobeDecorationsToAdd) {
305
- tobeBuilder.add(from, to, deco);
323
+ // TOBE 에디터용 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
324
+ const tobeLineBuilder = new RangeSetBuilder();
325
+ for (const { from, to, deco } of tobeLineDecos) {
326
+ tobeLineBuilder.add(from, to, deco);
306
327
  }
328
+ const tobeMarkBuilder = new RangeSetBuilder();
329
+ for (const { from, to, deco } of tobeMarkDecos) {
330
+ tobeMarkBuilder.add(from, to, deco);
331
+ }
332
+
333
+ // TOBE 에디터용 줄 및 마크 RangeSet 결합
334
+ const finalTobeDecorations = tobeLineBuilder.finish().update({
335
+ add: tobeMarkBuilder.finish().children
336
+ });
307
337
 
308
- // 데코레이션 업데이트를 위한 Effect 디스패치합니다.
338
+ // 데코레이션 업데이트를 위한 Effect 디스패치
309
339
  this.#asisEditorView.dispatch({
310
- effects: setAsisDecorationsEffect.of(asisBuilder.finish())
340
+ effects: setAsisDecorationsEffect.of(finalAsisDecorations)
311
341
  });
312
342
  this.#tobeEditorView.dispatch({
313
- effects: setTobeDecorationsEffect.of(tobeBuilder.finish())
343
+ effects: setTobeDecorationsEffect.of(finalTobeDecorations)
314
344
  });
315
345
  };
316
346