Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions ide/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand All @@ -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);
Expand Down
11 changes: 10 additions & 1 deletion ide/src/editor/projectModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
10 changes: 8 additions & 2 deletions ide/src/editor/symbolIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export interface SymbolAnswer {
sources: ("ts" | "lsp")[];
/** 모델이 너무 많아 다 훑지 못했다 — "없다" 를 단정하면 안 되는 경우. */
capped: boolean;
/** 파일이 너무 많아 색인을 아예 만들지 않았다. "이 언어는 지원 안 함" 과 다르다. */
tooBig: boolean;
}

/** 워커가 주는 파일 이름(모델 uri) → 워크스페이스 상대 경로 */
Expand All @@ -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 가 세워 두어 안 연 파일도 들어온다.
Expand Down Expand Up @@ -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<SymbolAnswer> {
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");
Expand All @@ -133,7 +139,7 @@ export async function findSymbols(query: string, max = 100): Promise<SymbolAnswe
}
// 테스트 파일은 뒤로. describe("이름", …) 이 심볼로 잡혀 첫 답이 테스트가 되곤 했다.
const ordered = [...hits.filter(h => !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() };
}

// ── 참조 찾기 ───────────────────────────────────────────────────────────────
Expand Down
Loading