ide-assi 0.346.0 → 0.347.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.
@@ -234751,10 +234751,6 @@ function requireDiffMatchPatch () {
234751
234751
 
234752
234752
  var diffMatchPatchExports = requireDiffMatchPatch();
234753
234753
 
234754
- // ... (나머지 코드는 동일) ...
234755
-
234756
- // --- 여기서부터 수정된 부분 ---
234757
-
234758
234754
  // Diff 데코레이션을 위한 StateField 정의 (asis 에디터용)
234759
234755
  const asisDiffDecorations = StateField.define({
234760
234756
  create() { return Decoration.none; },
@@ -234784,12 +234780,10 @@ const tobeDiffDecorations = StateField.define({
234784
234780
  });
234785
234781
 
234786
234782
  // 데코레이션 업데이트를 위한 Effect (트랜잭션에 포함시켜 상태 변경 알림)
234787
- // ⭐️ StateEffect.define()을 사용하여 이펙트를 정의합니다.
234783
+ // StateEffect.define()을 사용하여 이펙트를 정의합니다.
234788
234784
  const setAsisDecorationsEffect = StateEffect.define();
234789
234785
  const setTobeDecorationsEffect = StateEffect.define();
234790
234786
 
234791
- // --- 여기까지 수정된 부분 ---
234792
-
234793
234787
  class IdeDiff extends HTMLElement {
234794
234788
 
234795
234789
  #asisEditorView;
@@ -234943,6 +234937,7 @@ class IdeDiff extends HTMLElement {
234943
234937
  });
234944
234938
  };
234945
234939
 
234940
+ // ⭐️ #applyDiffDecorations 함수를 수정하여 Effect 객체를 반환하도록 변경
234946
234941
  #applyDiffDecorations = (asisSrc, tobeSrc) => {
234947
234942
  const dmp = new diffMatchPatchExports.diff_match_patch();
234948
234943
  const diffs = dmp.diff_main(asisSrc, tobeSrc);
@@ -234970,8 +234965,16 @@ class IdeDiff extends HTMLElement {
234970
234965
  let currentTobeLineOffset = tobeCursor;
234971
234966
  const tobeLines = text.split('\n');
234972
234967
  for (let i = 0; i < tobeLines.length; i++) {
234973
- // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
234974
- if (currentTobeLineOffset < this.#tobeEditorView.state.doc.length) {
234968
+ // 중요한 변경: line.from/to는 현재 에디터 뷰의 doc에서 가져옵니다.
234969
+ // 함수는 initialize 내에서 텍스트 변경과 함께 호출되므로,
234970
+ // 이 시점의 this.#tobeEditorView.state.doc은 아직 이전 내용일 수 있습니다.
234971
+ // 그러나, Effect로 반환되어 트랜잭션에 포함될 때 CodeMirror는
234972
+ // Effect를 처리하는 시점에 이미 텍스트가 업데이트되었음을 보장합니다.
234973
+ // 따라서 여기서 doc.length로 유효성 검사하는 것은 새로운 src2 기준이어야 더 정확합니다.
234974
+ // 하지만, 이 라인은 결국 새로운 doc에서 실행될 것이므로,
234975
+ // 여기서의 this.#tobeEditorView.state.doc.length 검사는 불필요하거나 오해의 소지가 있습니다.
234976
+ // 대신, tobeCursor + len (또는 currentTobeLineOffset)이 tobeSrc의 길이를 벗어나지 않는지 확인하는 것이 좋습니다.
234977
+ if (currentTobeLineOffset < tobeSrc.length) { // src2 (새로운 텍스트)의 길이와 비교
234975
234978
  const line = this.#tobeEditorView.state.doc.lineAt(currentTobeLineOffset);
234976
234979
  tobeLineDecos.push({
234977
234980
  from: line.from,
@@ -234979,11 +234982,8 @@ class IdeDiff extends HTMLElement {
234979
234982
  deco: Decoration.line({ class: "cm-inserted-line-bg" })
234980
234983
  });
234981
234984
  }
234982
- // 다음 줄의 시작 위치 계산. 마지막 줄이 아니면 +1 (개행문자)
234983
- currentTobeLineOffset += tobeLines[i].length + (i < tobeLines.length -1 ? 1 : 0);
234984
- // 만약 마지막 줄이 비어있다면, 더 이상 진행하지 않도록 방지
234985
+ currentTobeLineOffset += tobeLines[i].length + (i < tobeLines.length - 1 ? 1 : 0);
234985
234986
  if (i === tobeLines.length - 1 && tobeLines[i].length === 0 && text.endsWith('\n') === false) {
234986
- // 마지막 줄이 빈 줄이고 텍스트가 개행으로 끝나지 않으면 (오프셋이 더 이상 증가할 필요 없음)
234987
234987
  break;
234988
234988
  }
234989
234989
  }
@@ -235001,8 +235001,7 @@ class IdeDiff extends HTMLElement {
235001
235001
  let currentAsisLineOffset = asisCursor;
235002
235002
  const asisLines = text.split('\n');
235003
235003
  for (let i = 0; i < asisLines.length; i++) {
235004
- // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
235005
- if (currentAsisLineOffset < this.#asisEditorView.state.doc.length) {
235004
+ if (currentAsisLineOffset < asisSrc.length) { // src1 (새로운 텍스트)의 길이와 비교
235006
235005
  const line = this.#asisEditorView.state.doc.lineAt(currentAsisLineOffset);
235007
235006
  asisLineDecos.push({
235008
235007
  from: line.from,
@@ -235010,7 +235009,7 @@ class IdeDiff extends HTMLElement {
235010
235009
  deco: Decoration.line({ class: "cm-deleted-line-bg" })
235011
235010
  });
235012
235011
  }
235013
- currentAsisLineOffset += asisLines[i].length + (i < asisLines.length -1 ? 1 : 0);
235012
+ currentAsisLineOffset += asisLines[i].length + (i < asisLines.length - 1 ? 1 : 0);
235014
235013
  if (i === asisLines.length - 1 && asisLines[i].length === 0 && text.endsWith('\n') === false) {
235015
235014
  break;
235016
235015
  }
@@ -235026,13 +235025,11 @@ class IdeDiff extends HTMLElement {
235026
235025
  }
235027
235026
  }
235028
235027
 
235029
- // Sort all collections by 'from' position (항상 좋은 습관)
235030
235028
  asisLineDecos.sort((a, b) => a.from - b.from);
235031
235029
  asisMarkDecos.sort((a, b) => a.from - b.from);
235032
235030
  tobeLineDecos.sort((a, b) => a.from - b.from);
235033
235031
  tobeMarkDecos.sort((a, b) => a.from - b.from);
235034
235032
 
235035
- // ASIS 에디터용 줄 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
235036
235033
  const asisLineBuilder = new RangeSetBuilder();
235037
235034
  for (const { from, to, deco } of asisLineDecos) {
235038
235035
  asisLineBuilder.add(from, to, deco);
@@ -235041,13 +235038,10 @@ class IdeDiff extends HTMLElement {
235041
235038
  for (const { from, to, deco } of asisMarkDecos) {
235042
235039
  asisMarkBuilder.add(from, to, deco);
235043
235040
  }
235044
-
235045
- // ASIS 에디터용 줄 및 마크 RangeSet 결합
235046
235041
  const finalAsisDecorations = asisLineBuilder.finish().update({
235047
235042
  add: asisMarkBuilder.finish().children
235048
235043
  });
235049
235044
 
235050
- // TOBE 에디터용 줄 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
235051
235045
  const tobeLineBuilder = new RangeSetBuilder();
235052
235046
  for (const { from, to, deco } of tobeLineDecos) {
235053
235047
  tobeLineBuilder.add(from, to, deco);
@@ -235056,21 +235050,18 @@ class IdeDiff extends HTMLElement {
235056
235050
  for (const { from, to, deco } of tobeMarkDecos) {
235057
235051
  tobeMarkBuilder.add(from, to, deco);
235058
235052
  }
235059
-
235060
- // TOBE 에디터용 줄 및 마크 RangeSet 결합
235061
235053
  const finalTobeDecorations = tobeLineBuilder.finish().update({
235062
235054
  add: tobeMarkBuilder.finish().children
235063
235055
  });
235064
235056
 
235065
- // 데코레이션 업데이트를 위한 Effect 디스패치
235066
- this.#asisEditorView.dispatch({
235067
- effects: setAsisDecorationsEffect.of(finalAsisDecorations)
235068
- });
235069
- this.#tobeEditorView.dispatch({
235070
- effects: setTobeDecorationsEffect.of(finalTobeDecorations)
235071
- });
235057
+ // ⭐️ 중요: 데코레이션 효과를 반환합니다. 여기서 직접 dispatch 하지 않습니다.
235058
+ return {
235059
+ asisEffect: setAsisDecorationsEffect.of(finalAsisDecorations),
235060
+ tobeEffect: setTobeDecorationsEffect.of(finalTobeDecorations)
235061
+ };
235072
235062
  };
235073
235063
 
235064
+ // ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
235074
235065
  initialize = (src1, src2, language = 'javascript') => {
235075
235066
  if (!this.#asisEditorView || !this.#tobeEditorView) {
235076
235067
  console.warn('CodeMirror Editors not initialized yet.');
@@ -235086,23 +235077,26 @@ class IdeDiff extends HTMLElement {
235086
235077
  langExtension = javascript();
235087
235078
  }
235088
235079
 
235089
- this.#asisEditorView.dispatch({
235090
- effects: this.#languageCompartment.reconfigure(langExtension)
235091
- });
235092
- this.#tobeEditorView.dispatch({
235093
- effects: this.#languageCompartment.reconfigure(langExtension)
235094
- });
235080
+ // 먼저 데코레이션 효과를 계산하여 가져옵니다.
235081
+ const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
235095
235082
 
235096
- // 텍스트 내용 업데이트
235083
+ // asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
235097
235084
  this.#asisEditorView.dispatch({
235098
- changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 }
235085
+ changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
235086
+ effects: [
235087
+ this.#languageCompartment.reconfigure(langExtension),
235088
+ asisEffect // 계산된 데코레이션 효과를 포함
235089
+ ]
235099
235090
  });
235091
+
235092
+ // tobe 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
235100
235093
  this.#tobeEditorView.dispatch({
235101
- changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 }
235094
+ changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
235095
+ effects: [
235096
+ this.#languageCompartment.reconfigure(langExtension),
235097
+ tobeEffect // 계산된 데코레이션 효과를 포함
235098
+ ]
235102
235099
  });
