"decoration 은 소스를 안 바꾸고 그 위에 칠하기만 해. 그 성질 하나 덕에 메커니즘 하나로 전부 해낼 수 있어."
두 종류의 decoration
CodeMirror 6 이 소스 위에 시각 레이어를 얹는 방법은 딱 두 가지야. mark decoration 은 텍스트 범위에 스타일을 붙여. 4번부터 8번 문자까지 bold, 12번부터 16번까지 빨간 밑줄, 이런 식이지. widget decoration 은 특정 위치에 UI 조각을 끼워 넣어. 단어 뒤에 붙는 여백 노트나 줄 아래 통째로 들어가는 제안 블록 같은 것들. mark 는 이미 있는 걸 꾸미고, widget 은 없던 걸 집어넣어. 이 둘이면 기능이 원할 법한 '뭔가 더 보여주기' 는 거의 다 커버돼.
소스 위에 칠할 뿐, 절대 건드리지 않아
decoration 을 강력하게 만드는 건 그게 안 하는 일이야. 문서를 절대 안 바꾸거든. bold mark 가 텍스트에 서식 문자를 집어넣는 게 아니라, 이미 거기 있는 문자를 bold 로 렌더할 뿐이야. widget 도 파일에 뭘 쓰지 않고 화면의 한 지점에 UI 를 칠할 뿐이고. 소스 문자열은 그대로 남아. decoration 은 편집이 있을 때마다 문서에서 DecorationSet 을 다시 계산하는 StateField 안에 살아.
const decoField = StateField.define<DecorationSet>({
create: () => Decoration.none,
update: (deco, tr) => buildDecorations(tr.state), // 범위 -> mark + widget
provide: (f) => EditorView.decorations.from(f),
});
원시 요소 하나로 '더 보여주기' 기능 전부
요령은 전부 여기 있어. decoration 이 모델을 안 건드리고 레이어만 얹으니까, '사용자 텍스트 위에 뭔가 더 보여주기' 로 표현되는 기능이라면 무엇이든 같은 원시 요소로 정리돼. 범위 집합을 계산하고, mark 나 widget 을 붙이고, 렌더는 CM6 한테 맡기면 끝이야. Live Preview 는 범위를 mark 로 바꾸고, voice underline 은 범위를 mark 와 widget 으로 바꾸고, CMD+K 는 제안을 담은 widget 을 만들어. 겉으로는 상관없어 보이는 세 기능이 밑에서는 입력만 다른 같은 계산인 거지. 이 트랙 전체가 여기서 자라 나와.