[
  {
    "path": ".editorconfig",
    "content": "[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 4\nindent_style = space\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[doc/*.txt]\nmax_line_length = 78\n"
  },
  {
    "path": ".luarc.json",
    "content": "{\n    \"$schema\": \"https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json\",\n    \"runtime\": {\n        \"version\": \"LuaJIT\"\n    },\n    \"workspace\": {\n        \"library\": [\n            \"lua\",\n            \"$VIMRUNTIME\",\n            \"${3rd}/luv/library\"\n        ],\n        \"checkThirdParty\": false\n    }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Timothée Sterle\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# sqls.nvim\n\nNeovim plugin for [sqls](https://github.com/lighttiger2505/sqls) that leverages the built-in LSP client. Loosely based on the code from [sqls.vim](https://github.com/lighttiger2505/sqls.vim). Requires Neovim 0.11.0+\n\n## Installation\n\n- [packer.nvim](https://github.com/wbthomason/packer.nvim)\n    ```lua\n    use 'nanotee/sqls.nvim'\n    ```\n- [paq-nvim](https://github.com/savq/paq-nvim)\n    ```lua\n    paq 'nanotee/sqls.nvim'\n    ```\n- [vim-plug](https://github.com/junegunn/vim-plug)\n    ```vim\n    Plug 'nanotee/sqls.nvim'\n    ```\n\n## Usage\n\nEnable the configuration with [`vim.lsp.enable()`](https://neovim.io/doc/user/lsp.html#vim.lsp.enable()) and [`vim.lsp.config()`](https://neovim.io/doc/user/lsp.html#vim.lsp.config())\n\n```lua\nvim.lsp.config('sqls', {\n    -- your custom client configuration\n})\nvim.lsp.enable('sqls')\n```\n\nSee also: [`lsp-config`](https://neovim.io/doc/user/lsp.html#lsp-config)\n\n## Commands\n\nSee [sqls-nvim-commands](doc/sqls-nvim.txt#L14)\n\n## Mappings\n\nSee [sqls-nvim-maps](doc/sqls-nvim.txt#L54)\n\n## Events\n\nSee [sqls-nvim-events](doc/sqls-nvim.txt#L66)\n"
  },
  {
    "path": "autoload/sqls_nvim.vim",
    "content": "function! sqls_nvim#query(type, client_id)\n    call v:lua.require'sqls.commands'.query(a:type, a:client_id)\nendfunction\n\nfunction! sqls_nvim#query_vertical(type, client_id)\n    call v:lua.require'sqls.commands'.query_vertical(a:type, a:client_id)\nendfunction\n"
  },
  {
    "path": "doc/sqls-nvim.txt",
    "content": "*sqls-nvim.txt*\t\t                Neovim plugin for the sqls language server\n\nSETUP                                           *sqls-nvim-setup*\n\nEnable the configuration with |vim.lsp.enable()| and |vim.lsp.config()|\n>lua\n    vim.lsp.config('sqls', {\n        -- your custom client configuration\n    })\n    vim.lsp.enable('sqls')\n<\nSee also: |lsp-config|\n==============================================================================\nCOMMANDS                                        *sqls-nvim-commands*\n\n                                                *:SqlsExecuteQuery*\n:SqlsExecuteQuery          In normal mode, executes the query in the current\n                           buffer. In visual mode, executes the selected query\n                           (only works line-wise). Shows the results in a\n                           |preview-window|.\n\n\n                                                *:SqlsExecuteQueryVertical*\n:SqlsExecuteQueryVertical  Same as `:SqlsExecuteQuery`, but the results are\n                           displayed vertically.\n\n\n                                                *:SqlsShowDatabases*\n:SqlsShowDatabases         Shows a list of available databases in a\n                           |preview-window|.\n\n                                                *:SqlsShowSchemas*\n:SqlsShowSchemas           Shows a list of available schemas in a\n                           |preview-window|.\n\n                                                *:SqlsShowConnections*\n:SqlsShowConnections       Shows a list of available database connections in a\n                           |preview-window|.\n\n                                                *:SqlsSwitchDatabase*\n:SqlsSwitchDatabase {database_name}\n                           Switches to a different database. If\n                           {database_name} is omitted, displays an interactive\n                           prompt with |vim.ui.select()| to select a database.\n\n                                                *:SqlsSwitchConnection*\n:SqlsSwitchConnection {connection_index}\n                           Switches to a different database connection. If\n                           {connection_index} is omitted, displays an\n                           interactive prompt with |vim.ui.select()| to select\n                           a connection.\n\n==============================================================================\nMAPS                                            *sqls-nvim-maps*\n\n                                                *<Plug>(sqls-execute-query)*\n<Plug>(sqls-execute-query)           In visual mode, executes the selected\n                                     range. In normal mode, executes a motion\n                                     (like |ip| or |aw|)\n\n                                                *<Plug>(sqls-execute-query-vertical)*\n<Plug>(sqls-execute-query-vertical)  same as |<Plug>(sqls-execute-query)|, but\n                                     the results are displayed vertically\n\n==============================================================================\nEVENTS                                          *sqls-nvim-events*\n\nsqls.nvim dispatches custom |User| events that can be subscribed to using\n|autocommand|s. It exposes custom data via the `data` key in Lua callbacks\n(see |nvim_create_autocmd()|):\n>lua\n    vim.api.nvim_create_autocmd('User', {\n        pattern = 'SqlsConnectionChoice',\n        callback = function(event)\n            vim.notify(event.data.choice)\n        end,\n    })\n<\n                                                *User_SqlsDatabaseChoice*\nUser SqlsDatabaseChoice\n                           After switching databases. Callback data:\n                           • choice: (string) the chosen database\n\n                                                *User_SqlsConnectionChoice*\nUser SqlsConnectionChoice\n                           After switching connections. Callback data:\n                           • choice: (string) the chosen connection\n\nvim:tw=78:et:ft=help:norl:\n"
  },
  {
    "path": "ftplugin/sqls_output.vim",
    "content": "setlocal nowrap\nsetlocal nobuflisted\nsetlocal nomodifiable\nsetlocal nomodified\nsetlocal readonly\n"
  },
  {
    "path": "lsp/sqls.lua",
    "content": "local api = vim.api\n\nreturn {\n    cmd = { 'sqls' },\n    filetypes = { 'sql', 'mysql' },\n    single_file_support = true,\n    commands = {\n        executeQuery = function(_, client)\n            require('sqls.commands').exec(client.client_id, 'executeQuery')\n        end,\n        showDatabases = function(_, client)\n            require('sqls.commands').exec(client.client_id, 'showDatabases')\n        end,\n        showSchemas = function(_, client)\n            require('sqls.commands').exec(client.client_id, 'showSchemas')\n        end,\n        showConnections = function(_, client)\n            require('sqls.commands').exec(client.client_id, 'showConnections')\n        end,\n        showTables = function(_, client)\n            require('sqls.commands').exec(client.client_id, 'showTables')\n        end,\n        describeTable = function(_, client)\n            require('sqls.commands').exec(client.client_id, 'describeTable')\n        end,\n        switchConnections = function(_, client)\n            require('sqls.commands').switch_connection(client.client_id)\n        end,\n        switchDatabase = function(_, client)\n            require('sqls.commands').switch_database(client.client_id)\n        end,\n    },\n    on_attach = function(client, bufnr)\n        local client_id = client.id\n        api.nvim_buf_create_user_command(bufnr, 'SqlsExecuteQuery', function(args)\n            require('sqls.commands').exec(\n                client_id,\n                'executeQuery',\n                args.smods,\n                args.range ~= 0,\n                nil,\n                args.line1,\n                args.line2\n            )\n        end, { range = true })\n        api.nvim_buf_create_user_command(bufnr, 'SqlsExecuteQueryVertical', function(args)\n            require('sqls.commands').exec(\n                client_id,\n                'executeQuery',\n                args.smods,\n                args.range ~= 0,\n                '-show-vertical',\n                args.line1,\n                args.line2\n            )\n        end, { range = true })\n        api.nvim_buf_create_user_command(bufnr, 'SqlsShowDatabases', function(args)\n            require('sqls.commands').exec(client_id, 'showDatabases', args.smods)\n        end, {})\n        api.nvim_buf_create_user_command(bufnr, 'SqlsShowSchemas', function(args)\n            require('sqls.commands').exec(client_id, 'showSchemas', args.smods)\n        end, {})\n        api.nvim_buf_create_user_command(bufnr, 'SqlsShowConnections', function(args)\n            require('sqls.commands').exec(client_id, 'showConnections', args.smods)\n        end, {})\n        api.nvim_buf_create_user_command(bufnr, 'SqlsShowTables', function(args)\n            require('sqls.commands').exec(client_id, 'showTables', args.smods)\n        end, {})\n        -- Not yet supported by the language server:\n        -- api.nvim_buf_create_user_command(bufnr, 'SqlsDescribeTable', function(args)\n        --     require('sqls.commands').exec(client_id, 'describeTable', args.smods)\n        -- end, {})\n        api.nvim_buf_create_user_command(bufnr, 'SqlsSwitchDatabase', function(args)\n            require('sqls.commands').switch_database(client_id, args.args ~= '' and args.args or nil)\n        end, { nargs = '?' })\n        api.nvim_buf_create_user_command(bufnr, 'SqlsSwitchConnection', function(args)\n            require('sqls.commands').switch_connection(client_id, args.args ~= '' and args.args or nil)\n        end, { nargs = '?' })\n\n        api.nvim_buf_set_keymap(\n            bufnr,\n            'n',\n            '<Plug>(sqls-execute-query)',\n            \"<Cmd>let &opfunc='{type -> sqls_nvim#query(type, \" .. client_id .. \")}'<CR>g@\",\n            { silent = true }\n        )\n        api.nvim_buf_set_keymap(\n            bufnr,\n            'x',\n            '<Plug>(sqls-execute-query)',\n            \"<Cmd>let &opfunc='{type -> sqls_nvim#query(type, \" .. client_id .. \")}'<CR>g@\",\n            { silent = true }\n        )\n        api.nvim_buf_set_keymap(\n            bufnr,\n            'n',\n            '<Plug>(sqls-execute-query-vertical)',\n            \"<Cmd>let &opfunc='{type -> sqls_nvim#query_vertical(type, \" .. client_id .. \")}'<CR>g@\",\n            { silent = true }\n        )\n        api.nvim_buf_set_keymap(\n            bufnr,\n            'x',\n            '<Plug>(sqls-execute-query-vertical)',\n            \"<Cmd>let &opfunc='{type -> sqls_nvim#query_vertical(type, \" .. client_id .. \")}'<CR>g@\",\n            { silent = true }\n        )\n    end,\n}\n"
  },
  {
    "path": "lua/sqls/commands.lua",
    "content": "local api = vim.api\nlocal fn = vim.fn\n\nlocal nvim_exec_autocmds = api.nvim_exec_autocmds\n\nlocal M = {}\n\n---@param smods? vim.api.keyset.cmd.mods\n---@return lsp.Handler\nlocal function make_show_results_handler(smods)\n    return function(err, result, _)\n        if err then\n            vim.notify('sqls: ' .. err.message, vim.log.levels.ERROR)\n            return\n        end\n        if not result then\n            return\n        end\n        local tempfile = fn.tempname() .. '.sqls_output'\n        local bufnr = fn.bufnr(tempfile, true)\n        api.nvim_buf_set_lines(bufnr, 0, 1, false, vim.split(result, '\\n'))\n        vim.cmd.pedit({\n            args = { tempfile },\n            mods = smods or {},\n        })\n        api.nvim_set_option_value('filetype', 'sqls_output', { buf = bufnr })\n    end\nend\n\n---@param client_id integer\n---@param command string\n---@param smods? vim.api.keyset.cmd.mods\n---@param range_given? boolean\n---@param show_vertical? '-show-vertical'\n---@param line1? integer\n---@param line2? integer\nfunction M.exec(client_id, command, smods, range_given, show_vertical, line1, line2)\n    local client = assert(vim.lsp.get_client_by_id(client_id))\n\n    local range\n    if range_given then\n        range = vim.lsp.util.make_given_range_params(\n            { line1, 0 },\n            { line2, math.huge },\n            0,\n            client.offset_encoding\n        ).range\n        range['end'].character = range['end'].character - 1\n    end\n\n    client:request(\n        'workspace/executeCommand',\n        {\n            command = command,\n            arguments = { vim.uri_from_bufnr(0), show_vertical },\n            range = range,\n        },\n        make_show_results_handler(smods)\n    )\nend\n\n---@alias sqls_operatorfunc fun(type: 'block'|'line'|'char', client_id: integer)\n\n---@param show_vertical? '-show-vertical'\n---@return sqls_operatorfunc\nlocal function make_query_mapping(show_vertical)\n    return function(type, client_id)\n        local range\n        local _, lnum1, col1, _ = unpack(fn.getpos(\"'[\"))\n        local _, lnum2, col2, _ = unpack(fn.getpos(\"']\"))\n        if type == 'block' then\n            vim.notify('sqls does not support block-wise ranges!', vim.log.levels.ERROR)\n            return\n        end\n\n        local client = assert(vim.lsp.get_client_by_id(client_id))\n\n        if type == 'line' then\n            range = vim.lsp.util.make_given_range_params(\n                { lnum1, 0 },\n                { lnum2, math.huge },\n                0,\n                client.offset_encoding\n            ).range\n            range['end'].character = range['end'].character - 1\n        elseif type == 'char' then\n            range = vim.lsp.util.make_given_range_params(\n                { lnum1, col1 - 1 },\n                { lnum2, col2 - 1 },\n                0,\n                client.offset_encoding\n            ).range\n        end\n\n        client:request(\n            'workspace/executeCommand',\n            {\n                command = 'executeQuery',\n                arguments = { vim.uri_from_bufnr(0), show_vertical },\n                range = range,\n            },\n            make_show_results_handler()\n        )\n    end\nend\n\nM.query = make_query_mapping()\nM.query_vertical = make_query_mapping('-show-vertical')\n\n---@alias sqls_switch_function fun(client_id: integer, query: string)\n---@alias sqls_prompt_function fun(client_id: integer, switch_function: sqls_switch_function, query?: string)\n---@alias sqls_answer_formatter fun(answer: string): string\n---@alias sqls_switcher fun(client_id: integer, query?: string)\n---@alias sqls_event_name\n---| 'SqlsDatabaseChoice'\n---| 'SqlsConnectionChoice'\n\n\n---@param client_id integer\n---@param switch_function sqls_switch_function\n---@param answer_formatter sqls_answer_formatter\n---@param event_name sqls_event_name\n---@param query? string\n---@return lsp.Handler\nlocal function make_choice_handler(client_id, switch_function, answer_formatter, event_name, query)\n    return function(err, result, _)\n        if err then\n            vim.notify('sqls: ' .. err.message, vim.log.levels.ERROR)\n            return\n        end\n        if not result then\n            return\n        end\n        if result == '' then\n            vim.notify('sqls: No choices available')\n            return\n        end\n        local choices = vim.split(result, '\\n')\n        local function switch_callback(answer)\n            if not answer then return end\n            switch_function(client_id, answer_formatter(answer))\n            nvim_exec_autocmds('User', {\n                pattern = event_name,\n                data = { choice = answer },\n            })\n        end\n        if query then\n            local answer = choices[tonumber(query)]\n            switch_callback(answer)\n            return\n        end\n        vim.ui.select(choices, { prompt = 'sqls.nvim' }, switch_callback)\n    end\nend\n\n---@type lsp.Handler\nlocal function switch_handler(err, _, _)\n    if err then\n        vim.notify('sqls: ' .. err.message, vim.log.levels.ERROR)\n    end\nend\n\n---@param command string\n---@return sqls_switch_function\nlocal function make_switch_function(command)\n    return function(client_id, query)\n        local client = assert(vim.lsp.get_client_by_id(client_id))\n        client:request(\n            'workspace/executeCommand',\n            {\n                command = command,\n                arguments = { query },\n            },\n            switch_handler\n        )\n    end\nend\n\n---@param command string\n---@param answer_formatter sqls_answer_formatter\n---@param event_name sqls_event_name\n---@return sqls_prompt_function\nlocal function make_prompt_function(command, answer_formatter, event_name)\n    return function(client_id, switch_function, query)\n        local client = assert(vim.lsp.get_client_by_id(client_id))\n        client:request(\n            'workspace/executeCommand',\n            {\n                command = command,\n            },\n            make_choice_handler(client_id, switch_function, answer_formatter, event_name, query)\n        )\n    end\nend\n\n---@type sqls_answer_formatter\nlocal function format_database_answer(answer) return answer end\n---@type sqls_answer_formatter\nlocal function format_connection_answer(answer) return vim.split(answer, ' ')[1] end\n\nlocal database_switch_function = make_switch_function('switchDatabase')\nlocal connection_switch_function = make_switch_function('switchConnections')\nlocal database_prompt_function = make_prompt_function(\n    'showDatabases',\n    format_database_answer,\n    'SqlsDatabaseChoice'\n)\nlocal connection_prompt_function = make_prompt_function(\n    'showConnections',\n    format_connection_answer,\n    'SqlsConnectionChoice'\n)\n\n---@param prompt_function sqls_prompt_function\n---@param switch_function sqls_switch_function\n---@return sqls_switcher\nlocal function make_switcher(prompt_function, switch_function)\n    return function(client_id, query)\n        prompt_function(client_id, switch_function, query)\n    end\nend\n\nM.switch_database = make_switcher(database_prompt_function, database_switch_function)\nM.switch_connection = make_switcher(connection_prompt_function, connection_switch_function)\n\nreturn M\n"
  }
]