From a4fb5e6b0db62400fceb97deec86515d037601a3 Mon Sep 17 00:00:00 2001 From: Abao Zhang Date: Sun, 14 Apr 2024 23:46:49 +0800 Subject: [PATCH] fix: request pool block --- .neoconf.json | 7 +++ .stylua.toml | 6 +++ README.md | 1 - lua-tests/neopyter/utils_spec.lua | 2 +- lua/neopyter/health.lua | 10 ++-- lua/neopyter/jupyter/jupyterlab.lua | 7 +-- lua/neopyter/jupyter/notebook.lua | 2 +- lua/neopyter/logger.lua | 2 +- lua/neopyter/rpc/asyncclient.lua | 11 +--- lua/neopyter/rpc/wsserverclient.lua | 80 ++++++++++++++++++----------- lua/neopyter/utils.lua | 2 +- package.json | 2 +- scripts/minidoc.lua | 6 +-- src/index.ts | 34 ++++++++---- src/statusidepanel.ts | 62 ++++++++++++++-------- src/transport/websocketTransport.ts | 13 +++-- 16 files changed, 152 insertions(+), 95 deletions(-) create mode 100644 .neoconf.json create mode 100644 .stylua.toml diff --git a/.neoconf.json b/.neoconf.json new file mode 100644 index 0000000..041be9a --- /dev/null +++ b/.neoconf.json @@ -0,0 +1,7 @@ +{ + "neodev": { + "library": { + "plugins": ["plenary.nvim", "websocket.nvim"] + } + } +} diff --git a/.stylua.toml b/.stylua.toml new file mode 100644 index 0000000..38ed0aa --- /dev/null +++ b/.stylua.toml @@ -0,0 +1,6 @@ +column_width = 150 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 4 +quote_style = "AutoPreferDouble" +no_call_parentheses = false diff --git a/README.md b/README.md index 6fdff94..4364105 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,6 @@ With 💤lazy.nvim: -- auto connect server auto_connect = true, rpc_client = "async", - -- your jupyter host + neopyter port remote_address = "127.0.0.1:9001", file_pattern = { "*.ju.*" }, on_attach = function(bufnr) diff --git a/lua-tests/neopyter/utils_spec.lua b/lua-tests/neopyter/utils_spec.lua index b80c5bb..0df9fa5 100644 --- a/lua-tests/neopyter/utils_spec.lua +++ b/lua-tests/neopyter/utils_spec.lua @@ -209,7 +209,7 @@ describe("parse notebook", function() source = "# this is markdown cell with markdown too", start_line = 1, end_line = 5, - title= "cell's title", + title = "cell's title", cell_type = "markdown", }, }, cells) diff --git a/lua/neopyter/health.lua b/lua/neopyter/health.lua index 5754236..a46e8ee 100644 --- a/lua/neopyter/health.lua +++ b/lua/neopyter/health.lua @@ -20,7 +20,8 @@ end local function run_blocking(suspend_fn, ...) local resolved = false vim.schedule(function() - a.run(suspend_fn, function() + a.run(suspend_fn, function(ee) + print(ee) resolved = true end) end) @@ -34,10 +35,13 @@ function M.check() run_blocking(function() local status = jupyter.jupyterlab:is_attached() if status then - health.info("Neopyter status: active") + local nvim_plugin_ver = jupyter.jupyterlab:get_nvim_plugin_version() + health.info(string.format("Neopyter@%s status: active", nvim_plugin_ver)) local is_connecting = jupyter.jupyterlab.client:is_connecting() if is_connecting then health.info("Rpc server status: active") + local jupyterlab_extension_ver = jupyter.jupyterlab:get_jupyterlab_extension_version() + health.info(string.format("Jupyter lab extension version: %s", jupyterlab_extension_ver)) else health.info("Rpc server status: inactive") end @@ -48,9 +52,7 @@ function M.check() select_mark = "*" end local msg = "" - print(notebook.local_path) local nbconnect = notebook:is_connecting() - print(notebook.remote_path) if nbconnect then msg = string.format("%s %s 💫 %s", select_mark, notebook.local_path, notebook.remote_path) else diff --git a/lua/neopyter/jupyter/jupyterlab.lua b/lua/neopyter/jupyter/jupyterlab.lua index f604875..78235d5 100644 --- a/lua/neopyter/jupyter/jupyterlab.lua +++ b/lua/neopyter/jupyter/jupyterlab.lua @@ -95,11 +95,7 @@ function JupyterLab:connect(address) local nvim_version = self:get_nvim_plugin_version() if jupyterlab_version ~= nil and nvim_version ~= jupyterlab_version then utils.notify_error( - string.format( - "The version of jupyterlab extension(%s) and neovim plugin(%s) do not match", - jupyterlab_version, - nvim_version - ) + string.format("The version of jupyterlab extension(%s) and neovim plugin(%s) do not match", jupyterlab_version, nvim_version) ) end end @@ -211,7 +207,6 @@ JupyterLab = async_wrap(JupyterLab, { "attach", "is_attached", "is_connecting", - "get_nvim_plugin_version", }) return JupyterLab diff --git a/lua/neopyter/jupyter/notebook.lua b/lua/neopyter/jupyter/notebook.lua index 5f67ddc..aef3b15 100644 --- a/lua/neopyter/jupyter/notebook.lua +++ b/lua/neopyter/jupyter/notebook.lua @@ -99,7 +99,7 @@ function Notebook:attach() return end self:partial_sync(start_row, old_end_row, new_end_row) - end, function() end) + end) end, }) diff --git a/lua/neopyter/logger.lua b/lua/neopyter/logger.lua index 5b7524e..03efcb6 100644 --- a/lua/neopyter/logger.lua +++ b/lua/neopyter/logger.lua @@ -5,6 +5,6 @@ return { end, warn = function(msg) -- TODO:log to file - print(msg) + -- print(msg) end, } diff --git a/lua/neopyter/rpc/asyncclient.lua b/lua/neopyter/rpc/asyncclient.lua index 75b86c3..ad9352d 100644 --- a/lua/neopyter/rpc/asyncclient.lua +++ b/lua/neopyter/rpc/asyncclient.lua @@ -95,11 +95,7 @@ function AsyncRpcClient:request(method, ...) else if method == "getVersion" then utils.notify_error( - string.format( - "jupyterlab extension is outdated, it is recommended to update with `pip install -U neopyter`", - method, - res - ) + string.format("jupyterlab extension is outdated, it is recommended to update with `pip install -U neopyter`", method, res) ) else utils.notify_error(string.format("RPC request [%s] failed, with error: %s", method, res)) @@ -123,10 +119,7 @@ function AsyncRpcClient:handle_response(data) local callback = self.request_pool[msgid] self.request_pool[msgid] = nil logger.log(string.format("msgid [%s] response acceptd", msgid)) - assert( - callback, - string.format("msg %s can't find callback: request_pool=%s", msgid, vim.inspect(self.request_pool)) - ) + assert(callback, string.format("msg %s can't find callback: request_pool=%s", msgid, vim.inspect(self.request_pool))) if error == vim.NIL then callback(true, result) else diff --git a/lua/neopyter/rpc/wsserverclient.lua b/lua/neopyter/rpc/wsserverclient.lua index c4e49a2..83f5734 100644 --- a/lua/neopyter/rpc/wsserverclient.lua +++ b/lua/neopyter/rpc/wsserverclient.lua @@ -10,7 +10,6 @@ local a = require("plenary.async") ---@field single_connection? websocket.Connection ---@field private msg_count number ---@field private request_pool table ----@field private decoder neopyter.MsgpackDecoder local WSServerClient = RpcClient:new({}) --[[@as neopyter.WSServerClient]] ---RpcClient constructor @@ -20,7 +19,6 @@ function WSServerClient:new(opt) local o = setmetatable(opt or {}, { __index = self }) --[[@as neopyter.WSServerClient]] o.msg_count = 0 o.request_pool = {} - o.decoder = msgpack.Decoder:new() return o end @@ -29,6 +27,10 @@ end ---@async function WSServerClient:connect(address) local restart_server = address ~= nil and self.address ~= address + for _, fun in pairs(self.request_pool) do + fun(false, "cancel") + end + self.request_pool = {} self.address = address or self.address assert(self.address, "Rpc client address is empty") @@ -56,6 +58,9 @@ function WSServerClient:connect(address) end, }) end, + on_disconnect = function() + self:disconnect() + end, }) end @@ -65,6 +70,11 @@ function WSServerClient:disconnect() self.single_connection:close() self.single_connection = nil self.server:close() + + for _, fun in pairs(self.request_pool) do + fun(false, "cancel") + end + self.request_pool = {} else logger("disconnect, but connection not exists") end @@ -86,8 +96,11 @@ end ---@param ... unknown # name ---@return unknown|nil function WSServerClient:request(method, ...) - if not self:is_connecting() then - utils.notify_error(string.format("RPC tcp client is disconnected, can't request [%s]", method)) + if self.server == nil then + utils.notify_error(string.format("RPC websocket server is stop, can't request [%s]", method)) + return + elseif self.single_connection == nil then + utils.notify_error(string.format("RPC websocket server is listening, but without client, can't request [%s]", method)) return end local msgid = self:gen_id() @@ -105,11 +118,7 @@ function WSServerClient:request(method, ...) else if method == "getVersion" then utils.notify_error( - string.format( - "jupyterlab extension is outdated, it is recommended to update with `pip install -U neopyter`", - method, - res - ) + string.format("jupyterlab extension is outdated, it is recommended to update with `pip install -U neopyter`", method, res) ) else utils.notify_error(string.format("RPC request [%s] failed, with error: %s", method, res)) @@ -121,33 +130,44 @@ end ---@param data string ---@package function WSServerClient:handle_response(data) - self.decoder:feed(data) - while true do - local msg = self.decoder:next() - if msg == nil then - break - end - -- logger.log(vim.inspect(msg)) - if #msg == 4 and msg[1] == 1 then - local msgid, error, result = msg[2], msg[3], msg[4] - local callback = self.request_pool[msgid] - self.request_pool[msgid] = nil - logger.log(string.format("msgid [%s] response acceptd", msgid)) - assert( - callback, - string.format("msg %s can't find callback: request_pool=%s", msgid, vim.inspect(self.request_pool)) + local status, msg = pcall(vim.mpack.decode, data) + assert(status, vim.inspect(msg) .. data) + if status == false then + logger.warn("parse mpack error, reset request pool") + self:reset_request() + return + end + if #msg == 4 and msg[1] == 1 then + local msgid, error, result = msg[2], msg[3], msg[4] + local callback = self.request_pool[msgid] + self.request_pool[msgid] = nil + logger.log(string.format("msgid [%s] response acceptd", msgid)) + assert( + callback, + string.format( + "msg %s can't find callback: request_pool=%s, msg=%s", + msgid, + vim.inspect(vim.tbl_keys(self.request_pool)), + vim.inspect(msg) ) - if error == vim.NIL then - callback(true, result) - else - callback(false, error) - end + ) + if error == vim.NIL then + callback(true, result) else - assert(false, "msgpack rpc response spec error, msg=" .. data) + callback(false, error) end + else + assert(false, "msgpack rpc response spec error, msg=" .. data) end end function WSServerClient:notify(event, ...) end +function WSServerClient:reset_request() + for _, fun in pairs(self.request_pool) do + fun(false, "reset") + end + self.request_pool = {} +end + return WSServerClient diff --git a/lua/neopyter/utils.lua b/lua/neopyter/utils.lua index 94aeef8..0b8caf5 100644 --- a/lua/neopyter/utils.lua +++ b/lua/neopyter/utils.lua @@ -75,7 +75,7 @@ function M.nvim_create_autocmd(event, opts) end, function() end) end end - vim.api.nvim_create_autocmd(event, opts) + a.api.nvim_create_autocmd(event, opts) end ---@class neopyter.ParseOption diff --git a/package.json b/package.json index db9cb85..05e312a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "neopyter", - "version": "0.2.0", + "version": "0.2.1", "description": "A JupyterLab extension. Integrate JupyterLab and Neovim", "workspaces": [ "ui-tests" diff --git a/scripts/minidoc.lua b/scripts/minidoc.lua index 9a160dc..167c042 100644 --- a/scripts/minidoc.lua +++ b/scripts/minidoc.lua @@ -4,8 +4,4 @@ if _G.MiniDoc == nil then minidoc.setup({}) end -minidoc.generate( - { "lua/neopyter.lua", "lua/neopyter/jupyter/jupyterlab.lua", "lua/neopyter/jupyter/notebook.lua" }, - nil, - nil -) +minidoc.generate({ "lua/neopyter.lua", "lua/neopyter/jupyter/jupyterlab.lua", "lua/neopyter/jupyter/notebook.lua" }, nil, nil) diff --git a/src/index.ts b/src/index.ts index 022f7d4..e25c818 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,6 +15,7 @@ import { StatusSidePanel } from './statusidepanel'; import { statusPageIcon } from './icons'; import { WindowedList } from '@jupyterlab/ui-components'; import { IConfig } from './settings'; +import { DockPanel, Widget } from '@lumino/widgets'; // Transfer Data type TCell = { @@ -61,13 +62,6 @@ const neopyterPlugin: JupyterFrontEndPlugin = { restorer.add(sidebar, '@neopyter/graphsidebar'); } - let currentNotebookPanel: NotebookPanel | null = nbtracker.currentWidget; - labShell.currentChanged.connect(() => { - if (labShell.currentWidget instanceof NotebookPanel) { - currentNotebookPanel = labShell.currentWidget; - } - }); - const getNotebookModel = (path?: string) => { let notebookPanel; if (path) { @@ -75,6 +69,7 @@ const neopyterPlugin: JupyterFrontEndPlugin = { } let notebook = notebookPanel?.content as Notebook | undefined; if (!notebook) { + const currentNotebookPanel = labShell.currentWidget as NotebookPanel; if (currentNotebookPanel?.isUntitled) { notebookPanel = currentNotebookPanel; notebook = notebookPanel.content; @@ -117,7 +112,7 @@ const neopyterPlugin: JupyterFrontEndPlugin = { }; const docmanagerDispatcher = { getCurrentNotebook: () => { - const notebookPanel = currentNotebookPanel; + const notebookPanel = labShell.currentWidget as NotebookPanel; if (notebookPanel) { const context = docmanager.contextForWidget(notebookPanel); return context?.localPath; @@ -149,8 +144,27 @@ const neopyterPlugin: JupyterFrontEndPlugin = { activateNotebook: (path: string) => { const { notebookPanel } = getNotebookModel(path); labShell.activateById(notebookPanel.id); - notebookPanel.node.focus(); - currentNotebookPanel = notebookPanel; + notebookPanel.activate(); + const emitEvent = (widget: Widget) => { + const event = new MouseEvent('click', { + bubbles: true, + cancelable: true, + view: window + }); + widget.node.click(); + widget.node.dispatchEvent(event); + }; + emitEvent(labShell); + // @ts-expect-error hack private property + const dockPanel: DockPanel = labShell._dockPanel; + // for (const tabBar of dockPanel.tabBars) { + // tabBar.titles.findIndex(title => { + // console.log(title.owner); + // return false; + // }); + // } + + emitEvent(notebookPanel); }, closeFile: async (path: string) => { return await docmanager.closeFile(path); diff --git a/src/statusidepanel.ts b/src/statusidepanel.ts index b88deff..5eb9a2b 100644 --- a/src/statusidepanel.ts +++ b/src/statusidepanel.ts @@ -11,28 +11,48 @@ export class StatusSidePanel extends SidePanel { this.watchSettings(); } async updatePanel() { - const settings = ServerConnection.makeSettings(); - const url = URLExt.join(settings.baseUrl, 'neopyter', 'get_server_info'); - const response = await fetch(url); - + const settings = (await this.settingRegistry.load('neopyter:labplugin')).composite as unknown as IConfig; this.content.node.textContent = ''; - if (!response.ok) { - const p = document.createElement('p'); - this.content.node.appendChild(p); - p.textContent = 'Access API failed'; - return; - } - const { code, message, data } = await response.json(); - if (code !== 0) { - const p = document.createElement('p'); - this.content.node.appendChild(p); - p.textContent = message; - return; + { + const h2 = document.createElement('h2'); + this.content.node.appendChild(h2); + h2.textContent = `Work mode: ${settings.mode}`; } - for (const addr of data.addrs) { - const p = document.createElement('p'); - this.content.node.appendChild(p); - p.textContent = addr; + if (settings.mode === 'proxy') { + const serverSettings = ServerConnection.makeSettings(); + const url = URLExt.join(serverSettings.baseUrl, 'neopyter', 'get_server_info'); + const response = await fetch(url); + + if (!response.ok) { + const p = document.createElement('p'); + this.content.node.appendChild(p); + p.textContent = 'Access API failed'; + return; + } + const { code, message, data } = await response.json(); + if (code !== 0) { + const p = document.createElement('p'); + this.content.node.appendChild(p); + p.textContent = message; + return; + } + for (const addr of data.addrs) { + const p = document.createElement('p'); + this.content.node.appendChild(p); + p.textContent = addr; + } + } else { + { + const p = document.createElement('p'); + this.content.node.appendChild(p); + p.textContent = `IP: ${settings.ip}`; + } + + { + const p = document.createElement('p'); + this.content.node.appendChild(p); + p.textContent = `port: ${settings.port}`; + } } } @@ -52,7 +72,7 @@ export class StatusSidePanel extends SidePanel { connectSettings ); console.log(await response.json()); - // this.updatePanel(); + this.updatePanel(); }; // Fetch the initial state of the settings. const settings = await this.settingRegistry.load('neopyter:labplugin'); diff --git a/src/transport/websocketTransport.ts b/src/transport/websocketTransport.ts index 73889ef..2775d29 100644 --- a/src/transport/websocketTransport.ts +++ b/src/transport/websocketTransport.ts @@ -54,9 +54,9 @@ export class WebsocketTransport extends BaseTransport { this.onRead(message); } } catch (e) { - // throw e; console.error(e); this.checkRetry(); + throw e; } } @@ -85,16 +85,21 @@ export class WebsocketTransport extends BaseTransport { } protected onClose(_event: Event) { console.log(`Disconnect to neopyter jupyter server by websocket ${this.websocket!.url}`); + this.websocket!.close(); this.websocket = undefined; this.readableStream = undefined; this.checkRetry(); } protected checkRetry() { - if (this.autoRetry) { - console.log('will reconnect websocket server after 1s'); + if (this.autoRetry && this.websocket === undefined) { + console.log('reconnect websocket server after 1s'); setTimeout(() => { - this.start(); + if (this.autoRetry && this.websocket === undefined) { + this.start(); + } else { + console.error('checkRetry repeat'); + } }, 1000); } }