본문
VSCode에서 PlantUML 사용하기 #1. MPE Extension
plantuml 덕후이기 떄문에, 직접 작성한 standalone 프로그램부터 atom등의 다양한 ide 프로그램까지 plantuml을 지원한다는 모든 프로그램들을 사용해봤다. 그리고 수년간의 삽질끝에 eclipse에서 작업하는것이 가장 편하고 빠르다는것을 발견하여 한동안 정착중이었다. 이번에 개발서버환경이 바뀌면서 환경설정을 하다가 왠지모르게 이번에는 vscode를 좀 더 사용해보고 싶은 생각이 들었다. 그래서 역시나 가장 유명한 jebbs의 plantuml extension을 설치했는데 지난번엔 없었던 몇몇 기능이 추가된것을 보았다. 꽤 맘에들어서 extension을 수정해서 사용했는데 어쩌다보니 소스를 날려먹었다. (다음에 어떤것을 수정했는지 다시 살펴보는 글을 쓸예정)
다시 수정하기 귀찮아서 이참에 다른 extension이 없나 확인하다 이 글의 주인공인 Markdown Preview Enhanced (이하 MPE)를 찾게되었다. 이것 역시 옛날에 사용해 봤었는데 사용하기 번거로워서 쓰다 만 기억이 있었다. 이미 다른 extension도 수정했겠다, 그래서 이참에 이것도 수정해보기로 하였다. 설치는 아래 페이지 또는 extension에서 Markdown Preview Enhanced 을 검색하면 바로 나온다. 현재기준으로 약 80만명이 다운받은 인기 extension이다. 아래에 사용자 매뉴얼도 링크한다.
https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced
https://shd101wyy.github.io/markdown-preview-enhanced/
이 extension은 plantuml을 svg 형태의 미리보기를 지원해서 좀더 다양한 기능을 제공해준다. (jebbs는 이미지기반) 또한 신기한 기능으로, 정밀하진 않지만 스크롤 sync 기능이 있어서 소스의 스크롤바를 아래로 내리면 다이어그램도 같이 내려가기떄문에 확인이 편하다. 하지만 개인적으로 느끼는 최대단점은, 물론 markdown의 특징때문이지만, code chunk/block라는 개념이 들어가있다는것이다. ```으로 블럭을 지정해서 그 안에있는 내용을 렌더링해주는 기능인데 (사용자가 정의한 명령도 추가할 수 있다) 확장성에 상당히 좋은 기능이지만, 어짜피 plantuml위주로만 사용할 나로써는 기존 작업파일들을 변경해야하는 호환성문제를 일으킨다. 그래서 extension을 수정하여, ```으로 plantuml 코드를 감싸지 않더라도 plantuml 코드를 처리하는것이 필요했다. 그 과정을 아래에 정리한다.
https://github.com/shd101wyy/vscode-markdown-preview-enhanced
소스코드는 여기에 들어있다. 그런데 src 폴더로 들어가면 4개의 파일밖에 없다. 이것은 vscode의 npm을 통해 라이브러리를 엄청나게 사용하기 떄문에 가능한일이다. extension.ts -> preview-content-provider.ts의 흐름으로 진행되고, 거기서부터 import * as mume from "@shd101wyy/mume"; 로 이어지는 라이브러리 링킹이 시작된다. mume 라이브러리는 ide-agnostic한 library로 atom같은 다른 ide에서도 끌어와서 사용할 수 있다. 이건 또 markdown-it 및 markdown-it-plantuml과 연결되어있는데 이에대해서는 나중에 좀더 자세히 다루도록 한다.
https://github.com/shd101wyy/mume
중요한 부분은 markdown-engine.ts의 parseMD() 부분이다. ide로부터 받아온 text를 다른 함수를 통해 처리하고 이를 되돌려주는 부분이다. 실제로 text를 처리하는 부분은 transformer.ts의 transformMarkdown()함수이다. 이것은 text를 한줄한줄 읽어서 특정 라인에 대해 어떻게 처리할지 파악하고 그에맞는 처리를 수행하는 부분이다. 뒤에 어떻게 돌아가는지는 잘 모르지만, 우선 원하는 바를 이루기 위해 아래와 같은 코드를 이 함수안의 if(inCodeBlock)앞에 넣었다. 결국 사용자가 ```puml을 직접 입력하는것 대신, @startuml이 보이면 그 앞에 ```puml을 넣어주는 구조다. 그리고 이것은 매우 잘 동작했다.
if (line.match(/\@startuml/)) {
lastOpeningCodeBlockFence = currentCodeBlockFence;
inCodeBlock = !!lastOpeningCodeBlockFence;
line = "`\`\`puml\n" + line;
}
else if (line.match(/\@enduml/)) {
lastOpeningCodeBlockFence = null;
line = line + "\n`\`\`";
}
this.task = child_process_1.spawn("java", [
"-Djava.awt.headless=true",
"-DPLANTUML_LIMIT_SIZE=16384",
"-DMACRO_FILE=macros.txt",
"-Dplantuml.include.path=" + this.fileDirectoryPath,
댓글