diff --git a/README.md b/README.md index 0e86b0a..e2e28f7 100644 --- a/README.md +++ b/README.md @@ -137,12 +137,17 @@ files, and the shim has no custom editors and no debug adapter API. Those three `WorkspaceEdit` throws, and the other two are absent from the API object, so calling one is a `TypeError` rather than a quiet nothing. -One gap does not behave that way, and saying otherwise here was worse than the gap itself: -**`createTextEditorDecorationType` returns a working-looking handle and `setDecorations` -accepts ranges, and nothing is ever drawn.** An extension doing inline blame or coverage -highlighting gets a successful answer and no pixels. It is the one place left with the shape -this project spent a release removing; it is named here until it is built rather than -described as something it is not. +Decorations used to sit in that list as the one gap that answered successfully and drew +nothing. They now draw: `createTextEditorDecorationType` compiles the requested styling into +a real rule, `setDecorations` puts it on the Monaco editor, and passing an empty array clears +what that type drew. Inline blame and coverage highlighting work, `before`/`after` content +included. + +Fixing it surfaced the reason such an extension would have appeared broken anyway: +`onDidChangeActiveTextEditor` fired before the editor existed, and the notification was +dropped on the way out, so opening a file delivered either nothing or the *previous* file's +editor. Extensions that draw on editor changes — which is most of them — were being handed +the wrong editor. The event now waits for the pane and fires once per file. ## Contributing diff --git a/ide/src/editor/MonacoPane.tsx b/ide/src/editor/MonacoPane.tsx index ff3df98..949758d 100644 --- a/ide/src/editor/MonacoPane.tsx +++ b/ide/src/editor/MonacoPane.tsx @@ -60,7 +60,19 @@ export interface PaneApi { editor: monaco.editor.IStandaloneCodeEditor; save: () => Promise; } -export const paneRegistry: { panes: Map; focused: PaneApi | null } = { +/** 페인 하나가 실제로 뜬 뒤에 부른다. + * + * 확장에게 "이 편집기가 활성이다" 를 알리는 시점이 여기여야 한다. 예전엔 openFile 이 + * setState 전에 알려서, 그 사건을 받은 확장이 곧장 setDecorations 를 불러도 붙일 + * 편집기가 아직 없었다 — 조용히 아무 일도 안 일어났다. 데코레이션을 쓰는 확장은 + * 거의 다 이 사건에 매달려 그린다. + * + * 훅 필드로 두는 이유는 순환 임포트를 피하기 위해서다(extHost 가 여기를 읽는다). */ +export const paneRegistry: { + panes: Map; + focused: PaneApi | null; + onReady?: (rel: string) => void; +} = { panes: new Map(), focused: null, }; @@ -193,6 +205,7 @@ function MonacoPaneImpl({ root, rel, onDirtyChange, onSaved, onConfirm, onStatus const api: PaneApi = { rel, editor, save }; paneRegistry.panes.set(rel, api); + try { paneRegistry.onReady?.(rel); } catch { /* 확장이 던져도 페인은 살아 있어야 한다 */ } editor.onDidFocusEditorWidget(() => { paneRegistry.focused = api; const p = editor!.getPosition(); diff --git a/ide/src/ext/decoStore.ts b/ide/src/ext/decoStore.ts new file mode 100644 index 0000000..a92cf56 --- /dev/null +++ b/ide/src/ext/decoStore.ts @@ -0,0 +1,91 @@ +// 데코레이션 타입을 실제로 화면에 붙이는 쪽. 변환 규칙은 decorations.ts 에 있고, +// 여기는 스타일시트 한 장과 편집기별 적용분을 들고 있는 살림꾼이다. +// +// 스타일시트가 한 장인 이유: 타입마다