Lua에서 neovim 플러그인용 UI를 만드는 방법

40685 단어 vimneovimtutoriallua
마지막article에서는 부동 창을 사용하여 Lua에서 플러그인을 만드는 기본 사항을 살펴보았습니다. 이제 보다 전통적인 접근 방식이 필요합니다. 편리한 측면 탐색에서 마지막으로 연 파일을 표시하는 간단한 플러그인을 만들어 봅시다. 인터페이스 학습에 집중하면서 이러한 목적으로 vim 기본 oldfiles 목록을 사용할 것입니다. 다음과 같이 표시됩니다.



이전 문서article를 읽지 않았다면 이 문서를 읽을 것을 적극 권장합니다. 이 문서는 이전 문서의 아이디어를 확장하고 비교할 때 새로운 내용으로 가득 차 있기 때문입니다.

플러그인 창



좋습니다. oldfiles 목록이 표시될 첫 번째 창을 만드는 함수를 작성하는 것으로 시작해야 합니다. 그러나 먼저 스크립트의 기본 범위에서 탐색 창과 버퍼 참조를 포함하는 bufwin와 탐색을 연 위치를 기억하는 start_win의 세 가지 변수를 선언합니다. 플러그인 기능에서 자주 사용할 것입니다.

-- It's our main starting function. For now we will only creating navigation window here.
local function oldfiles()
  create_win()
end

local function create_win()
  -- We save handle to window from which we open the navigation
  start_win = vim.api.nvim_get_current_win()

  vim.api.nvim_command('botright vnew') -- We open a new vertical window at the far right
  win = vim.api.nvim_get_current_win() -- We save our navigation window handle...
  buf = vim.api.nvim_get_current_buf() -- ...and it's buffer handle.

  -- We should name our buffer. All buffers in vim must have unique names.
  -- The easiest solution will be adding buffer handle to it
  -- because it is already unique and it's just a number.
  vim.api.nvim_buf_set_name(buf, 'Oldfiles #' .. buf)

  -- Now we set some options for our buffer.
  -- nofile prevent mark buffer as modified so we never get warnings about not saved changes.
  -- Also some plugins treat nofile buffers different.
  -- For example coc.nvim don't triggers aoutcompletation for these.
  vim.api.nvim_buf_set_option(buf, 'buftype', 'nofile')
  -- We do not need swapfile for this buffer.
  vim.api.nvim_buf_set_option(buf, 'swapfile', false)
  -- And we would rather prefer that this buffer will be destroyed when hide.
  vim.api.nvim_buf_set_option(buf, 'bufhidden', 'wipe')
  -- It's not necessary but it is good practice to set custom filetype.
  -- This allows users to create their own autocommand or colorschemes on filetype.
  -- and prevent collisions with other plugins.
  vim.api.nvim_buf_set_option(buf, 'filetype', 'nvim-oldfile')

  -- For better UX we will turn off line wrap and turn on current line highlight.
  vim.api.nvim_win_set_option(win, 'wrap', false)
  vim.api.nvim_win_set_option(win, 'cursorline', true)

  set_mappings() -- At end we will set mappings for our navigation.
end

그리기 기능



좋습니다. 창이 생겼으니 이제 표시할 항목이 필요합니다. 이전에 연 파일의 경로를 저장하는 vimoldfiles 특수 변수를 사용합니다. 스크롤하지 않고 표시할 수 있는 만큼 항목을 가져오지만 물론 스크립트에서 원하는 만큼 가져올 수 있습니다. 탐색 콘텐츠를 새로 고치는 데 사용할 수 있으므로 이 함수redraw를 호출합니다. 파일 경로는 길 수 있으므로 작업 디렉토리에 상대적으로 만들려고 합니다.

