이 출시되었습니다! 11월의 새로운 기능 및 수정 사항을 읽어보세요.

Language Server Index Format (LSIF)

2019년 2월 19일, Dirk Bäumer 작성

체크아웃 없이 풍부한 코드 탐색

개발자는 많은 시간을 새로운 소스 코드를 작성하는 것이 아니라 코드를 읽고 검토하는 데 사용합니다. 예를 들어, GitHub와 같은 저장소에서 기존 코드베이스를 탐색하거나 동료의 Pull Request를 검토하고 싶을 수 있습니다.

일반적으로 로컬 머신으로 소스 코드를 가져오기 위해 브랜치를 체크아웃하거나 저장소를 클론한 다음, 선호하는 개발 도구를 열어야 비로소 코드를 읽고 탐색할 수 있습니다. 먼저 저장소를 클론하지 않고 이 작업을 수행할 수 있다면 얼마나 좋을까요? 소스 코드를 다운로드하지 않고도 호버 정보, 정의로 이동, 모든 참조 찾기와 같은 지능형 코드 기능을 사용할 수 있다고 상상해 보세요. 블로그 게시물 풍부한 코드 탐색 경험 미리보기는 Pull Request 검토 시나리오를 보여줍니다.

Language Server Index Format (LSIF, "else if"처럼 발음)의 목표는 로컬 소스 코드 복사본 없이도 개발 도구나 웹 UI에서 풍부한 코드 탐색을 지원하는 것입니다. 이 형식은 풍부한 코드 편집 기능을 개발 도구에 쉽게 통합할 수 있도록 하는 Language Server Protocol(LSP)과 정신적으로 유사합니다.

기존 LSP 언어 서버를 사용하면 안 될까요? LSP는 자동 완성, 입력 시 형식 지정, 풍부한 코드 탐색과 같은 풍부한 코드 작성 기능을 제공합니다. 이러한 기능을 효율적으로 제공하려면 언어 서버는 모든 소스 코드 파일이 로컬 디스크에 있어야 합니다. LSP 언어 서버는 또한 파일의 일부 또는 전체를 메모리로 읽고 추상 구문 트리를 계산하여 이러한 기능을 지원할 수 있습니다. Language Server Index Format의 목표는 이러한 요구 사항 없이 풍부한 코드 탐색 기능을 지원하기 위해 LSP 프로토콜을 보강하는 것입니다. LSIF는 언어 서버 또는 기타 프로그래밍 도구가 코드 작업 공간에 대한 지식을 내보낼 표준 형식을 정의합니다. 이 저장된 정보는 나중에 언어 서버를 실행하지 않고도 동일한 작업 공간에 대한 LSP 요청에 응답하는 데 사용할 수 있습니다.

Language Server Index Format

LSIF는 LSP를 기반으로 하며 LSP에 정의된 것과 동일한 데이터 타입을 사용합니다. 높은 수준에서 LSIF는 언어 서버 요청에서 반환된 데이터를 모델링합니다. LSP와 마찬가지로 LSIF는 프로그램 기호 정보를 포함하지 않으며 LSIF는 기호 의미(예: 기호의 정의를 만드는 것 또는 메서드가 다른 메서드를 재정의하는지 여부)를 정의하지도 않습니다. 따라서 LSIF는 LSP 접근 방식과 일치하는 기호 데이터베이스를 정의하지 않습니다.

LSIF의 기반으로 기존 LSP 데이터 타입을 사용하는 것은 LSIF가 이미 LSP를 이해하는 도구나 서버에 쉽게 통합될 수 있다는 또 다른 장점이 있습니다.

예를 살펴보겠습니다. 다음 내용이 있는 sample.ts라는 간단한 Typescript 파일로 시작합니다.

function bar(): void {}

bar() 위에 마우스를 올리면 Visual Studio Code에서 다음과 같은 호버 정보가 표시됩니다.


Hover over Bar


이 호버 정보는 LSP에서 Hover 타입을 사용하여 표현됩니다.

export interface Hover {
  /**
   * The hover's content
   */
  contents: MarkupContent | MarkedString | MarkedString[];
  /**
   * An optional range
   */
  range?: Range;
}

위 예에서 구체적인 값은 다음과 같습니다.

{
  contents: [{ language: 'typescript', value: 'function bar(): void' }];
}

클라이언트 도구는 file:///Users/username/sample.ts 문서의 {line: 0, character: 10} 위치에 대해 textDocument/hover 요청을 전송하여 언어 서버에서 호버 콘텐츠를 검색합니다.

LSIF는 언어 서버 또는 독립형 도구가 튜플 ['textDocument/hover', 'file:///Users/username/sample.ts', {line: 0, character: 10}]가 위의 호버를 해결한다고 설명하기 위해 내보내는 형식을 정의합니다. 그런 다음 데이터를 데이터베이스에 가져와 저장할 수 있습니다.

