Metadata-Version: 2.4
Name: parsing-lsp
Version: 1.0.0
Summary: libcst-powered Python refactors (qualify imports, split triple-quoted strings) as a CLI and an LSP server.
Requires-Python: >=3.13
Requires-Dist: libcst>=1.8.6
Requires-Dist: pygls>=1.3
Requires-Dist: pygments>=2.18
Requires-Dist: typer>=0.25.1
Description-Content-Type: text/markdown

# parsing

A small CLI of [libcst](https://github.com/Instagram/LibCST)-powered Python source transforms.

## Install

```sh
uv sync
```

Or as a tool:

```sh
uv tool install .
```

## Commands

### `qualify`

Rewrites `from X import a, b` into `import X` and qualifies the resulting references.

```sh
parsing qualify path/to/file.py -m os.path=op
parsing qualify path/to/file.py --all --in-place
parsing qualify path/to/file.py --interactive   # pick imports via fzf
```

| Flag | Description |
| --- | --- |
| `-m, --module name[=alias]` | Module to qualify; repeatable. |
| `-a, --all` | Collapse every `from X import a, b, ...` with 2+ names. |
| `-i, --in-place` | Write changes back to the file. |
| `-I, --interactive` | Pick imports via [fzf](https://github.com/junegunn/fzf), then prompt for a nickname per selection. |

### `split-strings`

Splits standalone single-line triple-quoted string statements (docstrings, block-comment-style strings) onto multiple lines. Only standalone string-expression statements are rewritten — `x = """foo"""` is left alone, since splitting it would change the string value.

```sh
parsing split-strings path/to/file.py --in-place
```

| Flag | Description |
| --- | --- |
| `-i, --in-place` | Write changes back to the file. |

## Neovim integration

Drop the snippet below into your Neovim config (e.g. `~/.config/nvim/lua/parsing.lua`, then `require("parsing")` from `init.lua`). It binds `<leader>p` to run `parsing qualify -Ii` on the current buffer inside a floating terminal so fzf and the nickname prompts work normally. The buffer is reloaded from disk when the command exits.

```lua
local M = {}

function M.run()
  local file = vim.api.nvim_buf_get_name(0)
  if file == "" or vim.bo.buftype ~= "" then
    vim.notify("parsing: current buffer is not a file", vim.log.levels.WARN)
    return
  end

  vim.cmd("silent! write")

  local ui = vim.api.nvim_list_uis()[1]
  local width = math.floor(ui.width * 0.8)
  local height = math.floor(ui.height * 0.8)

  local buf = vim.api.nvim_create_buf(false, true)
  local win = vim.api.nvim_open_win(buf, true, {
    relative = "editor",
    width = width,
    height = height,
    col = math.floor((ui.width - width) / 2),
    row = math.floor((ui.height - height) / 2),
    style = "minimal",
    border = "rounded",
    title = " parsing qualify -Ii ",
    title_pos = "center",
  })

  vim.fn.jobstart({ "parsing", "qualify", "-Ii", file }, {
    term = true,
    on_exit = function(_, code)
      if vim.api.nvim_win_is_valid(win) then
        vim.api.nvim_win_close(win, true)
      end
      if code == 0 then
        vim.cmd("checktime")
      else
        vim.notify("parsing exited with code " .. code, vim.log.levels.WARN)
      end
    end,
  })

  vim.cmd("startinsert")
end

vim.keymap.set("n", "<leader>p", M.run, { desc = "parsing: interactive qualify imports" })

return M
```

Requires `parsing` and `fzf` on `PATH`, and Neovim 0.10+ for `jobstart({ term = true })` (on older versions swap that call for `vim.fn.termopen`).
