Plugin UI architecture - minsuk-jang/teamproject GitHub Wiki

๊ตฌํ˜• Vim์˜ ๊ฑฐ๋Œ€ํ•œ ์ฝ”๋“œ๋ฒ ์ด์Šค์— ๊ธฐ์—ฌํ•˜๋Š” ์š”์†Œ๋Š” GUI ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์œ„ํ•œ ์ˆ˜์‹ญ ๊ฐœ์˜ widget toolkit์„ ๋ถ„๋ช…ํ•˜๊ฒŒ ์ง€์›ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Neovim์€ ์™ธ๋ถ€ ํด๋ผ์ด์–ธํŠธ์— GUI ๊ตฌํ˜„์„ ์œ„์ž„ํ•จ์œผ๋กœ์จ ์ด๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋Š” Neovim`nvim '์„ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ์„ธ์Šค๋Š” msgpack-rpc API๋ฅผ ํ†ตํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Vim ๋ช…๋ น ์‹คํ–‰
  • Vimscript ํ‘œํ˜„์‹ ํ‰๊ฐ€
  • ๋ฒ„ํผ, ์œˆ๋„์šฐ ๋ฐ ํƒญ ์กฐ์ž‘
  • ํŽธ์ง‘๊ธฐ ์ด๋ฒคํŠธ ์ˆ˜์‹  / ์ฒ˜๋ฆฌ

๊ฒŒ๋‹ค๊ฐ€ ์›๊ฒฉ API๋Š” ์‰ฝ๊ฒŒ ํ™•์žฅ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„ ๋˜์—ˆ์œผ๋ฏ€๋กœ ํ•ญ์ƒ ์ƒˆ๋กœ์šด ๊ฐ€๋Šฅ์„ฑ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

**Neovim * remote plugin * (rplugin)์€ nvim๊ณผ ๋Œ€ํ™”ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์ž…๋‹ˆ๋‹ค. ์›๊ฒฉ API ** (์ž„์˜์˜ ์ „์†ก ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ํ†ตํ•ด ๋„๋‹ฌ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค : TCP ์ฃผ์†Œ, ๋ช…๋ช… ๋œ ํŒŒ์ดํ”„, stdin / stdout ...).

ํŒŒ์ด์ฌ REPL๊ณผ client library๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋Œ€ํ™”์‹์œผ๋กœ ํ˜„์žฌ API๋ฅผ ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ ํŽธ์ง‘๊ธฐ๋ฅผ ํ™•์žฅํ•˜๋Š” ๋ฐ๋Š” ๊ทธ๋ฆฌ ์œ ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ Neovim ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๋‹ค์Œ์˜ ์˜์‚ฌ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค :

while true
    handle(next_vim_event())

์ƒ๊ฐํ•ด ๋ณด๋ฉด legacy Vim ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ํ˜„์žฌ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”๋Š” ๊ฝค ๋งŽ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํฅ๋ฏธ์žˆ๋Š” ์ผ์ด ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค ์ž๋™ ๋ช…๋ น์„ ๋“ฑ๋กํ•˜์—ฌ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ฐจ์ด์ ์€ Neovim์ด ์ด๋ฒคํŠธ๋ฅผ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๋กœ ์ „ํŒŒํ•˜๊ณ  ๋น„๋™๊ธฐ ์ ์œผ๋กœ ๋ช…๋ น์„ ์ˆ˜์‹  ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํ”Œ๋Ÿฌ๊ทธ์ธ์— ๋Œ€ํ•œ ํฅ๋ฏธ๋กœ์šด ๊ฐ€๋Šฅ์„ฑ์ด ์—ด๋ฆฝ๋‹ˆ๋‹ค.

  • ๊ทธ๋“ค์€ ์–ด๋–ค ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋กœ๋„ ์ž‘์„ฑ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์šด์˜ ์ฒด์ œ์™€ ๋” ์ž˜ ํ†ตํ•ฉ๋˜๋Š” ๊ณ ๊ธ‰ ์–ธ์–ด๋กœ ์ž‘์„ฑ๋œ ์ตœ์‹  GUI (์˜ˆ : Windows์˜ C # / WPF ๋˜๋Š” OS X์˜ Ruby / Cocoa).
  • ๋ฒ„๊ทธ๊ฐ€ ์žˆ์–ด ํŽธ์ง‘๊ธฐ(์„œ๋ฒ„)๊ฐ€ ์ถฉ๋Œ ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์ ์–ด ๋ณด์•ˆ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.
  • ํŽธ์ง‘๊ธฐ๋ฅผ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ  ๋ฐฐ๊ฒฝ ์ž‘์—…์„ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • GUI๋กœ ์ง์ ‘ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜์žˆ๋Š” ** ๋งž์ถค ์ด๋ฒคํŠธ **๋ฅผ ๋‚ด๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (Sublime์˜ minimap๊ณผ ๊ฐ™์€ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ๊ฐ€๋Šฅ)
  • ์—ฌ๋Ÿฌ ์›๊ฒฉ GUI๋ฅผ ์—ฐ๊ฒฐ / ๋ถ„๋ฆฌํ•˜์—ฌ ํŽธ์ง‘ ์„ธ์…˜์„ ๊ณต์œ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • headless ํ…Œ์ŠคํŠธ ๊ฐ„์†Œํ™”.
  • ํŽธ์ง‘๊ธฐ๋ฅผ ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋žจ์— ํฌํ•จํ•˜๊ธฐ. ์‚ฌ์‹ค, GUI๋Š” ๋„ค๋น„๊ฒŒ์ด์…˜์„ ๋‚ด์žฅํ•œ ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

UI ํ”„๋กœ๊ทธ๋žจ

UI๋Š” ์‚ฌ์šฉ์ž์™€ ํŽธ์ง‘๊ธฐ ์‚ฌ์ด์˜ ๋‹ค๋ฆฌ ์—ญํ• ์„ ํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ์€ UI ํ”„๋กœ๊ทธ๋žจ์˜ ์˜์‚ฌ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

while true
    handle(next_vim_or_user_event())

์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ (ํ‚ค ๋ˆ„๋ฆ„, ๋งˆ์šฐ์Šค ํด๋ฆญ ๋“ฑ)๋ฅผ ์ฒญ์ทจํ•˜๊ณ , ์ด๋ฅผ ์—ฐ๊ฒฐ๋œ Neovim ์žฅ์น˜๋กœ ๋ณ€ํ™˜ ํ•œ ๋‹ค์Œ ์‚ฌ์šฉ์ž์—๊ฒŒ * ๋‹ค์‹œ ๊ทธ๋ฆฌ๊ธฐ * ์ด๋ฒคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์„ ์ œ์™ธํ•˜๊ณ ๋Š” UI๊ฐ€ ์•„๋‹Œ ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ๊ฑฐ์˜ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ์ƒ˜ํ”Œ process tree์ž…๋‹ˆ๋‹ค.

Neovim ------> GUI 1 (attach/detach to running instance)
  |  |
  |   `------> GUI 2 (communicating on a different socket or transport 
  |                   mechanism, but sharing the same session with GUI 1)
   `--> Plugin 1
  |
   `--> Plugin 2
  |
   `--> Plugin 3

๋‹ค์Œ์€ nvim ์‹œ์ž‘ ํ”„๋กœ์„ธ์Šค์˜ ๊ฐœ์š”์ž…๋‹ˆ๋‹ค :

  1. ์†Œ์ผ“ ๋˜๋Š” TCP ์ฃผ์†Œ์—์„œ ์ˆ˜์‹  ๋Œ€๊ธฐ (๋ฌด์‹œํ•˜์ง€ ์•Š๋Š” ํ•œ ๋ฌด์ž‘์œ„)
    • UI๋Š” nvim์— ๋Œ€ํ•œ ๋ช…๋ น ํ–‰ ์ธ์ž๋กœ ์ง€์ • ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ~ / .config / nvim / init.vim์„ ์ฝ์œผ์‹ญ์‹œ์˜ค.
    • ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ฐœ๊ฒฌ (UI ํฌํ•จ)
  3. ์ฒญ์ทจ ์ฃผ์†Œ๋ฅผ ์ „๋‹ฌํ•˜์—ฌ UI ํ”„๋กœ๊ทธ๋žจ์„ ์‹œ์ž‘ํ•˜์‹ญ์‹œ์˜ค.
  4. ๋ชจ๋“  ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ์–‘๋ฐฉํ–ฅ ์˜์‚ฌ ์†Œํ†ต ์ฑ„๋„์„ ์—ฝ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ๊ฐ€์ƒ GUI ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

gui -> vim: {"id": 1, "method": "initClient", 
             "params": {"size": {"rows": 20, "columns": 25}}}
vim -> gui: {"id": 1, "result": {"clientId": 1}}
vim -> gui: {"method": "redraw", 
             "params": {"clientId": 1, "lines": {"5": "Welcome to neovim!"}}}
gui -> vim: {"id": 2, "method": "keyPress", 
             "params": {"keys": ["H", "e", "l", "l", "o"]}}
vim -> gui: {"method": "redraw", 
             "params": {"clientId": 1, "lines": {"1": "Hello", "5": ""}}}