├── .github └── workflows │ └── push.yaml ├── .stylua.toml ├── LICENSE ├── README.md ├── doc └── nvim-texlabconfig.txt ├── go.mod ├── go.sum ├── lua └── texlabconfig │ ├── cache.lua │ ├── config.lua │ ├── fn.lua │ ├── init.lua │ └── utils.lua └── main.go /.github/workflows/push.yaml: -------------------------------------------------------------------------------- 1 | name: Generate docs and format 2 | 3 | on: push 4 | 5 | jobs: 6 | postprocessing: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | with: 11 | fetch-depth: 0 # To get all tags 12 | - name: panvimdoc 13 | uses: kdheepak/panvimdoc@main 14 | with: 15 | vimdoc: nvim-texlabconfig 16 | pandoc: "README.md" 17 | version: "NVIM 0.10.4" 18 | 19 | - uses: stefanzweifel/git-auto-commit-action@v4 20 | with: 21 | commit_message: "ci(docs): auto generate docs" 22 | branch: ${{ github.head_ref }} 23 | 24 | - name: "Format with Stylua" 25 | uses: JohnnyMorganz/stylua-action@v1.1.2 26 | with: 27 | token: ${{ secrets.GITHUB_TOKEN }} 28 | version: v0.15.1 29 | args: . 30 | 31 | - uses: stefanzweifel/git-auto-commit-action@v4 32 | with: 33 | commit_message: "ci(format): format with stylua" 34 | branch: ${{ github.head_ref }} 35 | -------------------------------------------------------------------------------- /.stylua.toml: -------------------------------------------------------------------------------- 1 | column_width = 120 2 | line_endings = "Unix" 3 | indent_type = "Spaces" 4 | indent_width = 4 5 | quote_style = "AutoPreferSingle" 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nvim-texlabconfig 2 | 3 | **Texlab** is a popular Language Server for LaTeX, which supports **Forward Search** and **Inverse Search** between TeX and PDF files. 4 | 5 | **nvim-texlabconfig** provides some useful snippets to configure this capability for **neovim** and some viewers and a homonymous executable which allows a fast **Inverse Search**. 6 | 7 | ## Requirements 8 | 9 | - [nvim](https://github.com/neovim/neovim) 0.8+ 10 | - [TexLab](https://github.com/latex-lsp/texlab) 11 | - [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) 12 | - [go](https://go.dev/) 13 | 14 | ### Tags 15 | 16 | - `v0.1.0` does not depend on `go` for building purpose and does not require an additional executable 17 | - `v0.2.0` is compatible with nvim 0.7 18 | - `v0.2.1` adds the `-server` flag 19 | 20 | ## Installation 21 | 22 | **nvim-texlabconfig** can be installed for example with [lazy.nvim](https://github.com/folke/lazy.nvim). 23 | 24 | ```lua 25 | { 26 | 'f3fora/nvim-texlabconfig', 27 | config = function() 28 | require('texlabconfig').setup(config) 29 | end, 30 | -- ft = { 'tex', 'bib' }, -- Lazy-load on filetype 31 | build = 'go build' 32 | -- build = 'go build -o ~/.bin/' -- if e.g. ~/.bin/ is in $PATH 33 | } 34 | ``` 35 | 36 | Calling `require('texlabconfig').setup()` is required and can eventually be [configured with a table](#configuration). 37 | 38 | The executable `nvim-texlabconfig` has to be also build, e.g., with `go build`. By default, the result can be found in `:lua =require('texlabconfig').project_dir()` directory. However, the output location can be chosen with `-o` flag. From `go help build`: 39 | 40 | > The -o flag forces build to write the resulting executable or object to the named output file or directory, instead of the default behavior described in the last two paragraphs. If the named output is an existing directory or ends with a slash or backslash, then any resulting executables will be written to that directory.` 41 | 42 | ## Configuration 43 | 44 | **nvim-texlabconfig** is configured using the `setup` function. The argument is a table and is optional. The default values are listed below. 45 | 46 | ```lua 47 | local config = { 48 | cache_activate = true, 49 | cache_filetypes = { 'tex', 'bib' }, 50 | cache_root = vim.fn.stdpath('cache'), 51 | reverse_search_start_cmd = function() 52 | return true 53 | end, 54 | reverse_search_edit_cmd = vim.cmd.edit, 55 | reverse_search_end_cmd = function() 56 | return true 57 | end, 58 | file_permission_mode = 438, 59 | } 60 | ``` 61 | 62 | ### `cache_activate` 63 | 64 | Do not change this option. 65 | 66 | Type: boolean 67 | Default: `true` 68 | 69 | ### `cache_filetypes` 70 | 71 | Activate cache for buffers with these file types. 72 | 73 | Type: list of strings 74 | Default: `{ 'tex', 'bib' }` 75 | 76 | ### `cache_root` 77 | 78 | Specify the cache directory. **nvim-texlabconfig** creates a `nvim-texlabconfig.json` file in this directory. 79 | 80 | Type: string 81 | Default: `vim.fn.stdpath('cache')` 82 | 83 | ### `reverse_search_edit_cmd` 84 | 85 | When working in a multi-file project, initiating inverse search may require opening a file that is not currently open in a window. This option controls the command that is used to open files as a result of an inverse search. 86 | 87 | Type: function(file_path: string) 88 | Default: `vim.cmd.edit` 89 | Examples: 90 | 91 | - `vim.cmd.edit` open buffer in current window 92 | - `vim.cmd.tabedit` open buffer in new tab page 93 | - `vim.cmd.split` split current window to open buffer 94 | 95 | ### `reverse_search_{start,end}_cmd` 96 | 97 | Execute a custom function at the beginning or at end of the inverse search process. 98 | If the return value of this function if false or nil, the inverse search fails. 99 | 100 | Type: function() 101 | Default: `function() return true end` 102 | 103 | ### `file_permission_mode` 104 | 105 | See [luv-file-system-operations](https://github.com/luvit/luv/blob/master/docs.md#file-system-operations=). 106 | 107 | Type: integer 108 | Default: `438` 109 | 110 | ## Executable: `nvim-texlabconfig` 111 | 112 | `nvim-texlabconfig` is a convenient executable which simplifies the viewer configuration. It handles multiple neovim instances and choose the correct one. 113 | 114 | Assuming `nvim-texlabconfig` is placed in a `$PATH` directory and `cache_root` is the default one, the following command can be used, where `%f` is the absolute filename and `%l` is the line number. 115 | 116 | ```sh 117 | nvim-texlabconfig -file '%f' -line %l 118 | ``` 119 | 120 | Otherwise, if `nvim-texlabconfig` is not in `$PATH`, e.g. it is placed in `:lua =require('texlabconfig').project_dir()`, 121 | 122 | ```sh 123 | /path/to/nvim-texlabconfig -file '%f' -line %l 124 | ``` 125 | 126 | If a different [`cache_root`](#cache_root) is used, the directory used has to be specified after `-cache_root` optional flag. This flag is useful in macOS. See e.g. [Skim](#skim). 127 | 128 | ```sh 129 | nvim-texlabconfig -file '%f' -line %l -cache_root /path/to/cache_root/ 130 | ``` 131 | 132 | An optional flag `-server` is used to open the TeX file in the right neovim instance while working with multiple PDF documents. See e.g. [Zathura](#zathura). 133 | 134 | ```sh 135 | nvim-texlabconfig -file '%f' -line %l -server `vim.v.servername` 136 | ``` 137 | 138 | From `nvim-texlabconfig -help` on Linux: 139 | 140 | > Usage of nvim-texlabconfig: 141 | > -cache_root string 142 | > Path to nvim-texlabconfig.json file (default "/home/user/.cache/nvim") 143 | > -file string 144 | > Absolute filename [REQUIRED] 145 | > -line int 146 | > Line number [REQUIRED] 147 | > -server string 148 | > Server name (vim.v.servername) 149 | 150 | ## Status 151 | 152 | Help wanted to add and test other viewers, which are present in [Texlab Previewing Documentation](https://github.com/latex-lsp/texlab/wiki/Previewing). 153 | 154 | ## Previewing 155 | 156 | To configure Forward and Inverse Search, the default configuration of `texlab` defined in [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#texlab) has to be changed. 157 | 158 | Different values of `executable` and `args` are required for each viewer. 159 | 160 | > **Warning** 161 | > `args` will be escaped in some strange way. 162 | 163 | ```lua 164 | local lspconfig = require('lspconfig') 165 | local executable 166 | local args 167 | 168 | lspconfig.texlab.setup({ 169 | setting = { 170 | texlab = { 171 | forwardSearch = { 172 | executable = executable, 173 | args = args, 174 | }, 175 | }, 176 | }, 177 | }) 178 | ``` 179 | 180 | In the following sections, some configurations are reported. 181 | 182 | ### [Sioyek](https://sioyek.info/) 183 | 184 | ```lua 185 | executable = "sioyek", 186 | args = { 187 | "--reuse-window", 188 | "--execute-command", 189 | "toggle_synctex", -- Open Sioyek in synctex mode. 190 | "--inverse-search", 191 | 'nvim-texlabconfig -file "%%%1" -line "%%%2" -server ' .. vim.v.servername, 192 | "--forward-search-file", 193 | "%f", 194 | "--forward-search-line", 195 | "%l", 196 | "%p", 197 | } 198 | ``` 199 | 200 | From [Sioyek documentation](https://sioyek-documentation.readthedocs.io/en/latest/usage.html#synctex): 201 | 202 | > Press `f4` to toggle synctex mode (`toggle_synctex` command). While in this mode, **right clicking** on any text opens the corresponding `tex` file in the appropriate location. 203 | 204 | ### [Skim](https://skim-app.sourceforge.io/) 205 | 206 | ```lua 207 | local executable = '/Applications/Skim.app/Contents/SharedSupport/displayline' 208 | local args = { '%l', '%p', '%f' } 209 | ``` 210 | 211 | In the Skim preferences (Skim → Preferences → Sync → PDF-TeX Sync support) 212 | 213 | ``` 214 | Preset: Custom 215 | Command: nvim-texlabconfig 216 | Arguments: -file '%file' -line %line -cache_root $cache_root 217 | ``` 218 | 219 | Replace `$cache_root` with the `require('texlabconfig.config').get().cache_root`, whose default value is `vim.fn.stdpath('cache')`, which uses XDG directory specifications on macOS rather than Standard Directories guidelines and returns `~/.cache/nvim/`. 220 | 221 | ### [Zathura](https://pwmt.org/projects/zathura/) 222 | 223 | ```lua 224 | local executable = 'zathura' 225 | local args = { 226 | '--synctex-editor-command', 227 | [[nvim-texlabconfig -file '%%%{input}' -line %%%{line} -server ]] .. vim.v.servername, 228 | '--synctex-forward', 229 | '%l:1:%f', 230 | '%p', 231 | } 232 | ``` 233 | -------------------------------------------------------------------------------- /doc/nvim-texlabconfig.txt: -------------------------------------------------------------------------------- 1 | *nvim-texlabconfig.txt* For NVIM 0.10.4 Last change: 2025 February 24 2 | 3 | ============================================================================== 4 | Table of Contents *nvim-texlabconfig-table-of-contents* 5 | 6 | 1. nvim-texlabconfig |nvim-texlabconfig-nvim-texlabconfig| 7 | - Requirements |nvim-texlabconfig-nvim-texlabconfig-requirements| 8 | - Installation |nvim-texlabconfig-nvim-texlabconfig-installation| 9 | - Configuration |nvim-texlabconfig-nvim-texlabconfig-configuration| 10 | - Executable: nvim-texlabconfig|nvim-texlabconfig-nvim-texlabconfig-executable:-nvim-texlabconfig| 11 | - Status |nvim-texlabconfig-nvim-texlabconfig-status| 12 | - Previewing |nvim-texlabconfig-nvim-texlabconfig-previewing| 13 | 14 | ============================================================================== 15 | 1. nvim-texlabconfig *nvim-texlabconfig-nvim-texlabconfig* 16 | 17 | **Texlab** is a popular Language Server for LaTeX, which supports **Forward 18 | Search** and **Inverse Search** between TeX and PDF files. 19 | 20 | **nvim-texlabconfig** provides some useful snippets to configure this 21 | capability for **neovim** and some viewers and a homonymous executable which 22 | allows a fast **Inverse Search**. 23 | 24 | 25 | REQUIREMENTS *nvim-texlabconfig-nvim-texlabconfig-requirements* 26 | 27 | - nvim 0.8+ 28 | - TexLab 29 | - nvim-lspconfig 30 | - go 31 | 32 | 33 | TAGS ~ 34 | 35 | - `v0.1.0` does not depend on `go` for building purpose and does not require an additional executable 36 | - `v0.2.0` is compatible with nvim 0.7 37 | - `v0.2.1` adds the `-server` flag 38 | 39 | 40 | INSTALLATION *nvim-texlabconfig-nvim-texlabconfig-installation* 41 | 42 | **nvim-texlabconfig** can be installed for example with lazy.nvim 43 | . 44 | 45 | >lua 46 | { 47 | 'f3fora/nvim-texlabconfig', 48 | config = function() 49 | require('texlabconfig').setup(config) 50 | end, 51 | -- ft = { 'tex', 'bib' }, -- Lazy-load on filetype 52 | build = 'go build' 53 | -- build = 'go build -o ~/.bin/' -- if e.g. ~/.bin/ is in $PATH 54 | } 55 | < 56 | 57 | Calling `require('texlabconfig').setup()` is required and can eventually be 58 | |nvim-texlabconfig-configured-with-a-table|. 59 | 60 | The executable `nvim-texlabconfig` has to be also build, e.g., with `go build`. 61 | By default, the result can be found in `:lua 62 | =require('texlabconfig').project_dir()` directory. However, the output location 63 | can be chosen with `-o` flag. From `go help build`: 64 | 65 | 66 | The -o flag forces build to write the resulting executable or object to the 67 | named output file or directory, instead of the default behavior described in 68 | the last two paragraphs. If the named output is an existing directory or ends 69 | with a slash or backslash, then any resulting executables will be written to 70 | that directory.` 71 | 72 | CONFIGURATION *nvim-texlabconfig-nvim-texlabconfig-configuration* 73 | 74 | **nvim-texlabconfig** is configured using the `setup` function. The argument is 75 | a table and is optional. The default values are listed below. 76 | 77 | >lua 78 | local config = { 79 | cache_activate = true, 80 | cache_filetypes = { 'tex', 'bib' }, 81 | cache_root = vim.fn.stdpath('cache'), 82 | reverse_search_start_cmd = function() 83 | return true 84 | end, 85 | reverse_search_edit_cmd = vim.cmd.edit, 86 | reverse_search_end_cmd = function() 87 | return true 88 | end, 89 | file_permission_mode = 438, 90 | } 91 | < 92 | 93 | 94 | CACHE_ACTIVATE ~ 95 | 96 | Do not change this option. 97 | 98 | Type: boolean Default: `true` 99 | 100 | 101 | CACHE_FILETYPES ~ 102 | 103 | Activate cache for buffers with these file types. 104 | 105 | Type: list of strings Default: `{ 'tex', 'bib' }` 106 | 107 | 108 | CACHE_ROOT ~ 109 | 110 | Specify the cache directory. **nvim-texlabconfig** creates a 111 | `nvim-texlabconfig.json` file in this directory. 112 | 113 | Type: string Default: `vim.fn.stdpath('cache')` 114 | 115 | 116 | REVERSE_SEARCH_EDIT_CMD ~ 117 | 118 | When working in a multi-file project, initiating inverse search may require 119 | opening a file that is not currently open in a window. This option controls the 120 | command that is used to open files as a result of an inverse search. 121 | 122 | Type: function(file_path: string) Default: `vim.cmd.edit` Examples: 123 | 124 | - `vim.cmd.edit` open buffer in current window 125 | - `vim.cmd.tabedit` open buffer in new tab page 126 | - `vim.cmd.split` split current window to open buffer 127 | 128 | 129 | REVERSE_SEARCH_{START,END}_CMD ~ 130 | 131 | Execute a custom function at the beginning or at end of the inverse search 132 | process. If the return value of this function if false or nil, the inverse 133 | search fails. 134 | 135 | Type: function() Default: `function() return true end` 136 | 137 | 138 | FILE_PERMISSION_MODE ~ 139 | 140 | See luv-file-system-operations 141 | . 142 | 143 | Type: integer Default: `438` 144 | 145 | 146 | EXECUTABLE: NVIM-TEXLABCONFIG*nvim-texlabconfig-nvim-texlabconfig-executable:-nvim-texlabconfig* 147 | 148 | `nvim-texlabconfig` is a convenient executable which simplifies the viewer 149 | configuration. It handles multiple neovim instances and choose the correct one. 150 | 151 | Assuming `nvim-texlabconfig` is placed in a `$PATH` directory and `cache_root` 152 | is the default one, the following command can be used, where `%f` is the 153 | absolute filename and `%l` is the line number. 154 | 155 | >sh 156 | nvim-texlabconfig -file '%f' -line %l 157 | < 158 | 159 | Otherwise, if `nvim-texlabconfig` is not in `$PATH`, e.g. it is placed in 160 | `:lua =require('texlabconfig').project_dir()`, 161 | 162 | >sh 163 | /path/to/nvim-texlabconfig -file '%f' -line %l 164 | < 165 | 166 | If a different |nvim-texlabconfig-`cache_root`| is used, the directory used has 167 | to be specified after `-cache_root` optional flag. This flag is useful in 168 | macOS. See e.g. |nvim-texlabconfig-skim|. 169 | 170 | >sh 171 | nvim-texlabconfig -file '%f' -line %l -cache_root /path/to/cache_root/ 172 | < 173 | 174 | An optional flag `-server` is used to open the TeX file in the right neovim 175 | instance while working with multiple PDF documents. See 176 | e.g. |nvim-texlabconfig-zathura|. 177 | 178 | >sh 179 | nvim-texlabconfig -file '%f' -line %l -server `vim.v.servername` 180 | < 181 | 182 | From `nvim-texlabconfig -help` on Linux: 183 | 184 | 185 | Usage of nvim-texlabconfig: -cache_root string Path to nvim-texlabconfig.json 186 | file (default "/home/user/.cache/nvim") -file string Absolute filename 187 | [REQUIRED] -line int Line number [REQUIRED] -server string Server name 188 | (vim.v.servername) 189 | 190 | STATUS *nvim-texlabconfig-nvim-texlabconfig-status* 191 | 192 | Help wanted to add and test other viewers, which are present in Texlab 193 | Previewing Documentation . 194 | 195 | 196 | PREVIEWING *nvim-texlabconfig-nvim-texlabconfig-previewing* 197 | 198 | To configure Forward and Inverse Search, the default configuration of `texlab` 199 | defined in nvim-lspconfig 200 | 201 | has to be changed. 202 | 203 | Different values of `executable` and `args` are required for each viewer. 204 | 205 | 206 | **Warning** `args` will be escaped in some strange way. 207 | >lua 208 | local lspconfig = require('lspconfig') 209 | local executable 210 | local args 211 | 212 | lspconfig.texlab.setup({ 213 | setting = { 214 | texlab = { 215 | forwardSearch = { 216 | executable = executable, 217 | args = args, 218 | }, 219 | }, 220 | }, 221 | }) 222 | < 223 | 224 | In the following sections, some configurations are reported. 225 | 226 | 227 | SIOYEK ~ 228 | 229 | >lua 230 | executable = "sioyek", 231 | args = { 232 | "--reuse-window", 233 | "--execute-command", 234 | "toggle_synctex", -- Open Sioyek in synctex mode. 235 | "--inverse-search", 236 | 'nvim-texlabconfig -file "%%%1" -line "%%%2" -server ' .. vim.v.servername, 237 | "--forward-search-file", 238 | "%f", 239 | "--forward-search-line", 240 | "%l", 241 | "%p", 242 | } 243 | < 244 | 245 | From Sioyek documentation 246 | : 247 | 248 | 249 | Press `f4` to toggle synctex mode (`toggle_synctex` command). While in this 250 | mode, **right clicking** on any text opens the corresponding `tex` file in the 251 | appropriate location. 252 | 253 | SKIM ~ 254 | 255 | >lua 256 | local executable = '/Applications/Skim.app/Contents/SharedSupport/displayline' 257 | local args = { '%l', '%p', '%f' } 258 | < 259 | 260 | In the Skim preferences (Skim → Preferences → Sync → PDF-TeX Sync 261 | support) 262 | 263 | > 264 | Preset: Custom 265 | Command: nvim-texlabconfig 266 | Arguments: -file '%file' -line %line -cache_root $cache_root 267 | < 268 | 269 | Replace `$cache_root` with the 270 | `require('texlabconfig.config').get().cache_root`, whose default value is 271 | `vim.fn.stdpath('cache')`, which uses XDG directory specifications on macOS 272 | rather than Standard Directories guidelines and returns `~/.cache/nvim/`. 273 | 274 | 275 | ZATHURA ~ 276 | 277 | >lua 278 | local executable = 'zathura' 279 | local args = { 280 | '--synctex-editor-command', 281 | [[nvim-texlabconfig -file '%%%{input}' -line %%%{line} -server ]] .. vim.v.servername, 282 | '--synctex-forward', 283 | '%l:1:%f', 284 | '%p', 285 | } 286 | < 287 | 288 | Generated by panvimdoc 289 | 290 | vim:tw=78:ts=8:noet:ft=help:norl: 291 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module nvim-texlabconfig 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/adrg/xdg v0.4.0 7 | github.com/neovim/go-client v1.2.1 8 | ) 9 | 10 | require golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= 2 | github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/neovim/go-client v1.2.1 h1:kl3PgYgbnBfvaIoGYi3ojyXH0ouY6dJY/rYUCssZKqI= 5 | github.com/neovim/go-client v1.2.1/go.mod h1:EeqCP3z1vJd70JTaH/KXz9RMZ/nIgEFveX83hYnh/7c= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 8 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 9 | golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik= 10 | golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 11 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 12 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 13 | -------------------------------------------------------------------------------- /lua/texlabconfig/cache.lua: -------------------------------------------------------------------------------- 1 | local vim = vim 2 | local json = vim.json 3 | local uv = vim.loop 4 | local create_augroup = vim.api.nvim_create_augroup 5 | local create_autocmd = vim.api.nvim_create_autocmd 6 | 7 | local config = require('texlabconfig.config').options 8 | local utils = require('texlabconfig.utils') 9 | 10 | local M = {} 11 | 12 | M.fname = config.cache_root .. '/nvim-texlabconfig.json' 13 | M.cache_filetypes = config.cache_filetypes 14 | M.cache_activate = config.cache_activate 15 | M.mode = config.file_permission_mode 16 | 17 | M._servernames = {} 18 | 19 | function M:servernames() 20 | self:read() 21 | return self._servernames 22 | end 23 | 24 | function M:add_servernames() 25 | local avaiable_servernames = {} 26 | self:read() 27 | for _, server in 28 | ipairs( 29 | -- unique servernames 30 | utils.list_unique( 31 | -- last nvim session is always first 32 | vim.list_extend({ vim.v.servername }, self._servernames) 33 | ) 34 | ) 35 | do 36 | local socket = uv.new_pipe(false) 37 | local ok, _ = pcall(uv.pipe_connect, socket, server) 38 | socket:close() 39 | if ok then 40 | avaiable_servernames[#avaiable_servernames + 1] = server 41 | end 42 | end 43 | self._servernames = avaiable_servernames 44 | self:write() 45 | end 46 | 47 | function M:remove_servernames() 48 | for k, server in pairs(self:servernames()) do 49 | if server == vim.v.servername then 50 | table.remove(self._servernames, k) 51 | self:write() 52 | return 53 | end 54 | end 55 | end 56 | 57 | function M:write() 58 | local encode = json.encode({ servernames = self._servernames }) 59 | local fd = assert(uv.fs_open(self.fname, 'w', M.mode)) 60 | assert(uv.fs_write(fd, encode)) 61 | assert(uv.fs_close(fd)) 62 | end 63 | 64 | function M:read() 65 | if not utils.file_exists(self.fname, M.mode) then 66 | self:write() 67 | return 68 | end 69 | local fd = assert(uv.fs_open(self.fname, 'r', M.mode)) 70 | local stat = assert(uv.fs_fstat(fd)) 71 | local data = assert(uv.fs_read(fd, stat.size, 0)) 72 | assert(uv.fs_close(fd)) 73 | 74 | local decode = json.decode(data) 75 | self._servernames = decode.servernames 76 | end 77 | 78 | function M:autocmd_servernames() 79 | if not self.cache_activate then 80 | return 81 | end 82 | 83 | local augroup_id = create_augroup('TexlabCacheInit', { clear = true }) 84 | create_autocmd({ 'FileType' }, { 85 | pattern = M.cache_filetypes, 86 | callback = function() 87 | self:add_servernames() 88 | end, 89 | group = augroup_id, 90 | desc = 'nvim-texlabconfig: add servernames to cache', 91 | }) 92 | 93 | create_autocmd({ 'VimLeavePre' }, { 94 | callback = function() 95 | self:remove_servernames() 96 | end, 97 | group = augroup_id, 98 | desc = 'nvim-texlabconfig: remove servernames from cache', 99 | }) 100 | end 101 | 102 | return M 103 | -------------------------------------------------------------------------------- /lua/texlabconfig/config.lua: -------------------------------------------------------------------------------- 1 | local vim = vim 2 | 3 | local defaults = { 4 | cache_activate = true, 5 | cache_filetypes = { 'tex', 'bib' }, 6 | cache_root = vim.fn.stdpath('cache'), 7 | reverse_search_start_cmd = function() 8 | return true 9 | end, 10 | reverse_search_edit_cmd = vim.cmd.edit, 11 | reverse_search_end_cmd = function() 12 | return true 13 | end, 14 | file_permission_mode = 438, 15 | } 16 | 17 | local M = {} 18 | 19 | M.options = {} 20 | 21 | function M.setup(user_config) 22 | M.options = vim.tbl_deep_extend('force', defaults, user_config) 23 | vim.validate({ 24 | cache_activate = { M.options.cache_activate, 'boolean' }, 25 | cache_filetypes = { M.options.cache_filetypes, 'table' }, 26 | cache_root = { M.options.cache_root, 'string' }, 27 | reverse_search_start_cmd = { M.options.reverse_search_start_cmd, 'function' }, 28 | reverse_search_edit_cmd = { M.options.reverse_search_edit_cmd, 'function' }, 29 | reverse_search_end_cmd = { M.options.reverse_search_end_cmd, 'function' }, 30 | file_permission_mode = { M.options.file_permission_mode, 'number' }, 31 | }) 32 | end 33 | 34 | return M 35 | -------------------------------------------------------------------------------- /lua/texlabconfig/fn.lua: -------------------------------------------------------------------------------- 1 | local vim = vim 2 | local uv = vim.loop 3 | local api = vim.api 4 | 5 | local utils = require('texlabconfig.utils') 6 | 7 | local M = {} 8 | 9 | function M:inverse_search(filename, line) 10 | local config = require('texlabconfig.config').options 11 | 12 | if config.reverse_search_start_cmd() then 13 | else 14 | return false 15 | end 16 | 17 | local file = uv.fs_realpath(filename) 18 | local buf, win, tab 19 | 20 | local i = 1 21 | local allow_fail = 4 22 | while i < allow_fail do 23 | -- If the file is already open, move the cursor there. 24 | local ok 25 | ok, buf = pcall(utils.bufnr, file) 26 | if ok then 27 | ok, win = pcall(utils.winnr, buf) 28 | if ok then 29 | ok, tab = pcall(utils.tabnr, win) 30 | if ok then 31 | break 32 | end 33 | end 34 | end 35 | -- Otherwise open it 36 | config.reverse_search_edit_cmd(file) 37 | i = i + 1 38 | end 39 | 40 | if i >= allow_fail then 41 | return false 42 | end 43 | 44 | if (1 > line) or (line > api.nvim_buf_line_count(buf)) then 45 | return false 46 | end 47 | 48 | api.nvim_set_current_win(win) 49 | api.nvim_set_current_tabpage(tab) 50 | api.nvim_win_set_cursor(win, { line, 0 }) 51 | 52 | if config.reverse_search_end_cmd() then 53 | else 54 | return false 55 | end 56 | 57 | return true 58 | end 59 | 60 | return M 61 | -------------------------------------------------------------------------------- /lua/texlabconfig/init.lua: -------------------------------------------------------------------------------- 1 | local setup = require('texlabconfig.config').setup 2 | 3 | local M = {} 4 | 5 | M.fn = require('texlabconfig.fn') 6 | M.project_dir = require('texlabconfig.utils').project_dir 7 | 8 | function M.setup(user_config) 9 | setup(user_config or {}) 10 | local cache = require('texlabconfig.cache') 11 | cache:autocmd_servernames() 12 | end 13 | 14 | return M 15 | -------------------------------------------------------------------------------- /lua/texlabconfig/utils.lua: -------------------------------------------------------------------------------- 1 | local vim = vim 2 | local uv = vim.loop 3 | 4 | local M = {} 5 | 6 | function M.file_exists(name, mode) 7 | local fd = uv.fs_open(name, 'r', mode) 8 | if fd ~= nil then 9 | assert(uv.fs_close(fd)) 10 | return true 11 | end 12 | 13 | return false 14 | end 15 | 16 | function M.bufnr(filename) 17 | for _, buf in ipairs(vim.api.nvim_list_bufs()) do 18 | local _filename = vim.api.nvim_buf_get_name(buf) 19 | if _filename == filename then 20 | return buf 21 | end 22 | end 23 | error('No File') 24 | end 25 | 26 | function M.winnr(buf) 27 | for _, win in ipairs(vim.api.nvim_list_wins()) do 28 | local _buf = vim.api.nvim_win_get_buf(win) 29 | if _buf == buf then 30 | return win 31 | end 32 | end 33 | error('No Buf') 34 | end 35 | 36 | function M.tabnr(win) 37 | for _, tab in ipairs(vim.api.nvim_list_tabpages()) do 38 | local _win = vim.api.nvim_tabpage_get_win(tab) 39 | if _win == win then 40 | return tab 41 | end 42 | end 43 | error('No Win') 44 | end 45 | 46 | function M.list_unique(list) 47 | local hash = {} 48 | local res = {} 49 | 50 | for _, v in ipairs(list) do 51 | if not hash[v] then 52 | res[#res + 1] = v 53 | hash[v] = true 54 | end 55 | end 56 | 57 | return res 58 | end 59 | 60 | function M.project_dir() 61 | local paths = vim.api.nvim_list_runtime_paths() 62 | local init_path = debug.getinfo(1).source 63 | local dir 64 | local len = 0 65 | for _, path in ipairs(paths) do 66 | local current_len = path:len() 67 | if current_len > len and path == init_path:sub(2, 1 + current_len) then 68 | dir = path 69 | len = current_len 70 | end 71 | end 72 | return dir 73 | end 74 | 75 | return M 76 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "io/ioutil" 7 | "log" 8 | "path/filepath" 9 | 10 | "github.com/adrg/xdg" 11 | "github.com/neovim/go-client/nvim" 12 | ) 13 | 14 | const ( 15 | defaultFile = "" 16 | defaultLine = 0 17 | defaultServer = "" 18 | ) 19 | 20 | var ( 21 | defaultCacheRoot = filepath.Join(xdg.CacheHome, "nvim") 22 | ) 23 | 24 | type serverNames struct { 25 | ServerNames []string `json:"servernames"` 26 | } 27 | 28 | func main() { 29 | // Required flags are: -file string, -line int. 30 | // Optional flags are: -cache_root string, -server string. 31 | // No other argument is needed. 32 | file := flag.String("file", defaultFile, "Absolute filename [REQUIRED]") 33 | line := flag.Int("line", defaultLine, "Line number [REQUIRED] ") 34 | server := flag.String("server", defaultServer, "Server name (vim.v.servername)") 35 | cache_root := flag.String("cache_root", defaultCacheRoot, "Path to nvim-texlabconfig.json file") 36 | 37 | flag.Parse() 38 | 39 | if flag.NArg() > 0 || *file == defaultFile || *line == defaultLine { 40 | log.Fatal("Some flags are required") 41 | } 42 | 43 | // Parse cache file `nvim-texlabconfig.json`. 44 | content, err := ioutil.ReadFile(filepath.Join(*cache_root, "nvim-texlabconfig.json")) 45 | if err != nil { 46 | log.Fatal("Error when opening file: ", err) 47 | } 48 | 49 | var servers serverNames 50 | if err := json.Unmarshal(content, &servers); err != nil { 51 | log.Fatal("Error during Unmarshal(): ", err) 52 | } 53 | 54 | /// Try passed server first. 55 | if *server != defaultServer { 56 | servers.ServerNames = append([]string{*server}, servers.ServerNames...) 57 | } 58 | 59 | // ExecLua function `require('texlabconfig').fn:inverse_search` in the first avaiable nvim instance. 60 | for _, serverName := range servers.ServerNames { 61 | v, err := nvim.Dial(serverName) 62 | if err != nil { 63 | log.Print("Error during Dial nvim: ", err) 64 | continue 65 | } 66 | defer v.Close() 67 | 68 | var result bool 69 | if err := v.ExecLua("return require('texlabconfig').fn:inverse_search(...)", &result, file, line); err != nil { 70 | log.Print("Error during ExecLua: ", err) 71 | } else if !result { 72 | log.Print("Error during inverse_search") 73 | } else { 74 | log.Printf("Pipe: %s, File: %s, Line: %d", serverName, *file, *line) 75 | break 76 | } 77 | } 78 | } 79 | --------------------------------------------------------------------------------