언어 서비스
이 페이지에서

언어 서비스

컴파일러 프런트엔드와 에디터 사이에는 텍스트와 위치를 받아 데이터를 돌려주는 계층이 있습니다. 이것이 언어 서비스입니다. DOM이나 Node API는 건드리지 않습니다. 지금은 Playground가 Monaco 에디터를 통해 진단, 자동 완성, 호버를 여기에서 읽고, VS Code 확장은 TypeScript 서버 플러그인을 통해 같은 답을 읽습니다. 그래서 둘이 서로 어긋날 수 없습니다. 다른 에디터를 위한 언어 서버는 아직 공개하지 않았습니다.

계층

프런트엔드는 "use typeshade" 파일을 파싱하고 타입, 구조체, 바인딩을 검사한 뒤 소스 위치가 붙은 진단을 보고합니다. 언어 서비스는 두 층 위에 놓입니다. 하나는 그 프런트엔드이고, 다른 하나는 TypeShade 전역을 선언한 앰비언트 선언 위에서 돌아가는 TypeScript 언어 서비스입니다. 문서를 uri로 들고 있다가 그 문서에 관한 요청에 답합니다. 어댑터는 그 위에서 의미와 무관한 일만 맡습니다. Playground의 Monaco 어댑터는 좌표를 바꾸고 에디터의 마커를 관리하며, LSP 서버는 같은 답을 JSON-RPC로 옮길 뿐입니다. TypeShade에 관한 판단은 서비스 안에 두고, 어댑터는 형식만 바꿉니다.

요청

에디터가 보내는 요청은 문서 API 하나로 답합니다.

  • 진단 TypeScript 진단과 TypeShade 진단을 한 목록으로 돌려줍니다. 항목마다 typeshade 또는 typescript라는 source와 코드가 붙어 있어 어댑터가 둘을 구분할 수 있고, TypeScript 구문 오류는 TypeScript 코드로 한 번만 나타납니다.
  • 자동 완성 범위 안의 심볼과 키워드, 그리고 문맥에 맞는 TypeShade 항목을 제안합니다. @ 뒤에는 속성 이름, @builtin(" 안에는 내장 입력 이름, 타입 자리에는 GPU 타입 이름, 그리고 벡터와 진입 함수의 스니펫입니다.
  • 호버 심볼의 요약 정보를 보여 줍니다. TypeScript가 number라고 할 자리에 TypeShade 타입 이름을 쓰고, GPU 타입, 속성, 내장 입력의 설명을 붙입니다.
  • 시그니처 도움말 커서 아래 함수의 시그니처와 지금 입력하는 매개변수입니다.
  • 정의와 참조 심볼이 선언된 곳과 쓰인 곳을 서비스가 들고 있는 모든 문서에서 찾습니다.
  • 문서 심볼 문서의 함수, 구조체, 필드, 리소스를 개요로 정리하고, 진입 함수에는 스테이지를 표시합니다.
  • 이름 바꾸기 심볼을 쓰는 모든 문서에 적용할 편집 목록을 돌려줍니다. 그 위치의 이름을 바꿀 수 있는지 먼저 확인합니다.
  • 시맨틱 토큰 문서 순서대로 늘어놓은 토큰입니다. GPU 타입, 진입 함수, 리소스, @builtin(...) 안의 이름에는 종류를 표시합니다.
  • 컴파일 결과 문서를 컴파일한 WGSL 또는 GLSL을 출력 창이 요청할 때만 만듭니다. 진단은 셰이더 텍스트를 만들지 않으므로 키 입력마다 백엔드가 돌지 않습니다.

문서와 위치

typeshade/language-service 하위 경로에서 createTypeshadeLanguageService를 가져오고, 문서를 uri와 텍스트, 선택적인 버전으로 엽니다. 내용이 바뀔 때마다 전체 텍스트로 갱신하고, 에디터가 문서를 닫으면 함께 닫습니다. 다른 메서드는 모두 uri를 받고, 필요한 경우 위치를 함께 받습니다. 서비스 안에는 비동기 동작이 없으며, 오래된 버전의 결과를 버리는 일은 어댑터가 맡습니다.

위치는 줄과 문자의 쌍이며 둘 다 영에서 시작하고, 문자는 UTF-16 코드 단위로 셉니다. 범위는 끝을 포함하지 않는 반개구간입니다. LSP가 쓰는 규약 그대로이므로 언어 서버는 필드를 그대로 넘기면 됩니다. Monaco는 하나부터 세기 때문에 Playground의 어댑터가 자기 쪽에서 하나를 더하고, 돌아올 때 다시 뺍니다. 두 좌표계가 만나는 곳은 그 어댑터 하나뿐입니다.

문서는 상대 경로로 다른 셰이더 파일을 가져올 수 있고, 패키지의 파일은 패키지 이름으로 가져옵니다. 패키지는 Node가 찾는 방식대로 node_modules에서 찾습니다(규칙 3.9). 서비스는 그 가져오기를 compile()이 따르는 규칙대로, 또는 넘겨준 resolveImport로 문서의 uri에 대해 해석합니다. 열지 않은 파일은 readDocument로 읽으며, 이 함수는 파일의 텍스트를 돌려주고 그런 파일이 없으면 undefined를 돌려줍니다. 각 패키지의 package.json도 이 함수에 요청합니다. compile()도 같은 두 옵션을 받으므로 편집기와 컴파일러는 한 프로그램을 읽고, 둘 다 따라갈 수 없는 가져오기는 양쪽에서 TS8072가 됩니다.

패키지 구성

이 서비스는 typescript 위에서 돌아가며, 패키지는 이것을 필수 피어 의존성으로 둡니다. compile()이 "use typeshade" 파일을 TypeScript 파서로 읽기 때문에 기본 진입점에도 필요합니다. 그래서 컴파일러만 가져오는 프로그램도 TypeScript를 함께 설치하고, typeshade/language-service는 같은 패키지를 그대로 씁니다.

예제

호스트가 문서 하나를 열고, 진단과 한 위치의 호버를 요청하고, 출력 창을 위해 컴파일합니다.

editor.ts
import { createTypeshadeLanguageService } from 'typeshade/language-service'
// readDocument reads a file a document imports that the editor has not opened.
const service = createTypeshadeLanguageService({ readDocument: (uri) => files.get(uri) })
const uri = 'file:///hello.shade.ts'
service.openDocument(uri, source)
const diagnostics = service.getDiagnostics(uri)
const hover = service.getHover(uri, cursor)
const output = service.getCompiledOutput(uri, 'wgsl')
service.updateDocument(uri, nextSource)
service.closeDocument(uri)

더 읽을 자료

Playground는 브라우저에서 돌아가는 이 서비스입니다. 컴파일러 저장소의 설계 문서는 고정한 커밋 기준으로 규약, 어댑터 계약, 작업 순서를 기록합니다.

이 페이지 편집 문제 보고