235103
-
235104
- // Diff 데코레이션 적용
235105
- this.#applyDiffDecorations(src1, src2);
235106
235100
  };
235107
235101
 
235108
235102
  disconnectedCallback() {
@@ -234747,10 +234747,6 @@ function requireDiffMatchPatch () {
234747
234747
 
234748
234748
  var diffMatchPatchExports = requireDiffMatchPatch();
234749
234749
 
234750
- // ... (나머지 코드는 동일) ...
234751
-
234752
- // --- 여기서부터 수정된 부분 ---
234753
-
234754
234750
  // Diff 데코레이션을 위한 StateField 정의 (asis 에디터용)
234755
234751
  const asisDiffDecorations = StateField.define({
234756
234752
  create() { return Decoration.none; },
@@ -234780,12 +234776,10 @@ const tobeDiffDecorations = StateField.define({
234780
234776
  });
234781
234777
 
234782
234778
  // 데코레이션 업데이트를 위한 Effect (트랜잭션에 포함시켜 상태 변경 알림)
234783
- // ⭐️ StateEffect.define()을 사용하여 이펙트를 정의합니다.
234779
+ // StateEffect.define()을 사용하여 이펙트를 정의합니다.
234784
234780
  const setAsisDecorationsEffect = StateEffect.define();
234785
234781
  const setTobeDecorationsEffect = StateEffect.define();
234786
234782
 
234787
- // --- 여기까지 수정된 부분 ---
234788
-
234789
234783
  class IdeDiff extends HTMLElement {
234790
234784
 
234791
234785
  #asisEditorView;
@@ -234939,6 +234933,7 @@ class IdeDiff extends HTMLElement {
234939
234933
  });
234940
234934
  };
234941
234935
 
234936
+ // ⭐️ #applyDiffDecorations 함수를 수정하여 Effect 객체를 반환하도록 변경
234942
234937
  #applyDiffDecorations = (asisSrc, tobeSrc) => {
234943
234938
  const dmp = new diffMatchPatchExports.diff_match_patch();
234944
234939
  const diffs = dmp.diff_main(asisSrc, tobeSrc);
@@ -234966,8 +234961,16 @@ class IdeDiff extends HTMLElement {
234966
234961
  let currentTobeLineOffset = tobeCursor;
234967
234962
  const tobeLines = text.split('\n');
234968
234963
  for (let i = 0; i < tobeLines.length; i++) {
234969
- // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
234970
- if (currentTobeLineOffset < this.#tobeEditorView.state.doc.length) {
234964
+ // 중요한 변경: line.from/to는 현재 에디터 뷰의 doc에서 가져옵니다.
234965
+ // 함수는 initialize 내에서 텍스트 변경과 함께 호출되므로,
234966
+ // 이 시점의 this.#tobeEditorView.state.doc은 아직 이전 내용일 수 있습니다.
234967
+ // 그러나, Effect로 반환되어 트랜잭션에 포함될 때 CodeMirror는
234968
+ // Effect를 처리하는 시점에 이미 텍스트가 업데이트되었음을 보장합니다.
234969
+ // 따라서 여기서 doc.length로 유효성 검사하는 것은 새로운 src2 기준이어야 더 정확합니다.
234970
+ // 하지만, 이 라인은 결국 새로운 doc에서 실행될 것이므로,
234971
+ // 여기서의 this.#tobeEditorView.state.doc.length 검사는 불필요하거나 오해의 소지가 있습니다.
234972
+ // 대신, tobeCursor + len (또는 currentTobeLineOffset)이 tobeSrc의 길이를 벗어나지 않는지 확인하는 것이 좋습니다.
234973
+ if (currentTobeLineOffset < tobeSrc.length) { // src2 (새로운 텍스트)의 길이와 비교
234971
234974
  const line = this.#tobeEditorView.state.doc.lineAt(currentTobeLineOffset);
234972
234975
  tobeLineDecos.push({
234973
234976
  from: line.from,
@@ -234975,11 +234978,8 @@ class IdeDiff extends HTMLElement {
234975
234978
  deco: Decoration.line({ class: "cm-inserted-line-bg" })
234976
234979
  });
234977
234980
  }
234978
- // 다음 줄의 시작 위치 계산. 마지막 줄이 아니면 +1 (개행문자)
234979
- currentTobeLineOffset += tobeLines[i].length + (i < tobeLines.length -1 ? 1 : 0);
234980
- // 만약 마지막 줄이 비어있다면, 더 이상 진행하지 않도록 방지
234981
+ currentTobeLineOffset += tobeLines[i].length + (i < tobeLines.length - 1 ? 1 : 0);
234981
234982
  if (i === tobeLines.length - 1 && tobeLines[i].length === 0 && text.endsWith('\n') === false) {
234982
- // 마지막 줄이 빈 줄이고 텍스트가 개행으로 끝나지 않으면 (오프셋이 더 이상 증가할 필요 없음)
234983
234983
  break;
234984
234984
  }
234985
234985
  }
@@ -234997,8 +234997,7 @@ class IdeDiff extends HTMLElement {
234997
234997
  let currentAsisLineOffset = asisCursor;
234998
234998
  const asisLines = text.split('\n');
234999
234999
  for (let i = 0; i < asisLines.length; i++) {
235000
- // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
235001
- if (currentAsisLineOffset < this.#asisEditorView.state.doc.length) {
235000
+ if (currentAsisLineOffset < asisSrc.length) { // src1 (새로운 텍스트)의 길이와 비교
235002
235001
  const line = this.#asisEditorView.state.doc.lineAt(currentAsisLineOffset);
235003
235002
  asisLineDecos.push({
235004
235003
  from: line.from,
@@ -235006,7 +235005,7 @@ class IdeDiff extends HTMLElement {
235006
235005
  deco: Decoration.line({ class: "cm-deleted-line-bg" })
235007
235006
  });
235008
235007
  }
235009
- currentAsisLineOffset += asisLines[i].length + (i < asisLines.length -1 ? 1 : 0);
235008
+ currentAsisLineOffset += asisLines[i].length + (i < asisLines.length - 1 ? 1 : 0);
235010
235009
  if (i === asisLines.length - 1 && asisLines[i].length === 0 && text.endsWith('\n') === false) {
235011
235010
  break;
235012
235011
  }
@@ -235022,13 +235021,11 @@ class IdeDiff extends HTMLElement {
235022
235021
  }
235023
235022
  }
235024
235023
 
235025
- // Sort all collections by 'from' position (항상 좋은 습관)
235026
235024
  asisLineDecos.sort((a, b) => a.from - b.from);
235027
235025
  asisMarkDecos.sort((a, b) => a.from - b.from);
235028
235026
  tobeLineDecos.sort((a, b) => a.from - b.from);
235029
235027
  tobeMarkDecos.sort((a, b) => a.from - b.from);
235030
235028
 
235031
- // ASIS 에디터용 줄 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
235032
235029
  const asisLineBuilder = new RangeSetBuilder();
235033
235030
  for (const { from, to, deco } of asisLineDecos) {
235034
235031
  asisLineBuilder.add(from, to, deco);
@@ -235037,13 +235034,10 @@ class IdeDiff extends HTMLElement {
235037
235034
  for (const { from, to, deco } of asisMarkDecos) {
235038
235035
  asisMarkBuilder.add(from, to, deco);
235039
235036
  }
235040
-
235041
- // ASIS 에디터용 줄 및 마크 RangeSet 결합
235042
235037
  const finalAsisDecorations = asisLineBuilder.finish().update({
235043
235038
  add: asisMarkBuilder.finish().children
235044
235039
  });
235045
235040
 
235046
- // TOBE 에디터용 줄 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
235047
235041
  const tobeLineBuilder = new RangeSetBuilder();
235048
235042
  for (const { from, to, deco } of tobeLineDecos) {
235049
235043
  tobeLineBuilder.add(from, to, deco);
@@ -235052,21 +235046,18 @@ class IdeDiff extends HTMLElement {
235052
235046
  for (const { from, to, deco } of tobeMarkDecos) {
235053
235047
  tobeMarkBuilder.add(from, to, deco);
235054
235048
  }
235055
-
235056
- // TOBE 에디터용 줄 및 마크 RangeSet 결합
235057
235049
  const finalTobeDecorations = tobeLineBuilder.finish().update({
235058
235050
  add: tobeMarkBuilder.finish().children
235059
235051
  });
235060
235052
 
235061
- // 데코레이션 업데이트를 위한 Effect 디스패치
235062
- this.#asisEditorView.dispatch({
235063
- effects: setAsisDecorationsEffect.of(finalAsisDecorations)
235064
- });
235065
- this.#tobeEditorView.dispatch({
235066
- effects: setTobeDecorationsEffect.of(finalTobeDecorations)
235067
- });
235053
+ // ⭐️ 중요: 데코레이션 효과를 반환합니다. 여기서 직접 dispatch 하지 않습니다.
235054
+ return {
235055
+ asisEffect: setAsisDecorationsEffect.of(finalAsisDecorations),
235056
+ tobeEffect: setTobeDecorationsEffect.of(finalTobeDecorations)
235057
+ };
235068
235058
  };
235069
235059
 
235060
+ // ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
235070
235061
  initialize = (src1, src2, language = 'javascript') => {
235071
235062
  if (!this.#asisEditorView || !this.#tobeEditorView) {
235072
235063
  console.warn('CodeMirror Editors not initialized yet.');
@@ -235082,23 +235073,26 @@ class IdeDiff extends HTMLElement {
235082
235073
  langExtension = javascript();
235083
235074
  }
235084
235075
 
235085
- this.#asisEditorView.dispatch({
235086
- effects: this.#languageCompartment.reconfigure(langExtension)
235087
- });
235088
- this.#tobeEditorView.dispatch({
235089
- effects: this.#languageCompartment.reconfigure(langExtension)
235090
- });
235076
+ // 먼저 데코레이션 효과를 계산하여 가져옵니다.
235077
+ const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
235091
235078
 
235092
- // 텍스트 내용 업데이트
235079
+ // asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
235093
235080
  this.#asisEditorView.dispatch({
235094
- changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 }
235081
+ changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
235082
+ effects: [
235083
+ this.#languageCompartment.reconfigure(langExtension),
235084
+ asisEffect // 계산된 데코레이션 효과를 포함
235085
+ ]
235095
235086
  });
235087
+
235088
+ // tobe 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
235096
235089
  this.#tobeEditorView.dispatch({
235097
- changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 }
235090
+ changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
235091
+ effects: [
235092
+ this.#languageCompartment.reconfigure(langExtension),
235093
+ tobeEffect // 계산된 데코레이션 효과를 포함
235094
+ ]
235098
235095
  });
