mlua.nvim 플러그인 만들기
어쩌다보니 필요해졌다
이전에도 메이플스토리 월드 관련해서 깔짝댄 것이 있었지만, 이번에 mlua라는 모르는 새 나온 언어 기능을 접하고 나서 vscode만 지원하는 것이 매우 아쉬웠습니다.
그리고 하는 일 상으로 접할 일도 좀 생기다 보니 주로 쓰는 에디터인 Neovim에서도 이걸 다룰 수 있으면 좋겠다고 생각하게 되었습니다. 그렇게 삽질을 시작하게 되는데....
스탠드얼론
다행히도, 익스텐션으로 나온 부분들을 뜯어보니 TypeScript로 작성되어 이미 빌드된 JavaScript 파일들을 Node.js를 통해 랭귀지 서버로써 작동시키는 원리였습니다.
따로 진입점의 스크립트만 CLI로 실행해봐도 잘 도는걸 보니, 아무래도 다른 데 붙일수도 있을 것처럼 생겼습니다.
마침 이 이야기를 Neovim 커뮤니티에 질문해보니, 단순 언어 서버의 래퍼가 아닌 실제로 해당 기능을 구현한 것이라면 충분히 그걸 뜯어서 다른 에디터에 붙일 수도 있다는 이야기도 들었습니다.
그렇다면, 남은 것은 실행뿐입니다.
LLM의 힘을 빌려
평소에 잘 다루지 않는 순 lua를 다루는 작업을 저 혼자 하는건 무리라고 생각해서, 초장부터 상당 부분을 llm에게 의탁했습니다.
다행히도, 요새 llm들은 잘 나오는지 도움을 받아 몇 번의 실험 끝에 언어 서버를 Neovim에 연결할 수 있었습니다.
하지만, 단순히 LSP 클라이언트가 존재를 인식한 정도입니다. vsc 확장으로써 보여줬던 기능들은 분명히 있을 테지만, 그냥 연결만 된 상태였죠.
여기서 저는 단순히 이것이 닷파일에 몇 줄 써내리는 설정 수준에서 끝날게 아니라, 아예 하나 플러그인을 만들어야 한다는 쪽으로 생각이 굳어졌습니다.
될 때까지
어쨌든 최초 연결 후에는 기능을 연결하는 데 성공했다가 실패했다가를 반복하고 그럴 때마다 LLM을 갈구면서 어떻게든 방향성을 잡고 완성시켜 나갔습니다.
그 과정에서 몇 가지 문제점을 마주했습니다.
- 문법 강조
애초에, 원본 익스텐션에는 semantic token 등의 언어 서버에서의 문법 강조를 위한 기능 대신,
TypeScript의 모듈 중 하나인tmlanguage를 사용해 해결하고 있었습니다. 따라서 이걸 Neovim으로 그냥 포팅할 수는 없었습니다. 처음 접근한 방식은vim의 syntax 파일을 만드는 것이었지만, 생각보다 잘 되지 않아 토큰이 얼룩덜룩 이상하게 칠해지기 일쑤였습니다. 그러던 중 Neovim의TreeSitter를 이용한 접근을 떠올렸습니다. 뭔가 어렵다는 이미지로 굳어져 있었지만, 다행히도 원본 익스텐션에 상세가 자세히 나와있던tmlanguage설정 파일로부터 파서를 만드는 방법을 찾아서 시도했고, 다행히도 성공했습니다.
- 지연 시간
어떻게든 존재하는 기능 연결에는 성공했지만, 정작 편집을 위해
mlua파일을 켰을 떄 셋업 과정에서 엄청난 렉이 발생했습니다. 기본적으로 원본 익스텐션이 맥락을 획득하기 위해서 하위 디렉토리들을 탐색하는 동작을 나이브하게 모방한 것이 문제였습니다. 버퍼에 새로 파일이 어태치 될 때마다, 모든 디렉토리들을 돌면서 해당 동작을 반복했기에, 아무리 캐싱해봐도 상황은 나아지지 않았습니다. 원본 익스텐션은 아무래도 레이지 로딩을 통해서 일부만 조금씩 로딩해오고 있는 모양이었고, 제 플러그인 역시 비슷하게 레이지 로딩과 비동기 방식을 채택하기로 했습니다.
둘 다 어디가 해결된다 싶으면 자꾸 튀어나오는 문제들이었고, 이 문제들을 해결하기 위해 밤을 꼬박 새어야 했습니다.
하지만, 그럼에도 불구하고 플러그인을 정상적으로 동작시키는 데 성공했습니다!
https://github.com/seokgukim/mlua.nvim
기존에 사용하던 Lazy.nvim의 플러그인 로드부에 제 리포지토리를 끼워넣자니 감회가 새롭기도 합니다.
앞으로도 잘 써야지.