├── .gitignore ├── LICENSE ├── README.md ├── dev └── init.lua ├── doc └── presentation.md └── lua ├── nvim-docker.lua └── nvim-docker ├── container-keymaps.lua ├── container-layout.lua ├── container-logs.lua ├── containers.lua ├── docker.lua ├── popup-keymaps.lua └── tree-utils.lua /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 s1n7ax 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nvim-docker 2 | 3 | Docker management right inside Neovim! 4 | 5 | ## Features 6 | 7 | 8 | ![Screenshare - 2022-07-20 5_59_58 PM (1)](https://user-images.githubusercontent.com/38011308/180091781-a23adf85-a159-4caa-b2a9-4abe021a1ff4.gif) 9 | 10 | 11 | - Container management 12 | - View containers on your local machine with live reloading 13 | - Bring containers up/down 14 | - Remove and rebuild containers 15 | - Send commands to containers (SoonTM) 16 | - View container logs 17 | - Configurable (SoonTM) 18 | - Docker compose integration (SoonTM) 19 | - Image management 20 | - View images on you local machine with live reloading (SoonTM) 21 | - Create new containers from images (SoonTM) 22 | 23 | ## Installation 24 | 25 | ### Packer 26 | 27 | ```lua 28 | use { 29 | 'dgrbrady/nvim-docker', 30 | requires = {'nvim-lua/plenary.nvim', 'MunifTanjim/nui.nvim'}, 31 | rocks = '4O4/reactivex' -- ReactiveX Lua implementation 32 | } 33 | ``` 34 | 35 | ## How to use 36 | 37 | In your lua config: 38 | 39 | ```lua 40 | local nvim_docker = require('nvim-docker') 41 | -- list containers 42 | nvim_docker.containers.list_containers() 43 | 44 | -- OR 45 | -- setting up keybindings since the `list_containers` fn does not have a default binding 46 | vim.keymap.set('n', 'C', nvim_docker.containers.list_containers) 47 | ``` 48 | 49 | ### Default Keymaps 50 | 51 | | Binding | Where | Effect | 52 | | ------- | ----- | ------ | 53 | | `j` | Top popup | Move cursor down on container list | 54 | | `k` | Top popup | Move cursor up on container list | 55 | | `l` | Top popup | Expand container details | 56 | | `L` | Top popup | Expand all containers | 57 | | `h` | Top popup | Collapse container details | 58 | | `H` | Top popup | Collapse all containers | 59 | | `r` | Top popup | Restart container | 60 | | `dd` | Top popup | Deletes container (asks for confirmation) | 61 | | `t` | Top popup | View container logs | 62 | | `` | Top popup | Focus bottom popup | 63 | | `t` | Bottom popup | Toggles whether the cursor should be stuck to the bottom of the popup. Helpful if you want to watch the latest logs as they come in | 64 | | `` | Bottom popup | Focus top popup | 65 | 66 | 67 | ## Roadmap 68 | 69 | - [ ] Interact with containers 70 | - [x] Start container [12c8e6](https://github.com/dgrbrady/nvim-docker/commit/12c8e625a7f3864f89e11f0d24297a5ce1f09542) 71 | - [x] Stop container [12c8e6](https://github.com/dgrbrady/nvim-docker/commit/12c8e625a7f3864f89e11f0d24297a5ce1f09542) 72 | - [x] Restart container [39a1be](https://github.com/dgrbrady/nvim-docker/commit/39a1be419e7b6817bd9dd5474c1a2dd80790934b) 73 | - [x] Delete container [98b561](https://github.com/dgrbrady/nvim-docker/commit/98b5611fd81aca130f13d2bd319fa49a7a2f8ee5) 74 | - [x] View logs from container [c71005](https://github.com/dgrbrady/nvim-docker/commit/c71005aba5cc70fea33338cdcb50620e4fe2de8f) 75 | - [ ] Run command in container 76 | - [ ] Interact with images 77 | - [ ] List image 78 | - [ ] Delete image 79 | - [ ] Create container from image 80 | - [ ] Docker compose integration 81 | - [ ] Provide options for user configuration 82 | - [ ] Documentation 83 | - [ ] Telescope integration? 84 | 85 | ## Development 86 | 87 | * clone the project `git clone https://github.com/dgrbrady/nvim-docker` 88 | * go to the project folder `cd nvim-docker` 89 | * start editing `nvim --cmd "set rtp+=."` 90 | * reference the dev configurations `:luafile dev/init.lua` 91 | * run the `containers.list_containers()` function using `,w` keybind 92 | -------------------------------------------------------------------------------- /dev/init.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | -- plugin name will be used to reload the loaded modules 3 | --]] 4 | local package_name = 'nvim-docker' 5 | 6 | -- add the escape character to special characters 7 | local escape_pattern = function (text) 8 | return text:gsub("([^%w])", "%%%1") 9 | end 10 | 11 | -- unload loaded modules by the matching text 12 | local unload_packages = function () 13 | local esc_package_name = escape_pattern(package_name) 14 | 15 | for module_name, _ in pairs(package.loaded) do 16 | if string.find(module_name, esc_package_name) then 17 | package.loaded[module_name] = nil 18 | end 19 | end 20 | end 21 | 22 | -- executes the run method in the package 23 | local run_action = function () 24 | require(package_name).containers.list_containers() 25 | end 26 | 27 | -- unload and run the function from the package 28 | function Reload_and_run() 29 | unload_packages() 30 | run_action() 31 | end 32 | 33 | local set_keymap = vim.api.nvim_set_keymap 34 | 35 | set_keymap('n', ',r', 'luafile dev/init.lua', {}) 36 | set_keymap('n', ',w', 'lua Reload_and_run()', {}) 37 | -------------------------------------------------------------------------------- /doc/presentation.md: -------------------------------------------------------------------------------- 1 | # Neovim-Lua Plugin Development Beginner's Guide 2 | 3 | ## Before we start 4 | 5 | * Neovim Lua boilerplate: https://github.com/s1n7ax/neovim-lua-plugin-boilerplate 6 | * Neovim Lua video tutorials: https://youtube.com/playlist?list=PL0EgBggsoPCk1WCos2txThsxhg0fT5nqD 7 | * My Neovim configuration: https://github.com/s1n7ax/dotnvim 8 | * Terminal manager: https://github.com/s1n7ax/nvim-terminal 9 | 10 | ## Prerequisites 11 | 12 | * Neovim 0.5 or higher version 13 | * Git 14 | * GitHub Account 15 | 16 | ## Setting up the project 17 | 18 | * Create GitHub project 19 | * Clone the project 20 | 21 | ```bash 22 | git clone 23 | ``` 24 | 25 | * Open Neovim for editing 26 | 27 | ``` 28 | # NOTE: make sure to add current directory to runtime path 29 | # otherwise, Neovim does not know how to find your plugin 30 | nvim --cmd "set rtp+=." 31 | ``` 32 | 33 | * Create Lua module in Lua source directory 34 | 35 | ```bash 36 | mkdir -p lua/greetings 37 | ``` 38 | 39 | * Create init file and sub modules for the module 40 | 41 | ```bash 42 | touch lua/greetings/init.lua 43 | touch lua/greetings/awesome-module.lua 44 | ``` 45 | 46 | ## Create greetings plugin 47 | 48 | * Add greet function to `awesome-module.lua` 49 | 50 | ```lua 51 | local function greet() 52 | print('testing') 53 | end 54 | 55 | return greet 56 | ``` 57 | 58 | * Expose public APIs of the plugin 59 | 60 | ```lua 61 | local greet = require('greetings.awesome-module') 62 | 63 | return { 64 | greet = greet 65 | } 66 | ``` 67 | 68 | * Testing plugin APIs 69 | 70 | ```vim 71 | # you should run this in the vim command line 72 | :lua require('greetings').greet() 73 | ``` 74 | 75 | ## Few tips when developing plugins 76 | 77 | ### See changes without re opening Neovim 78 | 79 | Lua will not reload module if it already exists. The changes you made will be 80 | applied the next time you open the editor. But you can force Lua to reload the 81 | module for development. 82 | 83 | * Create `dev/init.lua` file 84 | 85 | ```bash 86 | mkdir -p dev 87 | touch dev/init.lua 88 | ``` 89 | 90 | * Add following to `dev/init.lua` to force reloading 91 | 92 | ``` 93 | -- force lua to import the modules again 94 | package.loaded['dev'] = nil 95 | package.loaded['greetings'] = nil 96 | package.loaded['greetings.awesome-module'] = nil 97 | 98 | -- [ , + r ] keymap to reload the lua file 99 | -- NOTE: someone need to source this file to apply these configurations. So, the 100 | -- very first time you open the project, you have to source this file using 101 | -- ":luafile dev/init.lua". From that point onward, you can hit the keybind to 102 | -- reload 103 | vim.api.nvim_set_keymap('n', ',r', 'luafile dev/init.lua', {}) 104 | ``` 105 | 106 | * While we are in the file, lets add shortcut to run the greeting plugin 107 | 108 | ```lua 109 | -- keybind to test the plugin 110 | Greetings = require('greetings') 111 | vim.api.nvim_set_keymap('n', ',w', 'lua Greetings.greet()', {}) 112 | ``` 113 | 114 | 115 | ## Learn more about Neovim-Lua 116 | 117 | * I release a video on YouTube once a year XD. [This is the link to playlist.](https://youtube.com/playlist?list=PL0EgBggsoPCk1WCos2txThsxhg0fT5nqD) You can subscribe to the channel too ;) 118 | * [nvim-lua-guide](https://github.com/nanotee/nvim-lua-guide) is awesome place 119 | to start learning Neovim-Lua APIs 120 | * Run `:h api` in Neovim to get all available Neovim-Lua APIs 121 | * Run `:h function` to learn about vim functions. You can run vim functions 122 | using `vim.fn.()` 123 | * Use [Neovim subreddit](https://www.reddit.com/r/neovim) to stay updated and ask questions 124 | -------------------------------------------------------------------------------- /lua/nvim-docker.lua: -------------------------------------------------------------------------------- 1 | local containers = require('nvim-docker.containers') 2 | 3 | return { 4 | containers = containers, 5 | } 6 | -------------------------------------------------------------------------------- /lua/nvim-docker/container-keymaps.lua: -------------------------------------------------------------------------------- 1 | local event = require('nui.utils.autocmd').event 2 | local docker = require('nvim-docker.docker') 3 | local container_logs = require('nvim-docker.container-logs') 4 | 5 | local _M = {} 6 | 7 | -- starts the container associated with the current tree node 8 | local function start_container(params) 9 | local node, _ = params.tree:get_node() 10 | if node ~= nil and node.container ~= nil then 11 | vim.notify('Starting container: ' .. node.container.name) 12 | docker({'container', 'start', node.container.id}):start() 13 | end 14 | end 15 | 16 | -- stops the container associated with the current tree node 17 | local function stop_container(params) 18 | local node, _ = params.tree:get_node() 19 | if node ~= nil and node.container ~= nil then 20 | vim.notify('Stopping container: ' .. node.container.name) 21 | docker({'container', 'stop', node.container.id}):start() 22 | end 23 | end 24 | 25 | -- restarts the container associated with the current tree node 26 | local function restart_container(params) 27 | local node, _ = params.tree:get_node() 28 | if node ~= nil and node.container ~= nil then 29 | vim.notify('Restarting container: ' .. node.container.name) 30 | docker({'container', 'restart', node.container.id}):start() 31 | end 32 | end 33 | 34 | -- deletes the container associated with the current tree node 35 | local function delete_container(params) 36 | local node, _ = params.tree:get_node() 37 | if node ~= nil and node.container ~= nil then 38 | vim.ui.select({'Yes', 'No'},{ 39 | prompt = 'Delete ' .. node.container.name .. '[y/n]?', 40 | }, function (choice) 41 | if choice == 'Yes' then 42 | vim.notify('Deleting container: ' .. node.container.name) 43 | docker({'container', 'rm', node.container.id}):start() 44 | end 45 | end) 46 | end 47 | end 48 | 49 | -- view logs of the container associated with the current tree node 50 | local function view_logs(params) 51 | local node, _ = params.tree:get_node() 52 | if node ~= nil and node.container ~= nil then 53 | local layout = params.layout 54 | local log_popup = layout.log_popup 55 | local log_bufnr = log_popup.bufnr 56 | log_popup.border:set_text('top', node.container.name .. ' Logs', 'center') 57 | local line_count = vim.api.nvim_buf_line_count(log_bufnr) 58 | vim.api.nvim_buf_set_lines(log_bufnr, 0, line_count, false, {}) 59 | local cursor_follow_logs = false 60 | 61 | 62 | local function toggle_cursor_follow_logs() 63 | cursor_follow_logs = not cursor_follow_logs 64 | if cursor_follow_logs == true then 65 | log_popup.border:set_text('bottom', '[stuck to bottom]', 'center') 66 | else 67 | log_popup.border:set_text('bottom', '') 68 | end 69 | end 70 | 71 | local function focus_logs() 72 | layout.main_popup:off(event.BufLeave) 73 | vim.api.nvim_set_current_win(layout.log_popup.winid) 74 | layout:_setup_main_popup_on_bufleave() 75 | vim.keymap.set('n', 't', toggle_cursor_follow_logs, {buffer = log_bufnr}) 76 | end 77 | 78 | local function focus_main() 79 | cursor_follow_logs = false 80 | vim.api.nvim_set_current_win(layout.main_popup.winid) 81 | layout:_setup_main_popup_on_bufleave() 82 | vim.keymap.del('n', 't', {buffer=log_bufnr}) 83 | end 84 | 85 | 86 | vim.keymap.set('n', '', focus_logs, {buffer = layout.main_popup.bufnr}) 87 | 88 | vim.keymap.set('n', '', focus_main, {buffer = log_bufnr}) 89 | 90 | focus_logs() 91 | 92 | container_logs.follow_logs(node.container.name, function (logs) 93 | if log_popup.bufnr ~= nil then 94 | for index, log in ipairs(logs) do 95 | vim.api.nvim_buf_set_lines(log_popup.bufnr, index, index + 1, false, {log}) 96 | end 97 | vim.schedule(function () 98 | if cursor_follow_logs == true then 99 | vim.api.nvim_win_set_cursor(log_popup.winid, { 100 | vim.api.nvim_buf_line_count(log_bufnr), 101 | 0 102 | }) 103 | end 104 | end) 105 | end 106 | end) 107 | end 108 | end 109 | 110 | _M.extra_keymaps = { 111 | {'n', 'u', start_container, 'Start the highlighted container'}, 112 | {'n', 'd', stop_container, 'Stop the highlighted container'}, 113 | {'n', 'r', restart_container, 'Restart the highlighted container'}, 114 | {'n', 'dd', delete_container, 'Delete the highlighted container'}, 115 | {'n', 't', view_logs, 'View container logs'}, 116 | } 117 | 118 | return _M 119 | -------------------------------------------------------------------------------- /lua/nvim-docker/container-layout.lua: -------------------------------------------------------------------------------- 1 | local Layout = require('nui.layout') 2 | local Tree = require('nui.tree') 3 | local Popup = require('nui.popup') 4 | local event = require('nui.utils.autocmd').event 5 | local rx = require('reactivex') 6 | 7 | local popup_keymaps = require('nvim-docker.popup-keymaps') 8 | local tree_utils = require('nvim-docker.tree-utils') 9 | local ContainerLayout = Layout:extend('ContainerLayout') 10 | local docker = require('nvim-docker.docker') 11 | 12 | ---Component used to for managing containers 13 | ---@param layout_options table https://github.com/MunifTanjim/nui.nvim/tree/main/lua/nui/layout#options 14 | function ContainerLayout:init(layout_options, keymaps) 15 | self.mounted = false 16 | self.keymaps = keymaps 17 | self._containers = rx.Subject.create() 18 | local options = vim.tbl_deep_extend('force', layout_options or {}, { 19 | position = '50%', 20 | relative = 'editor', 21 | size = { 22 | height = '70%', 23 | width = '50%' 24 | } 25 | }) 26 | 27 | self:_create_main_popup() 28 | self:_create_log_popup() 29 | ContainerLayout.super.init(self, options, Layout.Box({ 30 | Layout.Box(self.main_popup, {size='50%'}), 31 | Layout.Box(self.log_popup, {size='50%'}), 32 | }, {dir = 'col'})) 33 | end 34 | 35 | function ContainerLayout:_setup_main_popup_on_bufleave() 36 | self.main_popup:on(event.BufLeave, function() 37 | local container_logs = require('nvim-docker.container-logs') 38 | container_logs.teardown() 39 | self.main_popup:unmount() 40 | local function close_timer() 41 | self.main_popup_timer:close() 42 | end 43 | pcall(close_timer) 44 | self:unmount() 45 | end) 46 | end 47 | 48 | function ContainerLayout:_create_main_popup() 49 | self.main_popup = Popup({ 50 | enter = true, 51 | focusable = true, 52 | border = { 53 | style = 'rounded', 54 | text = { 55 | top = 'Docker Containers', 56 | top_align = 'center', 57 | bottom = ': Expand, : Expand All, : Collapse, : Collapse All' 58 | }, 59 | }, 60 | }) 61 | end 62 | 63 | function ContainerLayout:_create_log_popup() 64 | self.log_popup = Popup({ 65 | border = { 66 | text = { 67 | top = 'Logs', 68 | top_align = 'center', 69 | }, 70 | style = 'double' 71 | } 72 | }) 73 | end 74 | 75 | function ContainerLayout:get_containers() 76 | docker({ 77 | 'container', 78 | 'ls', 79 | '-a', 80 | '--format={"id": {{json .ID}}, "name": {{json .Names}}, "image": {{json .Image}}, "command": {{json .Command}}, "status": {{json .Status}}, "networks": {{json .Networks}}, "ports": {{json .Ports}}}' 81 | }, { 82 | on_exit = function (j) 83 | local result = j:result() 84 | if result ~= nil then 85 | local containers = {} 86 | for _, value in ipairs(result) do 87 | if value ~= nil then 88 | local container = vim.json.decode(value) 89 | table.insert(containers, container) 90 | end 91 | end 92 | self._containers(containers) 93 | end 94 | end 95 | }):start() 96 | return self._containers 97 | end 98 | 99 | function ContainerLayout:_render_containers(containers) 100 | local old_nodes = self.tree:get_nodes() 101 | tree_utils.remove_nodes(self.tree) 102 | for _, container in ipairs(containers) do 103 | local text = '' 104 | if string.find(container.status, 'Up') then 105 | text = container.name .. ' 🟢' 106 | else 107 | text = container.name .. ' 🔴' 108 | end 109 | 110 | local node = Tree.Node({ text = text, container = container }, { 111 | Tree.Node({ text = '[ID] ' .. container.id }), 112 | Tree.Node({ text = '[Image] ' .. container.image }), 113 | Tree.Node({ text = '[Command] ' .. container.command }), 114 | Tree.Node({ text = '[Status] ' .. container.status }), 115 | Tree.Node({ text = '[Networks] ' .. container.networks }), 116 | Tree.Node({ text = '[Ports] ' .. container.ports }), 117 | }) 118 | self.tree:add_node(node) 119 | if next(old_nodes) ~= nil then 120 | for _, old_node in ipairs(old_nodes) do 121 | -- if the node was expanded before it was cleared, expand it again 122 | if old_node.container.id == node.container.id then 123 | if old_node:is_expanded() then 124 | node:expand() 125 | end 126 | end 127 | end 128 | end 129 | end 130 | vim.schedule(function () 131 | self.tree:render() 132 | end) 133 | end 134 | 135 | function ContainerLayout:show_containers() 136 | if self.mounted == false then 137 | self:mount() 138 | self.tree = Tree({ winid = self.main_popup.winid}) 139 | popup_keymaps.create_keymaps(self.main_popup, self.keymaps, self.tree, self) 140 | self:_setup_main_popup_on_bufleave() 141 | self.mounted = true 142 | end 143 | self.main_popup_timer = vim.loop.new_timer() 144 | self.main_popup_timer:start(0, 5000, vim.schedule_wrap(function () 145 | self:get_containers():subscribe(vim.schedule_wrap(function (containers) 146 | self:_render_containers(containers) 147 | end)) 148 | end)) 149 | end 150 | 151 | return ContainerLayout 152 | -------------------------------------------------------------------------------- /lua/nvim-docker/container-logs.lua: -------------------------------------------------------------------------------- 1 | local rx = require('reactivex') 2 | local docker = require('nvim-docker.docker') 3 | 4 | local _M = { 5 | _timer = nil, 6 | _logs_subscription = nil 7 | } 8 | 9 | local function reset_timer() 10 | if _M._timer ~= nil then 11 | _M._timer:close() 12 | _M._timer = nil 13 | end 14 | end 15 | 16 | local function reset_logs_subscription() 17 | if _M._logs_subscription ~= nil then 18 | _M._logs_subscription:unsubscribe() 19 | _M._logs_subscription = nil 20 | end 21 | end 22 | 23 | function _M.teardown() 24 | reset_timer() 25 | reset_logs_subscription() 26 | end 27 | 28 | function _M.follow_logs(container_name, cb) 29 | _M.teardown() 30 | local logs_stream = rx.Subject.create() 31 | _M._logs_subscription = logs_stream:subscribe(function (logs) 32 | cb(logs) 33 | end) 34 | _M._timer = vim.loop.new_timer() 35 | _M._timer:start(0, 5000, vim.schedule_wrap(function () 36 | -- FORMAT: yyyy-mm-ddThh:mm:ssZ 37 | local start_from = os.date('%Y-%m-%dT%H:%M:%SZ') 38 | print('container_name: ' .. container_name .. 'time: ' .. start_from) 39 | docker( 40 | {'logs', '--since', start_from, container_name }, 41 | { 42 | on_exit = vim.schedule_wrap(function (job) 43 | logs_stream(job:result()) 44 | end) 45 | }):start() 46 | end)) 47 | end 48 | 49 | return _M 50 | -------------------------------------------------------------------------------- /lua/nvim-docker/containers.lua: -------------------------------------------------------------------------------- 1 | local extra_keymaps = require('nvim-docker.container-keymaps').extra_keymaps 2 | local ContainerLayout = require('nvim-docker.container-layout') 3 | local _M = {} 4 | 5 | function _M.list_containers() 6 | local layout = ContainerLayout({}, extra_keymaps) 7 | layout:show_containers() 8 | end 9 | 10 | return _M 11 | -------------------------------------------------------------------------------- /lua/nvim-docker/docker.lua: -------------------------------------------------------------------------------- 1 | local Job = require('plenary.job') 2 | 3 | local function docker(args, options) 4 | local job_options = vim.tbl_deep_extend('force', options or {}, { 5 | command = 'docker', 6 | args = args 7 | }) 8 | return Job:new(job_options) 9 | end 10 | 11 | return docker 12 | -------------------------------------------------------------------------------- /lua/nvim-docker/popup-keymaps.lua: -------------------------------------------------------------------------------- 1 | local _M = {} 2 | 3 | -- local help_popup = Popup({ 4 | -- enter = true, 5 | -- focusable = true, 6 | -- border = { 7 | -- style = 'rounded', 8 | -- text = { 9 | -- top = 'Help', 10 | -- top_align = 'center', 11 | -- }, 12 | -- }, 13 | -- position = '50%', 14 | -- size = { 15 | -- width = '40%', 16 | -- height = '60%', 17 | -- }, 18 | -- }) 19 | 20 | function _M.create_keymaps(popup, extra_keymaps, tree, layout) 21 | local tree_utils = require('nvim-docker.tree-utils') 22 | local map_options = { remap = false, nowait = true } 23 | 24 | -- help_popup:map('n', '?', function () 25 | -- help_popup:unmount() 26 | -- popup:update_layout({ 27 | -- position = '50%', 28 | -- size = { 29 | -- width = '80%', 30 | -- height = '60%', 31 | -- }, 32 | -- }) 33 | -- popup:on(event.BufLeave, function() 34 | -- state.popup = nil 35 | -- state.tree = nil 36 | -- local function unmount() 37 | -- state.popup_timer:close() 38 | -- popup:unmount() 39 | -- end 40 | -- pcall(unmount) 41 | -- end) 42 | -- end) 43 | 44 | -- popup:map('n', '?', function () 45 | -- -- if help_opened == nil then 46 | -- popup:off(event.BufLeave) 47 | -- popup:update_layout({ 48 | -- size = { 49 | -- width = '30%', 50 | -- height = '60%' 51 | -- }, 52 | -- position = { 53 | -- row = '50%', 54 | -- col = '0%' 55 | -- } 56 | -- }) 57 | -- help_popup:mount() 58 | -- if config.extra_keymaps ~= nil then 59 | -- for index, keymap in ipairs(config.extra_keymaps) do 60 | -- vim.api.nvim_buf_set_lines(help_popup.bufnr, index, index + 1, false, {'[Key]: ' .. keymap[2] .. ' ' .. keymap[4]}) 61 | -- end 62 | -- end 63 | -- end) 64 | 65 | 66 | -- collapse 67 | popup:map('n', 'h', function() 68 | local node, linenr = tree:get_node() 69 | if not node:has_children() then 70 | node, linenr = tree:get_node(node:get_parent_id()) 71 | end 72 | if node and node:collapse() then 73 | vim.api.nvim_win_set_cursor(popup.winid, { linenr, 0 }) 74 | tree:render() 75 | end 76 | end, map_options) 77 | 78 | -- collapse all 79 | popup:map('n', 'H', function () 80 | tree_utils.collapse_all_nodes(tree) 81 | end, map_options) 82 | 83 | -- expand 84 | popup:map("n", "l", function() 85 | local node, linenr = tree:get_node() 86 | if not node:has_children() then 87 | node, linenr = tree:get_node(node:get_parent_id()) 88 | end 89 | if node and node:expand() then 90 | if not node.checked then 91 | node.checked = true 92 | end 93 | 94 | vim.api.nvim_win_set_cursor(popup.winid, { linenr, 0 }) 95 | tree:render() 96 | end 97 | end, map_options) 98 | 99 | -- expand all 100 | popup:map('n', 'L', function () 101 | tree_utils.expand_all_nodes(tree) 102 | end, map_options) 103 | 104 | if extra_keymaps ~= nil then 105 | for _, keymap in ipairs(extra_keymaps) do 106 | popup:map( 107 | keymap[1], 108 | keymap[2], 109 | function () keymap[3]({popup = popup, tree = tree, layout = layout}) end, 110 | map_options 111 | ) 112 | end 113 | end 114 | end 115 | 116 | return _M 117 | -------------------------------------------------------------------------------- /lua/nvim-docker/tree-utils.lua: -------------------------------------------------------------------------------- 1 | local _M = {} 2 | 3 | function _M.get_expanded_nodes(tree) 4 | local nodes = {} 5 | 6 | local function process(node) 7 | if node:is_expanded() then 8 | table.insert(nodes, node) 9 | 10 | if node:has_children() then 11 | for _, child in ipairs(_M.tree:get_nodes(node:get_id())) do 12 | process(child) 13 | end 14 | end 15 | end 16 | end 17 | 18 | for _, node in ipairs(tree:get_nodes()) do 19 | process(node) 20 | end 21 | 22 | return nodes 23 | end 24 | 25 | function _M.collapse_all_nodes(tree) 26 | local nodes = tree:get_nodes() 27 | for _, node in ipairs(nodes) do 28 | local id = node:get_id() 29 | node:collapse(id) 30 | end 31 | tree:render() 32 | 33 | -- If you want to expand the root 34 | -- local root = tree:get_nodes()[1] 35 | -- root:expand() 36 | end 37 | 38 | function _M.expand_all_nodes(tree) 39 | local nodes = tree:get_nodes() 40 | for _, node in ipairs(nodes) do 41 | local id = node:get_id() 42 | node:expand(id) 43 | end 44 | tree:render() 45 | -- If you want to expand the root 46 | -- local root = tree:get_nodes()[1] 47 | -- root:expand() 48 | end 49 | 50 | function _M.remove_nodes(tree) 51 | local nodes = tree:get_nodes() 52 | if next(nodes) ~= nil then 53 | for _, node in ipairs(nodes) do 54 | if type(node.children) == 'table' and next(node.children) then 55 | for _, child in ipairs(node.children) do 56 | tree:remove_node(child:get_id()) 57 | end 58 | end 59 | tree:remove_node(node:get_id()) 60 | end 61 | end 62 | end 63 | 64 | return _M 65 | --------------------------------------------------------------------------------