{"id":13895020,"url":"https://github.com/WhoIsSethDaniel/nvim-go-client-examples","last_synced_at":"2025-07-17T10:32:03.207Z","repository":{"id":64301370,"uuid":"307219949","full_name":"WhoIsSethDaniel/nvim-go-client-examples","owner":"WhoIsSethDaniel","description":"Examples of using the nvim go client and documentation for the client","archived":false,"fork":false,"pushed_at":"2020-11-24T03:22:22.000Z","size":166,"stargazers_count":19,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-07T04:46:11.425Z","etag":null,"topics":["go","go-plugin","neovim"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/WhoIsSethDaniel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-26T00:15:34.000Z","updated_at":"2025-01-14T07:11:27.000Z","dependencies_parsed_at":"2023-01-15T09:30:39.853Z","dependency_job_id":null,"html_url":"https://github.com/WhoIsSethDaniel/nvim-go-client-examples","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/WhoIsSethDaniel/nvim-go-client-examples","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WhoIsSethDaniel%2Fnvim-go-client-examples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WhoIsSethDaniel%2Fnvim-go-client-examples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WhoIsSethDaniel%2Fnvim-go-client-examples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WhoIsSethDaniel%2Fnvim-go-client-examples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WhoIsSethDaniel","download_url":"https://codeload.github.com/WhoIsSethDaniel/nvim-go-client-examples/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WhoIsSethDaniel%2Fnvim-go-client-examples/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265595360,"owners_count":23794725,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","go-plugin","neovim"],"created_at":"2024-08-06T18:01:56.316Z","updated_at":"2025-07-17T10:32:02.929Z","avatar_url":"https://github.com/WhoIsSethDaniel.png","language":"Go","readme":"# nvim-go-client-examples\nExamples of using the nvim [go client](https://github.com/neovim/go-client) and documentation for the client.\n\nThis file, the code, and the comments in the code are intended to help those who are interested in using Go to write\nplugins for Neovim. Please do not take this document as absolute fact. I have done my best to document what I have\ndiscovered by experimentation and reading the code, but it is very likely at least a few things I write here aren't 100%\naccurate (**very** likely). I accept patches to the documentation and to the example code. My hope is to make this\ndocument as accurate and as useful as possible.\n\n## Disclaimer(s)\n* This is not intended to be a complete guide to all things [go-client](https://github.com/neovim/go-client). The api\nprovided by go-client is very large. Just have a look at the godoc for 'nvim' -- it's huge! \n* I show what I have learned. Particularly the stuff that struck me as non-obvious.\n* If you want more examples or there is not an example of something you are interested in please feel free to do one of two things:\n    1. Submit a bug report for this repository. If I can get to it, I will. \n    1. Figure it out yourself and submit a patch to this repository.\n\n## Assumptions\n* You are a Go programmer and have basic knowledge of Go modules and the Go programming language \n* You have a recent Go and Neovim installed\n* You are basically familiar with Neovim project layout and installation of plugins\n\n## Build \u0026 Install\nYou can clone the repository into the same place you place all your plugins. Lets assume this location is \n~/.config/nvim/pack/git-plugins/start. You will need to substitute wherever it is you install Vim plugins.\n\nClone the repository to where you install plugins:\n\n```\ncd ~/.config/nvim/pack/git-plugins/start\ngit clone https://github.com/WhoIsSethDaniel/nvim-go-client-examples\n```\n\nAt this point if you start Neovim you will get some errors upon start. You will need to finish building and installing\nthe project before you can start Neovim without receiving errors.\n\nNow you need to build the 'host'. In Neovim terms the 'host' is the program that will be talking to Neovim via\nthe msgpack RPC mechanism (see :help [msgpack-rpc](https://neovim.io/doc/user/api.html#msgpack-rpc) for more info). You don't actually need to know anything about the\nmsgpack protocol. If you want to learn more you can start [here](https://msgpack.org/index.html).\n\nFirst you need to download the one dependency (go-client). From within the nvim-go-client-examples directory run:\n\n```\ngo mod download\n```\n\nTo build the project simply run make in the project directory.\n\n```\nmake\n```\n\nThis should build the 'nvim-go-client-example' program. This is the 'host' that will be talking to Neovim. By default\nthe host talks to Neovim over stdout/stdin. \n\nTo verify the 'host' works as expected you should run:\n\n```\n./nvim-go-client-example -h\n```\n\nThe output should be something like this:\n\n```\nUsage of ./nvim-go-client-example:\n  -location .vim file\n        Manifest is automatically written to .vim file\n  -manifest host\n        Write plugin manifest for host to stdout\n```\n\nYou should copy the 'nvim-go-client-example' to somewhere in your path. Any time you rebuild the host you will need to\ncopy the new build to the same location. \n\nThe host is now installed. When you start Neovim there should no longer be any errors.\n\n## Code\n\n### plugin/nvim-go-client.vim\nThis is the glue code that ties the Go 'host' to Neovim. This code registers the host, eventually starts the host, and\ndetails what the host can do.\n\nThe code at the very bottom of the file that starts with 'call remote#host#RegisterPlugin' is copied and pasted from\nrunning:\n\n```\n./nvim-go-client-example -manifest nvim_go_client_example\n```\n\nThe output looks something like this (but may be different if I've added or removed code since I last copied this text):\n\n```vim\ncall remote#host#RegisterPlugin('nvim_go_client_example', '0', [\n\\ {'type': 'autocmd', 'name': 'BufAdd', 'sync': 0, 'opts': {'eval': '{''Cwd'': getcwd()}', 'group': 'ExmplNvGoClientGrp', 'pattern'\n: '*'}},\n\\ {'type': 'autocmd', 'name': 'BufEnter', 'sync': 0, 'opts': {'group': 'ExmplNvGoClientGrp', 'pattern': '*'}},\n\\ {'type': 'command', 'name': 'ExCmd', 'sync': 0, 'opts': {'bang': '', 'eval': '[getcwd(),bufname()]', 'nargs': '?'}},\n\\ {'type': 'function', 'name': 'GetVV', 'sync': 1, 'opts': {}},\n\\ {'type': 'function', 'name': 'ShowFirst', 'sync': 1, 'opts': {}},\n\\ {'type': 'function', 'name': 'ShowThings', 'sync': 1, 'opts': {'eval': '[getcwd(),argc()]'}},\n\\ {'type': 'function', 'name': 'TurnOffEvents', 'sync': 0, 'opts': {}},\n\\ {'type': 'function', 'name': 'TurnOnEvents', 'sync': 0, 'opts': {}},\n\\ {'type': 'function', 'name': 'Upper', 'sync': 1, 'opts': {}},\n\\ {'type': 'function', 'name': 'UpperCwd', 'sync': 1, 'opts': {'eval': 'getcwd()'}},\n\\ ])\n```\n\nThis generated code will be called the 'manifest' within this document. \n\nIn other languages, such as Python, this step is somewhat automatic: you just run :UpdateRemotePlugins. For now the\nGo client does not hook into this mechanism so you have to generate the manifest manually and paste it into your\nVim code.\n\nThe code just above the manifest registers a Vim function that starts the host job. \n\n```vim\ncall remote#host#Register('nvim_go_client_example', 'x', function('s:Start_example_nvim_go_client'))\n```\n\nIt does this by calling remote#host#Register(). The first argument to this function is the name of the host as given to\nthe -manifest argument above. e.g. nvim_go_client_example. The second argument, as best I can tell, can safely be\nignored. The third argument gives a function reference to a Vim function that starts the job. In this case that function\nis s:Start_example_nvim_go_client().\n\n```vim\nfunction! s:panic(ch, data, ...) abort\n    echom a:data\nendfunction\n\nfunction! s:Start_example_nvim_go_client(host) abort\n    return jobstart(['nvim-go-client-example'], {\n        \\ 'rpc': v:true, \n        \\ 'on_stderr': function('s:panic')\n        \\ })\nendfunction\n```\n\nThe function you register as the initiator of the host takes a single argument: the name of the host. In the example in\nthis code that argument is ignored. The key part of that function is the use of 'jobstart'. You can see :help\n[jobstart()](https://neovim.io/doc/user/eval.html#jobstart()) for more information. The first argument to 'jobstart' is\nthe name of the program we built earlier with 'make'. e.g. nvim-go-client-example. The 'rpc' argument is probably the\nmost important since this tells Neovim that you want this program to use the msgpack RPC mechanism to communicate with\nNeovim. The 'on_error' argument is important because it allows easier debugging of what is happening when things go\nwrong. Some errors get reported to stderr and all this code does is make sure that that error gets printed to messages.\nSee :help [:messages](https://neovim.io/doc/user/message.html#:messages) for more information about messages. Without\nthe on_error section, and the function it calls, you will not see many of the errors that occur when the msgpack\nencoding/decoding fails.\n\n### Read the godoc\n\nSee especially 'go doc -all nvim/plugin'. It has some details that are important such as the signature required for the\nfunction arguments for the various Handler methods. Details about the fields in public structs, etc.... It's worthwhile\nto read it. Some of what it talks about are discussed in this document.\n\nAlso see 'go doc -all nvim'. It's a much larger document. You do not need to be intimately familiar with it to get \nstarted with go-plugin work, but it would be good to be vaguely familiar with the capabilities of the API.\n\n### main.go\nThe go-plugin code uses the default Go logger. So the first few lines in the example code's main() function do just\nthis:\n\n```go\n  // create a log to log to right away. It will help with debugging\n  l, _ := os.Create(\"nvim-go-client-example.log\")\n  log.SetOutput(l)\n```\n\nThis is pretty straightforward. Create a file to log to and set the output to log to that file. The\ngo-plugin code doesn't perform a lot of logging so I have found this mostly useful for debugging my\nown code when using go-plugin. \n\nThe rest of the code in main creates new functions, commands, and autocommands via Go. \n\n**A slight tangent to editorialize**\n\u003e I'm not certain that there is great advantage in creating your own commands and autocommands via Go. It's more\n\u003e cumbersome than creating them via Vimscript. The real advantage of using go-plugin is to create Vim functions that are\n\u003e written in Go. You can then call them from your Vimscript created autocommands and commands. Your Go functions can\n\u003e perform computationally intensive tasks much faster than Vimscript and can be called as if they were native functions. \n\nRegardless of what I said above these examples show you how to create commands and autocommands using Go.\n\nThe code in main calls plugin.Main().\n\n```go\n  plugin.Main(func(p *plugin.Plugin) error {\n    ...\n  }\n```\n\nThis method does a number of things. It creates the basic flags (-manifest and -location) using the flag package and\nruns the passed in anonymous function. The anonymous function is expected to have code that creates handlers for\ncommands, autocommands, and functions. After the anonymous function is run Main runs nvim.Serve(). This starts your plugin\nwaiting to send and receive to and from Neovim. The Serve() method blocks forever.\n\nAs you get more comfortable with using go-plugin you may not want to continue to use Main(). Main() seems more like a\nconvenience function that does the bare minimum a good host should do. The only problem with not using the Main() method\nis that it uses several useful helper functions to help write out the manifest. Unfortunately neither of these helper methods\nare exported. So you either have to write new ones or copy and paste the ones in the source to your code. See\ngo-plugin/nvim/plugin/main.go to view the code for Main(). Also see go-plugin/nvim/plugin/plugin.go (or 'go doc -all\nnvim/plugin') to view the Manifest() method. This method returns the manifest as a byte slice. \n\n#### Commands\nThe first thing the anonymous function does is use p.HandleCommand() to create a new command:\n\n```go\n  p.HandleCommand(\u0026plugin.CommandOptions{Name: \"ExCmd\", NArgs: \"?\", Bang: true, Eval: \"[getcwd(),bufname()]\"},\n      func(args []string, bang bool, eval *cmdEvalExample) {\n          log.Print(\"called command ExCmd\")\n          exCmd(p, args, bang, eval)\n      })\n```\n\np.HandleCommand() takes two arguments. The first is a pointer to a plugin.CommandOptions struct and the second\nis a function that implements the functionality for the new command.\n\n**A Note About the p.HandleCommand Function Return Value**\n\u003e The second argument to p.HandleCommand (or p.HandleAutocmd or p.HandleFunction, as we'll see later) requires a very\n\u003e specific return value. The return value **must** be one of the following:\n\u003e 1. no return value:  func m() {}\n\u003e 2. returns an error:  func m() (error) {}\n\u003e 3. returns a type and an error:  func m() ([]string, error) {}\n\u003e\n\u003e This is enforced by go-client. If you ever see an error in :messages that looks like \"msgpack/rpc: handler return \n\u003e must be (), (error) or (valueType, error)\" it means the return signature you have for your function is wrong.\n\n**A Second Note About the p.HandleCommand Function Return Value**\n\u003e If you looked closely at the manifest generated earlier you may have noticed a field named 'sync'. It is a boolean\n\u003e field and some of the commands in the manifest have sync set to 0 and some have sync set to 1. This field tells Vim\n\u003e whether to run the command synchronously or asynchronously. The go-client code determines how to set this field \n\u003e automatically by looking for a return value from the function you pass to p.HandleCommand (or p.HandleAutocmd or\n\u003e p.HandleFunction). If there is **no** return value then sync will be set to 0. Otherwise it is set to 1.\n\nThe plugin.CommandOptions record has many fields, all of which correspond directly to the arguments you can pass to\n:command within Neovim (see :help [:command](https://neovim.io/doc/user/map.html#:command) for more info). You can look\nat the fields in the record by looking at the godoc (go doc -all nvim/plugin). A quick listing of the fields in the struct\nare: Name, NArgs, Range, Count, Addr, Bang, Register, Eval, Bar, Complete. This example doesn't cover every option, but\nshould help you figure out how to use those options should you need them.\n\nFor this example the plugin.CommandOptions struct assigns a name of 'ExCmd' to the command, specifies that the number\nof arguments is 0 or 1 (that's what the \"?\" means), says that a bang (\"!\") is allowed, and has an eval section.\n\nThe name is the name a user will use from Neovim to call the command. So, in this case, we have defined a command\nwith the name of ExCmd. So, from Neovim, you can use :ExCmd. Give it a try. Fire up Neovim and run \n\n```\n:ExCmd! hi\n```\n\nNothing much will happen since the command only logs to the log file. So feel free to quit Neovim and look at the \nlog file that was created in the same directory. It will probably look something like this:\n\n```\nJust entered a buffer\ncalled command ExCmd\n  Args to exCmd:\n    arg1: hi\n    bang: %!s(bool=true)\n    cwd: /home/seth\n    buffer: \n```\n\nYou can see that it logged your use of :ExCmd, logged the argument you gave it (\"hi\"), logged that you used a bang,\nand also current directory and a buffer name (in this case the buffer name was empty because the buffer had no name).\n\nIf we look closer at the second argument to p.HandleCommand():\n\n```go\n  func(args []string, bang bool, eval *cmdEvalExample) {\n      log.Print(\"called command ExCmd\")\n      exCmd(p, args, bang, eval)\n  })\n```\n\nwe can see that the code is using an anonymous function to handle the arguments, log the use of 'ExCmd', and also \ncall another function. The anonymous function is useful because it creates a closure that can be used to pass \nthe plugin object to the exCmd function. But before we get to that let's talk about the arguments to the anonymous\nfunction.\n\nThe first argument is 'args' and it is typed as a slice of strings. This is where the arguments to :ExCmd get placed.\nSo, when you ran :ExCmd earlier you passed \"hi\". This is an argument and it was passed in to this function as the\nfirst element in the args slice.\n\nThe second argument is 'bang' and it is typed as a bool. It simply lets us know if an exclamation point was given when\n:ExCmd is called. Above, when you typed :ExCmd!, you used a bang. So in that case bang would have been true.\n\nThe third argument is 'eval' and it is a pointer to a struct. That struct is defined in commands.go and looks like:\n\n```go\n  type cmdEvalExample struct {\n      Cwd     string `msgpack:\",array\"`\n      Bufname string\n  }\n```\n\nThe fields in the struct match up with the expression given in the 'Eval' field for plugin.CommandOptions. Notice that\nthe 'Eval' field is vimscript surrounded by quotes. The vimscript is a list with two fields, each field being the result\nof an expression. The first expression is getcwd() and the second expression is bufname(). Note the field tag in the\ncmdEvalExample struct definition. This works hand-in-hand with what is in Eval.\n\nHow does go-plugin map the defined fields in plugin.CommandOptions to the arguments in the function? It appears to\nassume that the function will take the argument in the order they are defined in the plugin.CommandOptions struct. \nThe order of the definition in the struct is Name, NArgs, Range, Count, Addr, Bang, Register, Eval, Bar, Complete. So\nif you define Name, NArgs, Range, Bang, Eval, and Bar the function signature will look like:\n\n```go\nfunc(args []string, range string, bang bool, eval *struct, bar bool)\n```\n\nI haven't tried every possible combination but this seems to be the case. This is one part of the go-plugin code I \nhaven't examined thoroughly yet.\n\n#### Autocommands\nThe example code creates two new autocommands. It does this using p.HandleAutocmd like so:\n\n```go\n  p.HandleAutocmd(\u0026plugin.AutocmdOptions{Event: \"BufEnter\", Group: \"ExmplNvGoClientGrp\", Pattern: \"*\"},\n      func() {\n          log.Print(\"Just entered a buffer\")\n          [...]\n      })\n  p.HandleAutocmd(\u0026plugin.AutocmdOptions{Event: \"BufAdd\", Group: \"ExmplNvGoClientGrp\", Pattern: \"*\", Eval: \"*\"},\n      func(eval *autocmdEvalExample) {\n          log.Printf(\"buffer has cwd: %s\", eval.Cwd)\n      })\n```\n\nIt's very similar to how commands are defined. Instead of calling p.HandleCommand it calls p.HandleAutocmd. Instead\nof filling in a plugin.CommandOptions struct it fills in a plugin.AutocmdOptions struct (you can see the fields in\ngo-plugin/nvim/plugin/plugin.go). There are fewer fields for defining autocommands, but, like commands, they matchup\nexactly with what you would use if you were defining the autocommand in vimscript. The fields available are:\nEvent, Group, Pattern, Nested, Eval. For more details on how autocommands are defined in vimscript see :help\n[autocommand](https://neovim.io/doc/user/autocmd.html#autocommand).\n\nMy experimentation showed that if you define more than one autocommand in the same Event the manifest will be screwed\nup. So two (or more) autocommands in event \"BufEnter\" seemed to cause a problem. However if one autocommand was in \nevent \"BufEnter,BufLeave\" and the other was in \"BufEnter\" there were no problems. This brings up something that I \ndon't show in the example code: the Event field can list more than one event. This maps exactly to how autocommands are\ndefined in vimscript, but may not be obvious from the examples I have provided.\n\nThe first autocommand created is very simple:\n\n```go\n  p.HandleAutocmd(\u0026plugin.AutocmdOptions{Event: \"BufEnter\", Group: \"ExmplNvGoClientGrp\", Pattern: \"*\"},\n      func() {\n          log.Print(\"Just entered a buffer\")\n          [...]\n      })\n```\n\nThis will simply log that a buffer was entered into whenever that event is fired by Neovim. In fact, we have already\nseen this autocommand in action. If you go back and look at the log file from when you ran ExCmd you will see a line\nthat says:\n\n```\nJust entered a buffer\n```\n\nThat log entry was created by this autocommand.\n\nThe Group field is the same as the group you would use in a vimscript autocommand definition. The Pattern field is \nthe same as the pattern you would pass to :autocommand in Neovim. i.e. it can be \"\\*.go\" if you only want the autocommand\nto work for Go files.\n\nThe second autocommand definition is:\n\n```go\n  p.HandleAutocmd(\u0026plugin.AutocmdOptions{Event: \"BufAdd\", Group: \"ExmplNvGoClientGrp\", Pattern: \"*\", Eval: \"*\"},\n      func(eval *autocmdEvalExample) {\n          log.Printf(\"buffer has cwd: %s\", eval.Cwd)\n      })\n```\n\nThis definition includes an Eval section and also has an argument that is passed to the anonymous function. The Eval,\nand how it works, is very similar to what was described above for the definition of the command. However, in this case,\nthe Eval is simply an asterisk. What does this mean? Let's look at the definition for the autocmdEvalExample struct:\n\n```go\ntype autocmdEvalExample struct {\n\tCwd string `eval:\"getcwd()\"`\n}\n```\n\nThe field tag is now different. It now describes, directly, that the value of the given expression should be stored \nin the field. So, above, the value of the \"getcwd()\" vimscript expression should be stored in the Cwd field. You can\nhave multiple fields and each one can have different expressions. This is what the asterisk means in the Eval field\nabove. Here is an example of a struct with multiple fields, each field with its own expression:\n\n```go\ntype multiExprExample struct {\n    Cwd string `eval:\"getcwd()\"`\n    Name string `eval:\"bufname()\"`\n    Id int `eval:\"bufnr()\"`\n}\n```\n\nYou can see the result from the second autocommand if you fire up Neovim and create a new buffer. You can then look\nat the log file and see an entry that looks like:\n\n```\nJust entered a buffer\nbuffer has cwd: /home/seth\nJust entered a buffer\n```\n\n#### Functions\nThe example code creates five new functions. It does this using p.HandleFunction like so:\n\n```go\n  p.HandleFunction(\u0026plugin.FunctionOptions{Name: \"Upper\"},\n      func(args []string) (string, error) {\n          log.Print(\"calling Upper\")\n          return upper(p, args[0]), nil\n      })\n  p.HandleFunction(\u0026plugin.FunctionOptions{Name: \"UpperCwd\", Eval: \"getcwd()\"},\n      func(args []string, dir string) (string, error) {\n          log.Print(\"calling UpperCwd\")\n          return upper(p, dir), nil\n      })\n  p.HandleFunction(\u0026plugin.FunctionOptions{Name: \"ShowThings\", Eval: \"[getcwd(),argc()]\"},\n      func(args []string, eval *someArgs) ([]string, error) {\n          log.Print(\"calling ShowThings\")\n          return returnArgs(p, eval)\n      })\n  p.HandleFunction(\u0026plugin.FunctionOptions{Name: \"GetVV\"},\n      func(args []string) ([]string, error) {\n          log.Print(\"calling GetVV\")\n          return getvv(p, args[0])\n      })\n  p.HandleFunction(\u0026plugin.FunctionOptions{Name: \"ShowFirst\"},\n      func(args []string) (string, error) {\n          log.Print(\"calling ShowFirst\")\n          return showfirst(p), nil\n      })\n```\n\nAt this point, if you have read the previous sections, it should be obvious what is going on. As it turns out, defining\nfunctions is the simplest of the bunch. There are only two fields: Name and Eval (see the definition of\nplugin.FunctionOptions in go-plugin/nvim/plugin/plugin.go). In this section I won't talk so much about Eval or the\ndefinition of the functions. Instead there are a few things the functions do that may be interesting.\n\nI continue to to use an anonymous function to wrap the code I want to use for the function. In this section we will\nfinally see a particularly good use for this: the ability of our code to use the plugin object.\n\nThe code above defines five new functions: Upper, UpperCwd, ShowThings, GetVV, and ShowFirst. The Go functions that each\nof these functions call is in functions.go in the example code.\n\nThe \"Upper\" function simply takes a single string as argument, converts it to upper case and returns it. Feel free to\ntry it from within Neovim:\n\n```\n:echo Upper(\"this is now upper case\")\n```\n\nAlso, look at the log and notice the log entry:\n\n```\ncalling Upper\n```\n\nFeel free to also run UpperCwd and ShowThings. Look at the code, see what each one does, and then run it.\n\n#### Completion Functions\n\nThe example code will now create a command completion function. First it creates a simple command named 'CompleteThis'.\nIt fills in the 'Complete' field with the exact same thing you'd use if you were defining it via Vimscript. (see :help\n[command-complete](https://neovim.io/doc/user/map.html#:command-completion-customlist) for more information) It names a function\ncalled 'CompleteThisC' which is defined using Go.\n\n```go\n  p.HandleCommand(\u0026plugin.CommandOptions{Name: \"CompleteThis\", NArgs: \"?\", Complete: \"customlist,CompleteThisC\"},\n          func() {\n                  log.Print(\"called command CompleteThis\")\n          })\n  p.HandleFunction(\u0026plugin.FunctionOptions{Name: \"CompleteThisC\"},\n          func(c *nvim.CommandCompletionArgs) ([]string, error) {\n                  log.Print(\"called CompleteThisC\")\n                  log.Printf(\"  arg lead: %s\", c.ArgLead)\n                  log.Printf(\"  cmdline: %s\", c.CmdLine)\n                  log.Printf(\"  cursorposstring: %d\", c.CursorPosString)\n                  return []string{\"abc\", \"def\", \"ghi\", \"jkl\"}, nil\n          })\n```\n\nNote that the anonymous function takes a single argument with a type of \\*nvim.CommandCompletionArgs. The CommandCompletionArgs\nstruct defines exactly what gets passed in to a typical Vimscript completion function and this is all documented in\n:help [command-complete](https://neovim.io/doc/user/map.html#:command-complete).\n\nFor a customlist function you simply return a slice of strings with the completion items. In the example above the \nreturn value was hardcoded.\n\nTo see the completion in action simply startup Neovim, type ':CompleteThis ' (note the space at the end) and hit \\\u003ctab\\\u003e.\nYou should see a little box pop up and display the completion elements. If you examine the log it will say something\nlike:\n\n```\nJust entered a buffer\ncalled CompleteThisC\n  arg lead: \n  cmdline: CompleteThis \n  cursorposstring: 13\n```\n\n#### NVim API\n\n##### Retrieving Vim Variables\n\nThe 'GetVV' function does something a little interesting. It uses the nvim API provided by go-plugin. The nvim API is\npretty large, but you can look at it using go doc:\n\n```\ngo doc -all nvim\n```\n\nGetVV uses the VVar() method to retrieve whatever v: variable you wish to retrieve (for more information on v: variables\nhave a look at :help [vim-variable](https://neovim.io/doc/user/eval.html#vim-variable)). Perhaps the simplest one is\nv:oldfiles. So let's use GetVV to retrieve v:oldfiles. Fire up Neovim and run:\n\n```\n:echo GetVV(\"oldfiles\")\n```\n\nIt should print out a fairly large Vim list. Each element in the list should be a path to a file that you have edited in\nNeovim (somewhat) recently.\n\nIf we look at the code for GetVV:\n\n```go\nfunc getvv(p *plugin.Plugin, name string) ([]string, error) {\n\tvar result []string\n\tp.Nvim.VVar(name, \u0026result)\n\treturn result, nil\n}\n```\n\nYou can see that we use the plugin object. The plugin object has an Nvim object stashed in it and we use that to call\nthe VVar() method. The API for some of these methods is odd and may not be what you are expecting. It is definitely not\nwhat I would have expected. The last argument to VVar() (per the VVar signature) is a pointer to interface{}. The\ngo-plugin restricts this to a pointer to either a slice or a map.\n\n##### Reading the Current Buffer\n\nThe ShowFirst function retrieves the first line of the current buffer and prints it out. This demonstrates that your Go\nprograms have access to the open buffers. So, first, try it out by bringing up Neovim and running:\n\n```\n:echo ShowFirst()\n```\n\nYou may want to type something in the empty buffer first. Or bring up a file with content on the first line. Otherwise\nyou'll just get a blank line when running ShowFirst().\n\nThe code used for ShowFirst is:\n\n```go\nfunc showfirst(p *plugin.Plugin) string {\n\tbr := nvim.NewBufferReader(p.Nvim, 0)\n\tr := bufio.NewReader(br)\n\tline, _ := r.ReadString('\\n')\n\treturn line\n}\n```\n\nWe once again use the plugin object. In this case we use it to create a new buffer reader object. See the go doc for the\nnvim api for more information about the arguments to NewBufferReader(). Once we have the new buffer reader object it is\nconverted to a bufio.Reader and we can grab the first line of the buffer and return it. Pretty simple!\n\n##### Subscribing to Special Events\n\nYou can subscribe to special events that Neovim emits and you can attach a function that is called when those events are\nemitted. It's similar to autocommands, but the events are different and are called with arguments. See :help \n[api-buffer-updates](https://neovim.io/doc/user/api.html#api-buffer-updates) for more information. (There are also the\nui-events which is not covered here but which works in a similar manner. See :help [ui-events](https://neovim.io/doc/user/ui.html#ui-events) for more information.)\n\nThe below code attaches to two of the events and logs when they occur along with the arguments that are passed to it.\nTo see the logging you must first subscribe to the event stream for the current buffer. This is performed by one of the\nnewly defined functions: TurnOnEvents.\n\n```go\n  p.Handle(\"nvim_buf_lines_event\",\n          func(e ...interface{}) {\n                  log.Printf(\"triggered buf lines event %#v\", e)\n          })\n  p.Handle(\"nvim_buf_changedtick_event\",\n          func(e ...interface{}) {\n                  log.Printf(\"triggered changed tick event %#v\", e)\n          })\n  // these functions are used to demo the turning off/on of\n  // buffer events\n  p.HandleFunction(\u0026plugin.FunctionOptions{Name: \"TurnOffEvents\"},\n          func() {\n                  log.Print(\"calling TurnOffEvents\")\n                  buffer, _ := p.Nvim.CurrentBuffer()\n                  p.Nvim.DetachBuffer(buffer)\n          })\n  p.HandleFunction(\u0026plugin.FunctionOptions{Name: \"TurnOnEvents\"},\n          func() {\n                  log.Print(\"calling TurnOnEvents\")\n                  buffer, _ := p.Nvim.CurrentBuffer()\n                  p.Nvim.AttachBuffer(buffer, false, map[string]interface{}{})\n          })\n```\n\nTo see this in action it is best to be tailing the log file in a separate terminal. So once you start Vim you should\ntail the log file:\n\n```\ntail -f nvim-go-client-example.log\n```\n\nBack in your Vim session you can type all you want but nothing will appear in the log file. However, once you run\n\n```\n:call TurnOnEvents()\n```\n\nyou will see a new log line for every character you type. This will be true **only** in the buffer you are currently\nin. If you switch buffers you no longer see anything in the log. However, if you switch back to the previous buffer\nyou will again see logging. This is because TurnOnEvents has subscribed to events emitted by one specific buffer.\nIf you run TurnOnEvents in another buffer you will start to see logging for that buffer also. That is, you will be\nsubscribed to two different buffers sending events.\n\nYou can also turn off the stream of events:\n\n```\n:call TurnOffEvents()\n```\n\nNotice the call to p.Nvim.AttachBuffer(). This is what starts the events flowing back to us. Without both calling\nHandle() and AttachBuffer() the callbacks will never be called.\n\nAlso notice that we used another nvim API call: CurrentBuffer(). This does what you'd probably expect, it returns\nan identifier for the currently active buffer. This is the first argument to each of DetachBuffer() and AttachBuffer().\n\nThe output may look something like this (after turning events on, I typed \"hi there\" in an empty buffer, then quit):\n\n```\n[...]\ntriggered changed tick event []interface {}{1, 2}\ntriggered buf lines event []interface {}{1, 3, 0, 1, []interface {}{\"h\"}, false}\ntriggered buf lines event []interface {}{1, 4, 0, 1, []interface {}{\"hi\"}, false}\ntriggered buf lines event []interface {}{1, 5, 0, 1, []interface {}{\"hi \"}, false}\ntriggered buf lines event []interface {}{1, 6, 0, 1, []interface {}{\"hi t\"}, false}\ntriggered buf lines event []interface {}{1, 7, 0, 1, []interface {}{\"hi th\"}, false}\ntriggered buf lines event []interface {}{1, 8, 0, 1, []interface {}{\"hi the\"}, false}\ntriggered buf lines event []interface {}{1, 9, 0, 1, []interface {}{\"hi ther\"}, false}\ntriggered buf lines event []interface {}{1, 10, 0, 1, []interface {}{\"hi there\"}, false}\ntriggered buf lines event []interface {}{1, 11, 0, 1, []interface {}{\"hi there\", \"\"}, false}\n```\n\nThat's a lot of output, but it is sending an event every time a key is pressed. Notice the tick event near the\ntop.\n","funding_links":[],"categories":["Go"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FWhoIsSethDaniel%2Fnvim-go-client-examples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FWhoIsSethDaniel%2Fnvim-go-client-examples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FWhoIsSethDaniel%2Fnvim-go-client-examples/lists"}