명령줄의 `gh`로 GitHub 이슈 본문과 댓글을 읽기 전용으로 확인하는 방법을 안내합니다. 번호나 URL로 이슈를 찾고 다른 저장소를 지정하는 방법, 댓글이 많은 경우 JSON과 `jq`로 내용을 추출하는 방법을 다룹니다.
- 이슈 본문과 댓글 읽기
- URL로 이슈 내용 확인
- 다른 저장소의 이슈 조회
Carbon 언어 서버와 이를 실행하는 VS Code 확장 개발을 안내합니다. LSP 요청 등록과 처리, 문서 유형, SemIR 텍스트 읽기, `file_test`로 요청과 응답을 검증하는 테스트 작성 및 기대값 갱신을 다룹니다.
Instructions for working on Carbon's LSP language server, including its architecture, its file_test-based tests, and the VS Code extension.
실행 점수 미검증
설치 방법 보기이 스킬은 Carbon 언어 서버와 서버를 실행하는 VS Code 확장을 다룹니다. LSP 요청을 등록하는 위치, 처리기와 문서 문맥의 역할 분담, Carbon 소스 문서와 테스트 데이터의 처리 차이를 설명합니다. 테스트에서는 file_test 입력 분할과 LSP 호출 키워드를 사용하며, 자동 갱신 스크립트로 기대 출력을 업데이트하는 방법을 안내합니다. SemIR 텍스트에서 이름을 찾는 방법, UTF-16 위치 오프셋, 테스트 통신 형식에서 주의할 점도 다룹니다.
Carbon 언어 서버에 기능을 추가하거나 기존 요청 처리 흐름을 확인할 때 활용할 수 있습니다. 테스트를 작성하거나 갱신하고, VS Code 확장이 서버를 시작하는 설정을 확인할 때도 유용합니다.
file_test를 작성해 주세요.”테스트 실행에는 bazelisk와 file_test를 사용합니다. VS Code 확장은 carbonPath 설정에 지정된 실행 파일을 stdio로 시작합니다.
이 소개문은 공개된 SKILL.md를 바탕으로 AI(OpenAI Codex)가 작성했습니다. 정확한 사양은 아래 원문을 확인하세요.
AI가 SKILL.md의 내용에서 주의가 필요한 작업과 도입 전제를 확인했습니다. 점수는 실제로 실행한 결과(실행 검증)만으로 매깁니다.
점수에는 넣지 않았습니다. 2026년 10월 26일 기준으로 AI(OpenAI Codex)가 판정했습니다. 참고용이므로 설치 전에 원문도 확인해 주세요.
설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.
This skill covers toolchain/language_server/,
which implements carbon language-server, and
utils/vscode/, the VS Code extension that launches it.
The server is built on clangd's LSP transport (clang::clangd), not on a
Carbon-specific one. That means clangd's Protocol.h types (Position,
Range, Location, Hover, MarkupContent) are the interface currency.
server.cpp / incoming_messages.cpp: message dispatch. A handler must be
registered in incoming_messages.cpp before it can be called.handle_*.cpp: one file per request family, each declaring its entry point
in handle.h.handle_initialize.cpp: the advertised capabilities. Adding a capability
changes Content-Length in every test that calls initialize, so expect
a large autoupdate diff.context.h / context.cpp: Context::File per open document, plus the
compile driver. Context::File::unit() has a CARBON_CHECK on the compile
driver, so any handler that reaches for the parse tree must first rule out
documents that were never compiled.position.h, sem_ir_index.h: mapping source positions to SemIR
instructions for real Carbon files.sem_ir_text.h, handle_sem_ir_text.h: navigation within the formatted
SemIR in a test file's // CHECK:STDOUT: lines. This is a heuristic text
index, deliberately independent of the real SemIR data structures. See
the SemIR text reader.The server handles two kinds of document, distinguished by the languageId
from textDocument/didOpen, with a content sniff as a fallback:
carbon: a real Carbon file. Compiled; diagnostics published.carbon-testdata: a test file. Not compiled, because we lack logic to
split it into one file per // --- split marker.[!IMPORTANT] A handler that assumes every file was compiled will crash on a test file. When adding one, give the test-file path an explicit early return.
There is no language-server-specific test target.
toolchain/language_server/BUILD only declares
filegroup(name = "testdata"), which is pulled into
//toolchain/testing:all_testdata and run by //toolchain/testing:file_test.
# Run just the language server tests (or any subset).
bazelisk test //toolchain/testing:file_test \
--test_arg=--file_tests=toolchain/language_server/testdata/position/hover_and_goto.carbon
# See the raw output, which is much easier to read than a test failure.
bazelisk run //toolchain/testing:file_test -- --dump_output \
--file_tests=toolchain/language_server/testdata/position/hover_and_goto.carbon
# Update expectations. Never hand-write CHECK lines.
./toolchain/autoupdate_testdata.py toolchain/language_server/testdata/...
These tests run serially, because clangd's logging is a global singleton.
The request stream is a // --- STDIN split written with the [[@LSP-*]]
keywords, and the responses land in a trailing // --- AUTOUPDATE-SPLIT.
Documents come from other splits by way of "text": "FROM_FILE_SPLIT", which
is substituted with the content of the split whose name matches the uri.
// --- position.carbon
fn Abs(n: i32) -> i32 { return n; }
// --- STDIN
[[@LSP-CALL:initialize:"capabilities": {}]]
[[@LSP-NOTIFY:textDocument/didOpen:
"textDocument": {
"uri": "file:/position.carbon",
"languageId": "carbon",
"text": "FROM_FILE_SPLIT"
}
]]
[[@LSP-CALL:textDocument/hover:
"textDocument": {"uri": "file:/position.carbon"},
"position": {"line": 0, "character": 3}
]]
[[@LSP-CALL:shutdown]]
[[@LSP-NOTIFY:exit]]
// --- AUTOUPDATE-SPLIT
Full keyword documentation is in
testing/file_test/README.md.
[!WARNING] A blank line inside the
STDINsplit breaks the JSON transport. It terminates a header block, so clangd logs a timestampedWarning: Missing Content-Length header, or zero-length message.The timestamp makes the test unreproducible, so it fails on the next run. Comment lines between messages are fine; blank lines are not. A single blank line immediately before// --- AUTOUPDATE-SPLITis also fine.
Other things worth knowing:
positionEncoding is UTF-16. A character is a UTF-16 code unit
offset, not a byte offset.locN_M
suffixes in SemIR output are 1-based. Off-by-ones here are silent: the
request succeeds and returns the wrong thing.// CHECK:STDOUT:
lines, because CHECK lines only form expectations inside the
AUTOUPDATE-SPLIT. Such a document still can't contain a literal // ---
line, which would split the enclosing test file; write it as
[[@0x2f]]/ --- name.carbon.sem_ir_text.cpp indexes the formatted SemIR inside a test file's
// CHECK:STDOUT: lines so that hover and go-to-definition work on operand
names. It is a heuristic reader, not a parser, and its correctness rests on
facts about toolchain/sem_ir/formatter.cpp and
toolchain/sem_ir/inst_namer.cpp. Re-check these if the formatter changes:
file, generated, imports, and
constants (InstNamer::GetScopeName). Everything else is @entityname.%name within its own scope and scope.%name otherwise
(InstNamer::GetNameFor). Names may contain . and may start with one,
as in %.Self.frozen.constants scope.
Formatter::FormatTypeOfInst does
llvm::SaveAndRestore file_scope(scope_, InstNamer::ScopeId::Constants),
so in %x: %foo = ... a bare %foo means constants.%foo. This does not
apply to ordinary operands or to [concrete = ...] annotations.*_decl braces hold the declared entity's scope. The braces of
%F.decl: ... = fn_decl @F [...] { ... } { ... } are lexically inside
file { }, but their names belong to @F.!with Self: switches scope without a brace, until !members:
switches it back. Brace counting alone cannot see this.specific @F(args) { } block uses @F's scope and defines nothing; each
%name => value row references an instruction of the generic.The unit tests only cover a handful of cases. To check a change against the
real corpus, drive the server over a sample of check testdata, hovering on
every %name, and compare the resolved fraction before and after. Roughly 99%
of names resolve; the residue are names the formatter references but never
emits a definition line for, such as %I.WithSelf.F, which only ever appears
inside a [symbolic = ...] annotation.
Two things to get right in such a harness:
error: Input/output error on a pipe.text=True rewrites the \r\n
framing, and Content-Length counts bytes.utils/vscode/ declares three languages in package.json:
| Language id | Applies to |
|---|---|
carbon | *.carbon |
carbon-testdata | **/testdata/**/*.carbon |
semir | *.semir |
[!NOTE] The TextMate scope for SemIR is
source.carbon-semir, but the language id issemir. Markdown code fences in hover text resolve language ids, so a fence must say```semir.
extension.ts launches the server over stdio, using the carbonPath setting
(default ./bazel-bin/toolchain/carbon). Its documentSelector controls which
files are sent to the server at all; a new document kind has to be added there
as well as in the server.
아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.
로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.
명령줄의 `gh`로 GitHub 이슈 본문과 댓글을 읽기 전용으로 확인하는 방법을 안내합니다. 번호나 URL로 이슈를 찾고 다른 저장소를 지정하는 방법, 댓글이 많은 경우 JSON과 `jq`로 내용을 추출하는 방법을 다룹니다.
Carbon 저장소에서 일하는 AI 도우미가 전용 환경 도구로 파일을 읽고 검색하고 편집하는 규칙을 안내합니다. 임시 파일을 둘 위치와 삭제 시점, 사용자 지정 프로그램을 허용하는 경우도 설명합니다.
Carbon 프로젝트에서 Bazelisk로 빌드, 테스트, 실행하는 기본 절차를 안내합니다. 대상을 지정한 검증, ASan 활성화, 시스템 의존성 변경 후 캐시 재생성도 다룹니다.
Carbon 컴파일러에 내장 함수를 추가하는 절차를 안내합니다. 함수 등록과 시그니처 검증부터 상수 평가, 런타임 호출의 LLVM IR 생성, Prelude 연결, 최소 Prelude를 이용한 개별 테스트까지 다룹니다.
Carbon 툴체인의 Bazel, C++, Carbon, Markdown, Python 파일 서식 기준을 안내합니다. 주석, 문서, 이름, 설계 원칙과 변경 내용을 설명하는 커밋 메시지 작성법도 다룹니다.
Carbon 도구 체인에서 오류와 경고 진단을 추가하거나 수정하는 방법을 안내합니다. 진단 종류 등록, 형식이 지정된 인수로 메시지 구성, 소스 위치에 라벨과 문맥 연결, 문구 규칙 및 테스트 범위 확인을 다룹니다.