235099
-
235100
- // Diff 데코레이션 적용
235101
- this.#applyDiffDecorations(src1, src2);
235102
235096
  };
235103
235097
 
235104
235098
  disconnectedCallback() {
@@ -9,12 +9,12 @@ import {
9
9
  import {
10
10
  EditorState, Compartment, StateField,
11
11
  RangeSetBuilder,
12
- StateEffect
12
+ StateEffect // StateEffect를 올바르게 임포트
13
13
  } from "@codemirror/state";
14
14
  import { history, historyKeymap, indentWithTab } from "@codemirror/commands";
15
15
  import { defaultKeymap, selectAll } from "@codemirror/commands";
16
16
  import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
17
- // ⭐️ 부분이 수정되었습니다: => 아니라 from 입니다.
17
+ // 올바른 import 구문: '=>' 대신 'from'
18
18
  import { bracketMatching } from "@codemirror/language";
19
19
  import { javascript } from "@codemirror/lang-javascript";
20
20
  import { indentOnInput, syntaxHighlighting, defaultHighlightStyle } from "@codemirror/language";
@@ -22,11 +22,7 @@ import { lintKeymap } from "@codemirror/lint";
22
22
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
23
23
 
24
24
  // Diff 로직을 위해 diff-match-patch 사용
25
- import { diff_match_patch } from 'diff-match-patch';
26
-
27
- // ... (나머지 코드는 동일) ...
28
-
29
- // --- 여기서부터 수정된 부분 ---
25
+ import { diff_match_patch } from 'diff-match-patch'; // 올바른 패키지 이름: 하이픈 '-'
30
26
 
31
27
  // Diff 데코레이션을 위한 StateField 정의 (asis 에디터용)
32
28
  const asisDiffDecorations = StateField.define({
@@ -57,12 +53,10 @@ const tobeDiffDecorations = StateField.define({
57
53
  });
58
54
 
59
55
  // 데코레이션 업데이트를 위한 Effect (트랜잭션에 포함시켜 상태 변경 알림)
60
- // ⭐️ StateEffect.define()을 사용하여 이펙트를 정의합니다.
56
+ // StateEffect.define()을 사용하여 이펙트를 정의합니다.
61
57
  const setAsisDecorationsEffect = StateEffect.define();
62
58
  const setTobeDecorationsEffect = StateEffect.define();
63
59
 
64
- // --- 여기까지 수정된 부분 ---
65
-
66
60
  export class IdeDiff extends HTMLElement {
67
61
 
68
62
  #asisEditorView;
@@ -216,6 +210,7 @@ export class IdeDiff extends HTMLElement {
216
210
  });
217
211
  };
218
212
 
213
+ // ⭐️ #applyDiffDecorations 함수를 수정하여 Effect 객체를 반환하도록 변경
219
214
  #applyDiffDecorations = (asisSrc, tobeSrc) => {
220
215
  const dmp = new diff_match_patch();
221
216
  const diffs = dmp.diff_main(asisSrc, tobeSrc);
@@ -243,8 +238,16 @@ export class IdeDiff extends HTMLElement {
243
238
  let currentTobeLineOffset = tobeCursor;
244
239
  const tobeLines = text.split('\n');
245
240
  for (let i = 0; i < tobeLines.length; i++) {
246
- // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
247
- if (currentTobeLineOffset < this.#tobeEditorView.state.doc.length) {
241
+ // 중요한 변경: line.from/to는 현재 에디터 뷰의 doc에서 가져옵니다.
242
+ // 함수는 initialize 내에서 텍스트 변경과 함께 호출되므로,
243
+ // 이 시점의 this.#tobeEditorView.state.doc은 아직 이전 내용일 수 있습니다.
244
+ // 그러나, Effect로 반환되어 트랜잭션에 포함될 때 CodeMirror는
245
+ // Effect를 처리하는 시점에 이미 텍스트가 업데이트되었음을 보장합니다.
246
+ // 따라서 여기서 doc.length로 유효성 검사하는 것은 새로운 src2 기준이어야 더 정확합니다.
247
+ // 하지만, 이 라인은 결국 새로운 doc에서 실행될 것이므로,
248
+ // 여기서의 this.#tobeEditorView.state.doc.length 검사는 불필요하거나 오해의 소지가 있습니다.
249
+ // 대신, tobeCursor + len (또는 currentTobeLineOffset)이 tobeSrc의 길이를 벗어나지 않는지 확인하는 것이 좋습니다.
250
+ if (currentTobeLineOffset < tobeSrc.length) { // src2 (새로운 텍스트)의 길이와 비교
248
251
  const line = this.#tobeEditorView.state.doc.lineAt(currentTobeLineOffset);
249
252
  tobeLineDecos.push({
250
253
  from: line.from,
@@ -252,11 +255,8 @@ export class IdeDiff extends HTMLElement {
252
255
  deco: Decoration.line({ class: "cm-inserted-line-bg" })
253
256
  });
254
257
  }
255
- // 다음 줄의 시작 위치 계산. 마지막 줄이 아니면 +1 (개행문자)
256
- currentTobeLineOffset += tobeLines[i].length + (i < tobeLines.length -1 ? 1 : 0);
257
- // 만약 마지막 줄이 비어있다면, 더 이상 진행하지 않도록 방지
258
+ currentTobeLineOffset += tobeLines[i].length + (i < tobeLines.length - 1 ? 1 : 0);
258
259
  if (i === tobeLines.length - 1 && tobeLines[i].length === 0 && text.endsWith('\n') === false) {
259
- // 마지막 줄이 빈 줄이고 텍스트가 개행으로 끝나지 않으면 (오프셋이 더 이상 증가할 필요 없음)
260
260
  break;
261
261
  }
262
262
  }
@@ -274,8 +274,7 @@ export class IdeDiff extends HTMLElement {
274
274
  let currentAsisLineOffset = asisCursor;
275
275
  const asisLines = text.split('\n');
276
276
  for (let i = 0; i < asisLines.length; i++) {
277
- // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
278
- if (currentAsisLineOffset < this.#asisEditorView.state.doc.length) {
277
+ if (currentAsisLineOffset < asisSrc.length) { // src1 (새로운 텍스트)의 길이와 비교
279
278
  const line = this.#asisEditorView.state.doc.lineAt(currentAsisLineOffset);
280
279
  asisLineDecos.push({
281
280
  from: line.from,
@@ -283,7 +282,7 @@ export class IdeDiff extends HTMLElement {
283
282
  deco: Decoration.line({ class: "cm-deleted-line-bg" })
284
283
  });
285
284
  }
286
- currentAsisLineOffset += asisLines[i].length + (i < asisLines.length -1 ? 1 : 0);
285
+ currentAsisLineOffset += asisLines[i].length + (i < asisLines.length - 1 ? 1 : 0);
287
286
  if (i === asisLines.length - 1 && asisLines[i].length === 0 && text.endsWith('\n') === false) {
288
287
  break;
289
288
  }
@@ -299,13 +298,11 @@ export class IdeDiff extends HTMLElement {
299
298
  }
300
299
  }
301
300
 
302
- // Sort all collections by 'from' position (항상 좋은 습관)
303
301
  asisLineDecos.sort((a, b) => a.from - b.from);
304
302
  asisMarkDecos.sort((a, b) => a.from - b.from);
305
303
  tobeLineDecos.sort((a, b) => a.from - b.from);
306
304
  tobeMarkDecos.sort((a, b) => a.from - b.from);
307
305
 
308
- // ASIS 에디터용 줄 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
309
306
  const asisLineBuilder = new RangeSetBuilder();
310
307
  for (const { from, to, deco } of asisLineDecos) {
311
308
  asisLineBuilder.add(from, to, deco);
@@ -314,13 +311,10 @@ export class IdeDiff extends HTMLElement {
314
311
  for (const { from, to, deco } of asisMarkDecos) {
315
312
  asisMarkBuilder.add(from, to, deco);
316
313
  }
317
-
318
- // ASIS 에디터용 줄 및 마크 RangeSet 결합
319
314
  const finalAsisDecorations = asisLineBuilder.finish().update({
320
315
  add: asisMarkBuilder.finish().children
321
316
  });
322
317
 
323
- // TOBE 에디터용 줄 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
324
318
  const tobeLineBuilder = new RangeSetBuilder();
325
319
  for (const { from, to, deco } of tobeLineDecos) {
326
320
  tobeLineBuilder.add(from, to, deco);
@@ -329,21 +323,18 @@ export class IdeDiff extends HTMLElement {
329
323
  for (const { from, to, deco } of tobeMarkDecos) {
330
324
  tobeMarkBuilder.add(from, to, deco);
331
325
  }
332
-
333
- // TOBE 에디터용 줄 및 마크 RangeSet 결합
334
326
  const finalTobeDecorations = tobeLineBuilder.finish().update({
335
327
  add: tobeMarkBuilder.finish().children
336
328
  });
337
329
 
338
- // 데코레이션 업데이트를 위한 Effect 디스패치
339
- this.#asisEditorView.dispatch({
340
- effects: setAsisDecorationsEffect.of(finalAsisDecorations)
341
- });
342
- this.#tobeEditorView.dispatch({
343
- effects: setTobeDecorationsEffect.of(finalTobeDecorations)
344
- });
330
+ // ⭐️ 중요: 데코레이션 효과를 반환합니다. 여기서 직접 dispatch 하지 않습니다.
331
+ return {
332
+ asisEffect: setAsisDecorationsEffect.of(finalAsisDecorations),
333
+ tobeEffect: setTobeDecorationsEffect.of(finalTobeDecorations)
334
+ };
345
335
  };
346
336
 
337
+ // ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
347
338
  initialize = (src1, src2, language = 'javascript') => {
348
339
  if (!this.#asisEditorView || !this.#tobeEditorView) {
349
340
  console.warn('CodeMirror Editors not initialized yet.');
@@ -359,23 +350,26 @@ export class IdeDiff extends HTMLElement {
359
350
  langExtension = javascript();
360
351
  }
361
352
 
362
- this.#asisEditorView.dispatch({
363
- effects: this.#languageCompartment.reconfigure(langExtension)
364
- });
365
- this.#tobeEditorView.dispatch({
366
- effects: this.#languageCompartment.reconfigure(langExtension)
367
- });
353
+ // 먼저 데코레이션 효과를 계산하여 가져옵니다.
354
+ const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
368
355
 
369
- // 텍스트 내용 업데이트
356
+ // asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
370
357
  this.#asisEditorView.dispatch({
371
- changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 }
358
+ changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
359
+ effects: [
360
+ this.#languageCompartment.reconfigure(langExtension),
361
+ asisEffect // 계산된 데코레이션 효과를 포함
362
+ ]
372
363
  });
364
+
365
+ // tobe 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
373
366
  this.#tobeEditorView.dispatch({
374
- changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 }
367
+ changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
368
+ effects: [
369
+ this.#languageCompartment.reconfigure(langExtension),
370
+ tobeEffect // 계산된 데코레이션 효과를 포함
371
+ ]
375
372
  });