local function redraw()
  -- First we allow introduce new changes to buffer. We will block that at end.
  vim.api.nvim_buf_set_option(buf, 'modifiable', true)

  local items_count =  vim.api.nvim_win_get_height(win) - 1 -- get the window height
  local list = {}

  -- If you using nightly build you can get oldfiles like this
  local oldfiles = vim.v.oldfiles
  -- In stable version works only that
  local oldfiles = vim.api.nvim_get_vvar('oldfiles')

  -- Now we populate our list with X last items form oldfiles
  for i = #oldfiles, #oldfiles - items_count, -1 do

    -- We use build-in vim function fnamemodify to make path relative
    -- In nightly we can call vim function like that
    local path = vim.fn.fnamemodify(oldfiles[i], ':.')
    -- and this is stable version:
    local path = vim.api.nvim_call_function('fnamemodify', {oldfiles[i], ':.'})

    -- We iterate form end to start, so we should insert items
    -- at the end of results list to preserve order
    table.insert(list, #list + 1, path)
  end

  -- We apply results to buffer
  vim.api.nvim_buf_set_lines(buf, 0, -1, false, list)
  -- And turn off editing
  vim.api.nvim_buf_set_option(buf, 'modifiable', false)
end

이제 주요 기능을 업데이트할 수 있습니다. 또한 여러 탐색 창을 여는 것을 방지하는 코드를 추가할 것입니다. 이를 위해 플러그인 창이 이미 존재하는지 확인하는 nvim_win_is_valid를 사용할 수 있습니다.

local function oldfiles()
  if win and vim.api.nvim_win_is_valid(win) then
    vim.api.nvim_set_current_win(win)
  else
    create_win()
  end

  redraw()
end

파일 열기



이제 오래된 파일을 볼 수 있지만 열 수 있다면 훨씬 더 편리할 것입니다. 사용자가 5가지 방법으로 파일을 열 수 있습니다! 새 탭, 가로 또는 세로 분할, 현재 창 및 탐색에 초점을 맞추는 미리보기 모드에서.

현재 창에서 파일을 열어 시작하겠습니다. 두 가지 시나리오를 준비해야 합니다.
  • 사용자가 탐색을 여는 창에서 파일 열기.
  • 파일을 열기 위한 새 창을 만들 때 시작 창을 닫습니다.

  • local function open()
      -- We get path from line which user push enter on
      local path = vim.api.nvim_get_current_line()
    
      -- if the starting window exists
      if vim.api.nvim_win_is_valid(start_win) then
        -- we move to it
        vim.api.nvim_set_current_win(start_win)
        -- and edit chosen file
        vim.api.nvim_command('edit ' .. path)
      else
        -- if there is no starting window we create new from lest side
        vim.api.nvim_command('leftabove vsplit ' .. path)
        -- and set it as our new starting window
        start_win = vim.api.nvim_get_current_win()
      end
    end
    
    -- After opening desired file user no longer need our navigation
    -- so we should create function to closing it.
    local function close()
      if win and vim.api.nvim_win_is_valid(win) then
        vim.api.nvim_win_close(win, true)
      end
    end
    
    -- Ok. Now we are ready to making two first opening functions
    
    local function open_and_close()
      open() -- We open new file
      close() -- and close navigation
    end
    
    local function preview()
      open() -- WE open new file
      -- but in preview instead of closing navigation
      -- we focus back to it
      vim.api.nvim_set_current_win(win)
    end
    



    -- To making splits we need only one function
    local function split(axis)
      local path = vim.api.nvim_get_current_line()
    
      -- We still need to handle two scenarios
      if vim.api.nvim_win_is_valid(start_win) then
        vim.api.nvim_set_current_win(start_win)
        -- We pass v in axis argument if we want vertical split
        -- or nothing/empty string otherwise.
        vim.api.nvim_command(axis ..'split ' .. path)
      else
        -- if there is no starting window we make new on left
        vim.api.nvim_command('leftabove ' .. axis..'split ' .. path)
        -- but in this case we do not need to set new starting window
        -- because splits always close navigation 
      end
    
      close()
    end
    
    

    그리고 결국 새 탭에서 가장 간단한 열기.

    local function open_in_tab()
      local path = vim.api.nvim_get_current_line()
    
      vim.api.nvim_command('tabnew ' .. path)
      close()
    end
    

    모든 것이 작동하려면 키 매핑을 추가하고 모든 공용 기능을 내보내고 탐색을 트리거하는 명령을 추가해야 합니다.

    
    local function set_mappings()
      local mappings = {
        q = 'close()',
        ['<cr>'] = 'open_and_close()',
        v = 'split("v")',
        s = 'split("")',
        p = 'preview()',
        t = 'open_in_tab()'
      }
    
      for k,v in pairs(mappings) do
        -- let's assume that our script is in lua/nvim-oldfile.lua file.
        vim.api.nvim_buf_set_keymap(buf, 'n', k, ':lua require"nvim-oldfile".'..v..'<cr>', {
            nowait = true, noremap = true, silent = true
          })
      end
    end
    
    -- at file end
    return {
      oldfiles = oldfiles,
      close = close,
      open_and_close = open_and_close,
      preview = preview,
      open_in_tab = open_in_tab,
      split = split
    }
    
    



    command! Oldfiles lua require'nvim-oldfile'.oldfiles()
    

    그리고 그게 다야! 재미있게 놀고 화격자 물건을 만드십시오!

    전체 플러그인



    local buf, win, start_win
    
    local function open()
      local path = vim.api.nvim_get_current_line()
    
      if vim.api.nvim_win_is_valid(start_win) then
        vim.api.nvim_set_current_win(start_win)
        vim.api.nvim_command('edit ' .. path)
      else
        vim.api.nvim_command('leftabove vsplit ' .. path)
        start_win = vim.api.nvim_get_current_win()
      end
    end
    
    local function close()
      if win and vim.api.nvim_win_is_valid(win) then
        vim.api.nvim_win_close(win, true)
      end
    end
    
    local function open_and_close()
      open()
      close()
    end
    
    local function preview()
      open()
      vim.api.nvim_set_current_win(win)
    end
    
    local function split(axis)
      local path = vim.api.nvim_get_current_line()
    
      if vim.api.nvim_win_is_valid(start_win) then
        vim.api.nvim_set_current_win(start_win)
        vim.api.nvim_command(axis ..'split ' .. path)
      else
        vim.api.nvim_command('leftabove ' .. axis..'split ' .. path)
      end
    
      close()
    end
    
    local function open_in_tab()
      local path = vim.api.nvim_get_current_line()
    
      vim.api.nvim_command('tabnew ' .. path)
      close()
    end
    
    
    local function redraw()
      vim.api.nvim_buf_set_option(buf, 'modifiable', true)
      local items_count =  vim.api.nvim_win_get_height(win) - 1
      local list = {}
      local oldfiles = vim.api.nvim_get_vvar('oldfiles')
    
      for i = #oldfiles, #oldfiles - items_count, -1 do
        pcall(function()
          local path = vim.api.nvim_call_function('fnamemodify', {oldfiles[i], ':.'})
          table.insert(list, #list + 1, path)
        end)
      end
    
      vim.api.nvim_buf_set_lines(buf, 0, -1, false, list)
      vim.api.nvim_buf_set_option(buf, 'modifiable', false)
    end
    
    local function set_mappings()
      local mappings = {
        q = 'close()',
        ['<cr>'] = 'open_and_close()',
        v = 'split("v")',
        s = 'split("")',
        p = 'preview()',
        t = 'open_in_tab()'
      }
    
      for k,v in pairs(mappings) do
        vim.api.nvim_buf_set_keymap(buf, 'n', k, ':lua require"nvim-oldfile".'..v..'<cr>', {
            nowait = true, noremap = true, silent = true
          })
      end
    end
    
    local function create_win()
      start_win = vim.api.nvim_get_current_win()
    
      vim.api.nvim_command('botright vnew')
      win = vim.api.nvim_get_current_win()
      buf = vim.api.nvim_get_current_buf()
    
      vim.api.nvim_buf_set_name(0, 'Oldfiles #' .. buf)
    
      vim.api.nvim_buf_set_option(0, 'buftype', 'nofile')
      vim.api.nvim_buf_set_option(0, 'swapfile', false)
      vim.api.nvim_buf_set_option(0, 'filetype', 'nvim-oldfile')
      vim.api.nvim_buf_set_option(0, 'bufhidden', 'wipe')
    
      vim.api.nvim_command('setlocal nowrap')
      vim.api.nvim_command('setlocal cursorline')
    
      set_mappings()
    end
    
    local function oldfiles()
      if win and vim.api.nvim_win_is_valid(win) then
        vim.api.nvim_set_current_win(win)
      else
        create_win()
      end
    
      redraw()
    end
    
    return {
      oldfiles = oldfiles,
      close = close,
      open_and_close = open_and_close,
      preview = preview,
      open_in_tab = open_in_tab,
      split = split
    }
    

    좋은 웹페이지 즐겨찾기