{"id":13526949,"url":"https://github.com/SUSTech-data/neopyter","last_synced_at":"2025-04-01T08:30:44.208Z","repository":{"id":215874776,"uuid":"738017539","full_name":"SUSTech-data/neopyter","owner":"SUSTech-data","description":"The  bridge between Neovim and Jupyterlab","archived":false,"fork":false,"pushed_at":"2024-10-30T04:08:29.000Z","size":2343,"stargazers_count":82,"open_issues_count":5,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-30T07:16:25.366Z","etag":null,"topics":["jupyter","jupyterlab","jupyterlab-extension","neovim","neovim-plugin","nvim"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SUSTech-data.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-02T08:08:14.000Z","updated_at":"2024-10-30T04:08:33.000Z","dependencies_parsed_at":"2024-02-04T09:29:56.592Z","dependency_job_id":"ad32397b-c96a-46c8-a85c-824a2cb77b58","html_url":"https://github.com/SUSTech-data/neopyter","commit_stats":{"total_commits":60,"total_committers":3,"mean_commits":20.0,"dds":0.08333333333333337,"last_synced_commit":"de8f75422fd47613a4989f4a18c57e1ec442bad5"},"previous_names":["sustech-data/neopyter"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SUSTech-data%2Fneopyter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SUSTech-data%2Fneopyter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SUSTech-data%2Fneopyter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SUSTech-data%2Fneopyter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SUSTech-data","download_url":"https://codeload.github.com/SUSTech-data/neopyter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246607106,"owners_count":20804518,"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":["jupyter","jupyterlab","jupyterlab-extension","neovim","neovim-plugin","nvim"],"created_at":"2024-08-01T06:01:38.251Z","updated_at":"2025-04-01T08:30:44.200Z","avatar_url":"https://github.com/SUSTech-data.png","language":"Lua","readme":"# Introduction\n\nThe bridge between Neovim and Jupyter Lab, edit in Neovim and preview/run in Jupyter Lab.\n\n## How does it work?\n\nThis project includes two parts: a [`JupyterLab extension`](https://jupyterlab.readthedocs.io/en/stable/user/extensions.html) and a Neovim plugin\n\n- The `JupyterLab extension` exposes functions of `Jupyter lab`, and provides a remote procedure call(RPC) service\n- The `Neovim plugin` calls the RPC service when it receives events from `Neovim` via `autocmd`\n\nThis project provides two work modes for different network environments. If the browser where your jupyter lab is\nlocated cannot directly access nvim, you must use `proxy` mode; If you need to collaborate and use the same Jupyter with\nothers, you must use direct mode\n\n\u003ctable\u003e\n    \u003ctr\u003e\n        \u003cth\u003e\u003c/th\u003e\n        \u003cth\u003edirect\u003c/th\u003e\n        \u003cth\u003eproxy\u003c/th\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003cth\u003eArchitecture\u003c/th\u003e\n        \u003cth style=\"text-align:center\"\u003e\n            \u003cimg alt=\"direct mode\" src=\"./doc/communication_direct.png\"\u003e\n        \u003c/th\u003e\n        \u003cth\u003e\n            \u003cimg alt=\"proxy mode\" src=\"./doc/communication_proxy.png\"\u003e\n        \u003c/th\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003cth\u003eAdvantage\u003c/th\u003e\n        \u003cth style=\"text-align:left;font-weight:lighter\"\u003e\n            \u003cul\u003e\n                \u003cli\u003eLower communication costs\u003c/li\u003e\n                \u003cli\u003eShareable JupyterLab instance\u003c/li\u003e\n            \u003c/ul\u003e\n        \u003c/th\u003e\n        \u003cth style=\"text-align:left;font-weight:lighter\"\u003e\n            \u003cul\u003e\n                \u003cli\u003eLower Neovim load\u003c/li\u003e\n            \u003c/ul\u003e\n        \u003c/th\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003cth\u003eDisadvantage\u003c/th\u003e\n        \u003cth style=\"text-align:left;font-weight:lighter\"\u003e\n            \u003cul\u003e\n                \u003cli\u003eHigher Neovim load\u003c/li\u003e\n            \u003c/ul\u003e\n        \u003c/th\u003e\n        \u003cth style=\"text-align:left;font-weight:lighter\"\u003e\n            \u003cul\u003e\n                \u003cli\u003eExclusive JupyterLab instance\u003c/li\u003e\n            \u003c/ul\u003e\n        \u003c/th\u003e\n    \u003c/tr\u003e\n\u003c/table\u003e\n\n- `direct` mode: (default, recommended) In this mode, neovim is server and neovim plugin(neopyter) is listening to `remote_address`,\n  the browser where jupyter lab is located will connect to neovim\n\n- `proxy` mode: In this mode, Jupyter lab server(server side, the host you run `jupyter lab` to start JupyterLab) is server\n  and jupyter lab server extension(neopyter) is listening to ``{IP}:{Port}`, the neovim plugin(neopyter) will connect to ``{IP}:{Port}`\n\nUltimately, `Neopyter` can control `Juppyter lab`. `Neopyter` can implement abilities like [jupynium.nvim](https://github.com/kiyoon/jupynium.nvim).\n\n## Specifications\n\nPlease refer to [doc/specification.ipynb](doc/specification.ipynb) and [doc/specification.ju.py](doc/specification.ju.py)\n\n\u003c!-- panvimdoc-ignore-start --\u003e\n## Screenshots\n\n\u003ctable\u003e\n    \u003ctr\u003e\n        \u003cth\u003e\u003c/th\u003e\n        \u003cth\u003e[Completion](#blinkcmp)\u003c/th\u003e\n        \u003cth\u003eCell Magic\u003c/th\u003e\n        \u003cth\u003eLine Magic\u003c/th\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003cth\u003e\n        \u003c/th\u003e\n        \u003cth\u003e\n            \u003cimg alt=\"Completion\" width=\"100%\" src=\"./doc/complete_blink.png\"\u003e\n        \u003c/th\u003e\n        \u003cth\u003e\n            \u003cimg alt=\"Cell Magic\" src=\"./doc/cell_magic_js.png\"\u003e\n        \u003c/th\u003e\n        \u003cth\u003e\n            \u003cimg alt=\"Line Magic\" src=\"./doc/line_magic.png\"\u003e\n        \u003c/th\u003e\n    \u003c/tr\u003e\n\u003c/table\u003e\n\u003c!-- panvimdoc-ignore-end --\u003e\n\n# Requirements\n\n- 📔JupyterLab \u003e= 4.0.0\n- ✌️ Neovim latest\n  - 👍`nvim-lua/plenary.nvim`\n  - 🤏`AbaoFromCUG/websocket.nvim` (optional for `mode=\"direct\"`)\n\n# Installation\n\n`Neopyter` support two parts, so we need to install them separately.\n\n## JupyterLab Extension\n\nTo install the jupyterlab extension, execute:\n\n```bash\npip install neopyter\n```\n\nConfigure `JupyterLab` in side panel\n\u003cimg alt=\"Neopyter side panel\" width=\"50%\" src=\"./doc/sidepanel.png\"/\u003e\n\n- `mode`: Refer to the previous introduction about mode\n- `IP`: If `mode=proxy`, set to the IP of the host where jupyter server is located. If `proxy=direct`, set to the IP of the\n  host where neovim is located\n- `Port`: Idle port of the `IP`'s' host\n\n*NOTICE:* all settings is saved to localStorage\n\n## Neovim Plugin\n\n- With 💤lazy.nvim:\n\n```lua\n{\n    \"SUSTech-data/neopyter\",\n    dependencies = {\n      'nvim-lua/plenary.nvim',\n      'nvim-treesitter/nvim-treesitter', -- neopyter don't depend on `nvim-treesitter`, but does depend on treesitter parser of python\n      'AbaoFromCUG/websocket.nvim',  -- for mode='direct'\n    },\n\n    ---@type neopyter.Option\n    opts = {\n        mode=\"direct\",\n        remote_address = \"127.0.0.1:9001\",\n        file_pattern = { \"*.ju.*\" },\n        on_attach = function(bufnr)\n            -- do some buffer keymap\n        end,\n    },\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eDefault configuration\u003c/strong\u003e\u003c/summary\u003e\n\n\u003c!-- doc-inject:default-config --\u003e\n```lua\n---@type neopyter.Option\nlocal default_config = {\n    remote_address = \"127.0.0.1:9001\",\n    file_pattern = { \"*.ju.*\" },\n    filename_mapper = function(ju_path)\n        local ipynb_path = vim.fn.fnamemodify(ju_path, \":r:r:r\") .. \".ipynb\"\n        return ipynb_path\n    end,\n    --- auto attach to buffer\n    auto_attach = true,\n    --- auto connect with remote jupyterlab\n    auto_connect = true,\n    mode = \"direct\",\n    ---@type neopyter.JupyterOption  # ref `:h neopyter.JupyterOption`\n    jupyter = {\n        auto_activate_file = true,\n        partial_sync = false,\n        -- Always scroll to the current cell.\n        scroll = {\n            enable = true,\n            align = \"center\",\n        },\n    },\n\n    ---@type neopyter.HighlightOption  # ref `:h neopyter.HighlightOption`\n    highlight = {\n        enable = true,\n        mode = \"separator\",\n    },\n    ---@type neopyter.TextObjectOption  # ref `:h neopyter.TextObjectOption`\n    textobject = {\n        enable = true,\n        -- more capture, poorer performance\n        queries = { \"cellseparator\", \"cellcontent\", \"cell\" },\n    },\n    ---@type neopyter.InjectionOption  # ref `:h neopyter.InjectionOption`\n    injection = {\n        enable = true,\n    },\n    ---@type neopyter.ParserOption  # ref `:h neopyter.ParserOption`\n    parser = {\n        trim_whitespace = false,\n        python = {},\n    },\n}\n```\n\n\u003c/details\u003e\n\nSee `:h neopyter-configuration-types` for all option type description.\n\nSuggest keymaps(`neopyter` don't provide default keymap):\n\n```lua\non_attach = function(buf)\n    local function map(mode, lhs, rhs, desc)\n        vim.keymap.set(mode, lhs, rhs, { desc = desc, buffer = buf })\n    end\n    -- same, recommend the former\n    map(\"n\", \"\u003cC-Enter\u003e\", \"\u003ccmd\u003eNeopyter execute notebook:run-cell\u003ccr\u003e\", \"run selected\")\n    -- map(\"n\", \"\u003cC-Enter\u003e\", \"\u003ccmd\u003eNeopyter run current\u003ccr\u003e\", \"run selected\")\n\n    -- same, recommend the former\n    map(\"n\", \"\u003cspace\u003eX\", \"\u003ccmd\u003eNeopyter execute notebook:run-all-above\u003ccr\u003e\", \"run all above cell\")\n    -- map(\"n\", \"\u003cspace\u003eX\", \"\u003ccmd\u003eNeopyter run allAbove\u003ccr\u003e\", \"run all above cell\")\n\n    -- same, recommend the former, but the latter is silent\n    map(\"n\", \"\u003cspace\u003ent\", \"\u003ccmd\u003eNeopyter execute kernelmenu:restart\u003ccr\u003e\", \"restart kernel\")\n    -- map(\"n\", \"\u003cspace\u003ent\", \"\u003ccmd\u003eNeopyter kernel restart\u003ccr\u003e\", \"restart kernel\")\n\n    map(\"n\", \"\u003cS-Enter\u003e\", \"\u003ccmd\u003eNeopyter execute runmenu:run\u003ccr\u003e\", \"run selected and select next\")\n    map(\"n\", \"\u003cM-Enter\u003e\", \"\u003ccmd\u003eNeopyter execute run-cell-and-insert-below\u003ccr\u003e\", \"run selected and insert below\")\n\n    map(\"n\", \"\u003cF5\u003e\", \"\u003ccmd\u003eNeopyter execute notebook:restart-run-all\u003ccr\u003e\", \"restart kernel and run all\")\nend\n```\n\n# Usage\n\n- Open JupyterLab `jupyter lab`, there is a sidebar named `Neopyter`, which display neopyter ip+port\n- Open a `*.ju.py` file in neovim\n- Now you can type `# %%` in Neovim to create a code cell.\n  - You'll see everything you type below that will be synchronised in the browser\n\n# Available Vim Commands\n\n- Status\n  - `:Neopyter status` alias to `:checkhealth neopyter` currently\n- Server\n  - `:Neopyter connect [remote 'ip:port']`, e.g. `:Neopyter command 127.0.0.1:9001`, connect `Jupyter lab` manually\n  - `:Neopyter disconnect`\n- Sync\n  - `:Neopyter sync current`, make sync current `*.ju.*` file with the currently open `*.ipynb`\n  - `:Neopyter sync [filename]`, e.g. `:Neopyter sync main.ipynb`\n- Run\n  - `:Neopyter run current`, same as `Run`\u003e`Run Selected Cell and Do not Advance` menu in `Jupyter lab`\n  - `:Neopyter run allAbove`, same as `Run`\u003e`Run All Above Selected Cell` menu in `Jupyter lab`\n  - `:Neopyter run allBelow`, same as `Run`\u003e`Run Selected Cell and All Below` menu in `Jupyter lab`\n  - `:Neopyter run all`, same as `Run`\u003e`Run All Cells` menu in `Jupyter lab`\n- Kernel\n  - `:Neopyter kernel restart`, same as `Kernel`\u003e`Restart Kernel` menu in `Jupyter lab`\n  - `:Neopyter kernel restartRunAll`, same as `Kernel`\u003e`Restart Kernel and Run All Cells` menu in `Jupyter lab`\n- Jupyter\n  - `:Neopyter execute [command_id] [args]`, execute `Jupyter lab`'s\n    [command](https://jupyterlab.readthedocs.io/en/stable/user/commands.html#commands-list)\n    directly, e.g. `:Neopyter execute notebook:export-to-format {\"format\":\"html\"}`\n\n# Integration\n\n## neoconf.nvim\n\nIf [neoconf.nvim](https://github.com/SUSTech-data/neopyter) is available, `neopyter` will automatically register/read `neoconf` settings\n\n[`.neoconf.json`](./.neoconf.json)\n\n```json\n{\n  \"neopyter\": {\n    \"mode\": \"proxy\",\n    \"remote_address\": \"127.0.0.1:9001\"\n  }\n}\n```\n\n## nvim-cmp\n\n- `nvim-cmp`\n- `lspkind.nvim`\n\n```lua\n\nlocal lspkind = require(\"lspkind\")\nlocal cmp = require(\"cmp\")\n\ncmp.setup({\n    sources = cmp.config.sources({\n        -- default: all source, maybe some noice\n        { name = \"neopyter\" },\n\n        -- { name = \"neopyter\", option={ source = { \"CompletionProvider:kernel\" } } },\n    }),\n    formatting = {\n        format = lspkind.cmp_format({\n            mode = \"symbol_text\",\n            menu = {\n                buffer = \"[Buf]\",\n                nvim_lsp = \"[LSP]\",\n                nvim_lua = \"[Lua]\",\n                neopyter = \"[Neopyter]\",\n            },\n            symbol_map = {\n                -- specific complete item kind icon\n                [\"Magic\"] = \"🪄\",\n                [\"Path\"] = \"📁\",\n                [\"Dict key\"] = \"🔑\",\n                [\"Instance\"] = \"󱃻\",\n                [\"Statement\"] = \"󱇯\",\n            },\n        }),\n    },\n)}\n\n    -- menu item highlight\nvim.api.nvim_set_hl(0, \"CmpItemKindMagic\", { bg = \"NONE\", fg = \"#D4D434\" })\nvim.api.nvim_set_hl(0, \"CmpItemKindPath\", { link = \"CmpItemKindFolder\" })\nvim.api.nvim_set_hl(0, \"CmpItemKindDictkey\", { link = \"CmpItemKindKeyword\" })\nvim.api.nvim_set_hl(0, \"CmpItemKindInstance\", { link = \"CmpItemKindVariable\" })\nvim.api.nvim_set_hl(0, \"CmpItemKindStatement\", { link = \"CmpItemKindVariable\" })\n\n```\n\nMore information, see [nvim-cmp wiki](https://github.com/hrsh7th/nvim-cmp/wiki/Menu-Appearance)\n\n## blink.cmp\n\n- `blink.cmp`\n\n```lua\nrequire(\"blink-cmp\").setup({\n    sources = {\n        default = {\n            \"neopyter\",\n        },\n        providers = {\n            neopyter = {\n                name = \"Neopyter\",\n                module = \"neopyter.blink\",\n                ---@type neopyter.CompleterOption\n                opts = {},\n            },\n        },\n    },\n})\n\n```\n\n## textobjects\n\nNeopyter load `textobjects.scm` dynamic according `config.textobject.queries`:\n\n```lua\n{\n    \"SUSTech-data/neopyter\",\n    ---@type neopyter.Option\n    opts = {\n        textobject = {\n            enable = true,\n            queries = {\n                \"linemagic\",\n                \"cellseparator\",\n                \"cellcontent\",\n                \"cell\"\n            },\n        },\n    },\n}\n```\n\nThe more queries you added, the poorer performance to capture, so only add what you need.\nThen you can config you `nvim-treesitter-textobjects` as usually:\n\n```lua\nrequire'nvim-treesitter.configs'.setup {\n    textobjects = {\n        select = {\n            enable = true,\n            lookahead = true,\n            keymaps = {\n                [\"aj\"] = { query = \"@cell\", desc = \"Select cell\" },\n                [\"ij\"] = { query = \"@cellcontent\", desc = \"Select cell content\" },\n            },\n        },\n        move = {\n            enable = true,\n            goto_next_start = {\n                [\"]j\"] = \"@cellseparator\",\n            },\n            goto_previous_start = {\n                [\"[j\"] = \"@cellseparator\",\n            },\n        },\n    },\n}\n\n```\n\nSupported queries:\n\n- `@linemagic`\n- `@cellseparator`\n  - `@cellseparator.code`\n  - `@cellseparator.markdown`\n  - `@cellseparator.raw`\n- `@cellcontent`\n- `@cell`\n\n# API\n\n`Neopyter` provides rich lua APIs, you could use below code as initialization:\n\n```lua\n\n-- Reference to `:h neopyter-jupyterlab-api` for all api document\nlocal current_lab = require(\"neopyter.jupyter\").jupyterlab\ncurrent_lab:execute_command(\"notebook:export-to-format\", {format=\"html\"})\n\n-- Reference to `:h neopyter-notebook-api` for all api document\nlocal current_notebook = require(\"neopyter.jupyter\").notebook\n\ncurrent_notebook:run_selected_cell()\ncurrent_notebook:run_all_above()\ncurrent_notebook:run_all_below()\n\n```\n\n- Notebook API: `:h neopyter-notebook-api`\n- JupyterLab API`:h neopyter-jupyterlab-api-api`\n\n## Async\n\n`Notebook` and `JupyterLab` APIs are wrapped by async context automatically.\n\n- If you call api from async context, anything is OK. Otherwise, the calling order cannot be guaranteed\n- A single API call always works\n\n```lua\nvim.defer_fn(function()\n    -- non-async context, API response may be unordered\n    current_notebook:run_selected_cell()\n    current_notebook:run_all_above()\n    current_notebook:run_all_below()\nend, 0)\n\nrequire(\"neopyter.async\").run(function()\n    -- async context, so which will call and return in order\n    current_notebook:run_selected_cell()\n    current_notebook:run_all_above()\n    current_notebook:run_all_below()\nend)\n\n```\n\n# Features\n\n- Neovim\n  - [x] Full sync\n  - [x] Partial sync\n  - [x] Scroll view automatically\n  - [x] Activate cell automatically\n  - [x] Save notebook automatically\n  - Completion\n    - [x] Magic completion item\n    - [x] Path completion item\n  - Tree-sitter\n    - [x] Highlight\n      - Separator+non-code\n      - Shortsighted\n    - [x] Textobjects\n    - [ ] Fold\n  - Kernel management\n    - [x] Restart kernel\n    - [x] Restart kernel and run all\n  - Run cell\n    - [x] Run selected cell\n    - [x] Run all above selected cell\n    - [x] Run selected cell and all below\n    - [x] Run all cell\n  - Sync\n    - [x] Set synchronized `.ipynb` manually\n  - Notebook manager\n    - [x] Open corresponding notebook if exists\n    - [x] Sync with untitled notebook default\n    - [ ] Close notebook when buffer unload\n- Jupyter Lab\n  - Settings\n    - [x] TCP server host/port settings\n  - Status [Sidebar](https://jupyterlab.readthedocs.io/en/stable/user/interface.html#left-and-right-sidebar)\n    - [x] Settings `ip:port`\n    - [ ] Display client info\n- Performance\n  - [x] Rewrite `RpcClient`, support async RPC request\n        `vim.rpcrequest` and `vim.rpcnotify`\n  - [x] Rewrite `highlights` and `textobjects` queries\n  - [x] Rewrite parser with tree-sitter\n  - [x] Unified `highlights`, `textobjects`, `parser` to unified parser\n- Document\n  - [x] API Document\n\n# Acknowledges\n\n- [jupynium.nvim](https://github.com/kiyoon/jupynium.nvim): Selenium-automated Jupyter Notebook that is synchronised with Neovim in real-time.\n- [snacks.nvim](https://github.com/folke/snacks.nvim): The `zen` highlight is inspired by `snacks.zen`\n\n\u003c!-- panvimdoc-include-comment\n\n# Configuration Types\nThis will be replaced by gen_doc.lua\n\n--\u003e\n","funding_links":[],"categories":["Lua","Utility","Live Preview"],"sub_categories":["Indent"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSUSTech-data%2Fneopyter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FSUSTech-data%2Fneopyter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSUSTech-data%2Fneopyter/lists"}