376
-
377
- // Diff 데코레이션 적용
378
- this.#applyDiffDecorations(src1, src2);
379
373
  };
380
374
 
381
375
  disconnectedCallback() {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ide-assi",
3
3
  "type": "module",
4
- "version": "0.346.0",
4
+ "version": "0.347.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
@@ -9,12 +9,12 @@ import {
9
9
  import {
10
10
  EditorState, Compartment, StateField,
11
11
  RangeSetBuilder,
12
- StateEffect
12
+ StateEffect // StateEffect를 올바르게 임포트
13
13
  } from "@codemirror/state";
14
14
  import { history, historyKeymap, indentWithTab } from "@codemirror/commands";
15
15
  import { defaultKeymap, selectAll } from "@codemirror/commands";
16
16
  import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
17
- // ⭐️ 부분이 수정되었습니다: => 아니라 from 입니다.
17
+ // 올바른 import 구문: '=>' 대신 'from'
18
18
  import { bracketMatching } from "@codemirror/language";
19
19
  import { javascript } from "@codemirror/lang-javascript";
20
20
  import { indentOnInput, syntaxHighlighting, defaultHighlightStyle } from "@codemirror/language";
@@ -22,11 +22,7 @@ import { lintKeymap } from "@codemirror/lint";
22
22
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
23
23
 
24
24
  // Diff 로직을 위해 diff-match-patch 사용
25
- import { diff_match_patch } from 'diff-match-patch';
26
-
27
- // ... (나머지 코드는 동일) ...
28
-
29
- // --- 여기서부터 수정된 부분 ---
25
+ import { diff_match_patch } from 'diff-match-patch'; // 올바른 패키지 이름: 하이픈 '-'
30
26
 
31
27
  // Diff 데코레이션을 위한 StateField 정의 (asis 에디터용)
32
28
  const asisDiffDecorations = StateField.define({
@@ -57,12 +53,10 @@ const tobeDiffDecorations = StateField.define({
57
53
  });
58
54
 
59
55
  // 데코레이션 업데이트를 위한 Effect (트랜잭션에 포함시켜 상태 변경 알림)
60
- // ⭐️ StateEffect.define()을 사용하여 이펙트를 정의합니다.
56
+ // StateEffect.define()을 사용하여 이펙트를 정의합니다.
61
57
  const setAsisDecorationsEffect = StateEffect.define();
62
58
  const setTobeDecorationsEffect = StateEffect.define();
63
59
 
64
- // --- 여기까지 수정된 부분 ---
65
-
66
60
  export class IdeDiff extends HTMLElement {
67
61
 
68
62
  #asisEditorView;
@@ -216,6 +210,7 @@ export class IdeDiff extends HTMLElement {
216
210
  });
217
211
  };
218
212
 
213
+ // ⭐️ #applyDiffDecorations 함수를 수정하여 Effect 객체를 반환하도록 변경
219
214
  #applyDiffDecorations = (asisSrc, tobeSrc) => {
220
215
  const dmp = new diff_match_patch();
221
216
  const diffs = dmp.diff_main(asisSrc, tobeSrc);
@@ -243,8 +238,16 @@ export class IdeDiff extends HTMLElement {
243
238
  let currentTobeLineOffset = tobeCursor;
244
239
  const tobeLines = text.split('\n');
245
240
  for (let i = 0; i < tobeLines.length; i++) {
246
- // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
247
- if (currentTobeLineOffset < this.#tobeEditorView.state.doc.length) {
241
+ // 중요한 변경: line.from/to는 현재 에디터 뷰의 doc에서 가져옵니다.
242
+ // 함수는 initialize 내에서 텍스트 변경과 함께 호출되므로,
243
+ // 이 시점의 this.#tobeEditorView.state.doc은 아직 이전 내용일 수 있습니다.
244
+ // 그러나, Effect로 반환되어 트랜잭션에 포함될 때 CodeMirror는
245
+ // Effect를 처리하는 시점에 이미 텍스트가 업데이트되었음을 보장합니다.
246
+ // 따라서 여기서 doc.length로 유효성 검사하는 것은 새로운 src2 기준이어야 더 정확합니다.
247
+ // 하지만, 이 라인은 결국 새로운 doc에서 실행될 것이므로,
248
+ // 여기서의 this.#tobeEditorView.state.doc.length 검사는 불필요하거나 오해의 소지가 있습니다.
249
+ // 대신, tobeCursor + len (또는 currentTobeLineOffset)이 tobeSrc의 길이를 벗어나지 않는지 확인하는 것이 좋습니다.
250
+ if (currentTobeLineOffset < tobeSrc.length) { // src2 (새로운 텍스트)의 길이와 비교
248
251
  const line = this.#tobeEditorView.state.doc.lineAt(currentTobeLineOffset);
249
252
  tobeLineDecos.push({
250
253
  from: line.from,
@@ -252,11 +255,8 @@ export class IdeDiff extends HTMLElement {
252
255
  deco: Decoration.line({ class: "cm-inserted-line-bg" })
253
256
  });
254
257
  }
255
- // 다음 줄의 시작 위치 계산. 마지막 줄이 아니면 +1 (개행문자)
256
- currentTobeLineOffset += tobeLines[i].length + (i < tobeLines.length -1 ? 1 : 0);
257
- // 만약 마지막 줄이 비어있다면, 더 이상 진행하지 않도록 방지
258
+ currentTobeLineOffset += tobeLines[i].length + (i < tobeLines.length - 1 ? 1 : 0);
258
259
  if (i === tobeLines.length - 1 && tobeLines[i].length === 0 && text.endsWith('\n') === false) {
259
- // 마지막 줄이 빈 줄이고 텍스트가 개행으로 끝나지 않으면 (오프셋이 더 이상 증가할 필요 없음)
260
260
  break;
261
261
  }
262
262
  }
@@ -274,8 +274,7 @@ export class IdeDiff extends HTMLElement {
274
274
  let currentAsisLineOffset = asisCursor;
275
275
  const asisLines = text.split('\n');
276
276
  for (let i = 0; i < asisLines.length; i++) {
277
- // ⭐️ 중요: lineAt() 호출 전에 유효한 문서 범위 내에 있는지 확인
278
- if (currentAsisLineOffset < this.#asisEditorView.state.doc.length) {
277
+ if (currentAsisLineOffset < asisSrc.length) { // src1 (새로운 텍스트)의 길이와 비교
279
278
  const line = this.#asisEditorView.state.doc.lineAt(currentAsisLineOffset);
280
279
  asisLineDecos.push({
281
280
  from: line.from,
@@ -283,7 +282,7 @@ export class IdeDiff extends HTMLElement {
283
282
  deco: Decoration.line({ class: "cm-deleted-line-bg" })
284
283
  });
285
284
  }
286
- currentAsisLineOffset += asisLines[i].length + (i < asisLines.length -1 ? 1 : 0);
285
+ currentAsisLineOffset += asisLines[i].length + (i < asisLines.length - 1 ? 1 : 0);
287
286
  if (i === asisLines.length - 1 && asisLines[i].length === 0 && text.endsWith('\n') === false) {
288
287
  break;
289
288
  }
@@ -299,13 +298,11 @@ export class IdeDiff extends HTMLElement {
299
298
  }
300
299
  }
301
300
 
302
- // Sort all collections by 'from' position (항상 좋은 습관)
303
301
  asisLineDecos.sort((a, b) => a.from - b.from);
304
302
  asisMarkDecos.sort((a, b) => a.from - b.from);
305
303
  tobeLineDecos.sort((a, b) => a.from - b.from);
306
304
  tobeMarkDecos.sort((a, b) => a.from - b.from);
307
305
 
308
- // ASIS 에디터용 줄 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
309
306
  const asisLineBuilder = new RangeSetBuilder();
310
307
  for (const { from, to, deco } of asisLineDecos) {
311
308
  asisLineBuilder.add(from, to, deco);
@@ -314,13 +311,10 @@ export class IdeDiff extends HTMLElement {
314
311
  for (const { from, to, deco } of asisMarkDecos) {
315
312
  asisMarkBuilder.add(from, to, deco);
316
313
  }
317
-
318
- // ASIS 에디터용 줄 및 마크 RangeSet 결합
319
314
  const finalAsisDecorations = asisLineBuilder.finish().update({
320
315
  add: asisMarkBuilder.finish().children
321
316
  });
322
317
 
323
- // TOBE 에디터용 줄 및 마크 데코레이션을 위한 별도 RangeSetBuilder 생성
324
318
  const tobeLineBuilder = new RangeSetBuilder();
325
319
  for (const { from, to, deco } of tobeLineDecos) {
326
320
  tobeLineBuilder.add(from, to, deco);
@@ -329,21 +323,18 @@ export class IdeDiff extends HTMLElement {
329
323
  for (const { from, to, deco } of tobeMarkDecos) {
330
324
  tobeMarkBuilder.add(from, to, deco);
331
325
  }
332
-
333
- // TOBE 에디터용 줄 및 마크 RangeSet 결합
334
326
  const finalTobeDecorations = tobeLineBuilder.finish().update({
335
327
  add: tobeMarkBuilder.finish().children
336
328
  });
337
329
 
338
- // 데코레이션 업데이트를 위한 Effect 디스패치
339
- this.#asisEditorView.dispatch({
340
- effects: setAsisDecorationsEffect.of(finalAsisDecorations)
341
- });
342
- this.#tobeEditorView.dispatch({
343
- effects: setTobeDecorationsEffect.of(finalTobeDecorations)
344
- });
330
+ // ⭐️ 중요: 데코레이션 효과를 반환합니다. 여기서 직접 dispatch 하지 않습니다.
331
+ return {
332
+ asisEffect: setAsisDecorationsEffect.of(finalAsisDecorations),
333
+ tobeEffect: setTobeDecorationsEffect.of(finalTobeDecorations)
334
+ };
345
335
  };
346
336
 
337
+ // ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
347
338
  initialize = (src1, src2, language = 'javascript') => {
348
339
  if (!this.#asisEditorView || !this.#tobeEditorView) {
349
340
  console.warn('CodeMirror Editors not initialized yet.');
@@ -359,23 +350,26 @@ export class IdeDiff extends HTMLElement {
359
350
  langExtension = javascript();
360
351
  }
361
352
 
362
- this.#asisEditorView.dispatch({
363
- effects: this.#languageCompartment.reconfigure(langExtension)
364
- });
365
- this.#tobeEditorView.dispatch({
366
- effects: this.#languageCompartment.reconfigure(langExtension)
367
- });
353
+ // 먼저 데코레이션 효과를 계산하여 가져옵니다.
354
+ const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
368
355
 
369
- // 텍스트 내용 업데이트
356
+ // asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
370
357
  this.#asisEditorView.dispatch({
371
- changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 }
358
+ changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
359
+ effects: [
360
+ this.#languageCompartment.reconfigure(langExtension),
361
+ asisEffect // 계산된 데코레이션 효과를 포함
362
+ ]
372
363
  });
364
+
365
+ // tobe 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
373
366
  this.#tobeEditorView.dispatch({
374
- changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 }
367
+ changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
368
+ effects: [
369
+ this.#languageCompartment.reconfigure(langExtension),
370
+ tobeEffect // 계산된 데코레이션 효과를 포함
371
+ ]
375
372
  });
376
-
377
- // Diff 데코레이션 적용
378
- this.#applyDiffDecorations(src1, src2);
379
373
  };
380
374
 
381
375
  disconnectedCallback() {