동적 문법 강조 Dynamic Syntax Highlighting

Posted on October 28, 2022
  1. Haskell Language Server를 적극적으로 쓰려고 보니, 지금은 꽤 의미Semantic적으로도 지원을 많이 해주는 걸로 보인다.
  2. Tree sitter를 쓰면 기존 문법 강조에, 의미 분석을 조금 가미해서 더 부가적인 정보를 색으로 표현할 수 있다.
  3. 더글라스 크락포드 교수님이 Gonad 강의 중에 nesting으로 구분해서 문법 강조를 하면 좋겠다는 아이디어를 제안했다.

위 세가지 경험을 하다 보니, 요즘 같이 위젯 끌어다 마우스로 프로그래밍 하는 세상에 텍스트 편집editing은 그리 바뀐 게 없구나란 생각이 들었습니다. 예전 모노크롬(단색) 모니터에서 컬러 모니터로 넘어 온 후, 컬러 문법 강조가 보편화 된 것처럼, 현 시스템이 실시간live으로 의미 분석하는 데 충분한 퍼포먼스가 가능하다고 보고, 아래와 같은 생각을 제안합니다.

동적 문법 강조

기존 문법 강조는 단 색의 코드보단 정보를 많이 주며 가독성을 올려주기도 하지만, 가끔은 너무 알록 달록해서 방해를 받기도 합니다. 그리고, 키워드나 의미별로 부여된 색상을 따로 인지하는 경우는 드물 것 같습니다. “아, 이건 분홍색이니 함수일거야, 이건 파란색이니 변수일거야”를 지속적으로 인지하면서 소스를 보진 않습니다. 그래서, 제 생각은 그런 “종류”를 구분하는 색깔이 아니라, 말 그대로, 현재 읽는데 “구분이 필요한” 요소들을 색깔을 달리하는 게 좋다는 의견입니다. 동적으로 말입니다.

  1. 어떤 스코프에 위치 하냐(포커스)에 따라 동일한 컬러톤을 지키며 keyword나 Identifier, Symbol등의 색을 변경한다.
  2. 품어져(nesting되어) 있는 블록으로 포커스를 옮기면(커서가 진입하면) 다른 컬러톤을 불러온다.
  3. Free변수로 바뀌어도 바인딩 될 스코프의 색깔을 그대로 유지한다.

※ 어디다, 제안한다는 건 아니고 ;-) 다른 분들의 의견을 들어보고 싶어 올립니다.

2023.3.16 추가
트리시터 관리자 중 한 분인 @kiyoon님께 이 아이디어 구현이 트리시터로 가능한가 물었는데, 와, 하루만에 Neovim 플러그인을 구현하셨습니다.
kiyoon/haskell-scope-highlighting.nvim
트리시터의 한계로 위에 얘기한 것과 완벽히 같지 않지만, 대부분 원하는 대로 작동합니다. 생각을 벗어나는 몇 개의 케이스는 아무래도 Haskell Language Server와 붙여서 해결해야 할 듯 합니다. 하스켈 하시는 분들은 한 번 써보시기 바랍니다.

아래는, Neovim, Lazy 패키지 매니저에 넣어 놓은, 현재 제가 사용 중인 설정입니다.

  {
    "kiyoon/haskell-scope-highlighting.nvim",
    ft = "haskell",
    config = function()
      require("haskell-scope-highlighting").setup {}
      vim.cmd("syntax off") -- neovim 디폴트 문법 강조 끄기
--      vim.cmd("TSDisable highlight") -- 트리시터 문법 강조 끄기
      vim.cmd("hi link @function.haskell purple")
      vim.cmd("hi link HaskellVariableDeclaredWithinFile BlueSign")
      vim.cmd("hi! link HaskellVariableNotDeclaredWithinFile Grey")
      vim.cmd("hi HaskellVariableDeclaredWithinScope guifg=lightblue")
      vim.cmd("hi HaskellVariableDeclarationWithinScope guifg=lightblue")
      vim.cmd("hi HaskellCurrentScope guibg=#343f44")
      local i = 1
      repeat -- 그라데이션 색처리를 위한 루프
        -- orange = #dc9271
        local color = string.format("%d guifg=#%02x%02x%02x",i,
          220 - (i*10)% 220,
          92 - (i*20) % 92,
          72 + (i*20) % 184
        )
        vim.cmd("hi HaskellVariableDeclaredWithinParent"..color)
        vim.cmd("hi HaskellParentScope"..i.." guibg=#2d353b")
        i = i + 1
      until (i > 10)
    end,
    dependencies = { 'nvim-treesitter/nvim-treesitter' },
  }
Github 계정이 없는 분은 메일로 보내주세요. lionhairdino at gmail.com