diff --git a/nvim/README.md b/nvim/README.md new file mode 100644 index 00000000..c307a122 --- /dev/null +++ b/nvim/README.md @@ -0,0 +1,99 @@ +# Neovim + +Neovim client for the Odools language server + +![screenshot](https://i.imgur.com/wuqsF9q.png) + +## Important ⚠️ +This plugin is still in its early development stage. Don't hesitate to submit bugs, issues and/or +feedbacks to improve the user experience. + +## Installation +### requirement +We recommend using nvim version `0.9.0` or later. This plugin is using +[lspconfig](https://github.com/neovim/nvim-lspconfig) to connect communicate with the language +server in your beloved editor. + +### downloads + 1. Install the plugin + +>Installing the neovim plugin is done through the entire Odools repositery. You need to specify to your package manager the `nvim` subfolder that must be added in the runtimepath. Check the examples listed below ⚠️ + +Using [packer.nvim](https://github.com/wbthomason/packer.nvim) + +```lua +use { + 'odoo/odoo-ls', + requires = { {'neovim/nvim-lspconfig'} }, + rtp = 'nvim/', +} +``` + +Using [lazy.nvim](https://github.com/folke/lazy.nvim) + +```lua +-- init.lua: + { + 'odoo/odoo-ls', + dependencies = { 'neovim/nvim-lspconfig' }, + config = function(plugin) + vim.opt.rtp:append(plugin.dir .. "/nvim") + require("lazy.core.loader").packadd(plugin.dir .. "/nvim") + end, + init = function(plugin) + require("lazy.core.loader").ftdetect(plugin.dir .. "/nvim") + end + } + +-- plugins/odoo.lua: +return { + 'odoo/odoo-ls', + dependencies = { 'neovim/nvim-lspconfig' }, + config = function(plugin) + vim.opt.rtp:append(plugin.dir .. "/nvim") + require("lazy.core.loader").packadd(plugin.dir .. "/nvim") + end, + init = function(plugin) + require("lazy.core.loader").ftdetect(plugin.dir .. "/nvim") + end + } +``` + + 2. Download the server executable from the release assets + ```bash + wget -O ~/.local/share/nvim/odoo/odoo_ls_server https://github.com/odoo/odoo-ls/releases/download/0.4.0/odoo_ls_server + ``` + + 3. downloads python [typeshed](https://github.com/python/typeshed) to enrich the server with builtin python package stubs + + (2. and 3. can be done automatically via the `:Odools` [command](https://github.com/odoo/odoo-ls/tree/master/nvim/README.md#usage)) + + +## Configuration +The plugin needs different local path and executable to be working. Here is the mandatory config +keys. + +```lua +local odools = require('odools') +local h = os.getenv('HOME') +odools.setup({ + -- mandatory + odoo_path = h .. "/src/odoo/", + python_path = h .. "/.pyenv/shims/python3", + + -- optional + server_path = h .. "/.local/share/nvim/odoo/odoo_ls_server", + addons = {h .. "/src/enterprise/"}, + additional_stubs = {h .. "/src/additional_stubs/", h .. "/src/typeshed/stubs"}, + root_dir = h .. "/src/", -- working directory, odoo_path if empty + settings = { + autoRefresh = true, + autoRefreshDelay = nil, + diagMissingImportLevel = "none", + }, +}) +``` + +## Usage +Try the command `:Odools install` to fetch the language server executable from Github as well as the +python typesheds diff --git a/nvim/lua/odools/init.lua b/nvim/lua/odools/init.lua new file mode 100644 index 00000000..679b596e --- /dev/null +++ b/nvim/lua/odools/init.lua @@ -0,0 +1,62 @@ +local util = require('odools.utils') +local M = {} + +local lsp_config = require('lspconfig.configs') + +if not lsp_config then + vim.api.nvim_err_writeln("lsp_config not available") + return +end + +M.setup = function(opt) + opt = opt or {} + opt.python_path = opt.python_path or '/usr/bin/python3' + opt.server_path = opt.server_path or util.get_server_path() + util.check_config(opt) + local odoo_path = opt.odoo_path + opt.root_dir = opt.root_dir or odoo_path + local odooConfig = { + id = 1, + name = "main config", + validatedAddonsPaths = opt.addons or {}, + addons = opt.addons or {}, + odooPath = odoo_path, + finalPythonPath = opt.python_path, + additional_stubs = opt.additional_stubs or {}, + } + local server_path = opt.server_path + lsp_config.odools = { + default_config = { + name = 'odools', + cmd = {server_path}, + root_dir = function() return vim.fn.fnamemodify(opt.server_path, ":h") end, + workspace_folders = { + { + uri = function() return opt.root_dir end, + name = function() return "base_workspace" end, + }, + }, + filetypes = { 'python' }, + settings = { + Odoo = { + autoRefresh = opt.settings and opt.settings.autoRefresh or true, + autoRefreshDelay = opt.settings and opt.settings.autoRefreshDelay or nil, + diagMissingImportLevel = opt.settings and opt.settings.diagMissingImportLevel or "none", + configurations = { mainConfig = odooConfig }, + selectedConfiguration = "mainConfig", + }, + }, + capabilities = { + textDocument = { + workspace = { + symbol = { + dynamicRegistration = true, + }, + }, + }, + }, + }, + } + lsp_config.odools.setup {} +end +return M diff --git a/nvim/lua/odools/utils.lua b/nvim/lua/odools/utils.lua new file mode 100644 index 00000000..37d62e30 --- /dev/null +++ b/nvim/lua/odools/utils.lua @@ -0,0 +1,30 @@ +local util = {} + +util.get_server_path = function() + local bin_dir_path = vim.fn.stdpath('data') .. '/odoo' + local bin_path = bin_dir_path .. '/odoo_ls_server' + if vim.fn.filereadable(bin_path) == 0 then + vim.api.nvim_out_write("[Odools] You should download the server executable\n") + end + return bin_path +end + +---Get the user config to assert basic values +---@param conf {[string]: string} +util.check_config = function(conf) + if not conf then + vim.api.nvim_err_writeln("You should give a minimal configuration") + end + + if not conf.odoo_path or type(conf.odoo_path) ~= 'string' or vim.fn.isdirectory(conf.odoo_path) == 0 then + vim.api.nvim_err_writeln("You should give a valid odoo path") +end + if not conf.python_path or type(conf.python_path) ~= 'string' or not vim.fn.isdirectory(conf.python_path) == 0 then + vim.api.nvim_err_writeln("You should give a valid python path") + end + if not conf.server_path or type(conf.server_path) ~= 'string' or not vim.fn.filereadable(conf.server_path) == 0 then + vim.api.nvim_err_writeln("You should give a valid server path or download it") + end +end + +return util diff --git a/nvim/plugin/command.lua b/nvim/plugin/command.lua new file mode 100644 index 00000000..d8f643c1 --- /dev/null +++ b/nvim/plugin/command.lua @@ -0,0 +1,95 @@ +local command = {} +command.target = "0.4.0" +command.bin_path = "" + +local download = function(url, output_path, asset_type) + local stdout = vim.uv.new_pipe(false) + local stderr = vim.uv.new_pipe(false) + + local handle + local args, cmd + if asset_type == "file" then + if vim.fn.filereadable(output_path) == 1 then + vim.api.nvim_echo({{"Delete previous file"}}, true, {}) + vim.uv.fs_unlink(output_path) + end + cmd = "wget" + args = { "-q", "-O", output_path, url } + else + cmd = "git" + args = { "clone", "-q", url, output_path} + end + vim.api.nvim_echo({{"Starting download from: " .. url}}, true, {}) + vim.api.nvim_echo({{"Saving to: " .. output_path}}, true, {}) + + handle = vim.uv.spawn(cmd, { + args = args, + stdio = { nil, stdout, stderr }, + }, function(code, signal) + stdout:close() + stderr:close() + handle:close() + + local msg = {"\nDownload successful!"} + if code ~= 0 then + msg = {"\nDownload failed with exit code " .. code} + else + print(vim.uv.fs_chmod(output_path, 493)) + end + vim.schedule(function() + vim.api.nvim_echo({msg}, true, {}) + end) + end) + + stdout:read_start(function(err, data) + assert(not err, err) + if data then + io.write(data) + io.flush() + end + end) + + stderr:read_start(function(err, data) + assert(not err, err) + if data then + io.write("\nError: " .. data) + io.flush() + end + end) +end + +local download_requirements = function() + local bin_dir_path = vim.fn.stdpath('data') .. '/odoo' + local bin_path = bin_dir_path .. '/odoo_ls_server' + if vim.fn.isdirectory(bin_dir_path) == 0 then + os.execute('mkdir -p ' .. bin_dir_path) + end + download("https://github.com/odoo/odoo-ls/releases/download/" .. command.target .. "/odoo_ls_server", bin_path, 'file') + if vim.fn.executable('git') == 1 then + local path = bin_dir_path .. '/typeshed' + if vim.fn.isdirectory(path) == 0 then + download('https://github.com/python/typeshed.git', path, 'repo') + else + vim.api.nvim_echo({{"typeshed already downloaded"}}, true, {}) + end + else + vim.api.nvim_err_writeln("git needed to download python typeshed") + end + vim.cmd.LspRestart('odools') +end + +local odoo_command = function(opts) + if opts.fargs and opts.fargs[1] == "install" then + download_requirements() + end +end + +vim.api.nvim_create_user_command('Odools', odoo_command, { + nargs = 1, + complete = function() + -- return completion candidates as a list-like table + return { "install" } + end, +}) + +return command