rust-analyzer 블로그가 2023년에 멈춘 자리, Rust Glancer 개발자가 푸는 'Rust LSP가 어려운 이유'
- 컴파일러는 완료와 미완료가 이분법이지만 LSP는 즉시 쓸 만한 답을 내야 함. 인덱싱이 끝날 때까지 기다리면 사용자는 에디터가 거의 쓸모없는 상태로 10초에서 100초를 버텨야 함
- initialize 요청에는 설정만 검증하고 바로 응답함. rust-analyzer는 응답 뒤에 workspace 탐색을 예약하고, Rust Glancer는 첫 쿼리가 들어올 때까지 아무 일도 하지 않음
- rust-analyzer 공식 블로그가 2023년을 끝으로 멈춘 뒤, 실험적 Rust LSP Rust Glancer를 만드는 개발자가 LSP 내부 구조를 정리한 글을 올림
- LSP는 파일시스템을 모른다는 전제로 설계됐고 모든 것을 문서(document)로 다룸. 프로토콜에는 파일이 아니라 문서만 존재함
- 글 전체를 관통하는 주장은 부분 정보로 유용한 답을 만들어야 한다는 것 한 가지이며, 저자는 컴파일러 용어를 피한 건축적 개요로 글을 묶고 세부 주제는 후속 글로 미룸
Hacker News opinions
윈도우에서 COM으로 직접 함수 호출할 걸 JSON을 TCP로 주고받는 거, Eclipse에서 자바 모듈끼리 직접 호출하던 걸 떠올리면 좀 역겹더라.
LSP가 지금 모양이 된 건 electron 세상 때문임. 플러그인은 2005년 이전부터 있었고 그때가 훨씬 효율적이었음. 그래도 코드 분석과 편집을 완전히 분리한다는 논리는 일리가 있고, LSP가 언어에 안 묶인 첫 편집기 프로토콜이었던 것도 맞음. 언어별 전용 편집기가 사라진 건 아쉽다. Rust랑 Elm이 에러 메시지로 칭찬받은 걸 보면 툴링도 언어 경쟁력인데.
그건 호스트 보안이랑 안정성 때문에 그런 거 아님? IDE 플러그인이 사고 친 사례를 보면. 그리고 JSON을 TCP로 보내는 것보다 나은 IPC가 많긴 한데 결국 electron임.
직접 함수 호출하려면 플러그인 코드를 내 메모리에 올려야 하고, 그러면 내 메모리를 플러그인에 노출하는 거임. 악성이나 버그 있는 플러그인이 프로그램을 박살낼 수 있음. IPC가 자연스러운 답이고 TCP는 최적은 아니어도 어디서나 통함. LSP는 무거운 분석이 다 서버에서 돌아서 통신 오버헤드는 무시할 수준임.
TCP까지는 필요 없음. 보통 stdin/stdout 파이프 씀. 언어 서버를 별도 프로세스로 돌리는 오버헤드도 크지 않고, JSON 인코딩과 디코딩이 병목이 되진 않음. 수정 사항이나 짧은 응답만 주고받게 설계돼 있음.
LSP는 자기가 진실의 원천이라고 가정하는데 파일시스템은 직접 접근해야 하고 그것도 동기화해야 함. 끔찍한 설계임. 마이크로소프트한테 프로토콜 좀 그만 만들게 해라.
LSP 쪽은 안 봐서 모르는데, 넌 어떻게 설계할 건데?
대안을 만들 사람이 아무도 안 나섰음. 편집기들이 기다리지 않고 LSP를 받아들인 게 다행임. 안 그랬으면 아직도 한 언어만 제대로 지원했을 거임. LSP 개발자 한 명이 고생하는 대신 수만 명이 혜택 봄.
LSP가 그렇게 나쁘진 않음. IDE 기능과 언어 기능을 분리하려 한 시도임. 파일 경로 해석이 언어마다 달라서 간단하지 않고, 서버 쪽에서 소스 변경을 동기화하는 건 패킷을 작게 유지하려면 필요함. span이랑 새 텍스트만 보내면 됨. 나도 LSP 클라이언트를 처음부터 짜서 텍스트 에디터 만들었음.
Rust는 간결함 쪽으로 가서 툴링이 더 힘들어짐. impl std::fmt::Display처럼 임포트를 정규화하면 프로젝트 일부가 파싱 안 돼도 이름 바꾸기 같은 게 안정적으로 됨. 근데 glob import를 두 개 쓰면 끝임. Display가 어디서 왔는지 알 수 없음. 지금 파싱 에러가 난 모듈일 수도 있음.
Rust 2년 써보니 glob import는 드물었음. LSP 때문이라기보다 코드 문서화 때문임. mod foo 줄은 빼면 공개 범위 기본값으로 못 떨어져서 있음.
글 내용 대부분 Rust 말고 다른 언어에도 적용됨. 요청을 비동기로 처리하고 UTF-16 변환하는 건 당연한 거고 사실 어렵지도 않음. 나도 언어 서버 짰는데 제일 어려운 건 편집 중이라 문법이 안 맞는 문서에서 쓸 만한 자동완성을 주는 거였음.
IntelliJ 언어 플러그인 짜봤는데, 올바른 재귀 하강 파서부터 만들고 자주 만나는 문법 에러를 감지하고 복구하게 확장했음. 세미콜론 빠짐, :: 대신 :, 불완전 리터럴 10e 같은 것들. 에러 복구 케이스마다 테스트도 붙였음.
제목이 'LSP 만드는 게 왜 어려운가'인데 LSP 뜻을 이해하는 것만큼 어렵진 않더라. 그냥 LSP 서버 프로젝트라는 걸 보고 흥미가 식었음.
글 읽고 LSP에 서로 충돌하는 두 가지를 원한다는 걸 알았음. 코드 작성 도움은 불완전한 상태에서 최선을 다한 답이면 되고, 코드를 이해할 땐 안정적이고 완전한 분석이 필요함. rust-analyzer, rust-glancer, JetBrains RustRover 엔진을 비교한 글도 보고 싶다.