LSP 요청은 위치 기반이지만 결과는 종종 단일 위치가 아니라 범위에 대해 달라집니다. 위의 호버 예에서 호버 값은 식별자 bar의 모든 위치에 대해 동일합니다. 즉, 사용자가 barb 위에 마우스를 올리거나 barr 위에 마우스를 올릴 때 동일한 호버 값이 반환됩니다. 내보낸 데이터를 더 간결하게 만들기 위해 LSIF는 위치 대신 범위를 사용합니다. 이 예의 경우 LSIF 도구는 범위 정보를 포함하는 튜플 ['textDocument/hover', 'file:///Users/username/sample.ts', { start: { line: 0, character: 9 }, end: { line: 0, character: 12 }]를 내보냅니다.

LSIF는 이 정보를 내보내기 위해 그래프를 사용합니다. 그래프에서 LSP 요청은 엣지로 표현됩니다. 문서, 범위 또는 요청 결과(예: 호버)는 정점으로 표현됩니다. 이 형식은 다음과 같은 이점이 있습니다.

  • 주어진 코드 범위에 대해 다른 결과가 있을 수 있습니다. 주어진 식별자 범위에 대해 사용자는 호버 값, 정의 위치 또는 모든 참조 찾기에 관심이 있습니다. 따라서 LSIF는 이러한 결과를 범위와 연결합니다.
  • 추가 요청 유형 또는 결과를 사용하여 형식을 쉽게 확장할 수 있습니다. 새로운 엣지 또는 정점 종류를 추가하면 됩니다.
  • 데이터가 사용 가능해지는 즉시 내보낼 수 있습니다. 이렇게 하면 메모리에 많은 양의 데이터를 저장해야 하는 대신 스트리밍이 가능합니다. 예를 들어, 문서를 위한 데이터는 구문 분석이 진행됨에 따라 각 파일에 대해 내보내야 합니다.

호버 예의 경우 내보낸 LSIF 그래프 데이터는 다음과 같습니다.

// a vertex representing the document
{ id: 1, type: "vertex", label: "document", uri: "file:///Users/username/sample.ts", languageId: "typescript" }
// a vertex representing the range for the identifier bar
{ id: 4, type: "vertex", label: "range", start: { line: 0, character: 9}, end: { line: 0, character: 12 } }
// an edge saying that the document with id 1 contains the range with id 4
{ id: 5, type: "edge", label: "contains", outV: 1, inV: 4}
// a vertex representing the actual hover result
{ id: 6, type: "vertex", label: "hoverResult",
  result: {
    contents: [
      { language: "typescript", value: "function bar(): void" }
    ]
  }
}
// an edge linking the hover result to the range.
{ id: 7, type: "edge", label: "textDocument/hover", outV: 4, inV: 6 }

해당하는 그래프는 다음과 같습니다.

LSIF graph for a hover

LSP는 문서만 매개변수로 사용하는 요청(위치 기반이 아님)도 지원합니다. 코드 이해에 유용한 예시 요청은 모든 문서 기호 목록을 가져오거나 모든 폴딩 범위를 계산하는 것입니다. 이러한 요청은 LSIF에서 [request, document] -> result 형식으로 모델링됩니다.

다른 예를 살펴보겠습니다.

function bar(): void {
  console.log('Hello World!');
}

위 함수 bar가 포함된 문서에 대한 폴딩 범위 결과는 다음과 같이 내보내집니다.

// a vertex representing the document
{ id: 1, type: "vertex", label: "document", uri: "file:///Users/username/sample.ts", languageId: "typescript" }
// a vertex representing the folding result
{ id: 2, type: "vertex", label: "foldingRangeResult", result: [ { startLine: 0, startCharacter: 20, endLine: 2, endCharacter: 1 } ] }
// an edge connecting the folding result to the document.
{ id: 3, type: "edge", label: "textDocument/foldingRange", outV: 1, inV: 2 }

LSIF graph for a folding range result

이것들은 LSIF에서 지원하는 LSP 요청의 두 가지 예일 뿐입니다. LSIF 사양의 현재 버전은 문서 기호, 문서 링크, 정의로 이동, 선언으로 이동, 타입 정의로 이동, 모든 참조 찾기 및 구현으로 이동을 지원합니다.

여러분의 피드백이 필요합니다!

LSIF 사양에 대해 초기 진전을 이루었으며 커뮤니티와의 대화를 시작하여 우리가 무엇을 하고 있는지 배울 수 있도록 하고 싶습니다. 피드백은 Language Server Index Format 이슈에 의견을 남겨주십시오.

시작하기

LSIF를 시작하려면 다음 리소스를 참조하세요.

  • LSIF 사양 - 이 문서는 내보낸 데이터를 간결하게 유지하기 위해 수행된 추가 최적화에 대해서도 설명합니다.
  • LSIF TypeScript용 - TypeScript용 LSIF를 생성하는 도구입니다. README에는 도구 사용 지침이 나와 있습니다.
  • Visual Studio Code LSIF 확장 - LSIF JSON 덤프를 사용하여 코드 이해 기능을 제공하는 VS Code용 확장입니다. 새 LSIF 생성기를 구현하는 경우 이 확장을 사용하여 임의의 소스 코드로 유효성을 검사할 수 있습니다.
© . This site is unofficial and not affiliated with Microsoft.