From 7b7537d268ccb0ec5863c420190bba20633cc617 Mon Sep 17 00:00:00 2001 From: Seungpyo1007 Date: Mon, 10 Aug 2026 14:56:00 +0900 Subject: [PATCH] =?UTF-8?q?=ED=81=B0=20TS=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20"TypeScript=20=EA=B0=80=20=EC=95=84?= =?UTF-8?q?=EB=8B=88=EB=8B=A4"=20=EB=9D=BC=EA=B3=A0=20=EB=8B=B5=ED=95=98?= =?UTF-8?q?=EB=8D=98=20=EA=B2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit preload 는 TS 파일이 500개 넘으면 모델을 하나도 안 만들고 끝낸다(전부-아니면- 전무). 그러면 find_symbol 이 "이 워크스페이스에는 심볼 색인이 없습니다 (TypeScript 프로젝트가 아니거나 …)" 라고 답한다. 600개짜리로 재현함. 모델은 그 말을 믿고 grep 도 안 해본다. 안 만든 것과 지원 안 하는 것을 갈라서 말하게 함. find_references 도 같은 벽이라 함께. find_symbol 이 capped 를 계산해놓고 답에 안 쓰고 있던 것도 같이. 400개까지만 훑는데 훑다 만 걸 "없음" 으로 답했다. --- ide/src/App.tsx | 16 +++++++++++++--- ide/src/editor/projectModels.ts | 11 ++++++++++- ide/src/editor/symbolIndex.ts | 10 ++++++++-- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/ide/src/App.tsx b/ide/src/App.tsx index 43a5669..1050c5d 100644 --- a/ide/src/App.tsx +++ b/ide/src/App.tsx @@ -3483,10 +3483,16 @@ export class App extends React.Component<{ playOpening?: boolean }, S> { // 색인이 아예 없는 것과 찾아봤는데 없는 것은 다르다. 섞으면 모델이 "그런 심볼은 // 없다" 고 단정하고 넘어간다. if (!r.sources.length) { - return "이 워크스페이스에는 심볼 색인이 없습니다(TypeScript 프로젝트가 아니거나 해당 언어 서버가 없음). search_files 로 찾으세요."; + // "너무 커서 색인을 포기했다" 를 "이 언어는 지원 안 함" 으로 말하면 거짓말이다. + return r.tooBig + ? "파일이 너무 많아 이 프로젝트의 심볼 색인을 만들지 않았습니다(TS/JS 500개 상한). 지원하지 않는 것이 아니라 안 만든 것입니다 — search_files 로 찾으세요." + : "이 워크스페이스에는 심볼 색인이 없습니다(TypeScript 프로젝트가 아니거나 해당 언어 서버가 없음). search_files 로 찾으세요."; } if (!r.hits.length) { - return `"${query}" 로 찾은 심볼 없음. 색인은 있습니다(${r.sources.join(", ")}) — 이름이 다르거나 색인이 없는 언어의 파일일 수 있으니 search_files 도 해 보세요.`; + // 훑다 만 것(capped)을 "없다" 로 말하면 안 된다 — 그건 안 찾아본 것이다. + return r.capped + ? `"${query}" 는 훑은 범위 안에 없었습니다. 파일이 많아 색인을 다 훑지 못했으니(${symbolIndex.TS_MODEL_CAP}개까지) 없다고 단정하지 말고 search_files 로 확인하세요.` + : `"${query}" 로 찾은 심볼 없음. 색인은 있습니다(${r.sources.join(", ")}) — 이름이 다르거나 색인이 없는 언어의 파일일 수 있으니 search_files 도 해 보세요.`; } return r.hits.map(h => `${h.rel}:${h.line}:${h.column} ${h.container ? h.container + "." : ""}${h.name}`).join(String.fromCharCode(10)); } @@ -3498,7 +3504,11 @@ export class App extends React.Component<{ playOpening?: boolean }, S> { const r = await symbolIndex.findReferences(name, inFile); this.setTool(toolId, { st: "done", note: t("sc2.noteHits", { n: r.hits.length }) }); const NL = String.fromCharCode(10); - if (r.noIndex) return "이 워크스페이스에는 심볼 색인이 없어 참조를 찾을 수 없습니다. search_files 로 찾으세요."; + if (r.noIndex) { + return symbolIndex.isTooBig() + ? "파일이 너무 많아 심볼 색인을 만들지 않아 참조를 찾을 수 없습니다(TS/JS 500개 상한). search_files 로 찾으세요." + : "이 워크스페이스에는 심볼 색인이 없어 참조를 찾을 수 없습니다. search_files 로 찾으세요."; + } if (r.ambiguous.length) { return `"${name}" 이(가) 여러 곳에 정의돼 있습니다. path 로 좁혀서 다시 부르세요:` + NL + r.ambiguous.map(a2 => ` ${a2.rel}:${a2.line}`).join(NL); diff --git a/ide/src/editor/projectModels.ts b/ide/src/editor/projectModels.ts index 3681ddf..0d68eaf 100644 --- a/ide/src/editor/projectModels.ts +++ b/ide/src/editor/projectModels.ts @@ -26,6 +26,14 @@ function uriFor(root: string, rel: string): monaco.Uri { /** 지금 열린 워크스페이스 루트. 모델이 없는 uri 를 상대 경로로 뗄 때 필요하다. */ export function currentRootPath(): string | null { return currentRoot; } +/** 파일이 너무 많아 모델을 **하나도** 안 세운 경우. + * + * 이걸 알아야 "TypeScript 프로젝트가 아니다" 와 "너무 커서 색인을 포기했다" 를 + * 가를 수 있다. 안 가르면 큰 TS 저장소에서 심볼 찾기가 "이 언어는 지원 안 함" + * 이라고 답한다 — 거짓말이고, 모델은 그 말을 믿고 grep 도 안 해 본다. */ +let preloadSkipped = false; +export function isPreloadSkipped(): boolean { return preloadSkipped; } + export function relFor(uriString: string): string | null { if (!currentRoot) return null; for (const [rel, u] of relIndex) if (u === uriString) return rel; @@ -154,7 +162,8 @@ export async function preload( const targets = entries.filter(e => !e.dir && isTsLike(e.rel) && !e.rel.split("/").some(seg => EXCLUDE.has(seg)), ); - if (targets.length > MAX_FILES) return { loaded: 0, skipped: true }; + if (targets.length > MAX_FILES) { preloadSkipped = true; return { loaded: 0, skipped: true }; } + preloadSkipped = false; let loaded = 0; const conc = 8; diff --git a/ide/src/editor/symbolIndex.ts b/ide/src/editor/symbolIndex.ts index ce2ba18..48af1f6 100644 --- a/ide/src/editor/symbolIndex.ts +++ b/ide/src/editor/symbolIndex.ts @@ -35,6 +35,8 @@ export interface SymbolAnswer { sources: ("ts" | "lsp")[]; /** 모델이 너무 많아 다 훑지 못했다 — "없다" 를 단정하면 안 되는 경우. */ capped: boolean; + /** 파일이 너무 많아 색인을 아예 만들지 않았다. "이 언어는 지원 안 함" 과 다르다. */ + tooBig: boolean; } /** 워커가 주는 파일 이름(모델 uri) → 워크스페이스 상대 경로 */ @@ -52,6 +54,7 @@ async function fromTypescript(query: string, max: number): Promise<{ hits: Symbo .filter(m => !m.isDisposed() && /typescript|javascript/.test(m.getLanguageId()) && projectModels.relFor(m.uri.toString())); if (!models.length) return { hits: [], available: false, capped: false }; + // Monaco 의 워커 프록시는 getNavigateToItems 를 노출하지 않는다(실측: "Missing // requestHandler or method"). 대신 파일별 navigation tree 를 주므로, 그걸 모아 // 워크스페이스 심볼을 만든다. 모델은 preload 가 세워 두어 안 연 파일도 들어온다. @@ -113,10 +116,13 @@ function uriToRel(uri: string): string | null { return p.replace(/\\/g, "/").slice(root.length).replace(/^\/+/, "") || null; } +/** 파일이 너무 많아 색인을 아예 안 만든 상태인가. 답을 고를 때 쓴다. */ +export function isTooBig(): boolean { return projectModels.isPreloadSkipped(); } + /** 이름으로 심볼을 찾는다. 두 통로에 모두 물어보고 합친다. */ export async function findSymbols(query: string, max = 100): Promise { const q = String(query ?? "").trim(); - if (!q) return { hits: [], sources: [], capped: false }; + if (!q) return { hits: [], sources: [], capped: false, tooBig: false }; const [ts, lsp] = await Promise.all([fromTypescript(q, max), fromLsp(q)]); const sources: ("ts" | "lsp")[] = []; if (ts.available) sources.push("ts"); @@ -133,7 +139,7 @@ export async function findSymbols(query: string, max = 100): Promise !isTestPath(h.rel)), ...hits.filter(h => isTestPath(h.rel))]; - return { hits: ordered.slice(0, max), sources, capped: ts.capped }; + return { hits: ordered.slice(0, max), sources, capped: ts.capped, tooBig: projectModels.isPreloadSkipped() }; } // ── 참조 찾기 ───────────────────────────────────────────────────────────────