Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Anton Gubarev
Anton Gubarev

Posted on

     

Extending NeoVim with Lua

I spend a lot of time with NeoVim because it is my main tool for development. It suits me more than others primarily because it has lua and the ability to write your plugins and automation in a convenient and uncomplicated way. Why do I need this when there are already many ready-made solutions for the most common problems? Because sooner or later, there are cases that are not among the ready. And there are also project-specific cases that will never be among the ready. In this article I will show in two examples how it is relatively easy to expand NeoVim and make your work more productive. All examples below can be viewed live in mydotfile.

Curl

None of the query UIs I know will give me a Vim-like environment. For example, I need to be able to search and navigate the answer from the API using the same hotkeys that I already have configured in my Vim. Previously, I always had to switch to Postman, write a query there and copy the answer to my editor to be able to analyze it. And all this with a mouse, which is very unusual for someone who used to use a lot of hot keys.

localutils={}functionutils.getCurrentParagraph()localrow,col=unpack(vim.api.nvim_win_get_cursor(0))-- search forwardlocalrowi=rowwhiletruedolocallastLine=vim.api.nvim_buf_get_lines(0,rowi,rowi+1,false)or{""}iflastLine[1]==""thenbreakendiflastLine[1]==nilthenbreakendrowi=rowi+1end-- search backlocalrowj=rowwhiletruedolocallastLine=vim.api.nvim_buf_get_lines(0,rowj,rowj+1,false)or{""}iflastLine[1]==""thenbreakendiflastLine[1]==nilthenbreakendrowj=rowj-1ifrowj<1thenbreakendendlocallines=vim.api.nvim_buf_get_lines(0,rowj+1,rowi,false)localresult=table.concat(lines," ")result=result:gsub('[%c]','')returnresultendreturnutils
Enter fullscreen modeExit fullscreen mode

This function searches for the first empty line after the cursor position and then the same line until the cursor position. The empty line at the beginning and at the end I consider the border of the paragraph. I will take a step-by-step look at what happens in the function above.

localrow,col=unpack(vim.api.nvim_win_get_cursor(0))
Enter fullscreen modeExit fullscreen mode

So I get the current cursor position in the buffer 0. Buffer 0 is always the current buffer. This API method returns a row and a column, but I’m only interested in a row.

localrowi=rowwhiletruedolocallastLine=vim.api.nvim_buf_get_lines(0,rowi,rowi+1,false)or{""}iflastLine[1]==""thenbreakendiflastLine[1]==nilthenbreakendrowi=rowi+1end
Enter fullscreen modeExit fullscreen mode

By getting one line and increasing the count by 1 I check the row that it is empty and that it exists at all (for example if the end of the file is reached). All I need to do is memorize the row number.False in function arguments means that you do not want to throw an error if there is no index.
Then I look in the opposite direction and I find the top of the paragraph.
Then all I have to do is get the lines along the lines and merge them into one.

locallines=vim.api.nvim_buf_get_lines(0,rowj+1,rowi,false)localresult=table.concat(lines," ")result=result:gsub('[%c]','')
Enter fullscreen modeExit fullscreen mode

Here I additionally delete the template[%c] The fact is that the buffer will have a large number of different special characters, which when placed in the console breed a lot of errors. Such as

line 1 v curl -X POST https://jsonplaceholder.typicode.com/commentsline 2 v ^I-H 'Content-Type: application/json'line 3 v ^I-d '{line 4 v ^I^I"postId": 1line 5 v ^I}'
Enter fullscreen modeExit fullscreen mode

Okay, now I have the current paragraph.
To allow this function to be imported into other lua scripts I made it as a module and return it when the file is imported. Below in this article in another example I again use this module in another script. And so far I send the command to the terminal. As a terminal emulator I liketoggleterm because it is flexible enough and has a convenient API.

localutils=require("apg.utils")functionexecCurl()localcommand=utils.getCurrentParagraph()localTerminal=require('toggleterm.terminal').Terminallocalrun=Terminal:new({cmd=command,hidden=true,direction="float",close_on_exit=false,on_open=function(term)vim.api.nvim_buf_set_keymap(term.bufnr,"t","q","<cmd>close<CR>",{noremap=true,silent=true})end,})run:toggle()end
Enter fullscreen modeExit fullscreen mode

And now let me show you how it works.

Image description

SQL

To work with SQL databases already there are a huge number of excellent tools. Every IDE I’m familiar with has database plugins. Vim is included.LSP server already exists. But sometimes I just need to run a simple query, without getting distracted by going to other tools, looking for the right connection, or the prepared SQL query. Therefore, using thegetCurrentParagraph function discussed above, I implemented a simple and fast start of SQL queries for PostgreSQL

localutils=require("apg.utils")functionexecPgSql()localquery=utils.getCurrentParagraph()localcfg=vim.api.nvim_buf_get_lines(0,0,1,false)localcommand='psql '..cfg[1]..' -c "'..query..'"'localTerminal=require('toggleterm.terminal').Terminallocalrun=Terminal:new({cmd=command,hidden=true,direction="float",close_on_exit=false,on_open=function(term)vim.api.nvim_buf_set_keymap(term.bufnr,"t","q","<cmd>close<CR>",{noremap=true,silent=true})end,})run:toggle()end
Enter fullscreen modeExit fullscreen mode

Now I have in the project files with prepared SQL queries, which I can reach and run unrealistically fast. Here is an example of such a file

-hlocalhost-p5432-Upostgres-ddemo-WSELECTa.aircraft_code,a.model,s.seat_no,s.fare_conditionsFROMaircraftsaJOINseatssONa.aircraft_code=s.aircraft_codeWHEREa.model='Cessna 208 Caravan'ORDERBYs.seat_no;SELECTs2.aircraft_code,string_agg(s2.fare_conditions||'('||s2.num::text||')',', ')asfare_conditionsFROM(SELECTs.aircraft_code,s.fare_conditions,count(*)asnumFROMseatssGROUPBYs.aircraft_code,s.fare_conditionsORDERBYs.aircraft_code,s.fare_conditions)s2GROUPBYs2.aircraft_codeORDERBYs2.aircraft_code;
Enter fullscreen modeExit fullscreen mode

Here you can see the additional first line. It includes the parameters of connection to the database server and is substituted before query

localcfg=vim.api.nvim_buf_get_lines(0,0,1,false)localcommand='psql '..cfg[1]..' -c "'..query..'"'
Enter fullscreen modeExit fullscreen mode

I would still like to have a separate file for the configuration of the connection to the base and select the desired one before executing the request. But so far, it’s working fine for me. Maybe in the future I’ll work on this script. I’ll show you how it works.

And now let me show you how it works.

Image description

Conclusion

With these scripts, I speed up my work and make it more pleasant. Because I don’t get distracted by unnecessary actions, like switching to other tools and memorizing their features. I hope my examples have helped you understand the general principle of writing extensions for NeoVim and maybe find new opportunities for yourself.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Software engineer with 11 years of experience. Focused on fault tolerant, distributed systems, platform as a service, highload, golang, high availability services. Now, I work at Avito - the biggest c
  • Location
    Armenia
  • Joined

Trending onDEV CommunityHot

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp