fix: set_field 후 저장/재오픈 시 필드 값 유실 수정#446
Conversation
rebuild_char_offsets()가 FIELD_END 갭만 생성하고 FIELD_BEGIN 갭을 누락하여, 중간 위치 필드의 FIELD_BEGIN 컨트롤이 직���화 시 텍스트 뒤로 밀림. 재오픈 시 빈 필드로 파싱되어 값이 유실됨. field_begin_at 배열을 추가하여 start_char_idx 위치에 8바��트 갭을 생성. ����� 테스트 3건 ��가. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes a persistence bug where set_field() updates were visible in-memory but lost after exportHwp() → reopen by ensuring rebuild_char_offsets() recreates the missing gap for mid-text FIELD_BEGIN markers, so serialization preserves the correct FIELD_BEGIN … text … FIELD_END ordering.
Changes:
- Add
field_begin_attracking inrebuild_char_offsets()to insert 8-code-unit gaps atstart_char_idxfor mid-text fields. - Keep existing
FIELD_ENDgap logic and update the rebuild loop to apply both begin/end gaps. - Add 3 regression tests covering mid-text begin-gap preservation, start-of-paragraph double-count avoidance, and post-
set_fieldserializability.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // FIELD_BEGIN: control_idx >= ctrls_before_text이고 start > 0인 필드의 시작 위치에 갭 필요 | ||
| let mut field_begin_at: Vec<usize> = vec![0; text_len + 1]; | ||
| for fr in ¶.field_ranges { |
| // Stream: [ColumnDef 8B] A(1) B(1) C(1) [FIELD_BEGIN 8B] X(1) Y(1) [FIELD_END 8B] | ||
| let mut para = Paragraph { |
| // 라=8(+1) 벨=9(+1) :=10(+1) ' '=11(+1) → field_begin gap +8 → N=20(+1) E=21(+1) W=22 | ||
| assert_eq!(para.char_offsets[0], 8); // 라 | ||
| assert_eq!(para.char_offsets[3], 11); // ' ' | ||
| assert_eq!(para.char_offsets[4], 20); // N — 8-byte gap after ' ' for FIELD_BEGIN | ||
| let gap = para.char_offsets[4] as i64 - (para.char_offsets[3] as i64 + 1); | ||
| assert_eq!(gap, 8); // serializer needs exactly 8 code units for FIELD_BEGIN |
- mydocs/pr/pr_446_review.md (cherry-pick 검토, 옵션 C→A→머지 변천) - mydocs/pr/pr_446_report.md (cherry-pick 머지 결정 + 한컴 2010/2020 양 환경 검증 통과) - mydocs/orders/20260429.md (PR #444 + PR #446 완료 행 추가) - rhwp-studio/e2e/issue-270-set-field-persist.test.mjs (메인테이너 후속 e2e, 155 lines) - rhwp-studio/public/samples/field-01.hwp (Vite /samples/ 라우팅 정합) 검증: 1069 passed + svg_snapshot 6/6 + issue_418 1/1 + clippy 0 + WASM + 작성자 단위 테스트 3건 + e2e 12 assertions 모두 통과 + 한컴 2010 + 한컴 2020 편집기 직접 검증 통과 (회사명 PERSIST_TEST 정상 표시) 본 검증 사이클은 메모리 feedback_self_verification_not_hancom 의 양 게이트 분리 실증 — 자기 라운드트립 (e2e) + 한컴 편집기 직접 검증. 본 e2e 패턴은 향후 컨트리뷰터들의 저장 경로 PR 검증 표준.
|
@oksure 님 PR 감사드립니다. 메인테이너가 cherry-pick 으로 devel 에 적용 완료했습니다. 본 PR 검증 사이클이 본 저장소의 메모리 원칙 (feedback_self_verification_not_hancom) 을 정확히 실증하는 사례가 됐습니다. 처리작성자 attribution 보존 + 메인테이너 후속 정정 (e2e 검증 게이트):
devel 머지 commit: 검증 — 양 게이트 분리본 PR 검증은 두 게이트 분리 통과 정황: 1. 자기 라운드트립 (rhwp e2e)
2. 한컴 편집기 직접 검증 (작업지시자 직접)
검증 게이트 통과 정황
검증 사이클의 의미 — 메인테이너 e2e 검증 표준 안내본 PR 머지 과정에서 메인테이너 결정이 옵션 C → 옵션 A → 머지 로 3차례 변경됐습니다. 이는 검증 사이클의 본질을 보여줍니다:
이 변천 자체가 메모리 e2e 패턴 향후 활용본 PR 의 메인테이너 후속 e2e ( cd rhwp-studio
CHROME_CDP=http://localhost:19222 node e2e/issue-270-set-field-persist.test.mjs --mode=host향후 set_field / FIELD / 직렬화 영역 PR 작성 시 같은 패턴 (rhwp-studio e2e + Chrome CDP + 한컴 편집기 직접 검증용 산출물) 활용 환영합니다. 매뉴얼: 본 PR 의 좋은 점
이슈 #270 도 함께 close 됩니다. 감사합니다. |
…Canvas→LayerTree 반영 (Task edwardkim#460 기반) - upstream/devel 46 commits 병합 (PR edwardkim#446 set_field fix ~ PR edwardkim#456 Canvas→LayerTree) - 소스 파일 자동 병합 성공 (layout.rs, paragraph_layout.rs, engine.rs) - orders/20260430.md add/add 충돌만 수동 해소 (Task edwardkim#460 섹션 + PR edwardkim#450 섹션 통합) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes #270
문제
set_field("필드명", "값")후exportHwp()→ 재오픈하면 필드 값이 빈 문자열로 유실됩니다. In-memory에���는 정상 동작합니다.근본 원인
rebuild_char_offsets()가 FIELD_END(0x04) 마커 갭만 생성하고 FIELD_BEGIN(0x03) 컨트롤 갭을 누락합니다.중간 위치(start_char_idx > 0) 필드의 경우:
rebuild_char_offsets가 FIELD_BEGIN 위치에 8바이트 갭을 생성하지 않음serialize_para_text가 갭이 없어 FIELD_BEGIN을 텍스트 뒤로 밀어냄수정 내용
field_begin_at배열을 추가하여start_char_idx위치에 8바이트 갭을 생성합니다. 이중 계산 방지를 위해control_idx >= ctrls_before_text && start_char_idx > 0조���으로 필터링합니다.테스트
회귀 테스트 3건 추가:
rebuild_preserves_mid_text_field_begin_gap— 중간 위치 필드 갭 보존rebuild_field_at_start_no_double_count— 문단 시작 필드 이중 계산 방지rebuild_after_set_field_creates_serializable_gap— set_field 후 직렬화 가능한 갭 생성